0
我有一个表单生成子类:define_method具有超强引起无限递归
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
'超级调用正在调用它封装的代码块正在定义的方法!'我不相信这是真的。你能分享足够的堆栈跟踪来显示递归循环吗? –
请分享'build_shell'代码。 – mudasobwa
我知道你是什么意思向导。但是我在超级调用之上放置了一个打印语句,并且无限次地打印,直到出现堆栈级别太深。上面添加的代码。 – AKWF