2013年09月30日

www.myhost.com というドメインでWebサイトを運営しており、/article の下に typepad で運営している blog.myhost.com にマッピングしたいという場合の Haproxy の設定方法を調べるのに手こずったのでメモ。

リダイレクトなら簡単なのだが、ブラウザのURLを http://www.myhost.com/article/~ にしたまま、透過プロキシのようにして他サーバーのリソースを表示させる方法となる。

作った /etc/haproxy.cfg は下記のような内容(option はどれが必須かはあまり自信なし)
global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
maxconn 4096
chroot /usr/share/haproxy
user haproxy
group haproxy
daemon

defaults
log global
retries 3
maxconn 2000
contimeout 15000
clitimeout 50000
srvtimeout 50000

#---------------------------------------------------------------------
frontend main
bind *:8000  #ここは自分の環境に合わせて
mode http
option forwardfor
option dontlognull
option httplog
option httpclose
acl acl-article path_dir article # パスが /artice だったら、という条件指定
acl acl-article path_dir blog # パスが /blog だったら、という条件指定
acl acl-pc hdr(host) -i www.myhost.com # ホスト名が www.myhost.com だったらという指定

use_backend article if acl-article acl-pc #暗黙のAND条件でbackendの指定
use_backend article if acl-article or acl-pc acl-blog #AND と ORの併用。(acl-article) or (acl-pc AND acl-blog)
use_backend pc-front if acl-pc

backend pc-front
balance roundrobin
mode http
option redispatch
option httpchk GET /alivecheck.html HTTP/1.0
cookie SERVERID insert nocache indirect
server web1 frontend-web1:8000 cookie web1 check inter 5000
server web2 frontend-web2:8000 cookie web2 check inter 5000

backend article
mode http
reqirep ^Host:\ ([^:]*?) Host:\ blog.myhost.com #ホスト名の書き換え
reqirep ([^\ ]*)\ /article/(.*) \1\ /\2 #パスの書き換え(ホスト名だけの書き換えでOKなら不要)
server blog blog.myhost.com:80
これで、例えば http://www.myhost.com/article/top.html にアクセスすると、http://blog.myhost.com/top.html の内容が表示される。

設定の要点は下記の通り。
  1. 各aclにはルールを一つだけ設定する

  2. backend_指定時に複数のaclを指定する場合

    1. 複数条件をスペース区切りで書くだけで暗黙的なAND宣言となる

    2. 複数条件を OR でつなげると OR 宣言となる

    3. 条件1 OR 条件2 条件3 と書くと 条件1 OR (条件2 AND 条件3) の意味になる

    4. backendの指定は先勝ちなので、条件がかぶる場合は複数条件を先に書く

  3. reqirep はホストを行ごと書き換えるイメージ

  4. reqirep でHTTPヘッダー中のホスト名を書き換える必要がある

  5. reqirep対象のHTTPヘッダー中のスペースはエスケープする必要がある

  6. server はホスト名とともにポート番号も指定する必要がある


maru_tak at 19:54|PermalinkComments(0)TrackBack(0)

2013年06月17日

久しぶりにautoitを使おうとしたところ、すっかり使い方を忘れていたのでメモ。
Windows7 + Ruby2.0の環境でも動いた。


■初期化
require 'win32ole'
ai = WIN32OLE.new('AutoItX3.Control')

