Help us understand the problem. What is going on with this article?

RailsとGrapeで行う最高のWeb API開発

More than 5 years have passed since last update.

Ruby Advent Calendar 2013 2日目

るびぃあどぅべんどぅくぁれんだー2013、2日目の記事となります。昨日はすうぱぁももんがさんのあくろばてぃっくな今年こそRubyを始めたいあなたに!ももんが流・最強のRuby学習法です。

概要

三回ほど手を変え品を変えWeb APIをRubyで作ってきました。ここではそこから学んだ今の私の全力全開、最高のWeb API開発についてコード例を交えてお話したいと思います。ここで言うWeb APIとはスマホアプリから使用する、サーバに置いてあるAPIをイメージして頂ければと思います。
ここではユーザが写真をサーバに保存できるAPIを想定し、応答のフォーマットはJSONとします。

使うもの

rails 4

Rails 3ではなくRails 4を使うのは趣味です。

grape

APIを書く際に非常に楽になります。APIで使用するURLとHTTPメソッドの構造をベースとしたDSLを使用することで、該当のAPIとコードの対応が非常に分かりやすく書けます。また一つのファイルにAPIのコントローラ全てが纏めることができ、コード全体を非常に把握しやすくなります。

rabl

JSONのビューを書く際に非常に便利なテンプレートエンジンです。ビューを使いまわして入れ子にしたりすることも出来るので、複雑なJSONを生成する際には欠かせません。

json_expressions

RSpecでJSONをテストする際に非常に便利なmatcharです。Rubyで予期されるJSONの構造をまんま書くことで、それにマッチするか判定してくれます。マッチしなかった時のエラーも単なる文字列比較に終わるのではなく、どの属性の値が違うとか配列の要素の数がおかしいとか賢い指摘をしてくれます。

その他

おなじみRailsで使うgemですがGrapeでも使えます。

  • kaminari
    • ページングに役立つgemです。
  • paperclip
    • おなじみのRailsで画像を取り扱うgemです。
  • oj
    • JSON生成に使うgemでなんかすっごいはやいらしい。
  • factory_girl_rails
    • おなじみのテスト用オブジェクト作成gem。
  • ffaker
  • faker-japanese
    • テスト用文字列を作成するgem。後者は日本語の文字列も生成できます。

コードはこんな風になります

この環境での開発を始めてからテスト駆動しやすくなりました。

テスト

it_behaves_like('201')は返ってきたレスポンスがlet(:result)で定義したJSONと一致するか、またレスポンスコードが201であるかを見ています。*_matcharというのは別所で定義した正規表現です。要はどんなURLにどんなメソッドでどんなパラメータを送ったらどんなJSONの応答が欲しいか書きます。

spec/requests/pictures_spec.rb
# -*- encoding: utf-8 -*-
require 'spec_helper'

describe API do
  describe 'Pictures' do
    let(:user) { FactoryGirl.create(:user) }

    describe 'POST /api/pictures' do
      let(:image) { fixture_file_upload('sample.jpg', 'image/jpeg') }

      context '正常な投稿' do
        subject { post '/api/pictures', { api_key: user.api_key, image: image } }

        let(:result) do
          {
            picture: {
              id: /\A\d+\z/,
              owner: {
                nickname: user.name,
              },
              hash: sha1_matchar,
              image_url: image_url_matchar,
              created_at: datetime_matchar,
              updated_at: datetime_matchar
            }
          }
        end

        it_behaves_like('201')
      end
    end
  end
end

コントローラ

このファイルにコントローラの全てが集約されます。URLの構造に沿ったコードの構造になっています。余談ですがRails 4で実装されたStrong Parametersに対応するためhelpersで通すパラメータを定義したメソッドを用意しています。

app/api/api.rb
# -*- encoding: utf-8 -*-
class API < Grape::API
  format :json
  formatter :json, Grape::Formatter::Rabl
  default_format :json

  helpers do
    def picture_params
      ActionController::Parameters.new(params).permit(:title)
    end
  end

  resource :user do
    # POST /api/user
    post do
      # ユーザ作成の処理が入ります
    end

    # PUT /api/user
    desc 'ユーザ更新' do
    put do
      # ユーザ更新の処理が入ります
    end
  end

  resource :pictures do
    # GET /api/pictures
    get '/', rabl: 'pictures' do
      @pictures = current_user.pictures
    end

    # POST /api/pictures
    post '/', rabl: 'picture' do
      @picture = current_user.pictures.build(picture_params)
      @picture.image = ActionDispatch::Http::UploadedFile.new(params[:picture]) if params[:picture]
      @picture.save!
      @picture.reload
    end

    resource ':picture_id' do
      # PUT /api/pictures/:picture_id
      put '/', rabl: 'picture' do
        # 写真の更新処理が入ります
      end
    end
  end
end

ビュー

rablを使いビューを記述します。extendsで他のビューを入れ子に出来ます。

app/views/api/picture.rabl
object @picture
attributes :hash, :image_url

child user: :owner do
  extends 'api/users/detail'
end

モデル

いつものRails。

終わりに

ステップバイステップで冒頭のAPIを作ろうとしましたが納期に間に合いませんでした!雰囲気を感じて貰えればと思います。
この開発方法の良さを語ります!!

  • コントローラが見やすい
    • URL構造そのまま
    • 複数のファイルに分散しない
  • JSONの応答を厳密にテストできる
    • わずかなデグレも見逃さない
  • ビューが使いまわせる
    • JSONのフォーマットの変更が簡単に出来る
  • Railsの資産を思う存分使える
  • Ruby愛してる

以上!
Ruby Advent Calendar 2013、3日目の担当はznzさんです。私はこの記事でやっと1 Contributionなのですが、znzさんを見ると・・・凄いです。勉強させて頂こうと思います!(業務中に学んだことを効率よくアウトプットする術を身につけたい・・・)

謝辞

以下の記事は何度も読み返し大変お世話になりました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした