我有一个表单构建器子类:
class ErrorHandlingFormBuilder < ActionView::Helpers::FormBuilder
使用以下代码块:
helpers.each do |name|
# We don't want to have a label for a hidden field
# ERROR: The call to super below is actually calling itself and causing infinite recursion.
# How can I get it to call
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(field,*args)
end
end
end
super
调用正在调用它封装代码块正在定义的方法!这导致无限递归和Stack Level Too Deep
。
我需要的是它在表单构建器实例本身中调用name
变量定义的方法。
我只是不知道如何引用该实例。即使使用self.send
而不是super
,仍会导致递归。
以下是完整的代码,以及我用于跟踪堆栈的一些日志记录:
class ErrorHandlingFormBuilder < ActionView::Helpers::FormBuilder
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|
ErrorPrinter.print "name: #{name}"
ErrorPrinter.print "field: #{field}"
options = args.detect {|argument| argument.is_a?(Hash)} || {}
build_shell(field, options) do
super(field,*args)
end
end
end
def build_shell(field, options)
# Capitalize the string, unless it's already been hardcoded.
options[:label] = field.to_s.humanize.gsub(/^[a-z]|\s+[a-z]/) { |a| a.upcase } unless options[:label]
options[:label].gsub!(/\w+/) { |word| CAPITALS.include?(word.upcase) ? word.upcase : word }
options[:label] += ":" unless options[:label].last==":"
@template.capture do
ErrorPrinter.print "Before"
locals = {:element => yield, :label => label(field, options[:label] )}
ErrorPrinter.print "After"
if has_errors_on?(field)
locals.merge!(:error => error_message(field, options))
@template.render :partial => 'forms/field_with_errors', :locals => locals
else
@template.render :partial => 'forms/field', :locals => locals
end
end
end
def error_message(field, options)
if has_errors_on?(field)
errors = object.errors.on(field)
errors.is_a?(Array) ? errors.to_sentence : errors
else
''
end
end
def has_errors_on?(field)
!(object.nil? || object.errors.on(field).blank?)
end
end
递归问题在于build_shell
:
locals = {:element => yield, :label => label(field, options[:label] )}
由日志证明:
XXXXXXXXX
name: collection_select
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb collection_select start
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb object: :review
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb method: :system_id
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb collection: #<Set: {#<System id: 1, name: "Catalog"}>
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb value_method: :id
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb text_method: :name
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb options: {:include_blank=>"Select a Standard", :label=>"System:", :object=>nil}
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb html_options: {:onchange=>"new Ajax.Request('/review/makes', {asynchronous:true, evalScripts:true, method:'post', parameters:Form.serialize('text')})"}
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb collection_select end
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb to_collection_select_tag start
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb collection: #<Set: {#<System id: 1, name: "Catalog"}>
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb value_method: id
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb text_method: name
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb options: {:include_blank=>"Select a Standard", :label=>"System:"}
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb html_options: {:onchange=>"new Ajax.Request('/review/makes', {asynchronous:true, evalScripts:true, method:'post', parameters:Form.serialize('text')})"}
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb to_collection_select_tag end
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb options_from_collection_for_select start
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb collection: #<Set: {#<System id: 1, name: "Catalog"}>
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb value_method: :id
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb text_method: :name
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb selected: {:selected=>nil, :disabled=>nil}
XXXXXXXXX
XXXXXXXXX
actionpack form_options_helper.rb options_from_collection_for_select end
XXXXXXXXX
XXXXXXXXX
End options_from_collection_for_select
XXXXXXXXX
XXXXXXXXX
name: label
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
XXXXXXXXX
name: label
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
XXXXXXXXX
name: label
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
XXXXXXXXX
name: label
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
XXXXXXXXX
name: label
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
XXXXXXXXX
name: label
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
XXXXXXXXX
name: label
XXXXXXXXX
XXXXXXXXX
field: system_id
XXXXXXXXX
XXXXXXXXX
Before
XXXXXXXXX
答案 0 :(得分:0)
3天后解决了。递归发生在label
的调用上。如果你看一下代码:
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)
那不行。 label
和fields_for
未从阵列中删除。它们作为符号出现在field_helpers
中。但即使将它们标记为符号也不起作用。
这段代码在Ruby 1.8.7中运行良好,但在其他任何方面都无法工作。显然,Ruby的现代版本有一些变化,可以防止像上面那样的声明。我刚刚决定跳过label
和fields_for
,就像我跳过hidden_field
一样,现在代码正常运行。