Используйте lambda / Proc в дочернем классе в ruby

#ruby #lambda #proc

#ruby #лямбда #proc

Вопрос:

Как я могу использовать или ссылаться на лямбду, которая была определена в базовом / суперклассе?

Код суперкласса

 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
 

Код дочернего класса

 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
 

Комментарии:

1. Я так не думаю, потому что, если я перенесу определение лямбды в подкласс (AnswerSerializer), он будет нормально работать с этим синтаксисом.

2. Это не имеет никакого отношения к лямбдам или процедурам. OP не понимает, как работают методы.

Ответ №1:

Как представлено, serializer_for_proc это метод экземпляра Api::ApplicationSerializer, но вы пытаетесь вызвать его непосредственно в классе (или, скорее, непосредственно в подклассе). Другими словами, все следующие ошибки не выполняются по более или менее одной и той же причине:

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

Вы на самом деле никогда не определяли ApplicationSerializer.serializer_for_proc , вы определили то, что традиционно записывается как ApplicationSerializer#serializer_for_proc , или, другими словами, ApplicationSerailizer.new.serializer_for_proc .

Самый быстрый способ исправить это — сделать исходное определение метода методом класса:

 module Api
  class ApplicationSerializer
    include JSONAPI::Serializer

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

Вы написали в комментарии, что это сработало, когда вы скопировали его в файл AnswerSerializer, но это маловероятно (в принципе невозможно). Что бы сработало, так это скопировать serializer_for_proc = -> (object) { SerializerLookup.new.(object) } версию в файл AnswerSerializer, потому что тогда serializer_for_proc это вообще не имя метода, это просто локальная переменная внутри AnswerSerializer файла, такая же, как x = 5 . Но это не наследуется.

Другим решением (хотя оно мне нравится значительно меньше) было бы создание serializer_for_proc константы, например:

 # 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
 

Это работает, но мне не нравится полагаться на это, потому что постоянный поиск на основе наследования в Ruby имеет несколько довольно странных крайних случаев. Я бы лично просто использовал Api::ApplicationSerializer::SERIALIZER_FOR_PROC , если бы хотел использовать константу.