Garageをもうちょっと触ってみた
-
Upload
yoichi-toyota -
Category
Technology
-
view
233 -
download
3
Transcript of Garageをもうちょっと触ってみた
今日のおはなし
「Garageを触ってみた」の続きhttp://www.slideshare.net/rs_wisteria/garage-44341097
実際にお仕事で使ってみたので、ユースケース毎に機能を紹介します
CRUDCRUD=Create, Read, Update, Delete
リソースの作成・取得・更新・削除機能を実装する
RESTful APIの根幹をなす概念
HTTPメソッドのPOST, GET, PUT(PATCH),DELETEがそれぞれの機能
にアサインされる
CRUD(cont.)例) 住所録つくるよ!
● POST /addresses…住所の新規登録● GET /addresses…登録されている住所一覧取得● GET /addresses/1…IDが1の住所を取得● PUT /addresses/1…IDが1の住所を更新● DELETE /addresses/1…IDが1の住所を削除
CRUD(cont.)class AddressesController < ApplicationController include Garage::RestfulActions
def require_resources @resources = Address.where(is_active: true).all end
def require_resource @resource = Address.find_by(id: params[:id], is_active: true) end
def create_resource @resource = @resources.create(address_params) end
def update_resource @resource.attributes = address_params @resource end
def destroy_resource @resource.destroy @resource end
private
def address_params params.permit %i(name address phone_number) endend
CRUD(cont.)Garage::RestfulActions
require_resources…一覧取得・新規作成時に呼ばれる
require_resource…1件取得・更新・削除時に呼ばれる
create_resource…新規作成時に呼ばれる
update_resource…更新時に呼ばれる
destroy_resource…削除時に呼ばれる
Controller側でincludeし、上記メソッドをオーバーライドする
CRUD(cont.)@resources:一覧を表すリレーション
@resource: IDで指定された1件を表すモデル
例) 新規作成の場合@resources = Address.where(is_active: true).all@resource = @resources.create(address_params)
新規作成したリソースのis_activeはtrueになるので、一覧取得で取得可能
Garage::Representerモデルにincludeする
propertyメソッドで返す値を定義する
モデルのメソッドを指定可能● レコードのカラム名● 関連オブジェクト● その他、任意に定義されたメソッドなど
Garage::Representer(cont.)class Address < ActiveRecord::Base include Garage::Representer include Garage::Authorizable
belongs_to :user
property :user, selectable: true property :name property :address property :phone
...end
Garage::Representer(cont.)リクエスト時にfieldsパラメータを受け取る
返すフィールドを指定可能
● fields=*property指定されているすべてのフィールドを返す
● fields未指定property指定されているうち、selectable: true以外のものをすべて返す
● fields=<property1>,<property2>fieldsで渡されたpropertyのみを返す
Garage::Representer(cont.)GET /addresses/1
selectable: trueのuser以外を返す
GET /addresses/1?fields=*selectable: trueのuserも含めて返す
GET /addresses/1?fields=user,name指定されたuserとnameのみ返す
{ “name” : “Rin Shibuya”,
“address”: “Shibuya, Tokyo”,
“phone”: “03-1234-5678” }
{ “user” : { “id”: 1, “name”: “Uduki, Shimamura” },
“name” : “Rin Shibuya”,
“address”: “Shibuya, Tokyo”,
“phone”: “03-1234-5678” }
{ “user” : { “id”: 1, “name”: “Uduki, Shimamura” },
“name” : “Rin Shibuya”,
条件によるAPI呼び出し制限
以下みたいなユースケースを実現したい● 住所録のオーナー以外は編集できない● 友達以外の住所録は閲覧できない
以下のメソッドが実装されているとする
User#friend?(user_id):指定したIDのユーザーが友達かどうか
Garage::Authorizableモデルにincludeする
build_permissions(perms, other)self.build_permissions(perms, other, target)
モデル毎に、readとwrite権限を設定可能
build_permissions: 1件取得、更新、削除の権限を設定
self.build_permission: 一覧取得、作成の権限を設定
Garage::Authorizable(cont.)class Profile < ActiveRecord::Base include Garage::Representer include Garage::Authorizable
…
def self.build_permissions(perms, other, target) perms.permits! :read if self.user.friend?(other.id) perms.permits! :write end
def build_permissions(perms, other) perms.permits! :read if self.user.friend?(other.id) perms.permits! :write if other.id == self.user_id endend
Garage::Authorizable(cont.)build_permissions(perms, other)
perms: Garage::PermissionsのインスタンスGarage::Permissions#permits!でread, writeの許可を与える
other: リソースにアクセスしているユーザー具体的なモデルはDoorkeeperで設定する
def.build_permissions(perms, other, target)target: protected_resource_asで渡すことが出来る
Garage::Authorizable(cont.)Garage::Permissionsについて
POST /addresses…Addressに:writeが必要
GET /addresses…Addressに:readが必要
GET /addresses/1…@resourceに:readが必要
PUT /addresses/1…@resourceに:writeが必要
DELETE /addresses/1…@resourceに:writeが必要
Garage::RestfulActions#protect_resource_asで上書き可能
@resourceに別のクラスのインスタンスがある場合など
条件によるproperty表示制限
以下みたいなユースケースを実現したい● 友達が自分の住所録を見る際、その人の友達以外は、名
前しか表示したくない例)登場人物
Aさん
Bさん(Aさんの友達で、Aさんの住所録を見ようとした)
Cさん(Aさんの住所録に登録されていて、Bさんの友達)
Dさん(Aさんの住所録に登録されていて、Bさんと友達でない)
BさんがAさんの住所録を見た際、Cさんの住所情報とDさんの名前だけ見える
property if: <lambda>Garage::Representer#property
if:で表示条件をlambdaで渡すことが出来る
property :if -> (representer, responder) { 条件式 }representer: アクセスされているモデル。selfに等しい
responder: Garage::AppResponderのインスタンス
responder.controllerでリクエスト元のコントローラが取得出来る
property if: <lambda> (cont.)class Address < ActiveRecord::Base include Garage::Representer include Garage::Authorizable belongs_to :user property :user, selectable: true property :name property :address,
if: -> (representer, responder) { representer.user.friend?(responder.controller.resource_owner_id) }
property :phone, if: -> (representer, responder) { representer.user.friend?(responder.controller.resource_owner_id) }
...end
property if: <lambda> (cont.)GET /addresses?user_id=Aさん
Bさんとして実行する
[
{ “user” : { “id”: 1, “name”: “Uduki, Shimamura” },
“name” : “Mio Honda”,
“address”: “Chiba, Chiba”,
“phone”: “046-234-3333” },
{ “user” : { “id”: 1, “name”: “Uduki, Shimamura” },
“name” : “Chieri Ogata”]
Garageで楽になるところ
CRUD前提で、誰が書いてもRESTful API的な感じ
APIやデータの条件による制御が非常に楽自分しか更新できない
友達しか閲覧できない
ブロックされていると閲覧できない
自分と管理者だけしか削除できない
etc...