2016-07-22 47 views
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 
+0

'超级调用正在调用它封装的代码块正在定义的方法!'我不相信这是真的。你能分享足够的堆栈跟踪来显示递归循环吗? –

+0

请分享'build_shell'代码。 – mudasobwa

+0

我知道你是什么意思向导。但是我在超级调用之上放置了一个打印语句,并且无限次地打印,直到出现堆栈级别太深。上面添加的代码。 – AKWF

回答

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) 

这是行不通的。 labelfields_for未被从阵列中移除。它们作为符号出现在field_helpers中。但即使将它们标记为符号也不起作用。

这段代码在Ruby 1.8.7中运行良好,但在其他任何情况下都不起作用。很显然,在现代版本的Ruby中有一些变化阻止了上面的声明。我只是决定跳过labelfields_for,就像我跳过hidden_field一样,代码现在可以工作。