Как я могу изменить этот супер вызов для явной передачи аргументов?
Я управляю наследством 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
параметр.)