RESTful APIにおいて一対多関係のデータ構造をGrape::Entityを使って表現する

RubyでRESTfulなAPIサーバを作るのに役立つ[Grape](https://github.com/intridea/grape)ですが、ぜひとも一緒に使いたいのが[Grape::Entity](https://github.com/intridea/grape-entity)です

[Grape::Entity](https://github.com/intridea/grape-entity)はView的な役割を果たし、DBの値を`present`するだけで、Entityで指定したデータ構造を作ってくれ、最終的な出力整形を[Grape](https://github.com/intridea/grape)が行ってくれます

これで、DBからの値をmap等を駆使してHash構造を作る...そんな人生とは決別です

日本語情報が少ないのですが、コードサンプルやspecが充実しているので、実装で困ることはありませんが、一対多なデータ構造を表現する方法が乏しかったので、メモ代わりに残しておきます

- Ruby 2.1.5
- Grape 0.11.0
- Grape::Entity 0.4.4

データ構造
----------

ParentとChildrenテーブルを用意、Parent/Childというモデル名で定義しています
Parent:Child = 1:N です

※ORMに[Sequel](http://sequel.jeremyevans.net/)を使っていますが、ActiveRecordでも同じです

```rb:001_devel_collection.rb
Sequel.migration do
  up do
    create_table :children do
      primary_key :id
      Integer :parent_id
      String  :name
      String  :birth_day
    end

    create_table :parents do
      primary_key :id
      String :name
      String :addr
    end
  end

  down do
    drop_table :children
    drop_table :parents
  end
end
```

```rb:app/models/child.rb 
class Child < Sequel::Model
  many_to_one :parent
end
```

```rb:app/models/parent.rb 
class Parent < Sequel::Model
  one_to_many :child
end
```

API実装
-------

Grape自体のインストール等はconfig.ruについては割愛

```rb:api.rb
module API::Entities
  class Child < Grape::Entity
    expose :name, as: "name"
    # birth_day column in DB does not expose (hide)
  end

  class Parent < Grape::Entity
    expose :name,  as: "parent_name"
    expose :addr
    expose :child, as: "children", using: API::Entities::Child
  end
end

class MyAPI < Grape::API
  prefix "/api"
  format :json
  desc "Testing collection expose w/ grape-entity"
  get "/collection/:name" do
    pt = Parent.find(name: params[:name])
    present pt, with: API::Entities::Parent
  end
end
```

これで、下記の通り`children`というHash keyの下に配列でchildが並びます

```console
$ curl http://localhost:9292/api/collection/pname1
{"parent_name":"pname1","addr":"Streat Name","children":[{"name":"c1"},{"name":"c2"}]}
```

RSpec
-----

curlでテストするのは忍びないので、RSpecも置いておきます

`spec/spec_helper.rb`は割愛しますが、このあたりのgemを使っています

```rb
require "rack/test"
require "rspec/its"
require "rspec/json_matcher"
RSpec.configuration.include RSpec::JsonMatcher
require "json"
require "database_cleaner"
```

```rb:spec/api_spec.rb
require "spec_helper"

describe MyAPI do
  include Rack::Test::Methods
  def app ; described_class ; end

  before {
    p1 = Parent.create(name: "pname1", addr: "Streat Name")
    p1.add_child(name: "c1")
    p1.add_child(name: "c2")
    Parent.create(name: "p2")
  }

  context "get" do
    subject { get "/api/collection/pname1" }
    let(:result) do
      {
        parent_name: "pname1",
        addr:        "Streat Name",
        children:    [
                       {name: "c1"},
                       {name: "c2"}
                     ]
      }
    end
    its(:body) { should be_json_as result }
  end
end
```



まとめ
------

実装コードが、たった2行というところが非常によろしいです

```rb
    pt = Parent.find(name: params[:name])
    present pt, with: API::Entities::Parent
```


また、DBには`children.birth_day`というカラムを作成していますが、`API::Entities::Child`ではexposeの指定をしていないので、出力を隠すことに成功しています

DBからは取得されてしまっているので通信量では不利ですが、宣言的にコードが書けるため保守性は極めて高いのではないでしょうか