■ウィンドウ名の指定
名前の部分に下記の表記を使うこともできる
特殊タグ [LAST] [ACTIVE]  (参考

■アプリ名を指定して実行
ai.Run("calc.exe")

■ Windowをアクティブにする
ai.winActivate("(ウインドウ名)","(含まれるテキスト名)",(wait秒数))
#例
ai.winActivate("検索結果","見つかりました")

■ Windowが開くのを待つ
ai.winWaitActive("(ウインドウ名)","(含まれるテキスト名)", (wait秒数))
#例
ai.winWaitActive("検索結果","見つかりました",10)

■ テキストボックスに文字を記入する
ai.ControlSetText("(ウインドウ名)","(含まれるテキスト名)","[NAME:txtCondSeihinmei]", "(記入文字列)")
ai.ControlSetText("詳細検索","","[NAME:txtCondSeihinmei]", "ミネラルウォーター1000ml")

■ ボタンを押す
ai.ControlClick("(ウインドウ名)","(含まれるテキスト名)", "[NAME:(コンポーネント名)]", "(マウスボタン)")
#例
ai.ControlClick("検索結果", "", "[NAME:btnDisplayDetail]", "left")

■ Windowのタイトルを取得する
取得されるタイトルの文字コードはShift-JISなので、判定する際には注意
#windowハンドルを使う場合
ai.winGetTitle("[????:(ウィンドウハンドルID)]")
#クラスとインスタンス番号を使う場合
ai.winGetTitle("[CLASS:(クラス名); INSTANCE:(インスタンス番号)]")
#例
ai.winGetTitle("[CLASS:WindowsForms10.Window.8.app.0.378734a; INSTANCE:2]")

■ キータイプ
ai.Send("(キーの文字)")
#例
ai.Send("{TAB}")
ai.Send("{ENTER}")
ai.Send("^c") #ctrl + c

■ クリップボード操作
ai.ClipPut("(文字列)") #クリップボードに記録
ai.ClipGet() #クリップボードの内容取得
ai.Send("^v") #ペースト

■ Windowを閉じる
ai.winClose("(ウインドウ名)","(含まれるテキスト名)")
#例
ai.winClose("検索結果")

■ Windowの存在確認
ai.winExists("(ウインドウ名)","(含まれるテキスト名)") == 1
#例
ai.winExists("検索結果", "") == 1



maru_tak at 10:43|PermalinkComments(0)TrackBack(0)

2013年04月18日

私のWindows PCでは Ruby2.0に上げた際にほとんどのgemはすんなりインストールできていた。nokogiri を除いては・・・。よく使う gem なので、これは痛い。

インストール時に libxml2やlibxsltが無い!と怒られるという状態だったので、下記の情報で解決かと思ったが甘かった。私のPCにはMinGWは入っておらず、libxml2 と libxslt をコンパイルするという方法はとれなかった。
WindowsのRuby 2.0でNokogiriを使う

そんな私の解決方法は、全然本質的ではないのだが、「aws-sdkのgemを入れること」。

なぜこれだとうまくいくのかはさっぱり分からない。

が、aws-sdk 1.8.5 を入れたら、依存 gem の中に nokogiri が入っており、すんなり nokogiri 1.5.9 x86-mingw32 がインストールされた。全く偶然の発見だったが、これでよしとする。

maru_tak at 20:20|PermalinkComments(0)TrackBack(0)

2013年01月22日

コードの任意の場所に binding.pry と書いておくと、そこで pry が開くので便利、という記事を見て試してみたくなった。

RubyistならデバッグにはPryのbinding.pryがおすすめ - (゚∀゚)o彡 sasata299's blog

まずは gem のインストール。
gem install pry pry-doc
ターミナルで pry と打つとちゃんと起動する。次に簡単なコードでbinding.pryを初体験してみることにする。
a = "hoge"
binding.pry
puts a
pryが開いたらaの変数の中身をのぞいてみるんだ!と思い実行すると・・・
NoMethodError: undefined method ‘pry’ for #
え?こんな簡単なコードで一体どこで間違うというのか???

しばし途方にくれたが、require 'pry' していないのが原因だった orz

あと、binding.pry は textmate の command+r や sublime text 2 の command +b での実行では(当然かもしれませんが)うまく動きません。ターミナルから実行して使うもののようです。

maru_tak at 23:23|PermalinkComments(0)TrackBack(0)

2012年09月09日

システム設定ファイルをいじる時くらいしか使わないので、最初からそうだったのか不明なのだけれど、vim のコマンドモードに戻る時に esc キーが効かないことに気づいた。

調べても同様の現象の情報はほとんどなく、唯一見つけた記事でも有効な回答がついていない状態なので、私の環境固有の問題なのかもしれない。
On OS X Lion, how can I get the escape key to work?

上記記事の回答中で Voice Recognition がどうのこうのと書いている人がおり(スピーチ⇢音声認識⇢リスンキーに esc が割り当てられている)、Better Touch Tool がアクセシビリティAPIを使っているようなので(ユニバーサルアクセス⇢補助装置にアクセスできるようにする、のチェックを外すと警告が出てくる)、そのあたりが何か関係しているのかもしれないが、結局分からず。

幸い、他の入力方法で逃げられるようなので、これで我慢するしかなさそうだ。
ctrl + [
ctrl + esc
alt + esc


maru_tak at 14:48|PermalinkComments(0)TrackBack(0)
スクリプト言語を学ぶ際に書いたコードをすぐに実行できるようにしておくと便利だ。Sublime text 2 では Ruby や Python は Command + B で実行できるが、PHPでもこれをしたいので方法を調べた。

メニューの Tools ⇢ Build System ⇢ New Build System を選択すると設定ファイルが開くので下記のようにした。
{
"path": "/usr/local/bin:$PATH",
"cmd": ["php", "-f", "$file"],
"selector": "source.php",
"encoding": "cp932"
}
  • path:自分のphpコマンドの場所

  • cmd:実行するコマンドとパラメーターを配列形式で記述する(利用可能な変数はこちら sublime text help build system

  • selector:Build System | Automatic を使用するときのための設定らしい。ソース上で alt + command + P で調べることができる。

  • encoding: ここでsp932を指定しておかないとUTF-8以外のサイトをfile_get_contentsで読み込んだ際に[Decode error - output not utf-8][Finished]というエラーが発生した(参考記事)。
これをPHP.sublime-build という名前で保存。保存先は私の環境では ~/Library/Application Support/Sublime Text 2/Packages/User 以下だった。
<?php echo "aaa" ?>
これでphpを書いたファイルを保存、command + B で実行できるようになった。

maru_tak at 01:04|PermalinkComments(0)TrackBack(0)

2012年05月16日

PCを新調した際に以前作業していたHeroku用のgitローカルレポジトリがなくなってしまった。

スクリプト本体だけはバックアップしてあったものの、gitでローカルレポジトリを作って、そこで既存アプリ名で heroku create しようとすると、当然ながら既に取得済のアプリ名であると怒られる。heroku には git 経由でないとアップできないし、かと言ってローカルレポジトリを作れないとなるとどうしたら良いのか?

そんな場合は本番から clone すれば良いらしい。
git clone git@heroku.com:(アプリ名).git -o heroku
秘訣は -o heroku のオプションをつけること。これをやらないと、リモートレポジトリの名前が heroku にならず、git push heroku master する時にエラーになってしまう。

(参考)How can I clone an existing Heroku app?

maru_tak at 00:43|PermalinkComments(1)TrackBack(0)

2012年04月28日

シェルでは日本語を貼り付けてもキー入力しても化けないのに irb のコンソールに日本語を貼り付けようとしたら文字化けした(コンソールの設定の「非ASCI文字入力はエスケープ」はONの状態)。

貼りつけた文字
puts "日本語で書くよ"
貼りつけた結果
1.9.3p125 :001 > puts "\U+FFE6\U+FF97\U+FFA5\U+FFE6\U+FF9C\U+
FFAC\U+FFE8\U+FFAA\U+FF9E\U+FFE3\U+FF81\U+FFA7\U+FFE6\U+
FF9B\U+FFB8\U+FFE3\U+FF81\U+FF8F\U+FFE3\U+FF82\U+FF88"

=> nil
調べてみたところ情報が新旧いろいろあるようで、Lionの場合はどうするのが良いのかがよく分からなかった・・・。

結局は下記の2点で無事日本語が化けずに入力・表示できるようになった。
・ irb の起動時に --noreadline というオプションをつける
・コンソールの設定の「非ASCI文字入力はエスケープ」をOFFにする
起動オプションについては、毎回指定するのは面倒なので ~/.bash_profile に
alias irb='irb --noreadline'
を追加しておいた。

<追記>
が、そのうち今度は突然矢印キーの入力で、Tabキーでの補完や矢印キーでの履歴参照が効かなくなってしまい、下記のリンク先と同じ状態になってしまった。
irbでの矢印キー、tabキーが使えません
readlineがないことが原因と書かれていたので、--no-readline 指定がまずかったのかと思い、./bash_profile を元に戻したところ現象は解消した。その状態でも元々の問題だった日本語の化けは発生せず・・・。前回の変更後、一回だけシステムアップデートがあったけれどもそれが原因なのか?よく分からないまま。。。

maru_tak at 10:00|PermalinkComments(0)TrackBack(0)
自分以外の人が使うちょっとした業務ツールをRubyで作る場合、実行環境をどう用意するかというのは悩ましい。

そのツールを使う以外にはRubyには縁もゆかりもない人のPCにRuby入れて gem 入れてというのは明らかに無駄が多い。さらに、こういったインストールには会社だといちいち管理者権限が必要だったりして手間がかかるので、「部署全員が使えるようにしたい」なんて言われた日には・・・。また、後日ちょっと機能追加をしようとしても、その頃に自分のRubyの開発環境のバージョンが変わっていたり、使用している gem がバージョンアップしていたりすると大変。

ということで、Rubyスクリプトを exe 化して、それをポンと渡せると非常に便利だ。

こういった用途ではいままで Exerbを便利に使っていた。ただ、使っている gem によっては実行時にモジュールの参照に失敗したりするので「exe化がうまくいったら儲けもの」という感じだったのも事実。加えて、自分がRuby1.9を使うようになったので、Ruby1.9対応が特に明記されていないあたりことも不安材料となっていた。

そんな中、同様のツールで Ocra というものがあることを知った。2009年くらいから世に出ているようでそれなりの長さの実績があるし、Ruby1.9対応もしっかり謳われている。

インストールは
gem install ocra
だけ。これで自作のスクリプトを引数で指定して
ocra my_script.rb
のように実行するとmy_script.exe が出来上がる。

手順が2ステップになることが多かったExerbと比べて、1ステップで済むという簡単さがうれしい。




maru_tak at 09:45|PermalinkComments(0)TrackBack(0)

2012年04月27日

Macのターミナルで何回もコマンドを叩いたり、実行結果を出力したりするとカーソルがどんどん下の行に移っていき、しまいには画面一番下で作業することになってしまう。

シェルを操作しているときは clear コマンドを実行してカーソルを最上段の行に戻したりしていたのだけど、irb では同様のことができないと思っていた。

が、Macの場合(Lionでしか確認していないけど)、 Command + k で、シェル、irb、pry などで画面をクリアできることを知った!これは地味に便利だ。

maru_tak at 15:00|PermalinkComments(1)TrackBack(0)

2012年04月26日

正規表現の先読み・後読みについて調べていたときに下記のエントリーに辿り着いた。

なんとなく分かった気がしたのだけど、後者の記事の中の下記の部分の理解でつまづいた。
/(?=.Red|Blue)Color/ は「RedColor」や「BlueColor」にマッチするわけではありません 
Redの前のドットをなくせばマッチするように思ったのだが、実行すると書いてあるとおりマッチしなかった。

それまでの理解ではなぜそうなるのか分からなかったので、先読みに関して理解を整理してみることにした。

先読み条件が抽出したい部分の後ろにある場合=先読み
これはわかりやすい。押さえておくべきポイントは「先読みでマッチした部分は抽出結果には含まれない」ということくらい。

住所中に「市」を含むデータだけから県名を取り出したいような場合を想定する(住所の詳細部分がかなりテキトーなのは容赦)。
p "埼玉県大宮市1-1-1".scan(/(.+)(都|道|府|県)(?=.+市)/)
# => [["埼玉", "県"]]
p "東京都渋谷区1-1-1".scan(/(.+)(都|道|府|県)(?=.+市)/)
# => []
p "東京都八王子市1-1-1".scan(/(.+)(都|道|府|県)(?=.+市)/)
# => [["東京", "都"]]
上記の結果のいずれも意外感はない。

先読み条件が抽出したい部分の前に来る場合=後読み
直感と動きが違うと感じたのはこちらのパターン。「先行正規表現と後続正規表現の両方にマッチするものにマッチする」とだけ意識していると、少々意外な結果となる。

新潟市の中央区だけを対象として区名以降を取り出したいような場合を想定する。
p "新潟県新潟市中央区1-1-1".scan(/(?=新潟市)中央区.+\z/)
# => []
p "新潟県新潟市中央区1-1-1".scan(/(?=\A.+新潟市.+\z)中央区.+/)
# => []
p "新潟県新潟市中央区1-1-1".scan(/(?=\A.+新潟市.+\z)(新潟県.+)(中央区.+)\z/)
# => [["新潟県新潟市", "中央区1-1-1"]]
まず、一番目のパターンがマッチ無しになるのが想定と違う。先読み部分と後続の正規表現の両方に合致しているはずなのに、なぜだ?

おそらくマッチ対象の範囲が自分の想定と違っているのだと予想。そこで上記の3パターンの正規表現にを試してみた。

■ 1個目のパターン
自分のイメージでは「後続の正規表現でチェックする対象は先行の正規表現がチェックした範囲と同一である」と思っていた。
p "新潟県新潟市中央区1-1-1".scan(/(?=新潟市)中央区.+\z/)
この動きを普通にRubyのIF分岐で書くとしたらこんなイメージ。
str = "新潟県新潟市中央区1-1-1"
if str =~ /新潟市/ && str =~ /中央区.+\z/
$&
end
この通りになるとしたら
"新潟県新潟市中央区1-1-1" =~ /新潟市/
"新潟県新潟市中央区1-1-1" =~ /中央区.+\z/
ともにマッチするので、中央区以降の部分が抽出されるはず・・・。が、実際にはそうならないので、このイメージは間違っているようだ

■ 2個目のパターン
先行の正規表現に合致した範囲のみが後続正規表現に渡されるというのはどうだろう?
p "新潟県新潟市中央区1-1-1".scan(/(?=\A.+新潟市.+\z)中央区.+/)
イメージでいうとシェルのパイプを使った grep 先読み正規表現 | grep 後続正規表現 のようなイメージ。この動きをRubyのIF分岐で書くとしたらこんな感じ。
if "新潟県新潟市中央区1-1-1" =~ /新潟市/   # ← 先読み条件
$& =~ /中央区.+\z/ # "新潟市" =~ /中央区.+\z/ となるのでマッチしないのが正しい
end
p $&
これはマッチなしとなって実際の実行結果と一致する。そこで次は先行正規表現のマッチ範囲が広くなるようにして、後続正規表現にもマッチするようにしてみる。
p "新潟県新潟市中央区1-1-1".scan(/(?=\A.+新潟市.+\z)中央区.+\z/)
挙動イメージとしてはこんな感じ。
if "新潟県新潟市中央区1-1-1" =~ /\A.+新潟市.+\z/    #← 先読み条件。全文をひっかけるようにした。
$& =~ /中央区.+/ # "新潟県新潟市中央区1-1-1" =~ /中央区.+/ なのでマッチしそうだが・・・
end
p $&
が、上のパターンはマッチなしになる。まだ何かイメージが足りないようだ。

考えているうちに「ひょっとして後続正規条件は先読みの合致範囲の文字列からさらに抽出を行うのではなく、抽出文字列そのものが後続正規条件の内容にもマッチするかを判定しているだけなのではないか?」と思い至った。
p "新潟県新潟市中央区1-1-1".scan(/(?=\A.+新潟市.+\z)(新潟県.+)(中央区.+)\z/)
RubyのIF分岐での動作イメージはこんな感じ。
if "新潟県新潟市中央区1-1-1" =~ /\A.+新潟市.+\z/             #← 先読み条件。全文をひっかけるようにした。
$& =~ /(新潟県.+)(中央区.+)\z/ # "新潟県新潟市中央区1-1-1" =~ /(新潟県.+)(中央区.+)\z/ なのでマッチ
end
無事マッチするようになった。

まとめ
つまり、先行正規表現はチェック対象の文章からマッチ部分を抽出するが、その結果抽出された部位は後続正規表現と「丸ごと」合致しないといけない、ということのようだ。

これであれば、/(?=Red|Blue)Color/ は「RedColor」や「BlueColor」に合致しないのは当然だし、マッチする正規表現は
p "RedColor" =~ /(?=Red|Blue)(R|B)../
p "RedColor" =~ /(?=Red.+|Blue.+)Red.+/
のように、後続正規条件が先行正規条件の対象も含むようなものになる。

maru_tak at 17:37|PermalinkComments(0)TrackBack(0)

2012年04月24日

ProcessingをRubyからいじれると知って、喜び勇んでruby-processingを入れた。

サンプルに沿って図形を書いたり、写真を読み込んで加工してみたりをやった後、Macbook Air(OSX10.7.3)についているカメラからの画像を取り込みたくなり、やり方を調べたところ、サンプルコードでは下記のようにすれば大丈夫らしかった。
class VideoCaptureTest < Processing::App
load_library :video
include_package "processing.video"

def setup
smooth
size(640, 480)
@video = Capture.new(self, width, height, 30)
end

def draw
@video.read if @video.available?
image(@video, 0, 0)
end

end
VideoCaptureTest.new :title => "My Sketch"
しかし、rp5 run (ファイル名).rb で実行すると、一瞬ウィンドウが表示された後に異常終了してしまう。
rp5 run camera.rb 
java.lang.reflect.InvocationTargetException
at java.awt.EventQueue.invokeAndWait(EventQueue.java:1091)
at quicktime.QTSession.(QTSession.java:94)
at processing.video.Capture.init(Unknown Source)
at processing.video.Capture.(Unknown Source)
at processing.video.Capture.(Unknown Source)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.jruby.javasupport.JavaConstructor.newInstanceDirect(JavaConstructor.java:243)
at org.jruby.java.invokers.ConstructorInvoker.call(ConstructorInvoker.java:68)
at org.jruby.java.invokers.ConstructorInvoker.call(ConstructorInvoker.java:143)
at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:272)
at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:80)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:84)
at org.jruby.java.proxies.ConcreteJavaProxy$2.call(ConcreteJavaProxy.java:39)
at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:272)
at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:80)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:84)
at org.jruby.RubyClass.newInstance(RubyClass.java:834)
at org.jruby.RubyClass$i$newInstance.call(RubyClass$i$newInstance.gen:65535)
at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:181)
at org.jruby.java.proxies.ConcreteJavaProxy$3.call(ConcreteJavaProxy.java:129)
at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:282)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:71)
at org.jruby.ast.CallManyArgsNode.interpret(CallManyArgsNode.java:59)
at org.jruby.ast.InstAsgnNode.interpret(InstAsgnNode.java:95)
at org.jruby.ast.NewlineNode.interpret(NewlineNode.java:104)
at org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
at org.jruby.evaluator.ASTInterpreter.INTERPRET_METHOD(ASTInterpreter.java:75)
at org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:120)
at org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:134)
at org.jruby.internal.runtime.methods.DefaultMethod.call(DefaultMethod.java:154)
at org.jruby.javasupport.proxy.JavaProxyConstructor$2.invoke(JavaProxyConstructor.java:225)
at org.jruby.proxy.processing.core.PApplet$Proxy0.setup(Unknown Source)
at processing.core.PApplet.handleDraw(Unknown Source)
at org.jruby.proxy.processing.core.PApplet$Proxy0.__super$handleDraw(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.jruby.javasupport.JavaMethod.invokeDirectSuperWithExceptionHandling(JavaMethod.java:476)
at org.jruby.javasupport.JavaMethod.tryProxyInvocation(JavaMethod.java:697)
at org.jruby.javasupport.JavaMethod.invokeDirect(JavaMethod.java:365)
at org.jruby.java.invokers.InstanceMethodInvoker.call(InstanceMethodInvoker.java:50)
at org.jruby.java.invokers.InstanceMethodInvoker.call(InstanceMethodInvoker.java:111)
at org.jruby.runtime.callsite.SuperCallSite.cacheAndCall(SuperCallSite.java:346)
at org.jruby.runtime.callsite.SuperCallSite.callBlock(SuperCallSite.java:146)
at org.jruby.runtime.callsite.SuperCallSite.call(SuperCallSite.java:151)
at org.jruby.runtime.callsite.SuperCallSite.callVarargs(SuperCallSite.java:107)
at org.jruby.ast.ZSuperNode.interpret(ZSuperNode.java:102)
at org.jruby.ast.NewlineNode.interpret(NewlineNode.java:104)
at org.jruby.ast.RescueNode.executeBody(RescueNode.java:216)
at org.jruby.ast.RescueNode.interpretWithJavaExceptions(RescueNode.java:120)
at org.jruby.ast.RescueNode.interpret(RescueNode.java:110)
at org.jruby.evaluator.ASTInterpreter.INTERPRET_METHOD(ASTInterpreter.java:75)
at org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:120)
at org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:134)
at org.jruby.internal.runtime.methods.DefaultMethod.call(DefaultMethod.java:154)
at org.jruby.javasupport.proxy.JavaProxyConstructor$2.invoke(JavaProxyConstructor.java:225)
at org.jruby.proxy.processing.core.PApplet$Proxy0.handleDraw(Unknown Source)
at processing.core.PApplet.run(Unknown Source)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.lang.UnsatisfiedLinkError: /System/Library/Java/Extensions/libQTJNative.jnilib: no suitable image found. Did find: /System/Library/Java/Extensions/libQTJNative.jnilib: mach-o, but wrong architecture
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1827)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1716)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1045)
at quicktime.QTSession$1.run(QTSession.java:96)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:199)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:682)
at java.awt.EventQueue.access$000(EventQueue.java:85)
at java.awt.EventQueue$1.run(EventQueue.java:643)
at java.awt.EventQueue$1.run(EventQueue.java:641)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:652)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
java.lang.UnsatisfiedLinkError: quicktime.QTSession.Gestalt(I[I)S
quicktime/QTSession.java:-2:in `Gestalt'
quicktime/QTSession.java:935:in `gestalt'
quicktime/QTSession.java:641:in `open'
quicktime/QTSession.java:608:in `open'
processing.video.Capture:-1:in `init'
processing.video.Capture:-1:in `'
processing.video.Capture:-1:in `'
sun/reflect/NativeConstructorAccessorImpl.java:-2:in `newInstance0'
sun/reflect/NativeConstructorAccessorImpl.java:39:in `newInstance'
sun/reflect/DelegatingConstructorAccessorImpl.java:27:in `newInstance'
java/lang/reflect/Constructor.java:513:in `newInstance'
org/jruby/javasupport/JavaConstructor.java:243:in `newInstanceDirect'
./camera.rb:15:in `setup'
/Users/xxxxxx/.rvm/gems/ruby-1.9.3-p125/gems/ruby-processing-1.0.11/lib/ruby-processing/app.rb:211:in `handleDraw'
調べてみたところ、どうも 64bit の video ライブラリーに何らかの原因があるらしく、32bit 用のライブラリーを使うべし、とのこと。
Snow Leopard & Video Capture (Read 10724 times)

具体的には実行時に rp5 run xxxx.rb -d32 のように末尾に -d32 というオプションをつければ無事起動するようになった。

maru_tak at 23:48|PermalinkComments(0)TrackBack(0)

2012年04月20日

1日数回ネット上の1点の定点観測結果を記録するだけの稼働率の低いボットを作るために heroku を使ってみた。2012年4月現在、既に使っている方がたくさんいて情報も豊富なのだけれど、いくつか使ってみて初めてわかったことがあったのでメモしておく。

1. インスタンスには世代がある
Heroku側でアプリケーションを作るときに heroku create xxxxx --stack cedar のように、--stack オプションを付けているサンプルとそうでないサンプルがあって、違いがよくわからず悩んだ。

が、cedar というのは新しいタイプのインスタンスで、このオプションをつけないと bamboo という古い世代のインスタンスが使われるらしい(ドキュメント)。

新しいだけあって新しいフレームワークにも対応している上、一度 bamboo スタックで作ったアプリを cedar に移植する手順は大変そうなので、今から何か作るのであれば --stack cedar を付けるのが標準、と考えておいた方がよさそう。

2. ディスクには書き込めるがあくまで一時的
Fileオブジェクトを作ってローカルディスクに書き込み・読み出しは可能だが、データとしての永続性は保証されていない。Heroku はGoogle App Engine と同様にしばらくアクセスされていないとスピンダウンするようで、こうなると起動後にローカルファイルに書き込んだ内容が消える。

実行結果をCSVとして書きだしておいて、後でCSVだけダウンロードという使い方はできなさそう。このような、ちょっとした「書いときたい」「それを後で読みたい」場合にスキーマレスのMongoHQは便利、ということなのだと思った。

3. アプリのパスは /app
heroku run console で irb を起動した後、require '/app/(スクリプト名.rb)' すると、コンソールから push したアプリのメソッドなどを実行できる。

4. ドキュメントの場所
Rails3を動かすだとか、Asset Pipelineのdevelopmentとproduction 環境で違いが出るあたりなどは質問が多いのかドキュメント整備されているので、まずは読んでおいた方がよいと思った。
Ruby | Heroku Dev Center

5. FTPでのアップロードができない?
ローカルでテストしていたときには動いていた、書き出したファイルをどこかのFTPサーバーにアップするというコードが本番にアップした途端に No such file or directory と出て動かなくなった(アップファイルを絶対パスで指定しても同様)。何かHerokuの仕様とぶつかっているのかもしれないが、検索してみてもあまり明快な答えが分からなかった。
stackoverflow: Rails upload file to ftp server

maru_tak at 15:43|PermalinkComments(0)TrackBack(0)

2012年04月17日

heroku コマンドに紐づいているアカウントは最初の実行時に回答したものがずっと使われる。

私はたまたま最初は会社アドレスでアカウント登録をして、後から自分のgmailアドレスのアカウントを作ったのだけど、コマンド実行の対象アカウントをgmailアドレスの方に変更しようとしてかなりはまった。

まず、herokuコマンドには設定ファイルらしきものが見当たらない。heroku helpで設定画面を呼び出せるかと思いきや、それもなさそうだ。

そこで検索してみたところ、そのものずばりの方法を書いて下さっている方がいた。
Better Than Nothing:Heroku で使うアカウントを変更する方法

書いてある通りに、heroku keys:clear を実行してから ~/.heroku/credentials を削除。そして、heroku apps を実行すると新しい情報を聞かれるはず・・・。が、認証情報を聞かれることもなく、あっさりappのリストが返ってきた。どこかに古い認証情報がまだ残ってしまっているようだ。

この状態でRailsのプロジェクトフォルダの中で git push heroku master を実行すると下記のようなエラーになった。
! Your key with fingerprint e6:81:04:60:3c:3e:5e:9f:a9:ca:21:13:a3:ae:e5:83 is not authorized to access (アプリケーション名).

fatal: The remote end hung up unexpectedly
このエラーメッセージで検索すると、他の情報が見つかった。
Stackoverflow: Cannot push to Heroku because key fingerprint [closed]

いわく、id-rsaの鍵を作り直せばよいらしい。~/.ssh/ 以下にあった id_rsa と id_rsa.pub を削除して、ssh-keygen -t rsa -f ~/.ssh/id_rsa_heroku で、id_rsa_heroku という名前で鍵ファイルを再生成してみた。

再度、heroku keys:clear を実行して、heroku keys:add してから、Railsプロジェクトのフォルダに戻り、.git フォルダを削除して git init と heroku create からやり直した後に git push heroku master したが結果は全く変わらなかった。どこに古い情報を持っているのかさっぱり分からない・・・。コンソールを一度終了してみたりしたが、全く変わらず。

そこで herokuのgemをアンインストールしてから gem install heroku で再インストールし、~/.heroku の削除後、heroku create からやり直してみたところ、これが効果があった。

(追記)Herokuのヘルプを見たところ、heroku keys:remove (メールアドレスを指定) というコマンドがあるようだ。これで紐付けを消せるのかもしれない。
Managing Your SSH Keys | Heroku Dev Center

gem の再インストール後に heroku auth:login でログインした時には自動的に id_rsa_heroku.pub が発見され、アップロードされていった。その後、git push heroku master したところ、エラーメッセージが変わった。
Permission denied (publickey).
fatal: The remote end hung up unexpectedly
id_rsa_pub のファイルのパーミッションが間違っているのかと思ったが、id_rsa_herokuを600、id_rsa_heroku.pubは644 になっている。

さらに調べたところ、このエラーの対処法を書いて下さっている方がいた。
opamp_sandoの日記 ...でいいよね:Herokuで鍵がPermission denied (publickey). になる時

書かれている通りに、~/.ssh/config に heroku の項目を足す。
Host heroku.com
User git
port 22
Hostname heroku.com
IdentityFile ~/.ssh/id_rsa_heroku
TCPKeepAlive yes
IdentitiesOnly yes
すると、git push heroku master が無事通るようになった!

アカウントの切り替えを恒常的に使う場合は heroku-accounts というプラグインを使うとよいらしい。
heroku 複数のアカウントの検索結果

maru_tak at 01:20|PermalinkComments(1)TrackBack(0)

2012年04月08日

多対多の has_many :through なモデルで、複数の項目選択をチェックボックスで行うやり方がようやく分かったのでメモ。

例として、会員(member)が受信したいメルマガ(mailmag)を選ぶような画面を作ってみる。中間テーブル用のモデルクラスとして mailmag_member を用意し、会員登録・変更画面で好きなメルマガをチェックボックスで選択するようにする。

リレーションの指定
pp/models/member.rb
class Member < ActiveRecord::Base
attr_accessible :email, :name, :mailmags, :mailmag_ids
has_many :mailmag_members
has_many :mailmags, :through => :mailmag_members
end
app/models/mailmag.rb
class Mailmag < ActiveRecord::Base
belongs_to :author
attr_accessible :from, :name
has_many :mailmag_members
has_many :members, :through => :mailmag_members
end
app/models/mailmag_members.rb
class MailmagMember < ActiveRecord::Base
belongs_to :mailmag
belongs_to :member
end

app/views/members/_form.rb
<%= form_for(@member) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<% Mailmag.all.each do |mag| %>
<%= f.check_box :mailmag_ids,
{:checked => @member.mailmags.include?(mag),
:name => "member[mailmag_ids][]"}, mag.id, ”” %>
<%= mag.name %>
<% end %>
<%= f.submit %>
<% end %>


メモ

  • has_many :throughでは、中間テーブルの向こう側の Mailmag モデル操作用のフィールドである mailmags と mailmag_ids を直接操作する。

  • 中間テーブルの mailmag_id を操作しようとしてはいけない。

  • :mailmags, :mailmag_ids は Memberモデル側で attr_accessible 指定をして、mass-assignment を可能にしておく必要がある。

  • checkbox の第1引数(フィールドのアクセッサメソッド)は :mailmag_ids を入れておく。複数指定可能なフィールドだが、ここを"mailmag_ids[]" と書いたりしても意図した通りに動かない。

  • checkbox の第2引数(オプション)では、初期チェック状態(:checked)の他に、項目名(:name) を「member[mailmag_ids][]」として指定する(超重要)。

  • 通常 チェックボックス名 は第1引数から作られるが、第2引数で :name を指定するとそちらが採用されるようだ。

  • check_boxはfにかかっているので、:nameは member[mailmag_ids][] ではなく、mailmag_ids[] でもいけそうな気がしてしまうが、こうすると mailmag_ids が member の属性ではなくなってしまってうまくいかなかった。

  • accepts_netsted_attributes_for が不要なのは、ここで mailmag オブジェクトを新規作成することを考えなくてよいから。

  • チェックボックスタグの外側で Mailmag.all の中身を取得するループを回しているのは、選択されていない選択肢のチェックボックスも生成するため。

  • チェックボックスの第3引数は、チェックをつけた時の入力値。

  • チェックボックスの第4引数は非チェック時の入力値。空文字「""」を指定する。nil を指定してしまうと、チェック項目を全て外した時にパラメーターが送出されなくなるので、変更が反映されなくなる。



参考にした記事その1: check_box_tagで実現するパターン
check_box_tagの引数はカラム名、チェック時の入力値、初期チェック済(true or false), options = {(:nameをここで指定する)}
Rails 3.0 Many to Many com has_many :through com checkboxes
<%= check_box_tag :mailmag_ids, mailmag.id, @member.mailmags.include?(mailmag), :name => 'member[mailmag_ids][]' %>

参考にした記事その2:check_box等のオプションについて
f.check_box・check_box_tag、 f.radio_button・radio_button_tagのchecked

maru_tak at 12:31|PermalinkComments(0)TrackBack(0)