ruby on rails - Conditional links with active_model_serializers -
i'm trying create hypermedia api in rails. i'd serialize payloads active_model_serializers using json_api adapter. doesn't seem trivial serialize links conditionaly.
it's kind of blog application users can follow other users. when serialize user resource, usera, want have link rel :follow if current_user not following usera , link rel :unfollow if current_user following usera. seems extremely trivial use case when creating hypermedia api. know if there's way of doing active_model_serializers?
i wrote (and include in serializers):
def self.link(rel, &block) serializer = self super user = scope next unless serializer.can?(user, rel, @object) instance_eval(&block) end end # , in serializer (just usual): link :self api_user_path(object.id) end
it work. don't feel right. , wouldn't surprised if future changes active_model_serializers screw things me.
if else looking solution here did. added gem pundit , made policy classes in charge of link serialization (as usual authorization) adding methods called "link_#{rel}". created base serializer this:
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}" # need let super class handle evaluation since # don't have object here in class method. block # evalutated instance_eval in adapter (which has # object serialized) super policy_class = policyfinder.new(object).policy unless policy_class rails.logger.warn "could not find policy class #{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 rel '#{rel}'. " \ "but no method '#{method}' in #{policy.class}." next end next unless policy.public_send(method) instance_eval(&block) end end end end
then other serializers inherit baseserializer, like:
module api class userserializer < baseserializer type 'user' attributes :name, :email, :followers_count, :following_count, :created_at, :updated_at link :self api_user_url(object) end link :edit api_user_url(object) end link :follow follow_api_user_url(object) end link :unfollow unfollow_api_user_url(object) end end end
so policies normal pundit policies added methods each link should serialized (or not).
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 not current_user , # current_user not following user def link_follow current_user && current_user.id != user.id && !current_user.following?(user) end # show follow link if user not current_user , # current_user following user def link_unfollow current_user && current_user.id != user.id && current_user.following?(user) end end end
Comments
Post a Comment