2015年9月20日日曜日

スマホサイトでメニューのスライドインをmmenuでいい感じに実装できた。

スマホサイトでよくあるメニューのスライドインを簡単に実装できる方法がないかと調べたが、mmenuというjqueryのプラグインでいい感じにできた。
http://rp.aroundtheclock.jp/

サブメニューへのドリルダウンもしてくれるし、オプションや拡張機能も充実で幸せ。



mmenu:
http://mmenu.frebsite.nl/

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の辞書に単語を登録する方法


2015年4月29日水曜日

Mecabの辞書に単語を登録する方法

Mroongaを使ってタグクラウドを作った。
(作ったのはこちら⇒http://www.direc.biz/home/tags)

よく見ると「足つぼマッサージ」という文字列の分解結果に「つぼ」がでてこない。

Mroongaのトークナイザーに使っているMecabに「足つぼマッサージ」を投げてみると、
「つぼ」は「つ」と「ぼ」になっていた。「つぼ」という単語が辞書にない模様。

[goy@myserver tmp]$ echo "足つぼマッサージ" | mecab
足      名詞,一般,*,*,*,*,足,アシ,アシ
つ      助動詞,*,*,*,下二・タ行,基本形,つ,ツ,ツ
ぼ      動詞,自立,*,*,五段・ラ行,体言接続特殊2,ぼる,ボ,ボ
マッサージ      名詞,一般,*,*,*,*,マッサージ,マッサージ,マッサージ

Mecabの辞書にはシステム辞書とユーザー辞書がありどちらにも追加登録できるが、システム辞書は
しくったらめんどそうだったので今回はユーザー辞書に登録する。


ユーザー辞書への登録


/tmp/foo.csv をつくり以下のように記載。4項目は検索スコアだが、いったん適当にいれる。
つぼ,,,5279,名詞,一般,*,*,*,*,つぼ,ツボ

辞書に登録するコマンドを実行する。実行ユーザーのホームディレクトリに拡張子が.dicのユーザー辞書ができる。

[goy@myserver tmp]$ /usr/libexec/mecab/mecab-dict-index -d /usr/lib64/mecab/dic/ipadic -u goy.dic -f utf-8 -t utf-8 /tmp/foo.csv
emitting double-array: 100% |###########################################|



Mecabにユーザー辞書を読み込ませる


/usr/lib64/mecab/dic/ipadic/dicrc を開き 末尾にユーザー辞書の設定を追加


; ChaSen (include spaces)
node-format-chasen2 = %M\t%f[7]\t%f[6]\t%F-[0,1,2,3]\t%f[4]\t%f[5]\n
unk-format-chasen2 = %M\t%m\t%m\t%F-[0,1,2,3]\t\t\n
eos-format-chasen2 = EOS\n

userdic = /home/goy/goy.dic # ←ここを追加


再度Mecabで形態素解析をしてみると、、、

[goy@myserver ~]$ echo "足つぼマッサージ" | mecab
足      名詞,一般,*,*,*,*,足,アシ,アシ
つぼ    名詞,一般,*,*,*,*,つぼ,ツボ
マッサージ      名詞,一般,*,*,*,*,マッサージ,マッサージ,マッサージ

今度は「つぼ」に分解された!



関連記事:
Mroongaを使ったタグクラウドの作り方

2015年4月28日火曜日

Mroongaを使ったタグクラウドの作り方

Mroongaを使って全文検索を導入した。
レスポンスが早くなった気がするので満足。

http://www.direc.biz/search?q=%E8%AA%BF%E6%9F%BB

しかし該当箇所を強調するためのsnippet機能は使えなかった


MronngaのトークナイザーにはMecabを使った。Mecabを使って文字列を形態素解析で
単語に分解できる。この機能を使って登録済みのテキストから自動でタグクラウドを作る仕組みを紹介。



単語への分解


DBに保存した文字列をMroongaを通してMecabで形態素解析にかけるには下記のようなSQLを実行する。

mysql> SELECT mroonga_command("tokenize TokenMecab '我思う故に我あり'");
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| mroonga_command("tokenize TokenMecab '我思う故に我あり'") |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| [{"value":"我","position":0},{"value":"思う","position":1},{"value":"故","position":2},{"value":"に","position":3},{"value":"我","position":4},{"value":"あり","position":5}] |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.07 sec)


