BSONでFixnumはサポート外?
mongodbを使っていてこんなエラーが出た。
Cannot serialize Fixnum as a BSON type; it either isn't supported or won't translate to BSON
FixnumはBSONの型としてシリアライズできない?んなはずないじゃん。
BSON.serialize({"A" => 123}) #=> #<BSON::ByteBuffer:0x100661fd8 @cursor=4, @int_pack_order="V", @buf=[12, 0, 0, 0, 16, 65, 0, 123, 0, 0, 0, 0], @order=:little_endian, @double_pack_order="E">
だよね。
でもエラーが出るので、発生していたテストを見ると、Hashの値の方が
d = {"foo" => 123.minutes}
という感じになっていたので、確認してみる
require 'active_support' #=> true BSON.serialize({"A" => 123.minutes}) BSON::InvalidDocument: Cannot serialize Fixnum as a BSON type; it either isn't supported or won't translate to BSON. from /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mm0.3/gems/bson-1.0/lib/../lib/bson/bson_ruby.rb:589:in `bson_type' from /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mm0.3/gems/bson-1.0/lib/../lib/bson/bson_ruby.rb:138:in `serialize_key_value' from /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mm0.3/gems/bson-1.0/lib/../lib/bson/bson_ruby.rb:111:in `serialize' from /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mm0.3/gems/bson-1.0/lib/../lib/bson/bson_ruby.rb:111:in `each' from /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mm0.3/gems/bson-1.0/lib/../lib/bson/bson_ruby.rb:111:in `serialize' from /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mm0.3/gems/bson-1.0/lib/../lib/bson/bson_ruby.rb:85:in `serialize' from /Users/takeshi/.rvm/gems/ruby-1.8.7-p249@mm0.3/gems/bson-1.0/lib/bson.rb:6:in `serialize' from (irb):10
ついでに、こういうので変換された値のクラスはFixnumなんだけど、
d1 = 123.hours #=> 442800 seconds d1.class #=> Fixnum class << d1; self; end #=> #<Class:#<ActiveSupport::Duration:0x10175e4d0>>
実際には、ActiveSupport::Durationが特異クラス。
で、ActiveSupport::Durationのソースコードによると、is_a?が、こんな感じになってるので、
def is_a?(klass) #:nodoc: klass == Duration || super end
ActiveSupport::Durationをis_a?に渡すと判断できる
123.is_a?(ActiveSupport::Duration) #=> false 123.hours.is_a?(ActiveSupport::Duration) #=> true
だから、
d = {"foo" => 123.minutes} d.each do |key, value| d[key] = value.to_i if value.is_a?(ActiveSupport::Duration) end
としてあげれば、BSONでserialize可能なデータになります。
case に注意
caseに対応してなかったりするので、要注意。
def is_int?(val) case val when Integer then true else false end end #=> nil is_int?(123) #=> true is_int?(123.hours) #=> false