振る舞いが把握しきれずどうにも気持ち悪いが、期待しない振る舞いを避ける方法は見つかったので一旦まとめ。
https://github.com/kyanny/playground/tree/gh-pages/grape-entity-collection-but-not-array
Itemに対するItemEntityがある場合、ItemEntity.new(item)とItemEntity.represent(item)は同じ意味になる- どちらの
as_jsonto_jsonを呼んでもちゃんと期待するものを返す
- どちらの
ItemのコレクションとしてItemsがある場合、ItemEntity.new(items).as_jsonもItemEntity.represent(items).as_jsonも例外を吐くItemEntity missing attributename' on #<Items:0x007fd0f09dfa80>` みたいな感じ- 何故かというと grape-entity は与えられた object が
to_aryに反応するときのみ object を配列とみなし、個々の要素を entity クラスでラップするから include Enumerableはto_aryを実装しないので grape-entity からはitemsがitemのコレクションであることがわからないas_jsonを呼ぶ前の entity クラスのインスタンスも#<ItemEntity:0x007fd0f09decc0 @object=#<Items:0x007fd0f09dfa80 @names=["taro", "hanako"]>, @options={}>のようになっている
to_aryが実装されているとItemEntity.newは変わらずだがItemEntity.representは期待した通りitemをItemEntityで表現したものの配列を返す- 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 クラスのインスタンスの内部表現の時点ですでに異なっている
- new の場合
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 ではダメ)