Home > OS >  Use lambda/Proc in child class in ruby
Use lambda/Proc in child class in ruby

Time:12-11

How can I use or reference a lambda that has been defined in base/super class?

Super class code

module Api
  class ApplicationSerializer
    include JSONAPI::Serializer

    # I Also tried it this way
    # serializer_for_proc = -> (object) { SerializerLookup.new.(object) }

    def serializer_for_proc
      -> (object) { SerializerLookup.new.(object) }
    end
  end
end

Child class code

module Api
  class AnswerSerializer < ApplicationSerializer
    attributes :values, :created_at, :updated_at, :usage

    belongs_to :question, serializer: serializer_for_proc # NameError (undefined local variable or method `serializer_for_proc' 
  end
end

CodePudding user response:

As presented, serializer_for_proc is an instance method of Api::ApplicationSerializer, but you're trying to invoke it directly on the class (or rather, directly on the subclass). In other words, all of the following fail for more-or-less the same reason:

belongs_to :question, serializer: serializer_for_proc
belongs_to :question, serializer: ApplicationSerializer.serializer_for_proc
belongs_to :question, serializer: AnswerSerializer.serializer_for_proc

You've never actually defined ApplicationSerializer.serializer_for_proc, you've defined what is traditionally written as ApplicationSerializer#serializer_for_proc, or in other words, ApplicationSerailizer.new.serializer_for_proc.

The quickest way to fix this would be to make the original method definition a class method:

module Api
  class ApplicationSerializer
    include JSONAPI::Serializer

    def self.serializer_for_proc
      -> (object) { SerializerLookup.new.(object) }
    end
  end
end

You wrote in a comment that it did work when you copied it into the AnswerSerializer file, but this is unlikely (basically impossible). What would work would be copying the serializer_for_proc = -> (object) { SerializerLookup.new.(object) } version into the AnswerSerializer file, because then serializer_for_proc isn't a method name at all, it's just a local variable within the AnswerSerializer file, same as x = 5. But that's not inheritable.

Another solution (although one I like significantly less) would be making serializer_for_proc a constant, e.g.:

# One file
module Api
  class ApplicationSerializer
    include JSONAPI::Serializer


    SERIALIZER_FOR_PROC = -> (object) { SerializerLookup.new.(object) }
  end
end

# Another file
module Api
  class AnswerSerializer < ApplicationSerializer
    attributes :values, :created_at, :updated_at, :usage

    belongs_to :question, serializer: SERIALIZER_FOR_PROC 
  end
end

That works, but I don't like to rely on this because inheritance-based constant lookup in Ruby has some pretty weird edge cases. I'd personally just use Api::ApplicationSerializer::SERIALIZER_FOR_PROC if I wanted to use a constant.

  • Related