2015年5月22日金曜日

『ホビット 思いがけない冒険』観た。

久々の映画ネタ。

ロードオブザリングは最後の方話がややこしくなってきて、流すかんじになっちゃった思い出があるけれど。TUTAYAで借りてみました。『ホビット 思いがけない冒険』。

ロードオブザリングの主人公のおじさんが主役なんですね、冒頭ドワーフ一行に荒らされる家がおしゃれすぎてホビットの暮らしいいな って感じになります。おしゃれな家を容赦なく汚すドワーフにイラっとしてしまう。

話は分かりやすくてロードオブザリングよりいい感じ。エルフの王様(?)と、よりえらいエルフの女性(?)とガンダルフの関係が謎。なんか何でも相談づくのような、分かってるよ俺たち のような。

大量生産・大量消費の世界じゃないのだけれど、一振りの剣とかみて「これは○○の剣!」みたいに、パッと共通認識が持たれる。いやいや、そんなに
物すくなくないだろ、どんだけ博識者なんだよ、あなた方。っという小さな突込み。

ガンダルフはなんか胡散臭い顔していると思う、個人的に。

ロードオブザリングのリングと、「いとしいしと」が口癖のあのキャラが終盤でてくるけど、そもそもなんでリングもってたんだこいつ。

数々の複線の謎がふんだんにでてくるんだけど、総じて面白かった。




2015年5月21日木曜日

【Ruby On Rails】Stringなど既存のクラスにメソッドを追加する方法

いつも忘れるのでメモ。

このアプリケーションだけで頻繁に使う処理をStringなど既存のrubyクラスに足したいときがある。
たとえばStringに 文字列を大文字・小文字が相互に続くように変換する dekoboko というメソッドを足すとする。

core_ext/string.rb というファイルを作ってそこでStringクラスを追記する。

X
# coding:utf-8

class String

def dekoboko
ret = ""
self.length.times do | idx |
if idx % 2 == 0
ret += self[ idx ].upcase
else
ret += self[ idx ].downcase
end
end
ret
end

end



application.rbでstring.rbをloadすればStringクラスを拡張できる。


module MyApp
class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. # Custom directories with classes and modules you want to be autoloadable. # config.autoload_paths += %W(#{config.root}/extras ) # 基本クラスの拡張を読み込む Dir[ config.root.join( "core_ext/*.rb" ) ].each do | f | load f end


コンソールから実行すると、
irb(main):001:0> "abcdefg".dekoboko
=> "AbCdEfG"

メソッドが追加できている!

2015年5月18日月曜日

【RubyOnRails】Parallelを使って並列処理をしているときにraiseした場合の動作

単純処理を繰り返し行う場合、高速化のためにParallelで並列処理する。
ふと思ったんだけど、処理を並列で進めているときに1つがraiseしたらそのほかの処理はどうなるんだろう。もちろんほかの処理も打ち切られてほしいんだけど、まさかraiseした以外のスレッドの処理は継続ってことはないよなぁ。。ということで確認してみた。

以下のサンプルプログラムで確認する。


# coding:utf-8

ary = [ *1..100 ]

Parallel.each( ary, :in_threads => 4 ) do | num |

p "#{num}: pid=#{Process.pid}"
raise if num == 50

end



さて、結果を予想すると、

50が割り当てられたthreadがraiseするが、その他のthreadも処理を打ち切るので50以降はプリントされないはず。

で実行してみると。。


$ bin/rails r script/multi_thread.rb

(省略)
"36: pid=28589"
"37: pid=28589"
"38: pid=28589"
"39: pid=28589"
"40: pid=28589"
"41: pid=28589"
"42: pid=28589"
"43: pid=28589"
"44: pid=28589"
"45: pid=28589"
"46: pid=28589"
"47: pid=28589"
"48: pid=28589"
"49: pid=28589"
"50: pid=28589"
script/multi_thread.rb:12:in `block in ': unhandled exception
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:430:in `call'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:430:in `call_with_index'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:240:in `block (3 levels) in work_in_threads'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:439:in `with_instrumentation'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:239:in `block (2 levels) in work_in_threads'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:233:in `loop'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:233:in `block in work_in_threads'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.3.3/lib/parallel.rb:136:in `block (2 levels) in in_threads'

予想通り50で止まった。マルチスレッドは並列処理といってもひとつのプロセス内で処理を分けているのでraiseすればプロセス自体が打ち切られ該当スレッド以外も止まる。
(プリントされているPIDもすべて同じ。)

ではマルチプロセスならどうなるだろう。


multi_thread.rbのin_threadsをin_processesに変えてプログラムは出来上がり。

$ bin/rails r script/multi_process.rb

