Как я могу изменить этот супер вызов для явной передачи аргументов?

Я управляю наследством Rails 2.3 (да, я знаю) приложение на новой виртуальной машине. Это работает Ruby v2.3.1, Я внес все рекомендуемые изменения, чтобы запустить это старое приложение в этой версии. Все работает отлично и намного быстрее, за исключением этого кода. Этот код из старого Advanced Recipes for Rails книга и error_handling_form_builder.rb,

Во-первых, вот декларация формы:

<% form_for(:review, :builder => ErrorHandlingFormBuilder) do |f| %>
    <%= f.collection_select :system_id, @systems, :id, :name, { :include_blank => "Select a Standard"}, { :onchange => remote_function(:url => { :action => :makes }, :submit => :text, :method => 'post') } %>
    <div id="categoriesdiv">
    </div>
<% end %>

Этот код работает нормально, если я удаляю параметр конструктора. Вот код из ErrorHandlingFormBuilder:

helpers = field_helpers +
          %w(date_select datetime_select calendar_date_select time_select collection_select) +
          %w(collection_select select country_select time_zone_select) -
          %w(label fields_for)

helpers.each do |name|
  # We don't want to have a label for a hidden field
  next if name=="hidden_field"
  define_method name do |field, *args|
    options = args.detect {|argument| argument.is_a?(Hash)} || {}
    build_shell(field, options) do
      super # This call fails
    end
  end
end

Призыв к super выше не удается со следующей ошибкой:

implicit argument passing of super from method defined by define_method()
is not supported. Specify all arguments explicitly.

Глядя на код, я не уверен, как его изменить.

Я знаю, что должен вызывать super с явными аргументами, но я попробовал это:

super(field, options)

и я получаю:

wrong number of arguments (given 2, expected 4..6)

Я также попробовал:

build_shell(field, options) do
  super(*args)
end

но я получаю:

undefined method `map' for :id:Symbol
Did you mean?  tap

Кажется, что он ищет collection атрибут 2-й в списке *args, Но это первое. Если я пройду field хотя в качестве первого аргумента я получаю Stack Level Too Deep,

1 ответ

Решение

super без списка аргументов пересылает текущее сообщение, начиная отправку сообщения в суперклассе, передавая точно такие же аргументы, которые были переданы текущему методу.

В вашем случае аргументы, передаваемые текущему методу, (field, *args), так super без списка аргументов будет проходить (field, *args), но, к сожалению super без списка аргументов не работает внутри define_methodТаким образом, вы должны передать аргументы явно, используя super(field, *args),

Это вызовет метод с именем, данным name в суперклассе, проходящем вдоль (field, *args) как если бы вы позвонили super без списка аргументов.

Глядя на код, я не уверен, как его изменить.

Просто: с super без списка аргументов неявно передает аргументы в точности так, как они были переданы, явным эквивалентом этого является просто скопировать и вставить список параметров из определения метода в список аргументов super, В этом случае определение метода define_method(name) do |field, *args| … endтак что список параметров (field, *args)и вы просто копируете это в список аргументов для super: super(field, *args),

Может быть или не быть возможным (и желательно) передавать разные аргументы, в зависимости от того, как разработан метод и как вы обрабатываете аргументы, но это всегда работает. (Или, говоря по-другому: если это не сработает, то оригинальный код, использующий только super тоже не работает. Единственное исключение - когда вы хотите передать блок, и вы не захватываете этот блок в Proc, вам придется изменить сигнатуру метода, чтобы добавить &blk параметр.)

Другие вопросы по тегам