From 461f309bc75586e9b1d4ff7566bca09e7952b0a1 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Fri, 11 Dec 2009 01:18:56 -0200 Subject: [PATCH] Lookup errors in associations. --- lib/simple_form/components/base.rb | 2 +- lib/simple_form/components/error.rb | 12 ++++++++++-- lib/simple_form/form_builder.rb | 23 ++++++++++++----------- test/components/error_test.rb | 6 ++++++ test/support/models.rb | 2 ++ test/test_helper.rb | 2 +- 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/simple_form/components/base.rb b/lib/simple_form/components/base.rb index bc68f726..01fded76 100644 --- a/lib/simple_form/components/base.rb +++ b/lib/simple_form/components/base.rb @@ -9,7 +9,7 @@ module SimpleForm # of prepending the content available in the method content. class Base delegate :template, :object, :object_name, :attribute, :column, - :input_type, :options, :to => :@builder + :reflection, :input_type, :options, :to => :@builder def self.basename @basename ||= name.split("::").last.underscore.to_sym diff --git a/lib/simple_form/components/error.rb b/lib/simple_form/components/error.rb index 74a10bce..94aae4be 100644 --- a/lib/simple_form/components/error.rb +++ b/lib/simple_form/components/error.rb @@ -9,11 +9,19 @@ module SimpleForm end def errors - @errors ||= object.errors[attribute] + @errors ||= (errors_on_attribute + errors_on_association).compact + end + + def errors_on_attribute + Array(object.errors[attribute]) + end + + def errors_on_association + reflection ? Array(object.errors[reflection.name]) : [] end def content - component_tag Array(errors).to_sentence + component_tag errors.to_sentence end end end diff --git a/lib/simple_form/form_builder.rb b/lib/simple_form/form_builder.rb index e90bb1bd..0da5f80a 100644 --- a/lib/simple_form/form_builder.rb +++ b/lib/simple_form/form_builder.rb @@ -1,14 +1,15 @@ module SimpleForm class FormBuilder < ActionView::Helpers::FormBuilder - attr_reader :template, :object_name, :object, :attribute, :column, :input_type, :options + attr_reader :template, :object_name, :object, :attribute, :column, + :reflection, :input_type, :options TERMINATOR = lambda { "" } - # Basic input helper, combines all components in the stack to generate input - # html based on options the user define and some guesses through + # Basic input helper, combines all components in the stack to generate + # input html based on options the user define and some guesses through # database column information. By default a call to input will generate - # label + input + hint (when defined) + errors (when exists), and all can be - # configured inside a wrapper html. + # label + input + hint (when defined) + errors (when exists), and all can + # be configured inside a wrapper html. # # == Examples: # @@ -98,18 +99,18 @@ module SimpleForm def association(attribute, options={}) raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object - association = find_association(attribute) - raise "Association not found #{attribute.inspect}" unless association + @reflection = find_association_reflection(attribute) + raise "Association not found #{attribute.inspect}" unless @reflection - attribute = association.options[:foreign_key] || :"#{association.name}_id" + attribute = @reflection.options[:foreign_key] || :"#{@reflection.name}_id" options[:collection] ||= begin find_options = { :conditions => options.delete(:conditions), :order => options.delete(:order) } - association.klass.all(find_options) + @reflection.klass.all(find_options) end - input(attribute, options) + returning(input(attribute, options)) { @reflection = nil } end # Creates a button: @@ -248,7 +249,7 @@ module SimpleForm end # Find association related to attribute - def find_association(attribute) + def find_association_reflection(attribute) @object.class.reflect_on_association(attribute) if @object.class.respond_to?(:reflect_on_association) end diff --git a/test/components/error_test.rb b/test/components/error_test.rb index 6282aece..d1f08c9e 100644 --- a/test/components/error_test.rb +++ b/test/components/error_test.rb @@ -5,6 +5,7 @@ class ErrorTest < ActionView::TestCase def with_error_for(object, attribute, type, options={}, &block) simple_form_for object do |f| f.attribute = attribute + f.reflection = Association.new(Company, :company, {}) if options.delete(:setup_association) f.input_type = type f.options = options @@ -46,4 +47,9 @@ class ErrorTest < ActionView::TestCase with_error_for @user, :name, :string, :error_html => { :id => 'error', :class => 'yay' } assert_select 'span#error.error.yay' end + + test 'error should find errors on attribute and association' do + with_error_for @user, :company_id, :select, :setup_association => true + assert_select 'span.error', 'must be valid and company must be present' + end end diff --git a/test/support/models.rb b/test/support/models.rb index 070112f0..c4c974e7 100644 --- a/test/support/models.rb +++ b/test/support/models.rb @@ -64,6 +64,8 @@ class User < OpenStruct :name => "can't be blank", :description => "must be longer than 15 characters", :age => ["is not a number", "must be greater than 18"], + :company => "company must be present", + :company_id => "must be valid" } end end diff --git a/test/test_helper.rb b/test/test_helper.rb index eef4cb6f..93b9dcf2 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -16,7 +16,7 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } I18n.default_locale = :en class SimpleForm::FormBuilder - attr_accessor :attribute, :column, :input_type, :options + attr_accessor :attribute, :column, :reflection, :input_type, :options end class ActionView::TestCase