のんびりSEの議事録

プログラミング系のポストからアプリに関してのポストなどをしていきます。まれにアニメ・マンガなど

5分でRails5のAPI modeを試してみた

Rails5のbeta2が公開されて、1ヶ月近く経とうとしています。

Rails5の新機能の一つAPI modeをさくっとやってみて、どんな感じになるのか見てみたいと思い、やってみました。

API modeについて

json出力のみに特化した、コンパクトなFWを提供してくれる機能

www.atmarkit.co.jp

Install

$ gem install rails -v 5.0.0.beta2 --pre
$ gem list | grep rails
rails (5.0.0.beta2)
rails-deprecated_sanitizer (1.0.3)
rails-dom-testing (1.0.7)
rails-html-sanitizer (1.0.3)
sprockets-rails (3.0.1)

railsアプリ作成

$ rails new backend --api
      create
      create  README.md
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/javascripts/application.js
      create  app/assets/javascripts/cable.coffee
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/assets/images/.keep
      create  app/assets/javascripts/channels
      create  app/assets/javascripts/channels/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/bundle
      create  bin/rails
      create  bin/rake
      create  bin/setup
      create  bin/update
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/secrets.yml
      create  config/cable.yml
      create  config/puma.rb
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/active_record_belongs_to_required_by_default.rb
      create  config/initializers/application_controller_renderer.rb
      create  config/initializers/assets.rb
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/callback_terminator.rb
      create  config/initializers/cookies_serializer.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/per_form_csrf_tokens.rb
      create  config/initializers/request_forgery_protection.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/favicon.ico
      create  public/robots.txt
      create  test/fixtures
      create  test/fixtures/.keep
      create  test/fixtures/files
      create  test/fixtures/files/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/test_helper.rb
      create  tmp
      create  tmp/.keep
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.keep
      remove  app/assets
      remove  lib/assets
      remove  tmp/cache/assets
      remove  vendor/assets
      remove  app/helpers
      remove  test/helpers
      remove  app/views
      remove  app/assets/javascripts
      remove  config/initializers/assets.rb
      remove  config/initializers/session_store.rb
      remove  config/initializers/cookies_serializer.rb
      remove  config/initializers/request_forgery_protection.rb
      remove  config/initializers/per_form_csrf_tokens.rb
         run  bundle install
Fetching gem metadata from https://rubygems.org/...........
Fetching version metadata from https://rubygems.org/...
Fetching dependency metadata from https://rubygems.org/..
Resolving dependencies...
Installing rake 10.5.0
Using concurrent-ruby 1.0.0
Using i18n 0.7.0
Using json 1.8.3
Using method_source 0.8.2
Installing minitest 5.8.4
Using thread_safe 0.3.5
Using builder 3.2.2
Using erubis 2.7.0
Using mini_portile2 2.0.0
Using nio4r 1.2.1
Using websocket-extensions 0.1.2
Using mime-types 2.99.1
Using arel 7.0.0
Using bundler 1.11.2
Installing byebug 8.2.2 with native extensions
Installing puma 2.16.0 with native extensions
Using thor 0.19.1
Installing redis 3.2.2
Installing spring 1.6.3
Installing sqlite3 1.3.11 with native extensions
Using rack 2.0.0.alpha
Using tzinfo 1.2.2
Using nokogiri 1.6.7.2
Using websocket-driver 0.6.3
Using mail 2.6.3
Using rack-test 0.6.3
Using sprockets 3.5.2
Using activesupport 5.0.0.beta2
Using loofah 2.0.3
Using rails-deprecated_sanitizer 1.0.3
Using globalid 0.3.6
Using activemodel 5.0.0.beta2
Using rails-html-sanitizer 1.0.3
Using rails-dom-testing 1.0.7
Using activejob 5.0.0.beta2
Using activerecord 5.0.0.beta2
Using actionview 5.0.0.beta2
Using actionpack 5.0.0.beta2
Using actioncable 5.0.0.beta2
Using actionmailer 5.0.0.beta2
Using railties 5.0.0.beta2
Using sprockets-rails 3.0.1
Using rails 5.0.0.beta2
Bundle complete! 7 Gemfile dependencies, 44 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.
         run  bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted

とりあえずのscaffold

$ bundle exec rails g scaffold Item name:string price:integer description:text
Running via Spring preloader in process 53777
      invoke  active_record
      create    db/migrate/20160223144234_create_items.rb
      create    app/models/item.rb
      invoke    test_unit
      create      test/models/item_test.rb
      create      test/fixtures/items.yml
      invoke  resource_route
       route    resources :items
      invoke  scaffold_controller
      create    app/controllers/items_controller.rb
      invoke    test_unit
      create      test/controllers/items_controller_test.rb

