@kyanny's blog

My thoughts, my life. Views/opinions are my own.

grape-entity と Array

振る舞いが把握しきれずどうにも気持ち悪いが、期待しない振る舞いを避ける方法は見つかったので一旦まとめ。

https://github.com/kyanny/playground/tree/gh-pages/grape-entity-collection-but-not-array

  • Item に対する ItemEntity がある場合、 ItemEntity.new(item)ItemEntity.represent(item) は同じ意味になる
    • どちらの as_json to_json を呼んでもちゃんと期待するものを返す
  • Item のコレクションとして Items がある場合、 ItemEntity.new(items).as_jsonItemEntity.represent(items).as_json も例外を吐く
    • ItemEntity missing attributename' on #<Items:0x007fd0f09dfa80>` みたいな感じ
    • 何故かというと grape-entity は与えられた object が to_ary に反応するときのみ object を配列とみなし、個々の要素を entity クラスでラップするから
    • include Enumerableto_ary を実装しないので grape-entity からは itemsitem のコレクションであることがわからない
    • as_json を呼ぶ前の entity クラスのインスタンスも #<ItemEntity:0x007fd0f09decc0 @object=#<Items:0x007fd0f09dfa80 @names=["taro", "hanako"]>, @options={}> のようになっている
  • to_ary が実装されていると ItemEntity.new は変わらずだが ItemEntity.represent は期待した通り itemItemEntity で表現したものの配列を返す
    • new の場合 #<ItemEntity:0x007faed3a0a120 @object=#<Items:0x007faed3a0a6e8 @names=["taro", "hanako"]>, @options={}>
    • represent の場合 [#<ItemEntity:0x007faed3a091f8 @object=#<Item:0x007faed3a096d0 @name="taro">, @options={:collection=>true}>, #<ItemEntity:0x007faed3a08fa0 @object=#<Item:0x007faed3a09608 @name="hanako">, @options={:collection=>true}>]
    • entity クラスのインスタンスの内部表現の時点ですでに異なっている
  • to_ary を実装したくない場合は items.map { |item| ItemEntity.new(item) }.as_json でも同じ結果が得られる
  • grape-entity の root とかが絡むとまたややこしくなるが、結局 [{...}, {...}] ではなく {"key": [{...},{...}]} という形にしたい時に使うものっぽいので、裸の Array で返したいときは無視してよい。

まとまっていないが要するに、

  • entity として表現したいオブジェクトが Enumerable の場合は items.map { |item| Entity.new(item) }.as_json する(new のかわりに represent でも可)
  • entity として表現したいオブジェクトが Array の場合(items << item)は Entity.represent(items) する(new ではダメ)