Mroongaのmroonga_commandはgroongaのコマンドを呼び出しているので実際は、

ruby(Rails) → Mroonga → Groonga → Mecab

という流れ。


上記で「我思う故に我あり」になっている部分にテーブルに入ったデータを入れれたらいいのだが、できない見たいなので
いったんselectしてからmroonga_commandの実行のために再度SQLを呼び出する形に。


結果をrubyで処理


Mecabは形態素解析の結果をjsonで返すが、
mroonga_commandをActiveRecordを通して実行した結果をrubyに格納すると、文字化けていた。


irb(main):022:0> dum = SitesMroonga.connection.execute %! SELECT mroonga_command("tokenize TokenMecab '我思う故に我あり'")!
(23.1ms) SELECT mroonga_command("tokenize TokenMecab '我思う故に我あり'")
=> #
irb(main):023:0> dum.first.first
=> "[{\"value\":\"\xE6\x88\x91\",\"position\":0},{\"value\":\"\xE6\x80\x9D\xE3\x81\x86\",\"position\":1},{\"value\":\"\xE6\x95\x85\",\"position\":2},{\"valu"


これではこの後処理できない。どうしたものかと結構なやんだがエンコードをUTF-8として読み込めば大丈夫だった。


irb(main):026:0> JSON.parse dum.first.first.force_encoding( "UTF-8" )
=> [{"value"=>"我", "position"=>0}, {"value"=>"思う", "position"=>1}, {"value"=>"故", "position"=>2}, {"value"=>"に", "position"=>3}, {"value"=>"我", "posiあり", "position"=>5}]


形態素解析の結果のjsonをパースし、Hashとしてrubyで読み込めた。


名詞以外を除去


形態素解析の結果は名詞だけではなく、助詞なども含まれるのでこのままでは「は」や「だが」などが出現回数の上位を占める。
「は」や「や」が並ぶタグクラウド見ても ? である。
なので、簡単なフィルタを作ってそれらを除去した。



NOT_NOUNS = %w(

いかに
いる

おり

かしら
かも
から

ください
くらい
けれど

こそ
こと
ことよ

さえ

しか
する


たい
たり
だけ
だに
だの
ため
つつ

です
ても
てよ

# 一部省略
)


# -------------------------------------------------------------
# 文章より以下のルールに従って、タグの候補を抽出する
# ・名詞のみ
# ・3回以上でてくる
# -------------------------------------------------------------
def tag_candidates
wards = self.tokenize
wards = wards.reject{ |w| NOT_NOUNS.include? w }

ret = []
wards.dup.uniq.each do | w |
if wards.count( w ) >= 3
unless [
Moji::HAN_ASYMBOL,
Moji::HAN_JSYMBOL,
Moji::ZEN_ASYMBOL,
Moji::ZEN_JSYMBOL,
].include? Moji.type( w )
ret << w end end end ret end


NOT_NOUNSに助詞など、結果から省く単語を配列で入れておく。
また、?(クエスチョンマーク)などの記号や句読点なども1単語になるためこのままでは結果に含まれてしまう。

mojiを使えば単語が記号かどうかを判定できる。これを使い記号の場合は除外する。


残った単語をタグとしてidと紐付けてテーブルに保存すれば準備よし。あとはテーブルから出てくる回数の多い順に単語をとってくればタグクラウドのできあがり♪

2015年4月12日日曜日

Mroonga導入してみた。mroonga_snippetがうまく動かない。

mroongaを入れてみた。
Railsで作っているディレクトリサービスサイトの検索機能をリニューアルしようと導入中。

使いはじめは簡単で、データを入れて FULLTEXTインデックスを張れば
すぐに全文検索できるようになった。

まだいつも間違ってしまうのが、例えば サイト名と、紹介テキストをそれぞれ、name,
descriptionカラムに持つテーブルがあるとして、FULLTEXTインデックスを(name, description)に対して張った場合、
SELECT時AGAINST句で両方のカラムを指定しなければいけないこと。