migrate

$ bundle exec rails db:migrate
== 20160223144234 CreateItems: migrating ======================================
-- create_table(:items)
   -> 0.0010s
== 20160223144234 CreateItems: migrated (0.0011s) =============================

routing確認

$ bundle exec rake routes
Prefix Verb   URI Pattern          Controller#Action
 items GET    /items(.:format)     items#index
       POST   /items(.:format)     items#create
  item GET    /items/:id(.:format) items#show
       PATCH  /items/:id(.:format) items#update
       PUT    /items/:id(.:format) items#update
       DELETE /items/:id(.:format) items#destroy

json固定のはずなのだが、(.:format)はまだ名残があるのか...

server起動

$ bundle exec rails s
=> Booting Puma
=> Rails 5.0.0.beta2 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
Puma 2.16.0 starting...
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:3000

curlで叩いてみる

create

$ curl -i "http://localhost:3000/items" -X POST -d "item[name]=item1" -d "item[price]=100" -d "item[description]=hogehogehoge"
HTTP/1.1 201 Created
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Location: http://localhost:3000/items/2
Content-Type: application/json; charset=utf-8
ETag: W/"6535c375d722504d841583154c4cd772"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 9af3f18d-3def-492c-8726-81360555c34b
X-Runtime: 0.006851
Transfer-Encoding: chunked

{"id":2,"name":"item1","price":100,"description":"hogehogehoge","created_at":"2016-02-23T14:53:13.653Z","updated_at":"2016-02-23T14:53:13.653Z"}% 

update

$ curl -i "http://localhost:3000/items/1" -X PUT -d "item[name]=item1" -d "item[price]=100" -d "item[description]=testtesttesttest"
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
ETag: W/"07496e9d5347840a6f8afe9beff31e3c"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 7bd1f736-9654-494a-9f30-7e779dfcf00b
X-Runtime: 0.010888
Transfer-Encoding: chunked

{"id":1,"name":"item1","price":100,"description":"testtesttesttest","created_at":"2016-02-23T14:52:44.706Z","updated_at":"2016-02-23T15:27:55.731Z"}% 

get

list

$ curl -i "http://localhost:3000/items"
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
ETag: W/"4611f00ddf692b8aefeaf254441373c4"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: fa956bfb-6856-41c0-a8df-d977342eb583
X-Runtime: 0.004649
Transfer-Encoding: chunked

[{"id":1,"name":"item1","price":100,"description":"testtesttesttest","created_at":"2016-02-23T14:52:44.706Z","updated_at":"2016-02-23T15:27:55.731Z"},{"id":2,"name":"item1","price":100,"description":"hogehogehoge","created_at":"2016-02-23T14:53:13.653Z","updated_at":"2016-02-23T14:53:13.653Z"}]% 

show

$ curl -i "http://localhost:3000/items/1"
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
ETag: W/"07496e9d5347840a6f8afe9beff31e3c"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 7cb6ae00-5e4a-4762-81ef-d7ef468fba2e
X-Runtime: 0.003824
Transfer-Encoding: chunked

{"id":1,"name":"item1","price":100,"description":"testtesttesttest","created_at":"2016-02-23T14:52:44.706Z","updated_at":"2016-02-23T15:27:55.731Z"}% 

delete

$ curl -i "http://localhost:3000/items/1" -X DELETE
HTTP/1.1 204 No Content
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Cache-Control: no-cache
X-Request-Id: fb8d04b3-b6d8-46ba-8836-5905a547fe6d
X-Runtime: 0.005990

生成されたcontroller

app/controllers/items_controller.rb

class ItemsController < ApplicationController
  before_action :set_item, only: [:show, :update, :destroy]

  # GET /items
  def index
    @items = Item.all

    render json: @items
  end

  # GET /items/1
  def show
    render json: @item
  end

  # POST /items
  def create
    @item = Item.new(item_params)

    if @item.save
      render json: @item, status: :created, location: @item
    else
      render json: @item.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /items/1
  def update
    if @item.update(item_params)
      render json: @item
    else
      render json: @item.errors, status: :unprocessable_entity
    end
  end

  # DELETE /items/1
  def destroy
    @item.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_item
      @item = Item.find(params[:id])
    end

    # Only allow a trusted parameter "white list" through.
    def item_params
      params.require(:item).permit(:name, :price, :description)
    end
end

おお、全てのrenderでjsonになっている、これはAPI実装時には楽かも