Hatena::Groupsubtech

#生存戦略 、それは

-> 12 { 01 / 20 }

Ruby 1.9 で起動オプションで外部エンコーディングを指定する

17:57 | はてなブックマーク - Ruby 1.9 で起動オプションで外部エンコーディングを指定する - #生存戦略 、それは

1.9 をよく解ってないオールドタイプの secondlife ですこんばんは。Ruby 1.9 では外部エンコーディングの Encoding.default_external はロケール( $LANG )によて変化する。

$ echo $LANG
ja_JP.UTF-8
$ ruby -e "puts Encoding.default_external"
UTF-8
$ echo $LANG
C
$ ruby -e "puts Encoding.default_external"
US-ASCII

外部エンコーディングを考慮してない、かつ UTF-8 を扱うスクリプトだとエラーになったりする。そんなときは

ruby -EUTF-8 script

で起動オプションで指定してやると良い。gem ライブラリのような場合

ruby -EUTF-8 -S command

とすると path から探索してくれる便利!後ろの席の Ruby コミッターに教えていただきました。

トラックバック - http://subtech.g.hatena.ne.jp/secondlife/20120120

-> 12 { 01 / 17 }

Ruby で解りにくい例外の発生元を調べる

18:23 | はてなブックマーク - Ruby で解りにくい例外の発生元を調べる - #生存戦略 、それは

例えばインテグレーションテストで

     Failure/Error: 
       expected there to be content Internal Server Error undefined method `name' for #<Hash:0x11b7d7578> WEBrick/1.3.1 (Ruby/1.8.7/2011-12-28) at 127.0.0.1:62045"
     # ./spec/integration/foo_spec.rb:174

みたいな例外が起きたとき、ぱっと見どこから呼び出させるのか解らなくて生きるのが辛い。name ぐらい汎用的な名前だと grep で引っかからないし。そんなときは

class Hash
  def name
    require 'pp'; pp caller.to_a
  end
end

みたいにオーバーライドしてメソッド定義しちゃうとあら簡単に解ります!特にこの場合なら spec/integration/foo_spec.rb の該当行らへんにかけば影響範囲も単体テストなら少なく探しやすい!

なんか泥沼っぽいですけど知っておくと便利!!同僚に教えて貰った。

トラックバック - http://subtech.g.hatena.ne.jp/secondlife/20120117

-> 12 { 01 / 16 }

AWS のリージョン/エンドポイント一覧と aws-sdk での設定

21:10 | はてなブックマーク - AWS のリージョン/エンドポイント一覧と aws-sdk での設定 - #生存戦略 、それは

AWS の各種サービスの API を叩こうとするとエンドポイントの指定が必要。いつも探すときググったりして面倒だったんだけど一覧ページあったんですね便利!!(なかなかたどり着けなかった…)

s3 のエンドポイントはサブドメイン区切りじゃなくて - (ハイフン) 区切りだったりしてカオスな…。

で、ruby のオフィシャルライブラリの aws-sdk でこのエンドポイントを設定するんですが、こいつの設定方法もソースの読み方解らないとハマリがちで、例えば SimpleDB だったら SimpleDB のライブラリの何処かに書いてあるかと思いきや、最上位のコンフィグで指定するので SimpleDB 以下のコードを読んでも解らない。正解は aws/core.rb か aws/core/configuration.rb にのってるので、それらの apidoc かソース見ればおkな感じ。うーん。

トラックバック - http://subtech.g.hatena.ne.jp/secondlife/20120116

-> 12 { 01 / 04 }

Ruby 1.8.7 の hashdos 対応による挙動の変更点

19:24 | はてなブックマーク - Ruby 1.8.7 の hashdos 対応による挙動の変更点 - #生存戦略 、それは

Ruby 1.8.7-p356 以前の人は p357 もしくは 1.9 にとっととあげよう、という話しなんだけど p357 にしたら一部テストがこけた。p357 ではハッシュの seed が起動毎に異なる*1ので

for i in {1..10}; do ruby-1.8.7-p357 -e 'p ({"fooooo"=>1,"baaaaar"=>2}).keys'; done
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["baaaaar", "fooooo"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["baaaaar", "fooooo"]
["baaaaar", "fooooo"]
["baaaaar", "fooooo"]

という結果になる。これが以前だと

for i in {1..10}; do ruby-1.8.7-p334 -e 'p ({"fooooo"=>1,"baaaaar"=>2}).keys'; done
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]
["fooooo", "baaaaar"]

なため、Hash#keys の戻り値の順序が一定だ、と書いていたテストがこけるようになった。(そもそも順序が一定前提のテストが悪いんだけど)

うっかりダメな実装してる人はこれで挙動が事なり出したりするんじゃないかなぁ。

toydevtoydev2012/01/05 10:26むしろ 1.8 系で順序の保障がされているバージョンがあったことに驚きです^^;
1.8 系は順序保障なし。1.9 系は順序保障ありだと勝手に思ってました。
順序保障が必要な場合、1.8 でも 1.9 でも動くように Hash ではなく Array(偶数がキー、奇数が値) を使っています。格納数が小さい場合だけですが。

secondlifesecondlife2012/01/05 12:001.8 は順序保証されてないです(そのために ActiveSupport::OrderdHash などがあるので)。
たまたまそういう挙動になっていて、その挙動前提で書かれたコードが問題になるかなぁと。

トラックバック - http://subtech.g.hatena.ne.jp/secondlife/20120104

-> 11 { 12 / 20 }

mongodb で capped と capped でないコレクションの変換

13:24 | はてなブックマーク - mongodb で capped と capped でないコレクションの変換 - #生存戦略 、それは

現状ふつうには出来ないぽいので、いったんテンポラリテーブルをつくって、そこに書き込み、最後 rename する、みたいな。昔の RDB を思い出す感じ…。

たとえば capped な DB にしたい場合は

db.noncapped.validate(); // validate で size, max がどれぐらい必要か見積もっておく
change_collection_type(db.noncapped, {capped: true, size: 1000000000});
db.noncapped.validate(); // capped:1 になってる

change_collection_type はこれ。ほんと JS すな〜。bulk insert じゃないので速度遅いけど、500Mぐらいのオンメモリなデータなら10秒ぐらいで終わった。だれか bulk insert 版書いて下さい。あとこのコレクションに CRUD の操作が走ってる場合、アップデート中その操作は失われると思います。(ロックされないので)

function change_collection_type(collection, options) {
  var collection_name = collection.getName();
  var tmp_name = 'tmp_change_collection.' + collection_name;
  var remove_name = 'tmp_change_collection.remove.' + collection_name;

  var collection = db[collection_name];
  db.createCollection(tmp_name, options);
  var tmp = db[tmp_name];

  collection.find().forEach(function (d) { tmp.insert(d) });

  collection.renameCollection(remove_name);
  tmp.renameCollection(collection_name);
  db[remove_name].drop();
}
トラックバック - http://subtech.g.hatena.ne.jp/secondlife/20111220