Googleでもそうだけど検索結果のテキストで該当部分を目出させるようにしたい。
mroonga_snippetを使うとそういうことができるのだろうけど、試してみるとwordNに日本語を指定したら

Can't initialize function 'mroonga_snippet'; Failed to add a condition to grn_snip: <>

とメッセージをだしてエラーになった。

まぁ実験的機能ということだしまだなのかな。しかし国産なのに日本語がだめとは。。。

ではどうするか。


Modelの方で、該当文字列をspanタグで囲って返す処理を作った。


def description_for_search_result keyword

desc = self.description.strip

first_pos = desc.index( keyword )
first_pos = 0 if first_pos.nil?
max_length = 200
prefix = "..."

if first_pos <= 5 start_pos = 0 elsif desc.length - first_pos < max_length start_pos = desc.length - max_length else start_pos = first_pos end start_pos = 0 if start_pos < 0 prefix = "" if start_pos == 0 postfix = "" if start_pos + max_length < desc.length postfix = "..." end text = self.description[ start_pos, max_length ] text = text.gsub(/(#{keyword})/, %!\\1!)

dum = start_pos + max_length
ret = %!#{prefix}#{text}#{postfix}!.html_safe

end


はじめに対象文字列があった場所から、200バイト分を出力。
抽出される文字列が200バイトより少なくなるようなら、開始位置を調整。
紹介分自体が200文字より短いなら全文を表示。
表示の前後で省略される部分があるなら「...」を表示。

該当箇所はで囲まれてでてくるので、
CSSで目立つようにしてやる。


まぁこれでいったんいいんだが、
mroongaの結果では「すぽーつ」で検索しても「スポーツ」を含む結果が
返されるが、この置き換えメソッドは強調できない。

カタカナ、ひらがな程度なら対応もできるが、自然言語検索した場合は
どうしようもない。検索結果毎に処理が走るのもできれば避けたいし、早くmroonga_snippetが
使えるようになったほしい。

2015年3月27日金曜日

【ActiveRecord】update_allでのwhere, limitの指定方法

使うたびに忘れるのでメモ

複数のレコードを一括で更新する場合ActiveRecordではupdate_allを使う。そのとき対象のレコードをwhereで指定したり、limitで件数をしぼったりするときには、


Member.update_all("lock=0").where("lock=1").limit(100)

っとしてしまいがちだが、こうではなくて、


Member.update_all("lock=0","lock=1", :order=>"last_update", limit: 100 )


こうする。

2015年3月15日日曜日

Nginxがerror_pageで設定した内容を返さない件

サイトのメンテナンスのためnginxがメンテナンスページを返すようにしようとググってみたら、サンプルがたくさんでてきたのでその通りにやってみたが、なぜか設定したメンテナンスページが表示されない。Nginxが吐く503のレスポンスの文字列が出てしまう。

調べてみるとerror_pageディレクティブの設定が反映されないのはNginxのバグではないかとのこと(http://serverfault.com/questions/326877/nginx-error-page-directive-is-silently-ignored)。


returnをlocationの中にいれると正常に表示された。


server {
listen 80;
server_name lw.aroundtheclock.jp;

error_page 503 /maintenance/maintenance.html;
location /maintenance/ {
root /var/www/html/;
}

location / {
return 503;
#proxy_pass http://localhost:3010;
}
}


余談

/tmp/do_maintenance があればメンテナンスページを返すようにnginxの設定するサンプルがたくさんでてきた。リクエストのたびにファイルの存在を確認しにいくのってどうなのか思う(きっと対した負荷じゃないんだろうけど)。今回は個人サイトなのでメンテナンスページなんて出さなくてもいいくらいだし、今後も滅多に使わないだろうからそこらへんの仕組みはいれず使うときはconfで設定をコメントアウトを解除してreloadするつもり。

2015年3月14日土曜日

Sinatora + bootstrap 「Uncaught SyntaxError: Unexpected token var」の対処方法

Sinatora + bootstrap でWebアプリを作って、Capistranoでデプロイしたところ

ブラウザのconsoleに
Uncaught SyntaxError: Unexpected token var
というエラーがでてjsが正しく動作しなかった。

調べるとbootstrap.jsの中で発生していた。
どうもSinatraのjsの圧縮が原因のよう。


app.rbのassets句でjsの圧縮方法を指定できるが、下記のようにそこを「js_compression :closure, :level=>"WHITESPACE_ONLY"」にしたら直った。

assets do
serve '/javascripts', from: 'assets/javascripts'
serve '/stylesheets', from: 'assets/stylesheets'

js :application, [
'/javascripts/jquery-*.js',
'/javascripts/jquery.zclip.js',
'/javascripts/*.js',
'/javascripts/*.coffee'
]

css :application, [
'/stylesheets/*.css',
'/stylesheets/*.scss'
]

js_compression :closure, :level=>"WHITESPACE_ONLY" # :jsminだとbootstrap.jsないでエラーが起こる。closureにしても "WHITESPACE_ONLY"指定しないとだめ。
#js_compression :jsmin
css_compression :sass
end


デフォルトはjsminだが、圧縮なしのオプションはない模様。

2015年3月8日日曜日

Capistrano3でSinatraをデプロイ

Railsでは使っていたがSinatraは初めてなのでメモ。

capistrano3インストール



Gemfileに以下のように書いてbundle install。


group :development do
gem 'capistrano'
gem 'capistrano-bundler'
gem 'capistrano-rails', '~> 1.1.3'
end



その後

$ bin/cap install

これでCapfileなどが作られる。

capistrano task設定

capistranoのタスクは http://qiita.com/satococoa/items/9b0cc416ffc042680b9b を参考に以下のようにして、
lib/capistrano/tasks/unicorn.rake に保存。

namespace :deploy do
task :environment do
set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
set :unicorn_config, "#{current_path}/config/unicorn/unicorn_#{fetch(:rails_env)}.rb"
set :unicorn_path, "#{current_path}/bin/unicorn"
end

def start_unicorn
within current_path do
execute "#{fetch(:unicorn_path)} -c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"
end
end

def stop_unicorn
execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})" end def reload_unicorn execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})" end def force_stop_unicorn execute :kill, "$(< #{fetch(:unicorn_pid)})" end desc "Start unicorn server" task :start => :environment do
on roles(:app) do
start_unicorn
end
end

desc "Stop unicorn server gracefully"
task :stop => :environment do
on roles(:app) do
stop_unicorn
end
end

desc "Restart unicorn server gracefully"
task :restart => :environment do
on roles(:app) do
if test("[ -f #{fetch(:unicorn_pid)} ]")
reload_unicorn
else
start_unicorn
end
end
end

desc "Stop unicorn server immediately"
task :force_stop => :environment do
on roles(:app) do
force_stop_unicorn
end
end
end
end


Capfile

Capfileはとりあえずbundle installしてくれるうにcapistrano/bundlerはONにした。
assets_compileするにはcapistrano/rails/assetsもONに。
あと先ほどのtaskファイルを読み込むように。

# Load DSL and set up stages
require 'capistrano/setup'

# Include default deployment tasks
require 'capistrano/deploy'

# Include tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
# https://github.com/capistrano/rvm
# https://github.com/capistrano/rbenv
# https://github.com/capistrano/chruby
# https://github.com/capistrano/bundler
# https://github.com/capistrano/rails
# https://github.com/capistrano/passenger
#
# require 'capistrano/rvm'
# require 'capistrano/rbenv'
# require 'capistrano/chruby'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
# require 'capistrano/rails/migrations'
# require 'capistrano/passenger'

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }


config/deploy.rb


# config valid only for current version of Capistrano
lock '3.4.0'

set :application, '#{app_name}'
set :repo_url, '#{repo_url}'

# Default branch is :master
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, '#{deploy_path}'
set :bundle_flags, "--binstubs" # bundleにbinstubsオプションをつけている。

# Default value for :scm is :git
set :scm, :git

# Default value for :format is :pretty
# set :format, :pretty

# Default value for :log_level is :debug
# set :log_level, :debug

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
# set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml')

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push( 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')

# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }

# Default value for keep_releases is 5
set :keep_releases, 10

namespace :deploy do

after :restart, :clear_cache do
on roles(:app), in: :groups, limit: 3, wait: 10 do
# Here we can do anything such as:
# within release_path do
# execute :rake, 'cache:clear'
# end
end
end
after 'deploy:publishing', 'deploy:restart' # これ入れないとdeployした後にunicornに更新が読みこまれない。

end


config/deploy/production

こちらは

set :rails_env, "production"

を入れるのと、assets_compileはroleがwebのときにしか実行されないので、
webサーバをかねる場合はwebのroleをつける必要がある。

またその場合、Railsでアセットのルーティングを処理しなければいけないので、
config/environments/production.rbでconfig.serve_static_assetsをtrueに設定する。
これをやらないと画像、css,jsなどがNot Foundになってしまう。




これで

$ bin/cap production deploy

で本番リリースできるようになります。


更新履歴

2015/06/20 assets_compileについての記載追加。

文字列修正サービス もでぃもでぃ リリース

データの調査をしているとき、重複を消去したり、前後の空白を消したり、ソートしたりと文字列の整形をすることがあると思いますが、どうされてるでしょうか。エクセルでがんばったりすることも多いでしょう。さくらエディタで正規表現使えば結構いけますが、重複はどうにもできません。私の場合Railsのconsoleで配列に入れて、、、なんてよくやります。
が毎回それも面倒なんでWebアプリにしました。

文字列修正サービス もでぃもでぃ

データ整形の際にご利用ください。

2015年2月25日水曜日

webサーバを並列化しているとき、各サーバのログを一括でgrepするShell。

/tmp/log_dir/#{host名} 下にそれぞれ複数の.gz化されたlogファイルを置き、
host毎に検索し、該当行を"#{host名}-#{検索対象文字列}".csvに書き出す というシェルプログラムを作った。

今回はnginxのaccess_logからスマホのUAで特定のURLへアクセスしているリクエストのログを抽出する。


#!/bin/bash


log_dir="/tmp/log_dir"
hosts=("zetton01" "zetton02" )
uris=("/login/" "/news/" "/todays/special/")

for host in ${hosts[@]}
do
read_dir="${log_dir}/${host}"
for uri in ${uris[@]}
do
dum=`echo $uri | sed -e "s/\//-/g"`
outfile="${host}_${dum}.csv"

echo $read_dir
echo $uri
zcat $read_dir/*.gz | grep "http://#{mydomain}${uri}" | grep "iPhone\|Android" > $outfile
done
done

2015年2月19日木曜日

【Rails】 pdfをダウンロードさせたり、ブラウザで開かせる方法

pdfをDLさせるには飛び先のactionの最後で


send_data #{pdfデータ(バイナリ)}, filename: "#{ダウンロード時のファイル名}.pdf"

とする。

DLでなくブラウザ内で開く(プレビュー)にするなら、

send_data #{pdfデータ(バイナリ)}, type: "application/pdf", disposition: 'inline'

とする。

2015年2月18日水曜日

Screenで起動してからエスケープ文字を変更する方法

Screenのエスケープ文字のデフォルトはCtrl+a だが、職場ではCtrl+tにする人が多い。共有ユーザーの.screenrcもCtrl+tにしてあるが、僕はCtrl+aになれてしまったので。

:escape ^Aa

2015年2月16日月曜日

【Sinatra】 開発環境でwebサーバ起動

binstubsオプションでbundleしているという前提で、以下のコマンドで起動。(3000番ポート指定の場合)

$ bin/rackup -p 3000 -o 0.0.0.0

2015年2月15日日曜日

Sinatra GET bootstrap.css.map 404 (Not Found)の対応

SinatraでWebサイトを開発中。

ブラウザのconsoleに、
  • Uncaught Error: Bootstrap's JavaScript requires jQuery
  • GET http://#{mysite-url}/stylesheets/bootstrap.css.map 404 (Not Found)

の2つのエラーがでている。bootstrapはインストール済みで画面は問題なくみれているが、
1つ目はbootstrapが使うjqueryがないということ。jqueryのサイトから最新版をDLして、js置き場のassets/javascripts/jquery-2.1.3.js として保存。app.rbのassets節には



assets do
serve '/javascripts', from: 'assets/javascripts'
serve '/stylesheets', from: 'assets/stylesheets'

js :application, [
'/javascripts/jquery*.js',
'/javascripts/*.js'
]

として、jqueryが先に読まれるようにして解決。
Railsだと専用のgem jquery-railsがあるが、sinatraではそういうのないっぽいなぁ。webサーバもつどrestartしないとjs読んでくれなかった。

2つめはbootstrapの.mapがないといっている。
bootstrapはDLしたフォルダごと assets/javascripts下にありmapファイルも入っているが.mapは読み込まれない模様。public/stylesyeets/bootstrap.css.map -> ../../assets/bootstrap/css/bootstrap.css.map とシンボリックリンクを張って解決。

2015年1月18日日曜日

【RoR】オブジェクトがnewされたものか、DBからselectされたものか(保存済みのものか)チェックする方法。

初回保存のときと、それ以外とかで処理を変えたいことはよくある。原理的にはidでDBをselectして該当があるか見る っということなんだけど、Railsではそれようのメソッドが既にあるので便利。しかし名前をよく忘れる。その名は persisted?


item = Item.new
item.persisted? # DBに保存されてないのでfalseになる。selectしたオブジェクトはtrueに。

2015年1月12日月曜日

300 RISE OF AN EMPIRE| スリーハンドレッド 帝国の進撃 観た

 まず私が言いたいのは、TUTAYAでレンタルすると順新作は370円。旧作は100円・・・・。旧作かりなよ ってことだ。(いつもうっかり忘れて準新作かりてしまう俺。。。)
でも面白かった「300 帝国の進撃」。血がどばどばでるからえぐいけど、きれい。引き込まれる。

話は前作300の戦いの裏で行われていた海戦の話。前作でスパルタ軍って最終的に全滅してたんだっけ?記憶がもうあいまいなんだけど、前作は7年前だったんだな。そんなに昔だったんだなぁ。海から迫るペルシア海軍にスパルタ以外の各都市は対抗すべく団結して連合軍を作り、かつてのマラトンの戦いでペルシア軍を破ったアテナイの将軍が指揮を執る・・・・

ペルシア側の女将軍(エヴァ・グリーン)が準主人公でペルシア側の戦に至った事情も描かれているのでこのタイトルになっている模様。
この女将軍がキレイ&かっこいい!残酷だが凛々しく色っぽい。ハマーン様の様。衣装がまたかっこよく戦場、しかも海の上だというのに出てくるたびに違うドレス(笑)甲冑要素はあるがほぼドレス!奇抜だけどかっこいいデザインはレディガガよりよっぽどいけてると思う。
主人公のギリシア軍主人公との濡れ場もあり、あぁ、、こりゃR-18納得のクオリティ。


戦いはペルシアの大型船相手にギリシアの小型船が機動性と戦術で当初圧倒する流れ。手扱ぎなのにめちゃスピード感があって、大型船もじゃんじゃん壊れて、見応えばっちり。そして相変わらず筋肉隆々の戦士達、剣で人がざくざくと切れていく。。。スピード感ばっちりで飽きない、面白かったっす。




2015年1月10日土曜日

【Rails】Sitemapの更新

 Sitemapをつくってwebmastertoolから送信するのは、googleのクロール最適化の第一歩だが、Sitemapを手動でメンテナンスするのは大変。
そんなときは sitemap_generator を使う

script/sitemap.rb


SitemapGenerator::Sitemap.default_host = "http://www.hogehoge.com"

SitemapGenerator::Sitemap.create do
# Put links creation logic here.
#
# The root path '/' and sitemap index file are added automatically for you.
# Links are added to the Sitemap in the order they are specified.
#
# Usage: add(path, options={})
# (default options are used if you don't specify)
#
# Defaults: :priority => 0.5, :changefreq => 'weekly',
# :lastmod => Time.now, :host => default_host
#
# Examples:
#
# Add '/articles'
#
# add articles_path, :priority => 0.7, :changefreq => 'daily'
#
# Add all articles:
#
# Article.find_each do |article|
# add article_path(article), :lastmod => article.updated_at
# end

add '/about', priority:0.9
add '/rule', priority:0.5
add '/registration', priority:0.8
add '/inquiry', priority:0.1
Category.all.each do |cate|
next if cate.id/100==99
pri = 0.5
pri = 0.8 if cate.id/100 == 0
add category_path( cate.id ), priority:pri
end
Site.all.each do | site |
add site_path( site.id ), priority: 0.5, lastmod: site.updated_at
end
end
SitemapGenerator::Sitemap.ping_search_engines


トップページは明示的にaddしてかかなくてもsitemap.xmlの先頭にはいる。

実行


$ bin/rails r script/sitemap.rb
In '/deploy/hogehoge/current/public/':
+ sitemap.xml.gz 1890 links / 16.3 KB
Sitemap stats: 1,890 links / 1 sitemaps / 0m01s

Pinging with URL 'http://www.hogehoge.com/sitemap.xml.gz':
Successful ping of Google
Successful ping of Bing


publicの下に sitemap.xml.gz ができる。解凍しなくてもクローラーはそのままでsitemapとして読んでくれるので、
robot.txtとwebmastertoolに登録してあるsitemapのパスを代えておく。CGM的なコンテンツならこのスクリプトを
自動でまわしておけばOK.

余談: googleへのsitemap.xml ping

上のスクリプトを動かすと、最後に「Successful ping of Google」とでる。
webmastertoolからSitemapの送信はできるがログインがいる。どうやってやるのかと思ったら、
googleへはgetでパスを送るがけでsitemapの更新を知らせることができるんだ。

http://www.google.com/webmasters/sitemaps/ping?sitemap=http://www.direc.biz/sitemap.xml

Bingも同様なんだろう。

2015年1月9日金曜日

【Ruby】クラスインスタンス変数という罠

 @var は インスタンス変数、 @@var はクラス変数というのはrubyの基礎だが、 @var をクラスメソッドのスコープ内で定義した場合はクラスインスタンス変数というらしい。インスタンス変数のつもりで使うと、スコープはクラス変数にちかく予期せぬ動きになってはまる。継承したクラスからはアクセスできないので 他の言語のprivateに近いか。



# coding:utf-8

class Test

def self.set v
@var = v
end

def self.get
@var
end

end

Test.set("first desu.")
Test.set("second desu.")

p Test.get
p Test.get



実行結果


$ ruby /tmp/test.rb
"second desu."
"second desu."


 間違えやすいわりに、使いどころが不明だ。

2015年1月7日水曜日

【Rails】Parallelで並列処理すると"DEPRECATION WARNING: Database connections will not be closed automatically, please close your database connection at the end of the thread by calling `close` on your connection. For example: ActiveRecord::Base.connection.close" と警告がでる

 ループ処理の高速化のためParallelでマルチスレッドにすると、処理後に、「DEPRECATION WARNING: Database connections will not be closed automatically, please close your database connection at the end of the thread by calling `close` on your connection. For example: ActiveRecord::Base.connection.close」という警告がでる。
コネクションを自分で閉じろとな、ふむふむ。言わんとしていることは分かるので、警告に従いclose処理をいれる。


Parallel.map_with_index( array, :in_threads=> 4 ) do | data, idx |
begin
# やりたい処理
rescue => ex
Rails.logger.error "エラーです。"
ensure
ActiveRecord::Base.connection.close
end
end

しかし警告は消えない。ホワイ? 結局解決方法はみつからず。しかし何回動かしてもプログラムの動作には
問題ないし、接続先のDBで接続すうを見てみると。

プログラム実行時


mysql> show status like 'Threads_connected';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 24 |
+-------------------+-------+
1 row in set (0.01 sec)

プログラム終了後


mysql> show status like 'Threads_connected';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 14 |
+-------------------+-------+

プログラム終了後に接続数はちゃんと減っているので大丈夫なんではないだろうか。

2015年1月6日火曜日

【RubyOnRails】オートコンプリート rails3-jquery-autocomplete を使うより jquery-ui のautocompleteを直接使う方がよかった。

 Railsでオートコンプリートというとrails3-jquery-autocompleteが主流なようだが、
  • マルチバイト文字を使うとき2文字以上入力しないと候補が表示されない
  • 該当する候補がないときにでる「no existing match」を消せない、且つ文字もかえれないっぽい。
という問題がある。
特に1つ目は今作ってるサービスはオートコンプリートで出したい候補に2・3文字のものが多いので、2文字手で打った段階でサジェストだとほぼ意味がない。
というわけでjquery-uiを使ってみる。
rails3-jquery-autocompleteも中ではjquery-uiのautocompleteを使っているようなので、同じことができるはず。

このブログの記事を参考にさせてもらいました。

ただ、同じページにオートコンプリートしたいテキストボックスが複数ある場合だとこのサンプルだとうまくいかないので、ちょっと修正。

■HTML










■Javascript





1文字入れただけで候補が表示されるオートコンプリートを実現できた。
次はフォーカスした段階で、候補(この時点では絞り込まれていないリスト全体)を表示できるようにしたい。

2015年1月5日月曜日

【Javascript】consoleに出力したときに [object Object] とでる変数の中身を見るには

 jsのデバッグは console.log をつかうけど、


function( val ){
console.log("val="+val);
}

みたいにした場合、valがオブジェクトだと、
consoleには


val=[object Object]


って表示されてよくわからない。
中身をみるには、その変数だけを console.logに渡す。


function( val ){
console.log( val );
}


そしたらconsoleで中身を開けていける。



2015年1月4日日曜日

さくらVPSでcronに設定したRailsのバッチが動かなくてはまった。

こんな漢字でRailsのscript下にあるバッチのプログラムをcrontabに設定していたがまったく動かない。


0 3 * * * cd /deploy/my_app/current; bin/rails runner -e production "MyBatch.new.run"


手で叩くと動くし、echoするだけのコマンドならcrontabで実行できる。cronのログを見てみると、ちゃんと実行しようとしているし。。。。
切り分けで ファイルを作成するだけのrubyのプログラムを作ってそれをcrontabに設定してみたらそれも動かない。ここまでやって、rubyのPATHがとってないってことに気づいた。
それで下記のようにcrontabの冒頭でPATH設定して解決。


PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/goy/bin

0 3 * * * cd /deploy/my_app/current; bin/rails runner -e production "MyBatch.new.run"



これも前にもはまった記憶があるな。。。
けっきょくRails関係ないLinuxの基本的なお話。

2015年1月3日土曜日

NAVY SEALS 観た。

 年末年始の休みになんか見ようということでレンタル。息子と一緒にTSUTAYAにいくと、まったく落ち着いて選べない。「アンパンマンのところ行く~」の連発。しかもなにやらトイレ我慢しているっぽいモジモジ君。。。しかし「おしっこでない!」っていや、我慢してるだろ絶対!

 っということで失敗しないようヒューマン系はさけてドンパチ系のわかりやすそうなやつに。

 軍人ものって、死と隣り合わせなこと、で隊員にも家族がいてその家族はいつも急に家族を失うことになるかもしれないという恐怖に耐えなきゃいけないことを強いられる職業だ ということがしっかり描かれている。特殊部隊をヒーローナイズする系のものだとそこらへんはアクションの引き立て程度っていうか、描かれてないのが普通だし、こっちの方がリアルだよな。
 そういう職業絶対無理だわ。おれはITエンジニアでよかった。きっとリアルにそういう職業のひとも若いときはそういうこと考えないで、危険な作戦に身をさらすことにスリルや使命感を感じるってことばっかりで入隊したりするのかもだけど、後で家族ができるときっと考えるんだろうなぁ。(俺も前のブラックな会社はある意味特殊部隊級で、子供が生まれるから考えて転職したな。ぜんぜんレベル違う話だけど。)

 メキシコ?の市街の一角が麻薬カルテルに占拠されてて、警察も手出しできなくて、その地下にアメリカまで伸びてる大規模なトンネルが複数あるって本当なんか? 

 http://commonpost.info/?p=80073
 あ、本当なんだぁ、メキシコすごいな。。。

 職務に忠実でも、隊員が大事でも、勇敢でも、マニュアルにどうあったって、
 自分の死確実って行為を1秒で決めて実行できるかって、その場にならないとわからないなぁ。
 今シュミレーションする限りは、無理だな。
1.5秒で次善の策を考えてそれにしそう。それでもいいよな。うん、きっといいはず。