2016-09-12 8 views
0

私は、ハイパーメディアAPIをレールに作成しようとしています。私はjson_apiアダプタを使用してactive_model_serializersでペイロードをシリアル化したいと思います。しかし、リンクを条件付きでシリアライズするのは簡単ではないようです。active_model_serializersとの条件付きリンク

これは、ユーザーが他のユーザーをフォローできる種類のブログアプリケーションです。ですから、UserAのようにUserリソースをシリアライズすると、current_userがUserAに従っていない場合はfollow、rel:unfollowの場合はcurrent_userが既にUserAに従っている場合はリンクします。 これは、ハイパーメディアAPIを作成する場合、非常に些細な使用例のようです。 active_model_serializersでこれを行う良い方法があれば誰にも知られていますか?

私は現在、このような何かを書いた(とすべてのシリアライザに含める):

def self.link(rel, &block) 
     serializer = self 
     super do 
     user = scope 
     next unless serializer.can?(user, rel, @object) 
     instance_eval(&block) 
     end 
    end 

# And in serializer (just as usual): 

link :self do 
    api_user_path(object.id) 
end 

それは作業を行います。しかし、ちょうどいい気分にはならない。そして、将来のactive_model_serializersへの変更が私のために物事をねじ込むなら、私は驚くことはありません。

答えて

0

他の誰かがこれに対する解決策を探しているなら、私がやったことがここにあります。私はPunditという宝石を追加し、 "link _#{rel}"というメソッドを追加して、リンクのシリアル化(通常の権限と同様に)を担当するPolicyクラスを作成しました。

module Api 
    class UserSerializer < BaseSerializer 
    type 'user' 
    attributes :name, 
       :email, 
       :followers_count, 
       :following_count, 
       :created_at, 
       :updated_at 

    link :self do 
     api_user_url(object) 
    end 

    link :edit do 
     api_user_url(object) 
    end 

    link :follow do 
     follow_api_user_url(object) 
    end 

    link :unfollow do 
     unfollow_api_user_url(object) 
    end 
    end 
end 

だからポリシーだけである必要があり、各リンクのためのいくつか追加されたメソッドを持つ通常の評論家のポリシーのようなものです:

module Api 
    class BaseSerializer < ActiveModel::Serializer 
    include Pundit 

    def self.link(rel, &block) 
     unless block_given? 
     Rails.logger.warn "Link without block (rel '#{rel}'), no authorization check" 
     return super 
     end 
     method = "link_#{rel}" 
     # We need to let the super class handle the evaluation since 
     # we don't have the object here in the class method. This block 
     # will be evalutated with instance_eval in the adapter (which has 
     # the object to be serialized) 
     super do 
     policy_class = PolicyFinder.new(object).policy 
     unless policy_class 
      Rails.logger.warn "Could not find policy class for #{object.class}." 
      next 
     end 
     user = scope 
     policy = policy_class.new(user, object) 
     unless policy.respond_to?(method) 
      Rails.logger.warn "Serialization of #{object.class} infers link with rel '#{rel}'. " \ 
      "But no method '#{method}' in #{policy.class}." 
      next 
     end 
     next unless policy.public_send(method) 
     instance_eval(&block) 
     end 
    end 

    end 
end 

その後、他のシリアライザは次のように、BaseSerializer継承:私はこのような基本シリアライザを作成しましたシリアライズされた(またはされない)。

class ApplicationPolicy 
    attr_reader :user, :record 

    def initialize(user, record) 
    @user = user 
    @record = record 
    end 

    def link_self 
    true 
    end 

end 

module Api 
    class UserPolicy < ApplicationPolicy 

    alias current_user user 
    alias user record 

    def link_edit 
     current_user && current_user.id == user.id 
    end 

    # show follow link if user is not current_user and 
    # current_user is not already following user 
    def link_follow 
     current_user && current_user.id != user.id && !current_user.following?(user) 
    end 

    # show follow link if user is not current_user and 
    # current_user is following user 
    def link_unfollow 
     current_user && current_user.id != user.id && current_user.following?(user) 
    end 
    end 
end