(省略)
"41: pid=26378"
"44: pid=26381"
"43: pid=26375"
"45: pid=26378"
"46: pid=26375"
"47: pid=26378"
"48: pid=26375"
"50: pid=26378"
"49: pid=26381"
"52: pid=26381"
"53: pid=26381"
"54: pid=26381"
"55: pid=26381"
"56: pid=26381"
"57: pid=26381"
"58: pid=26381"
"59: pid=26381"
"51: pid=26375"
script/multi_thread.rb:8:in `block in ': unhandled exception
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:438:in `call'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:438:in `call_with_index'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:342:in `process_incoming_jobs'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:324:in `block in worker'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:317:in `fork'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:317:in `worker'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:305:in `block in create_workers'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:304:in `each'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:304:in `create_workers'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:263:in `work_in_processes'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:193:in `map'
        from /goy/rails_app/vendor/bundle/ruby/1.9.1/gems/parallel-1.4.1/lib/parallel.rb:154:in `each'

止まったがぴったり50ではない。プリントされる順も昇順ではなく若干ばらばら(PIDもみんな一緒ではない)。50を引き当てたプロセスがraiseしてもその他のプロセスはすぐには止まらない模様。raiseしたプロセスが親プロセスに伝え、親プロセスが各子プロセスを止めて。。。みたいになると思うのでタイムラグがあると思う。出力される順番もばらつくので順番どおりに処理されないといけないという時はそもそもマルチプロセスだとだめっぽい。


どちらにしても、並列処理でraiseしたときに、raiseした以外のプロセス・スレッドも処理を停止する(マルチプロセスの場合タイムラグあり)ということはわかった。

2015年5月14日木曜日

アセットコンパイル時に Sprockets::FileNotFound: couldn't find file 'bootstrap-sprockets' が出たときの対策

Railsのアプリケーションにbootstrap-sassを導入して、bootsrapのバージョンを2から3にした。
開発環境では問題なく動いていたのに、本番にいれるためのアセットコンパイルをしようとすると下記のエラーが。

[goy@myserver]$ RAILS_ENV=production bin/rake assets:precompile
/usr/local/bin/ruby bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
rake aborted!
Sprockets::FileNotFound: couldn't find file 'bootstrap-sprockets'
  (in /deploy/mysite/app/assets/javascripts/application.js:14)
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/sprockets-2.1.4/lib/sprockets/context.rb:100:in `resolve'
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/sprockets-2.1.4/lib/sprockets/context.rb:140:in `require_asset'
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/sprockets-2.1.4/lib/sprockets/directive_processor.rb:215:in `process_require_directive'
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/sprockets-2.1.4/lib/sprockets/directive_processor.rb:165:in `block in process_directives'
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/sprockets-2.1.4/lib/sprockets/directive_processor.rb:163:in `each'
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/sprockets-2.1.4/lib/sprockets/directive_processor.rb:163:in `process_directives'
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/sprockets-2.1.4/lib/sprockets/directive_processor.rb:97:in `evaluate'
/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/tilt-1.4.1/lib/tilt/template.rb:103:in `render'


いろいろ調べた結果どうもアセットコンパイル対象のパスにbootstap-sassが入っていないようだ。


irb(main):001:0> Rails.application.config.assets.paths
=> ["/deploy/mysite/app/assets/images", "/deploy/mysite/app/assets/javascripts", "/deploy/mysite/app/assets/stylesheets", "/deploy/mysite/vendor/bundle/ruby/1.9.1/gems/jquery-rails-3.1.2/vendor/assets/javascripts"]

⇒bootstrap-sass がはいってない。

config/environments/production.rb に bootstrap-sassをアセットコンパイルの対象にするように追記。

Rails.application.config.assets.paths << Rails.root.join( "vendor/bundle/ruby/1.9.1/gems/bootstrap-sass-3.3.4.1/assets/javascripts" ).to_s Rails.application.config.assets.paths << Rails.root.join( "vendor/bundle/ruby/1.9.1/gems/bootstrap-sass-3.3.4.1/assets/stylesheets" ).to_s

# しかしバージョンがハードコードされてしまっているという問題があり。


[goy@myserver]$ RAILS_ENV=production bin/rake assets:precompile
/usr/local/bin/ruby bin/rake assets:precompile:all RAILS_ENV=production RAILS_GROUPS=assets
[goy@myserver]$

⇒今度はエラーなくコンパイルできた!


いつもはデプロイはcapistranoを使うのでbin/rake assets:precompileを直に実行することはない。
今回のサイトはあまり更新しないのでcapistranoを設定してなかったのだが、capistranoがアセットコンパイルを実行するときにはどうしているのだろう。
実行時にパスを渡すとかするのだろうか。。。


2015年5月5日火曜日

Mecabのユーザー辞書登録で nonexistent tokenizer: <TokenMecab>: が出たときの対応

Mroongaを使った全文検索で「足つぼ」が「足」「つ」「ぼ」に分解されてしまうため、「つぼ」をMecabの辞書に登録した。
Mecabに文字列を投げて「足」「つぼ」に分解することはできた。続いてMroongaでやってみる。


ActiveRecordを通さず、MySQLから直接実行してみると下記のようなエラーが。
[tokenize] nonexistent tokenizer: <TokenMecab>:

Mecabを認識していないようなメッセージだ。辞書を追加はしたが認識しなくなるとはどういうことだ?と思いつつ、
Mecabに読み込ませたユーザー辞書の設定を消してみるとエラーはでなくなった。

ん~、やっぱこれが原因かと首をかしげているとひらめいた。


ユーザー辞書のおき場所が一般ユーザーのホームディレクトリだったがMySQLはrootで起動するため所有者の違うファイルが読めなったのだろう。
っということで、ユーザー辞書を /usr/lib64/mecab/dic/ipadic/goy.dic に移動し、所有者、権限はそれぞれroot:rootの0644にした。


MySQLを再起動し、再度形態素解析してみると・・・、

mysql> SELECT mroonga_command("tokenize TokenMecab '足つぼマッサージ'");
+---------------------------------------------------------------------------------------------------------+
| mroonga_command("tokenize TokenMecab '足つぼマッサージ'")                                               |
+---------------------------------------------------------------------------------------------------------+
| [{"value":"足","position":0},{"value":"つぼ","position":1},{"value":"マッサージ","position":2}]         |
+---------------------------------------------------------------------------------------------------------+

今度はいけた。ActiveRecordを通してももちOK。


関連記事:
Mecabの辞書に単語を登録する方法