Garageをもうちょっと触ってみた

29
Garageもうちょっと 触ってみた 株式会社エクストーン 下っ端 豊田陽一

Transcript of Garageをもうちょっと触ってみた

Garageをもうちょっと触ってみた

株式会社エクストーン 下っ端 豊田陽一

今日のおはなし

「Garageを触ってみた」の続きhttp://www.slideshare.net/rs_wisteria/garage-44341097

実際にお仕事で使ってみたので、ユースケース毎に機能を紹介します

概要

CRUDJSONの返値の設定

条件によるAPI呼び出し制限

条件によるproperty表示制限

CRUD

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になるので、一覧取得で取得可能

JSONの返値の設定

JSONの返値の設定

どのデータを返すか?

例) 住所録を取得した際、編集者のユーザー情報を返すべ

きか?● 必ず返したい● 必ず返したくない● 必要に応じて返したい

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呼び出し制限

条件による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表示制限

条件による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...

おしまい