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の応答が欲しいか書きます。
# -*- 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で通すパラメータを定義したメソッドを用意しています。
# -*- 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で他のビューを入れ子に出来ます。
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さんを見ると・・・凄いです。勉強させて頂こうと思います!(業務中に学んだことを効率よくアウトプットする術を身につけたい・・・)
謝辞
以下の記事は何度も読み返し大変お世話になりました。