2016-07-22 24 views
0

を引き起こしている私は、フォームビルダのサブクラスを持ってdefine_methodは無限再帰

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で定義されているメソッドを呼び出す必要があります。

私はちょうどそのインスタンスを参照する方法がわかりません。 superの代わりにself.sendを使用しても、依然として再帰が発生します。

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

私はあなたがウィザードを意味することを知っています。私はスーパーコールの上にプリントステートメントを置いて、Stack Level Too Deepが表示されるまで無限にプリントします。上記のコードが追加されました。 – 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) 

これは機能しませんでした。 labelおよびfields_forはアレイから除去されていませんでした。それらは記号としてfield_helpersにあります。しかし、記号としてマークすることさえできませんでした。

このコードはRuby 1.8.7で正常に動作しましたが、これ以外のところでは動作しません。明らかに、Rubyの現代版には、上記のような宣言を防止するいくつかの変更があります。私はちょうど私がhidden_fieldをスキップしているのと同じ方法でlabelfields_forをスキップすることに決めました。

関連する問題