Share
Embed
Trailblazerを業務で使ってみた
Trailblazer::Tokyo #1 (http://connpass.com/event/20137/) にて発表
kbaba1001
September 20, 2015
Transcript
45 slides
-
Trailblazer を業 務で使ってみた Trailblazer を業 務で使ってみた @kbaba1001 Powered by Rabbit
2.1.6 and COZMIXNG -
APIリクエストの妥当性 Grapeの例 desc 'Return a status.' params do requires :id,
type: Integer, desc: 'Status id.' end route_param :id do get do Status.find(params[:id]) end end -
フォームオブジェクト module ShopService class Search include ActiveModel::Model attr_accessor :latitude, :longitude
validates :latitude, presence: true, numericality: true validates :longitude, presence: true, numericality: true def initialize(params = {}) @latitude = params[:latitude] @longitude = params[:longitude] end def collections # search は scope Shop.search(latitude: latitude, longitude: longitude) end end end -
コントローラ class ShopsController < ApplicationController def search service = ShopService::Search.new(params)
if service.valid? @shops = service.collections else raise( InvalidParamatersError, service.errors.full_messages.join(', ') ) end end end -
テスト describe 'ShopService::Search' do specify do shop = FactoryGirl.create(:shop, latitude:
35.0, longitude: 138.0 ) service = ShopService::Search.new( latitude: 35.0, longitude: 138.0 ) expect(service).to be_valid expect(service.collections).to include(shop) end end -
入れ子 params = { northeast: {latitude: 35.0, longitude: 138.0}, southwest:
{latitude: 34.0, longitude: 137.0} } これを ActiveModel で扱う…? -
ActiveModelで入れ子 module ShopService class Search include ActiveModel::Model attr_accessor :northeast_latitude, :northeast_longitude
attr_accessor :southwest_latitude, :southwest_longitude with_options(presence: true, numericality: true) do |v| v.validates :northeast_latitude, :northeast_longitude v.validates :southwest_latitude, :southwest_longitude end def initialize(params = {}) @northeast_latitude = params[:northeast][:latitude] @northeast_longitude = params[:northeast][:longitude] @southwest_latitude = params[:southwest][:latitude] @southwest_longitude = params[:southwest][:longitude] end end end -
Reform module ShopService class Search < Reform::Form property :northeast do
property :latitude property :longitude validates :latitude, presence: true, numericality: true validates :longitude, presence: true, numericality: true end property :southwest do property :latitude property :longitude validates :latitude, presence: true, numericality: true validates :longitude, presence: true, numericality: true end def collections # search は scope Shop.search(northeast: northeast, southwest: southwest) end end end -
Controller class ShopsController < ApplicationController def search service = ShopService::Search.new(Shop.new)
if service.validate(params) @shops = service.collections else raise( InvalidParamatersError, service.errors.full_messages.join(', ') ) end end end -
ShopService::Create module ShopService class Create < Reform::Form property :latitude property
:longitude property :prefecture_code, virtual: true validates :latitude, presence: true, numericality: true validates :longitude, presence: true, numericality: true validates :prefecture_code, presence: true end end -
Controller class ShopsController < ApplicationController def create service = ShopService::Create.new(Shop.new)
if service.validate(params) service.save do |hash| prefecture = Prefecture.find_by(code: service.prefecture_code) service.model.create( hash.except(:prefecture_code).merge(prefecture: prefecture) ) end head :no_content else raise( InvalidParamatersError, service.errors.full_messages.join(', ') ) end end end -
save を上書き module ShopService class Create < Reform::Form # property
は省略 def save super do |params| prefecture = Prefecture.find_by(code: prefecture_code) model.create( params.except(:prefecture_code).merge(prefecture: prefecture) ) yield params if block_given? end end end end -
Controller class ShopsController < ApplicationController def create service = ShopService::Create.new(Shop.new)
if service.validate(params) service.save head :no_content else raise( InvalidParamatersError, service.errors.full_messages.join(', ') ) end end end -
Service Spec describe 'ShopService::Create' do specify do shop = Shop.new
service = ShopService::Create.new(shop) validation_result = service.validate( latitude: 35.0, longitude: 138.0, prefecture_code: 'AE01', ) expect(validation_result).to be(true) service.save shop.reload! expect(shop.latitude).to eq(35.0) expect(shop.longitude).to eq(138.0) expect(shop.prefecture.code).to eq('AE01') end end -
Request Spec describe 'shop api' do describe 'POST /api/v1/shop' do
specify do post '/api/v1/shop', { latitude: 35.0, longitude: 138.0, prefecture_code: 'AE01' } expect(response.code).to be(204) expect(Shop.count).to be(1) end specify '異常系', rambulance: true do post '/api/v1/shop' expect(response.code).to be(400) expect(response.body).to include('緯度を入力してください') expect(response.body).to include('経度を入力してください') end end end -
before describe 'ShopService::Search' do specify do shop = FactoryGirl.create(:shop, latitude:
35.0, longitude: 138.0 ) service = ShopService::Search.new( latitude: 35.0, longitude: 138.0 ) expect(service).to be_valid expect(service.collections).to include(shop) end end -
after describe 'ShopService::Search' do specify do shop = ShopService::Create.new(Shop.new).validate( latitude:
35.0, longitude: 138.0, prefecture_code: 'AE01', ).save service = ShopService::Search.new( latitude: 35.0, longitude: 138.0 ) expect(service).to be_valid expect(service.collections).to include(shop) end end -
Refor -> Trailblazer class Shop < ActiveRecord::Base class Create <
Trailblazer::Operation contract do property :latitude property :longitude property :prefecture_code, virtual: true validates :latitude, presence: true, numericality: true validates :longitude, presence: true, numericality: true validates :prefecture_code, presence: true end def process(params) @model = Shop.new validate(params, @model) do |f| prefecture = Prefecture.find_by(code: f.prefecture_code) @model.create( params.except(:prefecture_code).merge(prefecture: prefecture) ).save end end end end -
Controller class ShopsController < ApplicationController include Trailblazer::Operation::Controller def create run
Shop::Create do |op| head :no_content and return end raise( InvalidParamatersError, @model.errors.full_messages.join(', ') ) end end -
Factory # before shop = ShopService::Create.new(Shop.new).validate( latitude: 35.0, longitude: 138.0,
prefecture_code: 'AE01' ).save # after shop = Shop::Create[ latitude: 35.0, longitude: 138.0, prefecture_code: 'AE01' ].model -
感想 Rails にも MVC 以外の層を作っ てもいい 小さな gem を組み合わせる導 入の柔軟さ
テストしやすさを重視 apotonick先生は何個gem作っ てるの…? Powered by Rabbit 2.1.6 and COZMIXNG