From bdf783b5a8b83b1d565027130de4743fda336523 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 10 Aug 2008 21:25:24 +0200 Subject: update i18n usage for pluralization hashes (api change) --- activerecord/lib/active_record/validations.rb | 2 +- activerecord/test/cases/validations_i18n_test.rb | 198 +++++++++++++++++++++-- 2 files changed, 189 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index e7a9676394..0de430567c 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -662,7 +662,7 @@ module ActiveRecord finder_class.with_exclusive_scope do if finder_class.exists?([condition_sql, *condition_params]) - message = record.errors.generate_message(attr_name, :taken, :default => configuration[:message]) + message = record.errors.generate_message(attr_name, :taken, :default => configuration[:message], :value => value) record.errors.add(attr_name, message) end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 86834fe920..89ca61a220 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -45,38 +45,38 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_errors_generate_message_translates_custom_model_attribute_key global_scope = [:active_record, :error_messages] custom_scope = global_scope + [:custom, 'topic', :title] - + I18n.expects(:t).with nil, :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] @topic.errors.generate_message :title, :invalid, :default => 'default from class def' end - + def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] - + I18n.expects(:t).with nil, :scope => [:active_record, :error_messages], :default => [:"custom.reply.title.invalid", :"custom.topic.title.invalid", 'default from class def', :invalid] Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' end - + def test_errors_add_on_empty_generates_message @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) @topic.errors.add_on_empty :title end - + def test_errors_add_on_empty_generates_message_with_custom_default_message @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'}) @topic.errors.add_on_empty :title, 'custom' end - + def test_errors_add_on_blank_generates_message @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) @topic.errors.add_on_blank :title end - + def test_errors_add_on_blank_generates_message_with_custom_default_message @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) @topic.errors.add_on_blank :title, 'custom' end - + def test_errors_full_messages_translates_human_attribute_name_for_model_attributes @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", :locale => 'en-US', :default => 'Title').returns('Title') @@ -203,14 +203,14 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_uniqueness_of_generates_message Topic.validates_uniqueness_of :title @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil}) + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'}) @topic.valid? end def test_validates_uniqueness_of_generates_message_with_custom_default_message Topic.validates_uniqueness_of :title, :message => 'custom' @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom'}) + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'}) @topic.valid? end @@ -620,4 +620,182 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase replied_topic.valid? assert_equal 'global message', replied_topic.errors.on(:replies) end +end + +class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase + def setup + reset_callbacks Topic + @topic = Topic.new + I18n.backend.store_translations :'en-US', { + :active_record => { + :error_messages => { + :inclusion => "is not included in the list", + :exclusion => "is reserved", + :invalid => "is invalid", + :confirmation => "doesn't match confirmation", + :accepted => "must be accepted", + :empty => "can't be empty", + :blank => "can't be blank", + :too_long => "is too long (maximum is {{count}} characters)", + :too_short => "is too short (minimum is {{count}} characters)", + :wrong_length => "is the wrong length (should be {{count}} characters)", + :taken => "has already been taken", + :not_a_number => "is not a number", + :greater_than => "must be greater than {{count}}", + :greater_than_or_equal_to => "must be greater than or equal to {{count}}", + :equal_to => "must be equal to {{count}}", + :less_than => "must be less than {{count}}", + :less_than_or_equal_to => "must be less than or equal to {{count}}", + :odd => "must be odd", + :even => "must be even" + } + } + } + end + + def reset_callbacks(*models) + models.each do |model| + model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + end + end + + # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) + def test_generate_message_inclusion_with_default_message + assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') + end + + def test_generate_message_inclusion_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) + def test_generate_message_exclusion_with_default_message + assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title') + end + + def test_generate_message_exclusion_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + def test_generate_message_invalid_with_default_message + assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title') + end + + def test_generate_message_invalid_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message]) + def test_generate_message_confirmation_with_default_message + assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil) + end + + def test_generate_message_confirmation_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message') + end + + # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message]) + def test_generate_message_accepted_with_default_message + assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil) + end + + def test_generate_message_accepted_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message') + end + + # add_on_empty: generate_message(attr, :empty, :default => custom_message) + def test_generate_message_empty_with_default_message + assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil) + end + + def test_generate_message_empty_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message') + end + + # add_on_blank: generate_message(attr, :blank, :default => custom_message) + def test_generate_message_blank_with_default_message + assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil) + end + + def test_generate_message_blank_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message') + end + + # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) + def test_generate_message_too_long_with_default_message + assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10) + end + + def test_generate_message_too_long_with_custom_message + assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10) + end + + # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) + def test_generate_message_too_short_with_default_message + assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10) + end + + def test_generate_message_too_short_with_custom_message + assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10) + end + + # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value) + def test_generate_message_wrong_length_with_default_message + assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10) + end + + def test_generate_message_wrong_length_with_custom_message + assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10) + end + + # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message]) + def test_generate_message_taken_with_default_message + assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title') + end + + def test_generate_message_taken_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) + def test_generate_message_not_a_number_with_default_message + assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title') + end + + def test_generate_message_not_a_number_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) + def test_generate_message_greater_than_with_default_message + assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_greater_than_or_equal_to_with_default_message + assert_equal "must be greater than or equal to 10", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_equal_to_with_default_message + assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_less_than_with_default_message + assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_less_than_or_equal_to_with_default_message + assert_equal "must be less than or equal to 10", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_odd_with_default_message + assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_even_with_default_message + assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10) + end end \ No newline at end of file -- cgit v1.2.3 From f26380b7757666fa793c150538e8444a640d29aa Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 13 Aug 2008 09:53:25 +0200 Subject: switch to using I18n.load_translations instead of requiring plain ruby files --- activerecord/lib/active_record.rb | 2 +- activerecord/lib/active_record/locale/en-US.rb | 47 +++++++++++++------------- 2 files changed, 25 insertions(+), 24 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 17a7949959..08b6b19f7f 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -82,6 +82,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' I18n.backend.populate do - require 'active_record/locale/en-US.rb' + I18n.load_translations File.dirname(__FILE__) + '/active_record/locale/en-US.rb' end diff --git a/activerecord/lib/active_record/locale/en-US.rb b/activerecord/lib/active_record/locale/en-US.rb index b31e13ed3a..4057a467e3 100644 --- a/activerecord/lib/active_record/locale/en-US.rb +++ b/activerecord/lib/active_record/locale/en-US.rb @@ -1,25 +1,26 @@ -I18n.backend.store_translations :'en-US', { - :active_record => { - :error_messages => { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :accepted => "must be accepted", - :empty => "can't be empty", - :blank => "can't be blank", - :too_long => "is too long (maximum is {{count}} characters)", - :too_short => "is too short (minimum is {{count}} characters)", - :wrong_length => "is the wrong length (should be {{count}} characters)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :greater_than => "must be greater than {{count}}", - :greater_than_or_equal_to => "must be greater than or equal to {{count}}", - :equal_to => "must be equal to {{count}}", - :less_than => "must be less than {{count}}", - :less_than_or_equal_to => "must be less than or equal to {{count}}", - :odd => "must be odd", - :even => "must be even" - } +{ :'en-US' => { + :active_record => { + :error_messages => { + :inclusion => "is not included in the list", + :exclusion => "is reserved", + :invalid => "is invalid", + :confirmation => "doesn't match confirmation", + :accepted => "must be accepted", + :empty => "can't be empty", + :blank => "can't be blank", + :too_long => "is too long (maximum is {{count}} characters)", + :too_short => "is too short (minimum is {{count}} characters)", + :wrong_length => "is the wrong length (should be {{count}} characters)", + :taken => "has already been taken", + :not_a_number => "is not a number", + :greater_than => "must be greater than {{count}}", + :greater_than_or_equal_to => "must be greater than or equal to {{count}}", + :equal_to => "must be equal to {{count}}", + :less_than => "must be less than {{count}}", + :less_than_or_equal_to => "must be less than or equal to {{count}}", + :odd => "must be odd", + :even => "must be even" + } + } } } \ No newline at end of file -- cgit v1.2.3 From aae9f916590b8ce2c84a9e404584de95b1705766 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 13 Aug 2008 10:00:54 +0200 Subject: fix validations_i18n tests for changed locale file format --- activerecord/test/cases/validations_i18n_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 89ca61a220..bf0a4c9829 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -11,7 +11,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def teardown reset_callbacks Topic - load 'active_record/locale/en-US.rb' + I18n.load_translations File.dirname(__FILE__) + '/../../lib/active_record/locale/en-US.rb' end def unique_topic -- cgit v1.2.3 From e3ecc3375fef4faa807638ff9e9cf309094272bd Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 13 Aug 2008 13:52:07 +0200 Subject: provide more useful feedback on missing translations for validation error messages --- activerecord/lib/active_record/validations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 0de430567c..85b8e6232b 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -70,7 +70,7 @@ module ActiveRecord msgs << options[:default] if options[:default] msgs << key - I18n.t nil, options.merge(:default => msgs, :scope => [:active_record, :error_messages]) + I18n.t msgs.shift, options.merge(:default => msgs, :scope => [:active_record, :error_messages]) end # Returns true if the specified +attribute+ has errors associated with it. -- cgit v1.2.3 From d0ee883e7c01dabf039525b80b7f43673e987265 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 13 Aug 2008 14:02:26 +0200 Subject: fixing unit tests for active_record validations_i18n --- activerecord/test/cases/validations_i18n_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index bf0a4c9829..a0beb8be1b 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -46,14 +46,14 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase global_scope = [:active_record, :error_messages] custom_scope = global_scope + [:custom, 'topic', :title] - I18n.expects(:t).with nil, :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] + I18n.expects(:t).with :"custom.topic.title.invalid", :scope => [:active_record, :error_messages], :default => ['default from class def', :invalid] @topic.errors.generate_message :title, :invalid, :default => 'default from class def' end def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] - I18n.expects(:t).with nil, :scope => [:active_record, :error_messages], :default => [:"custom.reply.title.invalid", :"custom.topic.title.invalid", 'default from class def', :invalid] + I18n.expects(:t).with :"custom.reply.title.invalid", :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' end -- cgit v1.2.3 From ffeab4e0c171aecced4ddbe29b82aed064be9bdb Mon Sep 17 00:00:00 2001 From: Iain Hecker Date: Thu, 14 Aug 2008 01:28:31 +0200 Subject: Cleaned up ActiveRecord i18n scoping --- activerecord/lib/active_record/base.rb | 30 ++++- activerecord/lib/active_record/locale/en-US.rb | 48 +++---- activerecord/lib/active_record/validations.rb | 120 ++++++++++-------- activerecord/test/cases/validations_i18n_test.rb | 155 ++++++++++++----------- 4 files changed, 196 insertions(+), 157 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 29c2995334..02a0ca7e44 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1220,13 +1220,35 @@ module ActiveRecord #:nodoc: subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information } end + def self_and_descendents_from_active_record#nodoc: + klass = self + classes = [klass] + while klass != klass.base_class + classes << klass = klass.superclass + end + classes + rescue + # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column' + # Appearantly the method base_class causes some trouble. + # It now works for sure. + [self] + end + # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example: # Person.human_attribute_name("first_name") # => "First name" - # Deprecated in favor of just calling "first_name".humanize - def human_attribute_name(attribute_key_name) #:nodoc: - attribute_key_name.humanize + # This used to be depricated in favor of humanize, but is now preferred, because it automatically uses the I18n + # module now. + # Specify +options+ with additional translating options. + def human_attribute_name(attribute_key_name, options = {}) + defaults = self_and_descendents_from_active_record.map do |klass| + :"#{klass.name.underscore}.#{attribute_key_name}" + end + defaults << options[:default] if options[:default] + defaults.flatten! + defaults << attribute_key_name.humanize + options[:count] ||= 1 + I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes])) end - # True if this isn't a concrete subclass needing a STI type condition. def descends_from_active_record? if superclass.abstract_class? diff --git a/activerecord/lib/active_record/locale/en-US.rb b/activerecord/lib/active_record/locale/en-US.rb index 4057a467e3..89a5baba06 100644 --- a/activerecord/lib/active_record/locale/en-US.rb +++ b/activerecord/lib/active_record/locale/en-US.rb @@ -1,26 +1,28 @@ { :'en-US' => { - :active_record => { - :error_messages => { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :accepted => "must be accepted", - :empty => "can't be empty", - :blank => "can't be blank", - :too_long => "is too long (maximum is {{count}} characters)", - :too_short => "is too short (minimum is {{count}} characters)", - :wrong_length => "is the wrong length (should be {{count}} characters)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :greater_than => "must be greater than {{count}}", - :greater_than_or_equal_to => "must be greater than or equal to {{count}}", - :equal_to => "must be equal to {{count}}", - :less_than => "must be less than {{count}}", - :less_than_or_equal_to => "must be less than or equal to {{count}}", - :odd => "must be odd", - :even => "must be even" - } + :activerecord => { + :errors => { + :messages => { + :inclusion => "is not included in the list", + :exclusion => "is reserved", + :invalid => "is invalid", + :confirmation => "doesn't match confirmation", + :accepted => "must be accepted", + :empty => "can't be empty", + :blank => "can't be blank", + :too_long => "is too long (maximum is {{count}} characters)", + :too_short => "is too short (minimum is {{count}} characters)", + :wrong_length => "is the wrong length (should be {{count}} characters)", + :taken => "has already been taken", + :not_a_number => "is not a number", + :greater_than => "must be greater than {{count}}", + :greater_than_or_equal_to => "must be greater than or equal to {{count}}", + :equal_to => "must be equal to {{count}}", + :less_than => "must be less than {{count}}", + :less_than_or_equal_to => "must be less than or equal to {{count}}", + :odd => "must be odd", + :even => "must be even" + } + } } } -} \ No newline at end of file +} diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 85b8e6232b..92b3e9a597 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -21,8 +21,8 @@ module ActiveRecord class << self def default_error_messages - ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('active_record.error_messages').") - I18n.translate 'active_record.error_messages' + ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages').") + I18n.translate 'activerecord.errors.messages' end end @@ -38,22 +38,24 @@ module ActiveRecord add(:base, msg) end - # Adds an error message (+msg+) to the +attribute+, which will be returned on a call to on(attribute) + # Adds an error message (+messsage+) to the +attribute+, which will be returned on a call to on(attribute) # for the same attribute and ensure that this error object returns false when asked if empty?. More than one # error can be added to the same +attribute+ in which case an array will be returned on a call to on(attribute). - # If no +msg+ is supplied, "invalid" is assumed. - def add(attribute, message = nil) - message ||= I18n.translate :"active_record.error_messages.invalid" + # If no +messsage+ is supplied, :invalid is assumed. + # If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error). + def add(attribute, message = nil, options = {}) + message ||= :invalid + message = generate_message(attribute, message, options) if message.is_a?(Symbol) @errors[attribute.to_s] ||= [] @errors[attribute.to_s] << message - end + end # Will add an error message to each of the attributes in +attributes+ that is empty. def add_on_empty(attributes, custom_message = nil) for attr in [attributes].flatten value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] is_empty = value.respond_to?("empty?") ? value.empty? : false - add(attr, generate_message(attr, :empty, :default => custom_message)) unless !value.nil? && !is_empty + add(attr, :empty, :default => custom_message) unless !value.nil? && !is_empty end end @@ -61,16 +63,48 @@ module ActiveRecord def add_on_blank(attributes, custom_message = nil) for attr in [attributes].flatten value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] - add(attr, generate_message(attr, :blank, :default => custom_message)) if value.blank? + add(attr, :blank, :default => custom_message) if value.blank? end end - def generate_message(attr, key, options = {}) - msgs = base_classes(@base.class).map{|klass| :"custom.#{klass.name.underscore}.#{attr}.#{key}"} - msgs << options[:default] if options[:default] - msgs << key + # Translates an error message in it's default scope (activerecord.errrors.messages). + # Error messages are first looked up in custom.MODEL.ATTRIBUTE.MESSAGE, if it's not there, it's looked up + # in custom.MODEL.ATTRIBUTE and if that is not there it returns the translation of the default message + # (e.g. activerecord.errors.messages.MESSAGE). Both the model name and the attribute name are available for + # interpolation. + # + # When using inheritence in your models, it will check all the inherited models too, but only if the model itself + # hasn't been found. Say you have class Admin < User; end and you wanted the translation for the :blank + # error +message+ for the title +attribute+, it looks for these translations: + # + #
    + #
  1. activerecord.errors.messages.custom.admin.title.blank
  2. + #
  3. activerecord.errors.messages.custom.admin.blank
  4. + #
  5. activerecord.errors.messages.custom.user.title.blank
  6. + #
  7. activerecord.errors.messages.custom.user.blank
  8. + #
  9. activerecord.errors.messages.blank
  10. + #
  11. any default you provided through the +options+ hash
  12. + #
+ def generate_message(attribute, message = :invalid, options = {}) + + defaults = @base.class.self_and_descendents_from_active_record.map do |klass| + [ :"custom.#{klass.name.underscore}.#{attribute}.#{message}", + :"custom.#{klass.name.underscore}.#{message}" ] + end + + defaults << options[:default] if options[:default] + defaults.flatten! << message + + model_name = @base.class.name + key = defaults.shift - I18n.t msgs.shift, options.merge(:default => msgs, :scope => [:active_record, :error_messages]) + options.merge!({ + :default => defaults, + :model => I18n.translate(model_name.underscore, :default => model_name.humanize, :scope => [:activerecord, :models], :count => 1), + :attribute => @base.class.human_attribute_name(attribute.to_s), + :scope => [:activerecord, :errors, :messages] }) + + I18n.translate(key, options) end # Returns true if the specified +attribute+ has errors associated with it. @@ -166,9 +200,9 @@ module ActiveRecord if attr == "base" full_messages << message else - key = :"active_record.human_attribute_names.#{@base.class.name.underscore.to_sym}.#{attr}" - attr_name = I18n.translate(key, :locale => options[:locale], :default => @base.class.human_attribute_name(attr)) - full_messages << attr_name + " " + message + #key = :"activerecord.att.#{@base.class.name.underscore.to_sym}.#{attr}" + attr_name = @base.class.human_attribute_name(attr) + full_messages << attr_name + ' ' + message end end end @@ -219,16 +253,6 @@ module ActiveRecord end end - protected - - # TODO maybe this should be on ActiveRecord::Base, maybe #self_and_descendents_from_active_record - def base_classes(klass) - classes = [klass] - while klass != klass.base_class - classes << klass = klass.superclass - end - classes - end end @@ -398,8 +422,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation") - message = record.errors.generate_message(attr_name, :confirmation, :default => configuration[:message]) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :confirmation, :default => configuration[:message]) end end end @@ -441,8 +464,7 @@ module ActiveRecord validates_each(attr_names,configuration) do |record, attr_name, value| unless value == configuration[:accept] - message = record.errors.generate_message(attr_name, :accepted, :default => configuration[:message]) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :accepted, :default => configuration[:message]) end end end @@ -544,11 +566,9 @@ module ActiveRecord validates_each(attrs, options) do |record, attr, value| value = options[:tokenizer].call(value) if value.kind_of?(String) if value.nil? or value.size < option_value.begin - message = record.errors.generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) - record.errors.add(attr, message) + record.errors.add(attr, :too_short, :default => options[:too_short], :count => option_value.begin) elsif value.size > option_value.end - message = record.errors.generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) - record.errors.add(attr, message) + record.errors.add(attr, :too_long, :default => options[:too_long], :count => option_value.end) end end when :is, :minimum, :maximum @@ -563,8 +583,7 @@ module ActiveRecord unless !value.nil? and value.size.method(validity_checks[option])[option_value] key = message_options[option] custom_message = options[:message] || options[key] - message = record.errors.generate_message(attr, key, :default => custom_message, :count => option_value) - record.errors.add(attr, message) + record.errors.add(attr, key, :default => custom_message, :count => option_value) end end end @@ -662,8 +681,7 @@ module ActiveRecord finder_class.with_exclusive_scope do if finder_class.exists?([condition_sql, *condition_params]) - message = record.errors.generate_message(attr_name, :taken, :default => configuration[:message], :value => value) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :taken, :default => configuration[:message], :value => value) end end end @@ -701,8 +719,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| unless value.to_s =~ configuration[:with] - message = record.errors.generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) end end end @@ -736,8 +753,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| unless enum.include?(value) - message = record.errors.generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value) end end end @@ -771,8 +787,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| if enum.include?(value) - message = record.errors.generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value) end end end @@ -814,8 +829,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| unless (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v } - message = record.errors.generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) end end end @@ -864,8 +878,7 @@ module ActiveRecord if configuration[:only_integer] unless raw_value.to_s =~ /\A[+-]?\d+\Z/ - message = record.errors.generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) next end raw_value = raw_value.to_i @@ -873,8 +886,7 @@ module ActiveRecord begin raw_value = Kernel.Float(raw_value) rescue ArgumentError, TypeError - message = record.errors.generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) - record.errors.add(attr_name, message) + record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) next end end @@ -883,12 +895,10 @@ module ActiveRecord case option when :odd, :even unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[] - message = record.errors.generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) - record.errors.add(attr_name, message) + record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message]) end else - message = record.errors.generate_message(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) - record.errors.add(attr_name, message) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]] + record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]] end end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index a0beb8be1b..e110595437 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -6,7 +6,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def setup reset_callbacks Topic @topic = Topic.new - I18n.backend.store_translations('en-US', :active_record => {:error_messages => {:custom => nil}}) + I18n.backend.store_translations('en-US', :activerecord => {:errors => {:messages => {:custom => nil}}}) end def teardown @@ -43,20 +43,23 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # ActiveRecord::Errors uses_mocha 'ActiveRecord::Errors' do def test_errors_generate_message_translates_custom_model_attribute_key - global_scope = [:active_record, :error_messages] + global_scope = [:activerecord, :errors, :messages] custom_scope = global_scope + [:custom, 'topic', :title] - - I18n.expects(:t).with :"custom.topic.title.invalid", :scope => [:active_record, :error_messages], :default => ['default from class def', :invalid] - @topic.errors.generate_message :title, :invalid, :default => 'default from class def' + + I18n.expects(:translate).with('topic', {:count => 1, :default => 'Topic', :scope => [:activerecord, :models]}).returns('Topic') + I18n.expects(:translate).with(:'topic.title', {:count => 1, :default => ['Title'], :scope => [:activerecord, :attributes]}).returns('Title') + I18n.expects(:translate).with(:"custom.topic.title.invalid", :scope => global_scope, :default => [:"custom.topic.invalid", 'default from class def error 1', :invalid], :attribute => "Title", :model => "Topic").returns('default from class def error 1') + @topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1' end - + def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti - custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] - - I18n.expects(:t).with :"custom.reply.title.invalid", :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] + custom_scope = [:activerecord, :errors, :custom, 'topic', :title] + I18n.expects(:translate).with('reply', {:count => 1, :default => 'Reply', :scope => [:activerecord, :models]}).returns('Reply') + I18n.expects(:translate).with(:'reply.title', {:count => 1, :default => [:'topic.title', 'Title'], :scope => [:activerecord, :attributes]}).returns('Title') + I18n.expects(:translate).with(:"custom.reply.title.invalid", :scope => [:activerecord, :errors, :messages], :default => [:"custom.reply.invalid", :"custom.topic.title.invalid", :"custom.topic.invalid", 'default from class def', :invalid], :model => 'Reply', :attribute => 'Title').returns("default from class def") Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' end - + def test_errors_add_on_empty_generates_message @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) @topic.errors.add_on_empty :title @@ -79,7 +82,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_errors_full_messages_translates_human_attribute_name_for_model_attributes @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } - I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", :locale => 'en-US', :default => 'Title').returns('Title') + I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title') @topic.errors.full_messages :locale => 'en-US' end end @@ -344,8 +347,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_confirmation_of w/o mocha def test_validates_confirmation_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}} Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @@ -354,7 +357,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_confirmation_of_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}} Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @@ -365,8 +368,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_acceptance_of w/o mocha def test_validates_acceptance_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}} Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? @@ -374,7 +377,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_acceptance_of_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}} Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? @@ -384,8 +387,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_presence_of w/o mocha def test_validates_presence_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:blank => 'global message'}}} Topic.validates_presence_of :title @topic.valid? @@ -393,7 +396,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_presence_of_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:blank => 'global message'}}} Topic.validates_presence_of :title @topic.valid? @@ -403,8 +406,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_length_of :within w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}} Topic.validates_length_of :title, :within => 3..5 @topic.valid? @@ -412,7 +415,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}} Topic.validates_length_of :title, :within => 3..5 @topic.valid? @@ -422,8 +425,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_length_of :is w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -431,7 +434,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -441,8 +444,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_uniqueness_of w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -450,7 +453,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -461,8 +464,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_format_of w/o mocha def test_validates_format_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? @@ -470,7 +473,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_format_of_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? @@ -480,8 +483,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_inclusion_of w/o mocha def test_validates_inclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}} Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? @@ -489,7 +492,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_inclusion_of_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}} Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? @@ -499,8 +502,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_exclusion_of w/o mocha def test_validates_exclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}} Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @@ -509,7 +512,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_exclusion_of_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}} Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @@ -520,8 +523,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of without :only_integer w/o mocha def test_validates_numericality_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} Topic.validates_numericality_of :title @topic.title = 'a' @@ -530,7 +533,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @@ -541,8 +544,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of with :only_integer w/o mocha def test_validates_numericality_of_only_integer_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @@ -551,7 +554,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_only_integer_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @@ -562,8 +565,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of :odd w/o mocha def test_validates_numericality_of_odd_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:odd => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @@ -572,7 +575,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_odd_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:odd => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @@ -583,8 +586,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of :less_than w/o mocha def test_validates_numericality_of_less_than_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @@ -593,7 +596,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_less_than_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @@ -605,8 +608,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_associated w/o mocha def test_validates_associated_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} Topic.validates_associated :replies replied_topic.valid? @@ -614,7 +617,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_associated_finds_global_default_translation - I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} Topic.validates_associated :replies replied_topic.valid? @@ -627,27 +630,29 @@ class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase reset_callbacks Topic @topic = Topic.new I18n.backend.store_translations :'en-US', { - :active_record => { - :error_messages => { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :accepted => "must be accepted", - :empty => "can't be empty", - :blank => "can't be blank", - :too_long => "is too long (maximum is {{count}} characters)", - :too_short => "is too short (minimum is {{count}} characters)", - :wrong_length => "is the wrong length (should be {{count}} characters)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :greater_than => "must be greater than {{count}}", - :greater_than_or_equal_to => "must be greater than or equal to {{count}}", - :equal_to => "must be equal to {{count}}", - :less_than => "must be less than {{count}}", - :less_than_or_equal_to => "must be less than or equal to {{count}}", - :odd => "must be odd", - :even => "must be even" + :activerecord => { + :errors => { + :messages => { + :inclusion => "is not included in the list", + :exclusion => "is reserved", + :invalid => "is invalid", + :confirmation => "doesn't match confirmation", + :accepted => "must be accepted", + :empty => "can't be empty", + :blank => "can't be blank", + :too_long => "is too long (maximum is {{count}} characters)", + :too_short => "is too short (minimum is {{count}} characters)", + :wrong_length => "is the wrong length (should be {{count}} characters)", + :taken => "has already been taken", + :not_a_number => "is not a number", + :greater_than => "must be greater than {{count}}", + :greater_than_or_equal_to => "must be greater than or equal to {{count}}", + :equal_to => "must be equal to {{count}}", + :less_than => "must be less than {{count}}", + :less_than_or_equal_to => "must be less than or equal to {{count}}", + :odd => "must be odd", + :even => "must be even" + } } } } @@ -798,4 +803,4 @@ class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase def test_generate_message_even_with_default_message assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10) end -end \ No newline at end of file +end -- cgit v1.2.3 From e3523f1d33c3cf53f1a65e520be5e937e9c68c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Fri, 15 Aug 2008 18:39:11 +0300 Subject: Fixed validates_uniqueness_of with decimal columns Only use special case-sensitive comparison operators for text columns in validates_uniqueness_of as mysql can fail at decimal comparisons with the BINARY operator. --- activerecord/lib/active_record/validations.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index e7a9676394..b7e6394748 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -629,12 +629,11 @@ module ActiveRecord if value.nil? comparison_operator = "IS ?" - else + elsif is_text_column comparison_operator = "#{connection.case_sensitive_equality_operator} ?" - - if is_text_column - value = value.to_s - end + value = value.to_s + else + comparison_operator = "= ?" end sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}" -- cgit v1.2.3 From b3c9d53b348f586ed223ec5de9f525faee6f564d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Mon, 2 Jun 2008 16:48:06 +0300 Subject: Use type_condition method for hmt STI condition --- .../lib/active_record/associations/has_many_through_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index e1bfff5923..24b02efc35 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -237,7 +237,7 @@ module ActiveRecord end def build_sti_condition - "#{@reflection.through_reflection.quoted_table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.sti_name)}" + @reflection.through_reflection.klass.send(:type_condition) end alias_method :sql_conditions, :conditions -- cgit v1.2.3 From 8f4d3957a6986fe450cfd9058bb92ae1d6e5e745 Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Fri, 15 Aug 2008 13:51:57 -0700 Subject: Don't raise exception when comparing ActiveRecord::Reflection. [#842 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/reflection.rb | 2 +- activerecord/test/cases/reflection_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 3f74c03714..935b1939d8 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -109,7 +109,7 @@ module ActiveRecord # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute, # and +other_aggregation+ has an options hash assigned to it. def ==(other_aggregation) - name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record + other_aggregation.kind_of?(self.class) && name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record end def sanitized_conditions #:nodoc: diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 723062e3b8..4b86e32dbf 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -166,6 +166,10 @@ class ReflectionTest < ActiveRecord::TestCase assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end + def test_reflection_should_not_raise_error_when_compared_to_other_object + assert_nothing_raised { Firm.reflections[:clients] == Object.new } + end + private def assert_reflection(klass, association, options) assert reflection = klass.reflect_on_association(association) -- cgit v1.2.3 From 2b69a636c431d62a85b2896d87b69cb13e2b8af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Sat, 16 Aug 2008 02:24:29 +0300 Subject: Fixed STI type condition for eager loading of associations Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 6 ++---- activerecord/lib/active_record/base.rb | 7 ++++--- .../test/cases/associations/cascaded_eager_loading_test.rb | 12 ++++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 4e33dfe69f..b72fdb305f 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2099,10 +2099,8 @@ module ActiveRecord else "" end || '' - join << %(AND %s.%s = %s ) % [ - connection.quote_table_name(aliased_table_name), - connection.quote_column_name(klass.inheritance_column), - klass.quote_value(klass.sti_name)] unless klass.descends_from_active_record? + join << %(AND %s) % [ + klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? [through_reflection, reflection].each do |ref| join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions] diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2c4ead081d..6eb4d42d51 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1609,10 +1609,11 @@ module ActiveRecord #:nodoc: sql << "WHERE #{merged_conditions} " unless merged_conditions.blank? end - def type_condition + def type_condition(table_alias=nil) + quoted_table_alias = self.connection.quote_table_name(table_alias || table_name) quoted_inheritance_column = connection.quote_column_name(inheritance_column) - type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass| - condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.sti_name}' " + type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass| + condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' " end " (#{type_condition}) " diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 1f8a1090eb..8c9ae8a031 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -68,6 +68,18 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end end + def test_eager_association_loading_with_has_many_sti_and_subclasses + silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1) + silly.parent_id = 1 + assert silly.save + + topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id') + assert_no_queries do + assert_equal 2, topics[0].replies.size + assert_equal 0, topics[1].replies.size + end + end + def test_eager_association_loading_with_belongs_to_sti replies = Reply.find(:all, :include => :topic, :order => 'topics.id') assert replies.include?(topics(:second)) -- cgit v1.2.3 From 8cfdcdb35db6e2f6fd5a72a38f4352beab148af1 Mon Sep 17 00:00:00 2001 From: Nathan Witmer Date: Sat, 16 Aug 2008 13:38:01 -0600 Subject: Updated has_and_belongs_to_many association to fix :finder_sql interpolation. [#848 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations/association_proxy.rb | 4 ---- .../associations/has_and_belongs_to_many_association.rb | 4 +--- .../associations/has_and_belongs_to_many_associations_test.rb | 7 +++++++ 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 77fc827e11..981be3b1a9 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -131,10 +131,6 @@ module ActiveRecord records.map { |record| record.quoted_id }.join(',') end - def interpolate_sql_options!(options, *keys) - keys.each { |key| options[key] &&= interpolate_sql(options[key]) } - end - def interpolate_sql(sql, record = nil) @owner.send(:interpolate_sql, sql, record) end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index d516d54151..e7e433b6b6 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -70,10 +70,8 @@ module ActiveRecord end def construct_sql - interpolate_sql_options!(@reflection.options, :finder_sql) - if @reflection.options[:finder_sql] - @finder_sql = @reflection.options[:finder_sql] + @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) else @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} " @finder_sql << " AND (#{conditions})" if conditions diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index f71b122ff0..dfd82534ff 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -450,6 +450,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find" end + def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations + # interpolate once: + assert_equal [developers(:david), developers(:poor_jamis), developers(:jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation" + # interpolate again, for a different project id + assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation" + end + def test_find_in_association_with_custom_finder_sql_and_string_id assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find" end -- cgit v1.2.3 From 96607996eaa826b5299780c7c9142e6e0157d198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Sun, 17 Aug 2008 00:20:55 +0300 Subject: Test for eager loading of STI subclasses from htm associations Signed-off-by: Pratik Naik --- activerecord/test/cases/associations/join_model_test.rb | 7 +++++++ activerecord/test/models/author.rb | 3 +++ 2 files changed, 10 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 9e79d9c8a1..7a0427aabc 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -694,6 +694,13 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert ! david.categories.include?(category) end + def test_has_many_through_goes_through_all_sti_classes + sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1) + new_comment = sub_sti_post.comments.create(:body => 'test') + + assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort + end + private # create dynamic Post models to allow different dependency options def find_post_with_dependency(post_id, association, association_name, dependency) diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 136dc39cf3..c6aa0293c2 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -32,6 +32,9 @@ class Author < ActiveRecord::Base has_many :special_posts has_many :special_post_comments, :through => :special_posts, :source => :comments + has_many :sti_posts, :class_name => 'StiPost' + has_many :sti_post_comments, :through => :sti_posts, :source => :comments + has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'" has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0" has_many :nonexistant_comments, :through => :posts -- cgit v1.2.3 From cd8e653d5b18e6d3c3acc9930832f8e23945e392 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 11 Aug 2008 10:25:57 -0700 Subject: Performance: freeze cached rows instead of duping --- activerecord/lib/active_record/base.rb | 2 +- .../connection_adapters/abstract/query_cache.rb | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6eb4d42d51..5357255bad 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -612,7 +612,7 @@ module ActiveRecord #:nodoc: # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] # > [#"The Cheap Man Buys Twice"}>, ...] def find_by_sql(sql) - connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) } + connection.select_all(sanitize_sql(sql), "#{name} Load").map { |record| instantiate(record) } end # Checks whether a record exists in the database that matches conditions given. These conditions diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 2afd6064ad..81a2e56b34 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -72,21 +72,12 @@ module ActiveRecord private def cache_sql(sql) - result = - if @query_cache.has_key?(sql) - log_info(sql, "CACHE", 0.0) - @query_cache[sql] - else - @query_cache[sql] = yield - end - - if Array === result - result.collect { |row| row.dup } + if @query_cache.has_key?(sql) + log_info(sql, "CACHE", 0.0) + @query_cache[sql] else - result.duplicable? ? result.dup : result + @query_cache[sql] = yield.freeze end - rescue TypeError - result end end end -- cgit v1.2.3 From c1a8690d582c08777055caf449c03f85b4c8aa4b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 18 Aug 2008 22:38:58 -0500 Subject: Consistently use the framework's configured logger and avoid reverting to RAILS_DEFAULT_LOGGER unless necessary. --- activerecord/test/connections/native_mysql/connection.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/connections/native_mysql/connection.rb b/activerecord/test/connections/native_mysql/connection.rb index 1fab444e58..140e06d631 100644 --- a/activerecord/test/connections/native_mysql/connection.rb +++ b/activerecord/test/connections/native_mysql/connection.rb @@ -2,9 +2,7 @@ print "Using native MySQL\n" require_dependency 'models/course' require 'logger' -RAILS_DEFAULT_LOGGER = Logger.new('debug.log') -RAILS_DEFAULT_LOGGER.level = Logger::DEBUG -ActiveRecord::Base.logger = RAILS_DEFAULT_LOGGER +ActiveRecord::Base.logger = Logger.new("debug.log") # GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost'; # GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost'; -- cgit v1.2.3 From ae8a35d8f6d5db6ae9a1877918d45c15d21e24fe Mon Sep 17 00:00:00 2001 From: Iain Hecker Date: Sat, 16 Aug 2008 20:01:42 +0200 Subject: Added Base.human_name method --- activerecord/lib/active_record/base.rb | 13 ++++++++++ activerecord/test/cases/i18n_test.rb | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 activerecord/test/cases/i18n_test.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 02a0ca7e44..edab017d26 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1249,6 +1249,19 @@ module ActiveRecord #:nodoc: options[:count] ||= 1 I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes])) end + + # Transform the modelname into a more humane format, using I18n. + # Defaults to the basic humanize method. + # Default scope of the translation is activerecord.models + # Specify +options+ with additional translating options. + def human_name(options = {}) + defaults = self_and_descendents_from_active_record.map do |klass| + :"#{klass.name.underscore}" + end + defaults << self.name.humanize + I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options)) + end + # True if this isn't a concrete subclass needing a STI type condition. def descends_from_active_record? if superclass.abstract_class? diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb new file mode 100644 index 0000000000..3527644f0d --- /dev/null +++ b/activerecord/test/cases/i18n_test.rb @@ -0,0 +1,46 @@ +require "cases/helper" +require 'models/topic' +require 'models/reply' + +class ActiveRecordI18nTests < Test::Unit::TestCase + + def setup + reset_translations + end + + def test_translated_model_attributes + I18n.store_translations 'en-US', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } + assert_equal 'topic title attribute', Topic.human_attribute_name('title') + end + + def test_translated_model_attributes_with_sti + I18n.store_translations 'en-US', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } } + assert_equal 'reply title attribute', Reply.human_attribute_name('title') + end + + def test_translated_model_attributes_with_sti_fallback + I18n.store_translations 'en-US', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } + assert_equal 'topic title attribute', Reply.human_attribute_name('title') + end + + def test_translated_model_names + I18n.store_translations 'en-US', :activerecord => {:models => {:topic => 'topic model'} } + assert_equal 'topic model', Topic.human_name + end + + def test_translated_model_names_with_sti + I18n.store_translations 'en-US', :activerecord => {:models => {:reply => 'reply model'} } + assert_equal 'reply model', Reply.human_name + end + + def test_translated_model_names_with_sti_fallback + I18n.store_translations 'en-US', :activerecord => {:models => {:topic => 'topic model'} } + assert_equal 'topic model', Reply.human_name + end + + private + def reset_translations + I18n.backend.send(:class_variable_set, :@@translations, {}) + end +end + -- cgit v1.2.3 From c531248938302477c5e52138d59a6c3d1527d963 Mon Sep 17 00:00:00 2001 From: Iain Hecker Date: Sat, 16 Aug 2008 20:22:40 +0200 Subject: Introduced AR::Base.human_name to validations --- activerecord/lib/active_record/validations.rb | 2 +- activerecord/test/cases/validations_i18n_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 92b3e9a597..040681a09c 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -100,7 +100,7 @@ module ActiveRecord options.merge!({ :default => defaults, - :model => I18n.translate(model_name.underscore, :default => model_name.humanize, :scope => [:activerecord, :models], :count => 1), + :model => @base.class.human_name, :attribute => @base.class.human_attribute_name(attribute.to_s), :scope => [:activerecord, :errors, :messages] }) diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index e110595437..96f86fbe0e 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -46,7 +46,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase global_scope = [:activerecord, :errors, :messages] custom_scope = global_scope + [:custom, 'topic', :title] - I18n.expects(:translate).with('topic', {:count => 1, :default => 'Topic', :scope => [:activerecord, :models]}).returns('Topic') + I18n.expects(:translate).with(:topic, {:count => 1, :default => ['Topic'], :scope => [:activerecord, :models]}).returns('Topic') I18n.expects(:translate).with(:'topic.title', {:count => 1, :default => ['Title'], :scope => [:activerecord, :attributes]}).returns('Title') I18n.expects(:translate).with(:"custom.topic.title.invalid", :scope => global_scope, :default => [:"custom.topic.invalid", 'default from class def error 1', :invalid], :attribute => "Title", :model => "Topic").returns('default from class def error 1') @topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1' @@ -54,7 +54,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti custom_scope = [:activerecord, :errors, :custom, 'topic', :title] - I18n.expects(:translate).with('reply', {:count => 1, :default => 'Reply', :scope => [:activerecord, :models]}).returns('Reply') + I18n.expects(:translate).with(:reply, {:count => 1, :default => [:topic, 'Reply'], :scope => [:activerecord, :models]}).returns('Reply') I18n.expects(:translate).with(:'reply.title', {:count => 1, :default => [:'topic.title', 'Title'], :scope => [:activerecord, :attributes]}).returns('Title') I18n.expects(:translate).with(:"custom.reply.title.invalid", :scope => [:activerecord, :errors, :messages], :default => [:"custom.reply.invalid", :"custom.topic.title.invalid", :"custom.topic.invalid", 'default from class def', :invalid], :model => 'Reply', :attribute => 'Title').returns("default from class def") Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' -- cgit v1.2.3 From e2b191681e1c8c8b4344f1fc755e48fccdd1d603 Mon Sep 17 00:00:00 2001 From: Iain Hecker Date: Sat, 16 Aug 2008 21:45:23 +0200 Subject: Added :value as interpolation variable available to error messages --- activerecord/lib/active_record/validations.rb | 18 ++++++++++-------- activerecord/test/cases/validations_i18n_test.rb | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 040681a09c..a442d008a9 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -92,17 +92,19 @@ module ActiveRecord :"custom.#{klass.name.underscore}.#{message}" ] end - defaults << options[:default] if options[:default] - defaults.flatten! << message + defaults << options.delete(:default) + defaults = defaults.compact.flatten << message model_name = @base.class.name key = defaults.shift - - options.merge!({ - :default => defaults, - :model => @base.class.human_name, - :attribute => @base.class.human_attribute_name(attribute.to_s), - :scope => [:activerecord, :errors, :messages] }) + value = @base.send(attribute) if @base.respond_to?(attribute) + + options = { :default => defaults, + :model => @base.class.human_name, + :attribute => @base.class.human_attribute_name(attribute.to_s), + :value => value, + :scope => [:activerecord, :errors, :messages] + }.merge(options) I18n.translate(key, options) end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 96f86fbe0e..469e9d6c03 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -48,7 +48,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase I18n.expects(:translate).with(:topic, {:count => 1, :default => ['Topic'], :scope => [:activerecord, :models]}).returns('Topic') I18n.expects(:translate).with(:'topic.title', {:count => 1, :default => ['Title'], :scope => [:activerecord, :attributes]}).returns('Title') - I18n.expects(:translate).with(:"custom.topic.title.invalid", :scope => global_scope, :default => [:"custom.topic.invalid", 'default from class def error 1', :invalid], :attribute => "Title", :model => "Topic").returns('default from class def error 1') + I18n.expects(:translate).with(:"custom.topic.title.invalid", :value => nil, :scope => global_scope, :default => [:"custom.topic.invalid", 'default from class def error 1', :invalid], :attribute => "Title", :model => "Topic").returns('default from class def error 1') @topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1' end @@ -56,7 +56,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase custom_scope = [:activerecord, :errors, :custom, 'topic', :title] I18n.expects(:translate).with(:reply, {:count => 1, :default => [:topic, 'Reply'], :scope => [:activerecord, :models]}).returns('Reply') I18n.expects(:translate).with(:'reply.title', {:count => 1, :default => [:'topic.title', 'Title'], :scope => [:activerecord, :attributes]}).returns('Title') - I18n.expects(:translate).with(:"custom.reply.title.invalid", :scope => [:activerecord, :errors, :messages], :default => [:"custom.reply.invalid", :"custom.topic.title.invalid", :"custom.topic.invalid", 'default from class def', :invalid], :model => 'Reply', :attribute => 'Title').returns("default from class def") + I18n.expects(:translate).with(:"custom.reply.title.invalid", :value => nil, :scope => [:activerecord, :errors, :messages], :default => [:"custom.reply.invalid", :"custom.topic.title.invalid", :"custom.topic.invalid", 'default from class def', :invalid], :model => 'Reply', :attribute => 'Title').returns("default from class def") Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' end -- cgit v1.2.3 From a6a62004c93d792c230d80f8a65da0bb45cb369f Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 20 Aug 2008 17:51:42 +0200 Subject: add human_name and value to ar validation #generate_message --- activerecord/test/cases/i18n_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb index 3527644f0d..06036733f5 100644 --- a/activerecord/test/cases/i18n_test.rb +++ b/activerecord/test/cases/i18n_test.rb @@ -39,8 +39,8 @@ class ActiveRecordI18nTests < Test::Unit::TestCase end private - def reset_translations - I18n.backend.send(:class_variable_set, :@@translations, {}) - end + def reset_translations + I18n.backend = I18n::Backend::Simple.new + end end -- cgit v1.2.3 From febe2ea9775c863cb9744c6343291e550e4628b8 Mon Sep 17 00:00:00 2001 From: Iain Hecker Date: Tue, 19 Aug 2008 23:19:57 +0200 Subject: Locale file changed to yaml --- activerecord/lib/active_record.rb | 2 +- activerecord/lib/active_record/locale/en-US.yml | 33 ++++++++++++++++++++++++ activerecord/test/cases/validations_i18n_test.rb | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 activerecord/lib/active_record/locale/en-US.yml (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 08b6b19f7f..bc27d17ccd 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -82,6 +82,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' I18n.backend.populate do - I18n.load_translations File.dirname(__FILE__) + '/active_record/locale/en-US.rb' + I18n.load_translations File.dirname(__FILE__) + '/active_record/locale/en-US.yml' end diff --git a/activerecord/lib/active_record/locale/en-US.yml b/activerecord/lib/active_record/locale/en-US.yml new file mode 100644 index 0000000000..8148f31a81 --- /dev/null +++ b/activerecord/lib/active_record/locale/en-US.yml @@ -0,0 +1,33 @@ +en-US: + activerecord: + errors: + # The values :model, :attribute and :value are always available for interpolation + # The value :count is available when applicable. Can be used for pluralization. + messages: + inclusion: "is not included in the list" + exclusion: "is reserved" + invalid: "is invalid" + confirmation: "doesn't match confirmation" + accepted: "must be accepted" + empty: "can't be empty" + blank: "can't be blank" + too_long: "is too long (maximum is {{count}} characters)" + too_short: "is too short (minimum is {{count}} characters)" + wrong_length: "is the wrong length (should be {{count}} characters)" + taken: "has already been taken" + not_a_number: "is not a number" + greater_than: "must be greater than {{count}}" + greater_than_or_equal_to: "must be greater than or equal to {{count}}" + equal_to: "must be equal to {{count}}" + less_than: "must be less than {{count}}" + less_than_or_equal_to: "must be less than or equal to {{count}}" + odd: "must be odd" + even: "must be even" + # Append your own errors here or at the model/attributes scope. + + models: + # Overrides default messages + + attributes: + # Overrides model and default messages. + diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 469e9d6c03..96af3b17a6 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -11,7 +11,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def teardown reset_callbacks Topic - I18n.load_translations File.dirname(__FILE__) + '/../../lib/active_record/locale/en-US.rb' + I18n.load_translations File.dirname(__FILE__) + '/../../lib/active_record/locale/en-US.yml' end def unique_topic -- cgit v1.2.3 From 72366398b28cec2f8b9809f463e0b1271e3d5ba0 Mon Sep 17 00:00:00 2001 From: Iain Hecker Date: Tue, 19 Aug 2008 23:22:59 +0200 Subject: Removed en-US ruby locale in favor of yaml --- activerecord/lib/active_record/locale/en-US.rb | 28 -------------------------- 1 file changed, 28 deletions(-) delete mode 100644 activerecord/lib/active_record/locale/en-US.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/locale/en-US.rb b/activerecord/lib/active_record/locale/en-US.rb deleted file mode 100644 index 89a5baba06..0000000000 --- a/activerecord/lib/active_record/locale/en-US.rb +++ /dev/null @@ -1,28 +0,0 @@ -{ :'en-US' => { - :activerecord => { - :errors => { - :messages => { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :accepted => "must be accepted", - :empty => "can't be empty", - :blank => "can't be blank", - :too_long => "is too long (maximum is {{count}} characters)", - :too_short => "is too short (minimum is {{count}} characters)", - :wrong_length => "is the wrong length (should be {{count}} characters)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :greater_than => "must be greater than {{count}}", - :greater_than_or_equal_to => "must be greater than or equal to {{count}}", - :equal_to => "must be equal to {{count}}", - :less_than => "must be less than {{count}}", - :less_than_or_equal_to => "must be less than or equal to {{count}}", - :odd => "must be odd", - :even => "must be even" - } - } - } - } -} -- cgit v1.2.3 From 2415652660242d6b0da97119c562ecff82928575 Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Thu, 21 Aug 2008 12:37:19 +0100 Subject: Support find_all on named scopes. [#730 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/named_scope.rb | 2 +- activerecord/test/cases/named_scope_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 0902018155..26701548c2 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -103,7 +103,7 @@ module ActiveRecord attr_reader :proxy_scope, :proxy_options [].methods.each do |m| - unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?|respond_to?)/ + unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|^find$|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?|respond_to?)/ delegate m, :to => :proxy_found end end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index bd6ec23853..7cc51f5d68 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -238,4 +238,8 @@ class NamedScopeTest < ActiveRecord::TestCase assert topic.approved assert_equal 'lifo', topic.author_name end + + def test_find_all_should_behave_like_select + assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved) + end end -- cgit v1.2.3 From ea40f71431a821b2ddb37be6ea3ee7d8dac63b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Thu, 21 Aug 2008 12:55:35 +0200 Subject: Fix that has_one natural assignment to already associated record. [#854 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/associations/has_one_association.rb | 4 ++-- .../test/cases/associations/has_one_associations_test.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index fdc0fa52c9..18733255d2 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -21,8 +21,8 @@ module ActiveRecord def replace(obj, dont_save = false) load_target - unless @target.nil? - if dependent? && !dont_save && @target != obj + unless @target.nil? || @target == obj + if dependent? && !dont_save @target.destroy unless @target.new_record? @owner.clear_association_cache else diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 99639849a5..ec06be5eba 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -79,6 +79,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) } end + def test_natural_assignment_to_already_associated_record + company = companies(:first_firm) + account = accounts(:signals37) + assert_equal company.account, account + company.account = account + company.reload + account.reload + assert_equal company.account, account + end + def test_assignment_without_replacement apple = Firm.create("name" => "Apple") citibank = Account.create("credit_limit" => 10) -- cgit v1.2.3 From a970f916fb1e05376733e2d42d9bcc2b873af355 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 21 Aug 2008 15:45:06 +0100 Subject: Fix has_many#count_records. [#865 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/associations/has_many_association.rb | 7 +++++-- .../test/cases/associations/has_many_associations_test.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index e6fa15c173..ce62127505 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -35,8 +35,11 @@ module ActiveRecord else @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include]) end - - @target = [] and loaded if count == 0 + + # If there's nothing in the database and @target has no new records + # we are certain the current target is an empty array. This is a + # documented side-effect of the method that may avoid an extra SELECT. + @target ||= [] and loaded if count == 0 if @reflection.options[:limit] count = [ @reflection.options[:limit], count ].min diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index b806e885e1..da3c8fb28e 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -395,6 +395,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 3, company.clients_of_firm.size end + def test_collection_size_twice_for_regressions + post = posts(:thinking) + assert_equal 0, post.readers.size + # This test needs a post that has no readers, we assert it to ensure it holds, + # but need to reload the post because the very call to #size hides the bug. + post.reload + post.readers.build + size1 = post.readers.size + size2 = post.readers.size + assert_equal size1, size2 + end + def test_build_many company = companies(:first_firm) new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) } -- cgit v1.2.3 From 49c0e1e594c95d7e8446ebabecc9147afa62de7d Mon Sep 17 00:00:00 2001 From: Philip Hallstrom Date: Thu, 21 Aug 2008 16:08:42 +0100 Subject: Fix generated WHERE IN query for named scopes. [#583 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/named_scope_test.rb | 5 +++++ activerecord/test/models/developer.rb | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 5357255bad..15c6bc1b4a 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1750,7 +1750,7 @@ module ActiveRecord #:nodoc: def attribute_condition(argument) case argument when nil then "IS ?" - when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)" + when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)" when Range then "BETWEEN ? AND ?" else "= ?" end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 7cc51f5d68..db31ddb293 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -4,6 +4,7 @@ require 'models/topic' require 'models/comment' require 'models/reply' require 'models/author' +require 'models/developer' class NamedScopeTest < ActiveRecord::TestCase fixtures :posts, :authors, :topics, :comments, :author_addresses @@ -242,4 +243,8 @@ class NamedScopeTest < ActiveRecord::TestCase def test_find_all_should_behave_like_select assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved) end + + def test_should_use_where_in_query_for_named_scope + assert_equal Developer.find_all_by_name('Jamis'), Developer.find_all_by_id(Developer.jamises) + end end diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 9f26cacdec..c08476f728 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -43,6 +43,8 @@ class Developer < ActiveRecord::Base has_many :audit_logs + named_scope :jamises, :conditions => {:name => 'Jamis'} + validates_inclusion_of :salary, :in => 50000..200000 validates_length_of :name, :within => 3..20 -- cgit v1.2.3 From 0d74e72e6de7b96e158950a449ea1ccce6f5b8d7 Mon Sep 17 00:00:00 2001 From: Miles Georgi Date: Sun, 17 Aug 2008 23:45:25 -0700 Subject: Fix postgres bug when change_column is called with invalid parameters. [#861 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tarmo Tänav Signed-off-by: Pratik Naik --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 856435517a..74da0d9c85 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -761,7 +761,8 @@ module ActiveRecord begin execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - rescue ActiveRecord::StatementInvalid + rescue ActiveRecord::StatementInvalid => e + raise e if postgresql_version > 80000 # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it. begin begin_db_transaction -- cgit v1.2.3 From 3724dafe71f4afb2ca9f4d7d2526b228aa6c05a3 Mon Sep 17 00:00:00 2001 From: Tom Lea Date: Thu, 21 Aug 2008 16:38:27 +0100 Subject: Fix incorrect signature for NamedScope#respond_to? [#852 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/named_scope.rb | 4 ++-- activerecord/test/cases/named_scope_test.rb | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 26701548c2..c99c4beca9 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -140,8 +140,8 @@ module ActiveRecord @found ? @found.empty? : count.zero? end - def respond_to?(method) - super || @proxy_scope.respond_to?(method) + def respond_to?(method, include_private = false) + super || @proxy_scope.respond_to?(method, include_private) end def any? diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index db31ddb293..6f6ea1cbe9 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -52,6 +52,11 @@ class NamedScopeTest < ActiveRecord::TestCase assert Topic.approved.respond_to?(:length) end + def test_respond_to_respects_include_private_parameter + assert !Topic.approved.respond_to?(:load_found) + assert Topic.approved.respond_to?(:load_found, true) + end + def test_subclasses_inherit_scopes assert Topic.scopes.include?(:base) -- cgit v1.2.3 From 8622787f8748434b4ceb2b925a35b17a38e1f2d6 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Wed, 2 Jul 2008 21:27:42 -0400 Subject: Don't interpret decimals as table names in ActiveRecord::Associations::ClassMethods#references_eager_loaded_tables? [#532 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 6 +++--- activerecord/test/cases/associations/eager_test.rb | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) mode change 100644 => 100755 activerecord/lib/active_record/associations.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb old mode 100644 new mode 100755 index b72fdb305f..b9039ce996 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1679,19 +1679,19 @@ module ActiveRecord else all << cond end end - conditions.join(' ').scan(/([\.\w]+).?\./).flatten + conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten end def order_tables(options) order = [options[:order], scope(:find, :order) ].join(", ") return [] unless order && order.is_a?(String) - order.scan(/([\.\w]+).?\./).flatten + order.scan(/([\.a-zA-Z_]+).?\./).flatten end def selects_tables(options) select = options[:select] return [] unless select && select.is_a?(String) - select.scan(/"?([\.\w]+)"?.?\./).flatten + select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten end # Checks if the conditions reference a table other than the current model table diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 58506574f8..f37e18df35 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -559,6 +559,13 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_nothing_raised { Post.find(:all, :include => 'comments') } end + def test_eager_with_floating_point_numbers + assert_queries(2) do + # Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query + Comment.find :all, :conditions => "123.456 = 123.456", :include => :post + end + end + def test_preconfigured_includes_with_belongs_to author = posts(:welcome).author_with_posts assert_no_queries {assert_equal 5, author.posts.size} -- cgit v1.2.3 From cf6840773b4f5046151f4d28c286069aabaafa10 Mon Sep 17 00:00:00 2001 From: Iain Hecker Date: Wed, 20 Aug 2008 23:43:46 +0200 Subject: Custom error messages scope improved --- activerecord/lib/active_record/validations.rb | 28 +++---- activerecord/test/cases/validations_i18n_test.rb | 98 ++++++++++++++++++------ 2 files changed, 89 insertions(+), 37 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index a442d008a9..1ea520f3b6 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -68,42 +68,42 @@ module ActiveRecord end # Translates an error message in it's default scope (activerecord.errrors.messages). - # Error messages are first looked up in custom.MODEL.ATTRIBUTE.MESSAGE, if it's not there, it's looked up - # in custom.MODEL.ATTRIBUTE and if that is not there it returns the translation of the default message - # (e.g. activerecord.errors.messages.MESSAGE). Both the model name and the attribute name are available for - # interpolation. + # Error messages are first looked up in models.MODEL.attributes.ATTRIBUTE.MESSAGE, if it's not there, + # it's looked up in models.MODEL.MESSAGE and if that is not there it returns the translation of the + # default message (e.g. activerecord.errors.messages.MESSAGE). The translated model name, + # translated attribute name and the value are available for interpolation. # # When using inheritence in your models, it will check all the inherited models too, but only if the model itself # hasn't been found. Say you have class Admin < User; end and you wanted the translation for the :blank # error +message+ for the title +attribute+, it looks for these translations: # #
    - #
  1. activerecord.errors.messages.custom.admin.title.blank
  2. - #
  3. activerecord.errors.messages.custom.admin.blank
  4. - #
  5. activerecord.errors.messages.custom.user.title.blank
  6. - #
  7. activerecord.errors.messages.custom.user.blank
  8. + #
  9. activerecord.errors.models.admin.attributes.title.blank
  10. + #
  11. activerecord.errors.models.admin.blank
  12. + #
  13. activerecord.errors.models.user.attributes.title.blank
  14. + #
  15. activerecord.errors.models.user.blank
  16. #
  17. activerecord.errors.messages.blank
  18. - #
  19. any default you provided through the +options+ hash
  20. + #
  21. any default you provided through the +options+ hash (in the activerecord.errors scope)
  22. #
def generate_message(attribute, message = :invalid, options = {}) defaults = @base.class.self_and_descendents_from_active_record.map do |klass| - [ :"custom.#{klass.name.underscore}.#{attribute}.#{message}", - :"custom.#{klass.name.underscore}.#{message}" ] + [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}", + :"models.#{klass.name.underscore}.#{message}" ] end defaults << options.delete(:default) - defaults = defaults.compact.flatten << message + defaults = defaults.compact.flatten << :"messages.#{message}" model_name = @base.class.name key = defaults.shift - value = @base.send(attribute) if @base.respond_to?(attribute) + value = @base.respond_to?(attribute) ? @base.send(attribute) : nil options = { :default => defaults, :model => @base.class.human_name, :attribute => @base.class.human_attribute_name(attribute.to_s), :value => value, - :scope => [:activerecord, :errors, :messages] + :scope => [:activerecord, :errors] }.merge(options) I18n.translate(key, options) diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 96af3b17a6..881f219112 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -42,22 +42,74 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # ActiveRecord::Errors uses_mocha 'ActiveRecord::Errors' do + def test_errors_generate_message_translates_custom_model_attribute_key - global_scope = [:activerecord, :errors, :messages] - custom_scope = global_scope + [:custom, 'topic', :title] - I18n.expects(:translate).with(:topic, {:count => 1, :default => ['Topic'], :scope => [:activerecord, :models]}).returns('Topic') - I18n.expects(:translate).with(:'topic.title', {:count => 1, :default => ['Title'], :scope => [:activerecord, :attributes]}).returns('Title') - I18n.expects(:translate).with(:"custom.topic.title.invalid", :value => nil, :scope => global_scope, :default => [:"custom.topic.invalid", 'default from class def error 1', :invalid], :attribute => "Title", :model => "Topic").returns('default from class def error 1') + I18n.expects(:translate).with( + :topic, + { :count => 1, + :default => ['Topic'], + :scope => [:activerecord, :models] + } + ).returns('Topic') + + I18n.expects(:translate).with( + :"topic.title", + { :count => 1, + :default => ['Title'], + :scope => [:activerecord, :attributes] + } + ).returns('Title') + + I18n.expects(:translate).with( + :"models.topic.attributes.title.invalid", + :value => nil, + :scope => [:activerecord, :errors], + :default => [ + :"models.topic.invalid", + 'default from class def error 1', + :"messages.invalid"], + :attribute => "Title", + :model => "Topic" + ).returns('default from class def error 1') + @topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1' end def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti - custom_scope = [:activerecord, :errors, :custom, 'topic', :title] - I18n.expects(:translate).with(:reply, {:count => 1, :default => [:topic, 'Reply'], :scope => [:activerecord, :models]}).returns('Reply') - I18n.expects(:translate).with(:'reply.title', {:count => 1, :default => [:'topic.title', 'Title'], :scope => [:activerecord, :attributes]}).returns('Title') - I18n.expects(:translate).with(:"custom.reply.title.invalid", :value => nil, :scope => [:activerecord, :errors, :messages], :default => [:"custom.reply.invalid", :"custom.topic.title.invalid", :"custom.topic.invalid", 'default from class def', :invalid], :model => 'Reply', :attribute => 'Title').returns("default from class def") + + I18n.expects(:translate).with( + :reply, + { :count => 1, + :default => [:topic, 'Reply'], + :scope => [:activerecord, :models] + } + ).returns('Reply') + + I18n.expects(:translate).with( + :"reply.title", + { :count => 1, + :default => [:'topic.title', 'Title'], + :scope => [:activerecord, :attributes] + } + ).returns('Title') + + I18n.expects(:translate).with( + :"models.reply.attributes.title.invalid", + :value => nil, + :scope => [:activerecord, :errors], + :default => [ + :"models.reply.invalid", + :"models.topic.attributes.title.invalid", + :"models.topic.invalid", + 'default from class def', + :"messages.invalid"], + :model => 'Reply', + :attribute => 'Title' + ).returns("default from class def") + Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' + end def test_errors_add_on_empty_generates_message @@ -347,7 +399,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_confirmation_of w/o mocha def test_validates_confirmation_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:confirmation => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}} Topic.validates_confirmation_of :title @@ -368,7 +420,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_acceptance_of w/o mocha def test_validates_acceptance_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:accepted => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}} Topic.validates_acceptance_of :title, :allow_nil => false @@ -387,7 +439,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_presence_of w/o mocha def test_validates_presence_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:blank => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:blank => 'global message'}}} Topic.validates_presence_of :title @@ -406,7 +458,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_length_of :within w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:too_short => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}} Topic.validates_length_of :title, :within => 3..5 @@ -425,7 +477,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_length_of :is w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} Topic.validates_length_of :title, :is => 5 @@ -444,7 +496,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_uniqueness_of w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} Topic.validates_length_of :title, :is => 5 @@ -464,7 +516,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_format_of w/o mocha def test_validates_format_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:invalid => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @@ -483,7 +535,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_inclusion_of w/o mocha def test_validates_inclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:inclusion => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}} Topic.validates_inclusion_of :title, :in => %w(a b c) @@ -502,7 +554,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_exclusion_of w/o mocha def test_validates_exclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:exclusion => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}} Topic.validates_exclusion_of :title, :in => %w(a b c) @@ -523,7 +575,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of without :only_integer w/o mocha def test_validates_numericality_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} Topic.validates_numericality_of :title @@ -544,7 +596,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of with :only_integer w/o mocha def test_validates_numericality_of_only_integer_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true @@ -565,7 +617,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of :odd w/o mocha def test_validates_numericality_of_odd_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:odd => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:odd => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true, :odd => true @@ -586,7 +638,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_numericality_of :less_than w/o mocha def test_validates_numericality_of_less_than_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:less_than => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}} Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @@ -608,7 +660,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_associated w/o mocha def test_validates_associated_finds_custom_model_key_translation - I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}}} + I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} Topic.validates_associated :replies -- cgit v1.2.3 From aee14630d4dc0856e597794cc731fac68c2d2e34 Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Mon, 18 Aug 2008 15:56:37 -0700 Subject: coerce blank strings to nil values for boolean and integer fields Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/base.rb | 13 ++++++++----- .../connection_adapters/abstract/schema_definitions.rb | 6 +++++- activerecord/test/cases/validations_test.rb | 8 ++++---- 3 files changed, 17 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 15c6bc1b4a..f4f07aa740 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2572,11 +2572,14 @@ module ActiveRecord #:nodoc: end def convert_number_column_value(value) - case value - when FalseClass; 0 - when TrueClass; 1 - when ''; nil - else value + if value == false + 0 + elsif value == true + 1 + elsif value.is_a?(String) && value.blank? + nil + else + value end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 31d6c7942c..08b2c79389 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -138,7 +138,11 @@ module ActiveRecord # convert something to a boolean def value_to_boolean(value) - TRUE_VALUES.include?(value) + if value.blank? + nil + else + TRUE_VALUES.include?(value) + end end # convert something to a BigDecimal diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 4b2d28c80b..a40bda2533 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -1420,8 +1420,8 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase def test_validates_numericality_of_with_nil_allowed Topic.validates_numericality_of :approved, :allow_nil => true - invalid!(BLANK + JUNK) - valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL + INFINITY) + invalid!(JUNK) + valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY) end def test_validates_numericality_of_with_integer_only @@ -1434,8 +1434,8 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase def test_validates_numericality_of_with_integer_only_and_nil_allowed Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true - invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY) - valid!(NIL + INTEGERS) + invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY) + valid!(NIL + BLANK + INTEGERS) end def test_validates_numericality_with_greater_than -- cgit v1.2.3 From bbedb6a624a3d9eb02e0470f31cda8112df06d75 Mon Sep 17 00:00:00 2001 From: "S. Brent Faulkner" Date: Sun, 17 Aug 2008 20:43:15 -0400 Subject: remember created records and select a random one instead of relying on sequential id values starting at 1 Signed-off-by: Michael Koziarski --- .../associations/eager_load_nested_include_test.rb | 42 +++++++++++++++------- 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index 80cfc84b32..12dec5ccd1 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -1,5 +1,20 @@ require 'cases/helper' +module Remembered + def self.included(base) + base.extend ClassMethods + base.class_eval do + after_create :remember + protected + def remember; self.class.remembered << self; end + end + end + + module ClassMethods + def remembered; @@remembered ||= []; end + def rand; @@remembered.rand; end + end +end class ShapeExpression < ActiveRecord::Base belongs_to :shape, :polymorphic => true @@ -8,26 +23,33 @@ end class Circle < ActiveRecord::Base has_many :shape_expressions, :as => :shape + include Remembered end class Square < ActiveRecord::Base has_many :shape_expressions, :as => :shape + include Remembered end class Triangle < ActiveRecord::Base has_many :shape_expressions, :as => :shape + include Remembered end class PaintColor < ActiveRecord::Base has_many :shape_expressions, :as => :paint belongs_to :non_poly, :foreign_key => "non_poly_one_id", :class_name => "NonPolyOne" + include Remembered end class PaintTexture < ActiveRecord::Base has_many :shape_expressions, :as => :paint belongs_to :non_poly, :foreign_key => "non_poly_two_id", :class_name => "NonPolyTwo" + include Remembered end class NonPolyOne < ActiveRecord::Base has_many :paint_colors + include Remembered end class NonPolyTwo < ActiveRecord::Base has_many :paint_textures + include Remembered end @@ -49,23 +71,19 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase end - # meant to be supplied as an ID, never returns 0 - def rand_simple - val = (NUM_SIMPLE_OBJS * rand).round - val == 0 ? 1 : val - end - def generate_test_object_graphs 1.upto(NUM_SIMPLE_OBJS) do [Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!) end - 1.upto(NUM_SIMPLE_OBJS) do |i| - PaintColor.create!(:non_poly_one_id => rand_simple) - PaintTexture.create!(:non_poly_two_id => rand_simple) + 1.upto(NUM_SIMPLE_OBJS) do + PaintColor.create!(:non_poly_one_id => NonPolyOne.rand.id) + PaintTexture.create!(:non_poly_two_id => NonPolyTwo.rand.id) end - 1.upto(NUM_SHAPE_EXPRESSIONS) do |i| - ShapeExpression.create!(:shape_type => [Circle, Square, Triangle].rand.to_s, :shape_id => rand_simple, - :paint_type => [PaintColor, PaintTexture].rand.to_s, :paint_id => rand_simple) + 1.upto(NUM_SHAPE_EXPRESSIONS) do + shape_type = [Circle, Square, Triangle].rand + paint_type = [PaintColor, PaintTexture].rand + ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.rand.id, + :paint_type => paint_type.to_s, :paint_id => paint_type.rand.id) end end -- cgit v1.2.3 From a5eb297424f68583636b762686726bc0c84703c0 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 21 Aug 2008 21:34:17 -0700 Subject: Revert "coerce blank strings to nil values for boolean and integer fields" This reverts commit aee14630d4dc0856e597794cc731fac68c2d2e34. [#860 state:incomplete] --- activerecord/lib/active_record/base.rb | 13 +++++-------- .../connection_adapters/abstract/schema_definitions.rb | 6 +----- activerecord/test/cases/validations_test.rb | 8 ++++---- 3 files changed, 10 insertions(+), 17 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f4f07aa740..15c6bc1b4a 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2572,14 +2572,11 @@ module ActiveRecord #:nodoc: end def convert_number_column_value(value) - if value == false - 0 - elsif value == true - 1 - elsif value.is_a?(String) && value.blank? - nil - else - value + case value + when FalseClass; 0 + when TrueClass; 1 + when ''; nil + else value end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 08b2c79389..31d6c7942c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -138,11 +138,7 @@ module ActiveRecord # convert something to a boolean def value_to_boolean(value) - if value.blank? - nil - else - TRUE_VALUES.include?(value) - end + TRUE_VALUES.include?(value) end # convert something to a BigDecimal diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index a40bda2533..4b2d28c80b 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -1420,8 +1420,8 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase def test_validates_numericality_of_with_nil_allowed Topic.validates_numericality_of :approved, :allow_nil => true - invalid!(JUNK) - valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY) + invalid!(BLANK + JUNK) + valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL + INFINITY) end def test_validates_numericality_of_with_integer_only @@ -1434,8 +1434,8 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase def test_validates_numericality_of_with_integer_only_and_nil_allowed Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true - invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY) - valid!(NIL + BLANK + INTEGERS) + invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY) + valid!(NIL + INTEGERS) end def test_validates_numericality_with_greater_than -- cgit v1.2.3 From 6e3d2a7996f52bd0d7a5157f73a471307ba8aabd Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 21 Aug 2008 21:40:49 -0700 Subject: Revert "Performance: freeze cached rows instead of duping" This reverts commit cd8e653d5b18e6d3c3acc9930832f8e23945e392. --- activerecord/lib/active_record/base.rb | 2 +- .../connection_adapters/abstract/query_cache.rb | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 15c6bc1b4a..0cce1e0157 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -612,7 +612,7 @@ module ActiveRecord #:nodoc: # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] # > [#"The Cheap Man Buys Twice"}>, ...] def find_by_sql(sql) - connection.select_all(sanitize_sql(sql), "#{name} Load").map { |record| instantiate(record) } + connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) } end # Checks whether a record exists in the database that matches conditions given. These conditions diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 81a2e56b34..2afd6064ad 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -72,12 +72,21 @@ module ActiveRecord private def cache_sql(sql) - if @query_cache.has_key?(sql) - log_info(sql, "CACHE", 0.0) - @query_cache[sql] + result = + if @query_cache.has_key?(sql) + log_info(sql, "CACHE", 0.0) + @query_cache[sql] + else + @query_cache[sql] = yield + end + + if Array === result + result.collect { |row| row.dup } else - @query_cache[sql] = yield.freeze + result.duplicable? ? result.dup : result end + rescue TypeError + result end end end -- cgit v1.2.3 From d3b894563a114912113b816f07ed16511363fb65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Fri, 22 Aug 2008 09:44:38 +0300 Subject: Properly quote CREATE DATABASE parameters in postgresql [#771 state:resolved] --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 74da0d9c85..723e1a42dd 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -534,13 +534,13 @@ module ActiveRecord option_string = options.symbolize_keys.sum do |key, value| case key when :owner - " OWNER = '#{value}'" + " OWNER = \"#{value}\"" when :template - " TEMPLATE = #{value}" + " TEMPLATE = \"#{value}\"" when :encoding " ENCODING = '#{value}'" when :tablespace - " TABLESPACE = #{value}" + " TABLESPACE = \"#{value}\"" when :connection_limit " CONNECTION LIMIT = #{value}" else -- cgit v1.2.3 From 52ac9d04442296abe7f05fc4701e1be7a0eed1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Fri, 22 Aug 2008 09:53:59 +0300 Subject: Fixed ordering in test_find_in_association_with_custom_finder_sql_and_multiple_interpolations --- .../cases/associations/has_and_belongs_to_many_associations_test.rb | 2 +- activerecord/test/models/project.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index dfd82534ff..432d245b5b 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -452,7 +452,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations # interpolate once: - assert_equal [developers(:david), developers(:poor_jamis), developers(:jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation" + assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation" # interpolate again, for a different project id assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation" end diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index e1ab89eca5..44c692b5e7 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -7,7 +7,7 @@ class Project < ActiveRecord::Base has_and_belongs_to_many :developers_named_david, :class_name => "Developer", :conditions => "name = 'David'", :uniq => true has_and_belongs_to_many :developers_named_david_with_hash_conditions, :class_name => "Developer", :conditions => { :name => 'David' }, :uniq => true has_and_belongs_to_many :salaried_developers, :class_name => "Developer", :conditions => "salary > 0" - has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id}' + has_and_belongs_to_many :developers_with_finder_sql, :class_name => "Developer", :finder_sql => 'SELECT t.*, j.* FROM developers_projects j, developers t WHERE t.id = j.developer_id AND j.project_id = #{id} ORDER BY t.id' has_and_belongs_to_many :developers_by_sql, :class_name => "Developer", :delete_sql => "DELETE FROM developers_projects WHERE project_id = \#{id} AND developer_id = \#{record.id}" has_and_belongs_to_many :developers_with_callbacks, :class_name => "Developer", :before_add => Proc.new {|o, r| o.developers_log << "before_adding#{r.id || ''}"}, :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || ''}"}, -- cgit v1.2.3 From 683ff235e6b81d28962f5a71ff53730a1c118fc8 Mon Sep 17 00:00:00 2001 From: Patrick Reagan Date: Fri, 22 Aug 2008 12:48:00 +0100 Subject: Ensure t.timestamps respects options. [#828 state:resolved] Signed-off-by: Pratik Naik --- .../abstract/schema_definitions.rb | 7 ++-- activerecord/test/cases/migration_test.rb | 37 ++++++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 31d6c7942c..98016ddab5 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -443,9 +443,10 @@ module ActiveRecord # Appends :datetime columns :created_at and # :updated_at to the table. - def timestamps - column(:created_at, :datetime) - column(:updated_at, :datetime) + def timestamps(*args) + options = args.extract_options! + column(:created_at, :datetime, options) + column(:updated_at, :datetime, options) end def references(*args) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 920f719995..b3e6b29165 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -237,6 +237,39 @@ if ActiveRecord::Base.connection.supports_migrations? end end + def test_create_table_with_timestamps_should_create_datetime_columns + table_name = :testings + + Person.connection.create_table table_name do |t| + t.timestamps + end + created_columns = Person.connection.columns(table_name) + + created_at_column = created_columns.detect {|c| c.name == 'created_at' } + updated_at_column = created_columns.detect {|c| c.name == 'updated_at' } + + assert created_at_column.null + assert updated_at_column.null + ensure + Person.connection.drop_table table_name rescue nil + end + + def test_create_table_with_timestamps_should_create_datetime_columns_with_options + table_name = :testings + + Person.connection.create_table table_name do |t| + t.timestamps :null => false + end + created_columns = Person.connection.columns(table_name) + + created_at_column = created_columns.detect {|c| c.name == 'created_at' } + updated_at_column = created_columns.detect {|c| c.name == 'updated_at' } + + assert !created_at_column.null + assert !updated_at_column.null + ensure + Person.connection.drop_table table_name rescue nil + end # SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL # column to a table without a default value. @@ -1192,8 +1225,8 @@ if ActiveRecord::Base.connection.supports_migrations? def test_timestamps_creates_updated_at_and_created_at with_new_table do |t| - t.expects(:column).with(:created_at, :datetime) - t.expects(:column).with(:updated_at, :datetime) + t.expects(:column).with(:created_at, :datetime, kind_of(Hash)) + t.expects(:column).with(:updated_at, :datetime, kind_of(Hash)) t.timestamps end end -- cgit v1.2.3 From 707ee0e2695e85186d59aa407f09691ebfcc3125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Fri, 22 Aug 2008 23:53:31 +0300 Subject: Made migrations transactional for PostgreSQL [#834 state:resolved] Patch originally from http://dev.rubyonrails.org/ticket/5470 --- activerecord/CHANGELOG | 2 ++ .../connection_adapters/abstract_adapter.rb | 7 ++++++ .../connection_adapters/postgresql_adapter.rb | 4 ++++ activerecord/lib/active_record/migration.rb | 25 ++++++++++++++++++---- activerecord/test/cases/migration_test.rb | 15 +++++++++++++ .../broken/100_migration_that_raises_exception.rb | 10 +++++++++ 6 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 activerecord/test/migrations/broken/100_migration_that_raises_exception.rb (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 90be9b700a..74a1239aeb 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Transactional migrations for databases which support them. #834 [divoxx, Adam Wiggins, Tarmo Tänav] + * Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin] * change_column_default preserves the not-null constraint. #617 [Tarmo Tänav] diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 47dbf5a5f3..6924bb7e6f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -51,6 +51,13 @@ module ActiveRecord true end + # Does this adapter support DDL rollbacks in transactions? That is, would + # CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL, + # SQL Server, and others support this. MySQL and others do not. + def supports_ddl_transactions? + false + end + # Should primary key values be selected from their corresponding # sequence before the insert statement? If true, next_sequence_value # is called before each insert to set the record's primary key. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 723e1a42dd..bc6fd4e722 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -335,6 +335,10 @@ module ActiveRecord postgresql_version >= 80200 end + def supports_ddl_transactions? + true + end + # Returns the configured supported identifier length supported by PostgreSQL, # or report the default of 63 on PostgreSQL 7.x. def table_alias_length diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index fd77f27b77..c7bc76264d 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -461,14 +461,22 @@ module ActiveRecord Base.logger.info "Migrating to #{migration.name} (#{migration.version})" # On our way up, we skip migrating the ones we've already migrated - # On our way down, we skip reverting the ones we've never migrated next if up? && migrated.include?(migration.version.to_i) + # On our way down, we skip reverting the ones we've never migrated if down? && !migrated.include?(migration.version.to_i) migration.announce 'never migrated, skipping'; migration.write - else - migration.migrate(@direction) - record_version_state_after_migrating(migration.version) + next + end + + begin + ddl_transaction do + migration.migrate(@direction) + record_version_state_after_migrating(migration.version) + end + rescue => e + canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" + raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace end end end @@ -531,5 +539,14 @@ module ActiveRecord def down? @direction == :down end + + # Wrap the migration in a transaction only if supported by the adapter. + def ddl_transaction(&block) + if Base.connection.supports_ddl_transactions? + Base.transaction { block.call } + else + block.call + end + end end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index b3e6b29165..9639588ef2 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -937,6 +937,21 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal(0, ActiveRecord::Migrator.current_version) end + if current_adapter?(:PostgreSQLAdapter) + def test_migrator_one_up_with_exception_and_rollback + assert !Person.column_methods_hash.include?(:last_name) + + e = assert_raises(StandardError) do + ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100) + end + + assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message + + Person.reset_column_information + assert !Person.column_methods_hash.include?(:last_name) + end + end + def test_finds_migrations migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations [['1', 'people_have_last_names'], diff --git a/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb b/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb new file mode 100644 index 0000000000..ffb224dad9 --- /dev/null +++ b/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb @@ -0,0 +1,10 @@ +class MigrationThatRaisesException < ActiveRecord::Migration + def self.up + add_column "people", "last_name", :string + raise 'Something broke' + end + + def self.down + remove_column "people", "last_name" + end +end -- cgit v1.2.3 From e48e77e0222292176cd9f68658dd54524f582d9b Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Mon, 18 Aug 2008 15:56:37 -0700 Subject: coerce blank strings to nil values for boolean and integer fields [#860 state:resolved] --- activerecord/lib/active_record/base.rb | 13 ++++++++----- .../connection_adapters/abstract/schema_definitions.rb | 6 +++++- activerecord/test/cases/base_test.rb | 10 +++++++++- activerecord/test/cases/validations_test.rb | 8 ++++---- activerecord/test/schema/schema.rb | 2 +- 5 files changed, 27 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 45d9372842..2e139c5cc0 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2607,11 +2607,14 @@ module ActiveRecord #:nodoc: end def convert_number_column_value(value) - case value - when FalseClass; 0 - when TrueClass; 1 - when ''; nil - else value + if value == false + 0 + elsif value == true + 1 + elsif value.is_a?(String) && value.blank? + nil + else + value end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 98016ddab5..75032efe57 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -138,7 +138,11 @@ module ActiveRecord # convert something to a boolean def value_to_boolean(value) - TRUE_VALUES.include?(value) + if value.is_a?(String) && value.blank? + nil + else + TRUE_VALUES.include?(value) + end end # convert something to a BigDecimal diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 36d30ade5e..730d2a89bd 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -138,7 +138,7 @@ class BasicsTest < ActiveRecord::TestCase if current_adapter?(:MysqlAdapter) def test_read_attributes_before_type_cast_on_boolean bool = Booleantest.create({ "value" => false }) - assert_equal 0, bool.attributes_before_type_cast["value"] + assert_equal "0", bool.reload.attributes_before_type_cast["value"] end end @@ -1114,11 +1114,15 @@ class BasicsTest < ActiveRecord::TestCase end def test_boolean + b_nil = Booleantest.create({ "value" => nil }) + nil_id = b_nil.id b_false = Booleantest.create({ "value" => false }) false_id = b_false.id b_true = Booleantest.create({ "value" => true }) true_id = b_true.id + b_nil = Booleantest.find(nil_id) + assert_nil b_nil.value b_false = Booleantest.find(false_id) assert !b_false.value? b_true = Booleantest.find(true_id) @@ -1126,11 +1130,15 @@ class BasicsTest < ActiveRecord::TestCase end def test_boolean_cast_from_string + b_blank = Booleantest.create({ "value" => "" }) + blank_id = b_blank.id b_false = Booleantest.create({ "value" => "0" }) false_id = b_false.id b_true = Booleantest.create({ "value" => "1" }) true_id = b_true.id + b_blank = Booleantest.find(blank_id) + assert_nil b_blank.value b_false = Booleantest.find(false_id) assert !b_false.value? b_true = Booleantest.find(true_id) diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 4b2d28c80b..a40bda2533 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -1420,8 +1420,8 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase def test_validates_numericality_of_with_nil_allowed Topic.validates_numericality_of :approved, :allow_nil => true - invalid!(BLANK + JUNK) - valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL + INFINITY) + invalid!(JUNK) + valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY) end def test_validates_numericality_of_with_integer_only @@ -1434,8 +1434,8 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase def test_validates_numericality_of_with_integer_only_and_nil_allowed Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true - invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY) - valid!(NIL + INTEGERS) + invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY) + valid!(NIL + BLANK + INTEGERS) end def test_validates_numericality_with_greater_than diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 487a08f4ab..ab5c7c520b 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -60,7 +60,7 @@ ActiveRecord::Schema.define do end create_table :booleantests, :force => true do |t| - t.integer :value + t.boolean :value end create_table :categories, :force => true do |t| -- cgit v1.2.3 From cf28109158054fbab91de2d6d86efe1b40e68d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Sat, 23 Aug 2008 18:05:52 +0300 Subject: Always require activesupport, even if its constant already exists This is needed because the existance of the ActiveSupport constant by itself does not guarantee that the whole library has been loaded. Also load the StringInquirer in the Rails#env method as the it might be called inside the initializer block before activesupport itself has been loaded. --- activerecord/lib/active_record.rb | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index bc27d17ccd..7612015ca5 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -24,16 +24,14 @@ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) -unless defined? ActiveSupport - active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib" - if File.exist?(active_support_path) - $:.unshift active_support_path - require 'active_support' - else - require 'rubygems' - gem 'activesupport' - require 'active_support' - end +active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib" +if File.exist?(active_support_path) + $:.unshift active_support_path + require 'active_support' +else + require 'rubygems' + gem 'activesupport' + require 'active_support' end require 'active_record/base' -- cgit v1.2.3 From 74c3c701f73407a5bb1a11be2b5b221fe39895d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Sat, 23 Aug 2008 19:51:09 +0300 Subject: Don't set "NULL" as a constraint on nullable columns [#398 state:resolved] This is already the default and adding it breaks SQL standards compatibility. --- .../connection_adapters/abstract/schema_statements.rb | 8 ++------ activerecord/test/cases/column_definition_test.rb | 8 ++++---- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 0f60a91ef1..bececf82a0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -384,12 +384,8 @@ module ActiveRecord def add_column_options!(sql, options) #:nodoc: sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options) # must explicitly check for :null to allow change_column to work on migrations - if options.has_key? :null - if options[:null] == false - sql << " NOT NULL" - else - sql << " NULL" - end + if options[:null] == false + sql << " NOT NULL" end end diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb index 540f42f4b6..98abc8eac8 100644 --- a/activerecord/test/cases/column_definition_test.rb +++ b/activerecord/test/cases/column_definition_test.rb @@ -9,13 +9,13 @@ class ColumnDefinitionTest < ActiveRecord::TestCase end # Avoid column definitions in create table statements like: - # `title` varchar(255) DEFAULT NULL NULL + # `title` varchar(255) DEFAULT NULL def test_should_not_include_default_clause_when_default_is_null column = ActiveRecord::ConnectionAdapters::Column.new("title", nil, "varchar(20)") column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new( @adapter, column.name, "string", column.limit, column.precision, column.scale, column.default, column.null) - assert_equal "title varchar(20) NULL", column_def.to_sql + assert_equal "title varchar(20)", column_def.to_sql end def test_should_include_default_clause_when_default_is_present @@ -23,7 +23,7 @@ class ColumnDefinitionTest < ActiveRecord::TestCase column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new( @adapter, column.name, "string", column.limit, column.precision, column.scale, column.default, column.null) - assert_equal %Q{title varchar(20) DEFAULT 'Hello' NULL}, column_def.to_sql + assert_equal %Q{title varchar(20) DEFAULT 'Hello'}, column_def.to_sql end def test_should_specify_not_null_if_null_option_is_false @@ -33,4 +33,4 @@ class ColumnDefinitionTest < ActiveRecord::TestCase column.limit, column.precision, column.scale, column.default, column.null) assert_equal %Q{title varchar(20) DEFAULT 'Hello' NOT NULL}, column_def.to_sql end -end \ No newline at end of file +end -- cgit v1.2.3 From c471f13db63844fe290615ed6e1ddca32b26570d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 23 Aug 2008 21:26:14 -0700 Subject: Ruby 1.9 compat: Hash is now flattenable, so explicitly exclude it --- activerecord/lib/active_record/associations/association_proxy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 981be3b1a9..99b8748a48 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -213,7 +213,7 @@ module ActiveRecord # Array#flatten has problems with recursive arrays. Going one level deeper solves the majority of the problems. def flatten_deeper(array) - array.collect { |element| element.respond_to?(:flatten) ? element.flatten : element }.flatten + array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten end def owner_quoted_id -- cgit v1.2.3 From e02f0dcc24f871d8429229db4219ee1e93636496 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 24 Aug 2008 02:51:45 +0200 Subject: Rollback the transaction when a before_* callback returns false. Previously this would have committed the transaction but not carried out save or destroy operation. [#891 state:committed] Signed-off-by: Michael Koziarski --- activerecord/CHANGELOG | 2 + activerecord/lib/active_record/callbacks.rb | 12 +++++ activerecord/lib/active_record/transactions.rb | 16 ++++++- activerecord/test/cases/transactions_test.rb | 62 +++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 74a1239aeb..12295ac799 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* before_save, before_validation and before_destroy callbacks that return false will now ROLLBACK the transaction. Previously this would have been committed before the processing was aborted. #891 [Xavier Noria] + * Transactional migrations for databases which support them. #834 [divoxx, Adam Wiggins, Tarmo Tänav] * Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin] diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index be2621fdb6..eec531c514 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -169,6 +169,18 @@ module ActiveRecord # If a before_* callback returns +false+, all the later callbacks and the associated action are cancelled. If an after_* callback returns # +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks # defined as methods on the model, which are called last. + # + # == Transactions + # + # The entire callback chain of a +save+, save!, or +destroy+ call runs + # within a transaction. That includes after_* hooks. If everything + # goes fine a COMMIT is executed once the chain has been completed. + # + # If a before_* callback cancels the action a ROLLBACK is issued. You + # can also trigger a ROLLBACK raising an exception in any of the callbacks, + # including after_* hooks. Note, however, that in that case the client + # needs to be aware of it because an ordinary +save+ will raise such exception + # instead of quietly returning +false+. module Callbacks CALLBACKS = %w( after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 0531afbb52..81462a2917 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -91,11 +91,11 @@ module ActiveRecord end def destroy_with_transactions #:nodoc: - transaction { destroy_without_transactions } + with_transaction_returning_status(:destroy_without_transactions) end def save_with_transactions(perform_validation = true) #:nodoc: - rollback_active_record_state! { transaction { save_without_transactions(perform_validation) } } + rollback_active_record_state! { with_transaction_returning_status(:save_without_transactions, perform_validation) } end def save_with_transactions! #:nodoc: @@ -118,5 +118,17 @@ module ActiveRecord end raise end + + # Executes +method+ within a transaction and captures its return value as a + # status flag. If the status is true the transaction is committed, otherwise + # a ROLLBACK is issued. In any case the status flag is returned. + def with_transaction_returning_status(method, *args) + status = nil + transaction do + status = send(method, *args) + raise ActiveRecord::Rollback unless status + end + status + end end end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 06a76eacc3..af3ee6ddba 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -2,6 +2,7 @@ require "cases/helper" require 'models/topic' require 'models/reply' require 'models/developer' +require 'models/book' class TransactionTest < ActiveRecord::TestCase self.use_transactional_fixtures = false @@ -86,8 +87,7 @@ class TransactionTest < ActiveRecord::TestCase assert Topic.find(2).approved?, "Second should still be approved" end - - def test_callback_rollback_in_save + def test_raising_exception_in_callback_rollbacks_in_save add_exception_raising_after_save_callback_to_topic begin @@ -102,6 +102,54 @@ class TransactionTest < ActiveRecord::TestCase end end + def test_cancellation_from_before_destroy_rollbacks_in_destroy + add_cancelling_before_destroy_with_db_side_effect_to_topic + begin + nbooks_before_destroy = Book.count + status = @first.destroy + assert !status + assert_nothing_raised(ActiveRecord::RecordNotFound) { @first.reload } + assert_equal nbooks_before_destroy, Book.count + ensure + remove_cancelling_before_destroy_with_db_side_effect_to_topic + end + end + + def test_cancellation_from_before_filters_rollbacks_in_save + %w(validation save).each do |filter| + send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") + begin + nbooks_before_save = Book.count + original_author_name = @first.author_name + @first.author_name += '_this_should_not_end_up_in_the_db' + status = @first.save + assert !status + assert_equal original_author_name, @first.reload.author_name + assert_equal nbooks_before_save, Book.count + ensure + send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") + end + end + end + + def test_cancellation_from_before_filters_rollbacks_in_save! + %w(validation save).each do |filter| + send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") + begin + nbooks_before_save = Book.count + original_author_name = @first.author_name + @first.author_name += '_this_should_not_end_up_in_the_db' + @first.save! + flunk + rescue => e + assert_equal original_author_name, @first.reload.author_name + assert_equal nbooks_before_save, Book.count + ensure + send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") + end + end + end + def test_callback_rollback_in_create new_topic = Topic.new( :title => "A new topic", @@ -221,6 +269,16 @@ class TransactionTest < ActiveRecord::TestCase def remove_exception_raising_after_create_callback_to_topic Topic.class_eval { remove_method :after_create } end + + %w(validation save destroy).each do |filter| + define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do + Topic.class_eval "def before_#{filter}() Book.create; false end" + end + + define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do + Topic.class_eval "remove_method :before_#{filter}" + end + end end if current_adapter?(:PostgreSQLAdapter) -- cgit v1.2.3 From 950ea332425ca862f4f38d01ddb2db7073b744c1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 24 Aug 2008 11:08:49 -0700 Subject: Fix test to assign the module instead of a new instance --- activerecord/test/cases/i18n_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb index 06036733f5..9f934ab569 100644 --- a/activerecord/test/cases/i18n_test.rb +++ b/activerecord/test/cases/i18n_test.rb @@ -40,7 +40,7 @@ class ActiveRecordI18nTests < Test::Unit::TestCase private def reset_translations - I18n.backend = I18n::Backend::Simple.new + I18n.backend = I18n::Backend::Simple end end -- cgit v1.2.3 From 49859b0bb1dc41dca33331f198eaf2ccf034a6b4 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 25 Aug 2008 11:48:03 +0200 Subject: I18n: fix activerecord i18n test for classy backend --- activerecord/test/cases/i18n_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb index 9f934ab569..06036733f5 100644 --- a/activerecord/test/cases/i18n_test.rb +++ b/activerecord/test/cases/i18n_test.rb @@ -40,7 +40,7 @@ class ActiveRecordI18nTests < Test::Unit::TestCase private def reset_translations - I18n.backend = I18n::Backend::Simple + I18n.backend = I18n::Backend::Simple.new end end -- cgit v1.2.3 From 1c54ca4f75f9c761ee0f721c38cc4f315ac5ee01 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 25 Aug 2008 18:16:21 -0700 Subject: Ruby 1.9 compat: fix test error masked by old String#each behavior --- activerecord/test/cases/validations_i18n_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 881f219112..c353cbb078 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -133,7 +133,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_errors_full_messages_translates_human_attribute_name_for_model_attributes - @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } + @topic.errors.instance_variable_set :@errors, { 'title' => ['empty'] } I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title') @topic.errors.full_messages :locale => 'en-US' end -- cgit v1.2.3 From e5cad349164ae512c45376e00578855b780d7a48 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 25 Aug 2008 18:16:58 -0700 Subject: strip trailing whitespace --- activerecord/test/cases/validations_i18n_test.rb | 314 +++++++++++------------ 1 file changed, 157 insertions(+), 157 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index c353cbb078..43592bcee3 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -8,16 +8,16 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic = Topic.new I18n.backend.store_translations('en-US', :activerecord => {:errors => {:messages => {:custom => nil}}}) end - + def teardown reset_callbacks Topic I18n.load_translations File.dirname(__FILE__) + '/../../lib/active_record/locale/en-US.yml' end - + def unique_topic @unique ||= Topic.create :title => 'unique!' end - + def replied_topic @replied_topic ||= begin topic = Topic.create(:title => "topic") @@ -25,7 +25,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase topic end end - + def reset_callbacks(*models) models.each do |model| model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) @@ -33,43 +33,43 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) end end - + def test_default_error_messages_is_deprecated assert_deprecated('ActiveRecord::Errors.default_error_messages') do ActiveRecord::Errors.default_error_messages end end - + # ActiveRecord::Errors uses_mocha 'ActiveRecord::Errors' do def test_errors_generate_message_translates_custom_model_attribute_key I18n.expects(:translate).with( - :topic, - { :count => 1, - :default => ['Topic'], + :topic, + { :count => 1, + :default => ['Topic'], :scope => [:activerecord, :models] } ).returns('Topic') I18n.expects(:translate).with( - :"topic.title", - { :count => 1, - :default => ['Title'], + :"topic.title", + { :count => 1, + :default => ['Title'], :scope => [:activerecord, :attributes] } ).returns('Title') I18n.expects(:translate).with( - :"models.topic.attributes.title.invalid", - :value => nil, - :scope => [:activerecord, :errors], + :"models.topic.attributes.title.invalid", + :value => nil, + :scope => [:activerecord, :errors], :default => [ - :"models.topic.invalid", - 'default from class def error 1', - :"messages.invalid"], - :attribute => "Title", + :"models.topic.invalid", + 'default from class def error 1', + :"messages.invalid"], + :attribute => "Title", :model => "Topic" ).returns('default from class def error 1') @@ -79,66 +79,66 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti I18n.expects(:translate).with( - :reply, - { :count => 1, - :default => [:topic, 'Reply'], + :reply, + { :count => 1, + :default => [:topic, 'Reply'], :scope => [:activerecord, :models] } ).returns('Reply') I18n.expects(:translate).with( - :"reply.title", - { :count => 1, - :default => [:'topic.title', 'Title'], + :"reply.title", + { :count => 1, + :default => [:'topic.title', 'Title'], :scope => [:activerecord, :attributes] } ).returns('Title') - + I18n.expects(:translate).with( - :"models.reply.attributes.title.invalid", - :value => nil, - :scope => [:activerecord, :errors], + :"models.reply.attributes.title.invalid", + :value => nil, + :scope => [:activerecord, :errors], :default => [ - :"models.reply.invalid", - :"models.topic.attributes.title.invalid", - :"models.topic.invalid", - 'default from class def', - :"messages.invalid"], - :model => 'Reply', + :"models.reply.invalid", + :"models.topic.attributes.title.invalid", + :"models.topic.invalid", + 'default from class def', + :"messages.invalid"], + :model => 'Reply', :attribute => 'Title' ).returns("default from class def") - + Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' - + end def test_errors_add_on_empty_generates_message @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) @topic.errors.add_on_empty :title end - + def test_errors_add_on_empty_generates_message_with_custom_default_message @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'}) @topic.errors.add_on_empty :title, 'custom' end - + def test_errors_add_on_blank_generates_message @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) @topic.errors.add_on_blank :title end - + def test_errors_add_on_blank_generates_message_with_custom_default_message @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) @topic.errors.add_on_blank :title, 'custom' end - + def test_errors_full_messages_translates_human_attribute_name_for_model_attributes @topic.errors.instance_variable_set :@errors, { 'title' => ['empty'] } I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title') @topic.errors.full_messages :locale => 'en-US' end - end - + end + # ActiveRecord::Validations uses_mocha 'ActiveRecord::Validations' do # validates_confirmation_of w/ mocha @@ -156,7 +156,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'}) @topic.valid? end - + # validates_acceptance_of w/ mocha def test_validates_acceptance_of_generates_message @@ -170,9 +170,9 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'}) @topic.valid? end - + # validates_presence_of w/ mocha - + def test_validates_presence_of_generates_message Topic.validates_presence_of :title @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) @@ -184,7 +184,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) @topic.valid? end - + def test_validates_length_of_within_generates_message_with_title_too_short Topic.validates_length_of :title, :within => 3..5 @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) @@ -238,7 +238,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) @topic.valid? end - + # validates_length_of :is w/ mocha def test_validates_length_of_is_generates_message @@ -252,7 +252,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'}) @topic.valid? end - + # validates_uniqueness_of w/ mocha def test_validates_uniqueness_of_generates_message @@ -268,7 +268,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'}) @topic.valid? end - + # validates_format_of w/ mocha def test_validates_format_of_generates_message @@ -284,7 +284,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'}) @topic.valid? end - + # validates_inclusion_of w/ mocha def test_validates_inclusion_of_generates_message @@ -300,7 +300,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'}) @topic.valid? end - + # validates_exclusion_of w/ mocha def test_validates_exclusion_of_generates_message @@ -316,7 +316,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'}) @topic.valid? end - + # validates_numericality_of without :only_integer w/ mocha def test_validates_numericality_of_generates_message @@ -332,7 +332,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) @topic.valid? end - + # validates_numericality_of with :only_integer w/ mocha def test_validates_numericality_of_only_integer_generates_message @@ -348,7 +348,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) @topic.valid? end - + # validates_numericality_of :odd w/ mocha def test_validates_numericality_of_odd_generates_message @@ -364,7 +364,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'}) @topic.valid? end - + # validates_numericality_of :less_than w/ mocha def test_validates_numericality_of_less_than_generates_message @@ -380,7 +380,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) @topic.valid? end - + # validates_associated w/ mocha def test_validates_associated_generates_message @@ -395,282 +395,282 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase replied_topic.valid? end end - + # validates_confirmation_of w/o mocha - + def test_validates_confirmation_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:confirmation => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}} - + Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_confirmation_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}} - + Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_acceptance_of w/o mocha - + def test_validates_acceptance_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:accepted => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}} - + Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_acceptance_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}} - + Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_presence_of w/o mocha - + def test_validates_presence_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:blank => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:blank => 'global message'}}} - + Topic.validates_presence_of :title @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_presence_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:blank => 'global message'}}} - + Topic.validates_presence_of :title @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_length_of :within w/o mocha - + def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:too_short => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}} - + Topic.validates_length_of :title, :within => 3..5 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_length_of_within_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}} - + Topic.validates_length_of :title, :within => 3..5 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_length_of :is w/o mocha - + def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_length_of_within_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_uniqueness_of w/o mocha - + def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_length_of_within_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - - + + # validates_format_of w/o mocha - + def test_validates_format_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:invalid => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} - + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_format_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} - + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_inclusion_of w/o mocha - + def test_validates_inclusion_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:inclusion => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}} - + Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_inclusion_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}} - + Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_exclusion_of w/o mocha - + def test_validates_exclusion_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:exclusion => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}} - + Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_exclusion_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}} - + Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_numericality_of without :only_integer w/o mocha - + def test_validates_numericality_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} - + Topic.validates_numericality_of :title @topic.title = 'a' @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_numericality_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} - + Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_numericality_of with :only_integer w/o mocha - + def test_validates_numericality_of_only_integer_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} - + Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_numericality_of_only_integer_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} - + Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_numericality_of :odd w/o mocha - + def test_validates_numericality_of_odd_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:odd => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:odd => 'global message'}}} - + Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_numericality_of_odd_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:odd => 'global message'}}} - + Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - + # validates_numericality_of :less_than w/o mocha - + def test_validates_numericality_of_less_than_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:less_than => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}} - + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) end - + def test_validates_numericality_of_less_than_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}} - + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) end - - + + # validates_associated w/o mocha - + def test_validates_associated_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}} I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} - + Topic.validates_associated :replies replied_topic.valid? assert_equal 'custom message', replied_topic.errors.on(:replies) end - + def test_validates_associated_finds_global_default_translation I18n.backend.store_translations 'en-US', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} - + Topic.validates_associated :replies replied_topic.valid? assert_equal 'global message', replied_topic.errors.on(:replies) @@ -705,11 +705,11 @@ class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase :odd => "must be odd", :even => "must be even" } - } + } } } end - + def reset_callbacks(*models) models.each do |model| model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) @@ -717,115 +717,115 @@ class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) end end - + # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) def test_generate_message_inclusion_with_default_message assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') end - + def test_generate_message_inclusion_with_custom_message assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title') end - + # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) def test_generate_message_exclusion_with_default_message assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title') end - + def test_generate_message_exclusion_with_custom_message assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title') end - + # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) def test_generate_message_invalid_with_default_message assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title') end - + def test_generate_message_invalid_with_custom_message assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title') end - + # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message]) def test_generate_message_confirmation_with_default_message assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil) end - + def test_generate_message_confirmation_with_custom_message assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message') end - + # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message]) def test_generate_message_accepted_with_default_message assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil) end - + def test_generate_message_accepted_with_custom_message assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message') end - + # add_on_empty: generate_message(attr, :empty, :default => custom_message) def test_generate_message_empty_with_default_message assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil) end - + def test_generate_message_empty_with_custom_message assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message') end - + # add_on_blank: generate_message(attr, :blank, :default => custom_message) def test_generate_message_blank_with_default_message assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil) end - + def test_generate_message_blank_with_custom_message assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message') end - + # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) def test_generate_message_too_long_with_default_message assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10) end - + def test_generate_message_too_long_with_custom_message assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10) end - + # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) def test_generate_message_too_short_with_default_message assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10) end - + def test_generate_message_too_short_with_custom_message assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10) - end - + end + # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value) def test_generate_message_wrong_length_with_default_message assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10) end - + def test_generate_message_wrong_length_with_custom_message assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10) - end + end # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message]) def test_generate_message_taken_with_default_message assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title') end - + def test_generate_message_taken_with_custom_message assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title') - end + end # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) def test_generate_message_not_a_number_with_default_message assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title') end - + def test_generate_message_not_a_number_with_custom_message assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title') - end + end # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) def test_generate_message_greater_than_with_default_message -- cgit v1.2.3 From b7a37b742c0abd1df8ea48cc82f76385cc0c41ea Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Mon, 25 Aug 2008 22:36:19 +0100 Subject: Fix preloading of has_one through associations [#903 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/association_preload.rb | 8 ++------ .../associations/has_one_through_associations_test.rb | 14 +++++++++----- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index c7594809b7..61fa34ac39 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -51,9 +51,7 @@ module ActiveRecord def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record) parent_records.each do |parent_record| - association_proxy = parent_record.send(reflection_name) - association_proxy.loaded - association_proxy.target = associated_record + parent_record.send("set_#{reflection_name}_target", associated_record) end end @@ -112,8 +110,8 @@ module ActiveRecord def preload_has_one_association(records, reflection, preload_options={}) id_to_record_map, ids = construct_id_map(records) options = reflection.options + records.each {|record| record.send("set_#{reflection.name}_target", nil)} if options[:through] - records.each {|record| record.send(reflection.name) && record.send(reflection.name).loaded} through_records = preload_through_records(records, reflection, options[:through]) through_reflection = reflections[options[:through]] through_primary_key = through_reflection.primary_key_name @@ -126,8 +124,6 @@ module ActiveRecord end end else - records.each {|record| record.send("set_#{reflection.name}_target", nil)} - set_association_single_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options), reflection.primary_key_name) end end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 3eb66bc941..4a5d7e27c1 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -44,19 +44,23 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_polymorphic assert_equal clubs(:moustache_club), @member.sponsor_club end - + def has_one_through_to_has_many assert_equal 2, @member.fellow_members.size end - + def test_has_one_through_eager_loading - members = Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"]) + members = assert_queries(3) do #base table, through table, clubs table + Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"]) + end assert_equal 1, members.size assert_not_nil assert_no_queries {members[0].club} end - + def test_has_one_through_eager_loading_through_polymorphic - members = Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"]) + members = assert_queries(3) do #base table, through table, clubs table + Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"]) + end assert_equal 1, members.size assert_not_nil assert_no_queries {members[0].sponsor_club} end -- cgit v1.2.3 From 172606e21f54fea39af68ede5f55a43deaf3ac68 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 25 Aug 2008 21:22:34 -0700 Subject: Harmonize framework require strategy. Don't add self to load path since Rails initializer and RubyGems handle it. --- activerecord/lib/active_record.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 7612015ca5..e9767c2d5e 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -21,17 +21,14 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -$:.unshift(File.dirname(__FILE__)) unless - $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) - -active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib" -if File.exist?(active_support_path) - $:.unshift active_support_path - require 'active_support' -else - require 'rubygems' - gem 'activesupport' +begin require 'active_support' +rescue LoadError + activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" + if File.directory?(activesupport_path) + $:.unshift activesupport_path + require 'active_support' + end end require 'active_record/base' -- cgit v1.2.3 From 2dbda11945507a0541d61d13b8c564121c1cd362 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Mon, 25 Aug 2008 23:20:10 +0100 Subject: Implement old-skool eagerloading for has_one :through Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations.rb | 4 ++-- .../associations/has_one_through_associations_test.rb | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index b9039ce996..46b79c5a0d 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1974,7 +1974,7 @@ module ActiveRecord @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join") end - if reflection.macro == :has_many && reflection.options[:through] + if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through] @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join") end end @@ -1998,7 +1998,7 @@ module ActiveRecord ] when :has_many, :has_one case - when reflection.macro == :has_many && reflection.options[:through] + when reflection.options[:through] through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 4a5d7e27c1..ed24794444 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -75,4 +75,20 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase assert_not_nil assert_no_queries {clubs[0].sponsored_member} end + def test_has_one_through_nonpreload_eagerloading + members = assert_queries(1) do + Member.find(:all, :include => :club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback + end + assert_equal 1, members.size + assert_not_nil assert_no_queries {members[0].club} + end + + def test_has_one_through_nonpreload_eager_loading_through_polymorphic + members = assert_queries(1) do + Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback + end + assert_equal 1, members.size + assert_not_nil assert_no_queries {members[0].sponsor_club} + end + end -- cgit v1.2.3 From a445cdd8840c4e99c40c6d5b15ab380d39a56be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 03:01:24 +0300 Subject: Load the first and not the last has_one result when doing join-based eager loading This matters when the has_one is defined with an order in which case there is an expectation that the first one will be loaded. [#904 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations.rb | 1 + activerecord/test/cases/associations/eager_test.rb | 6 ++++++ .../cases/associations/has_one_through_associations_test.rb | 10 ++++++++++ 3 files changed, 17 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 46b79c5a0d..f915daafba 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1893,6 +1893,7 @@ module ActiveRecord collection.target.push(association) when :has_one return if record.id.to_s != join.parent.record_id(row).to_s + return if record.instance_variable_defined?("@#{join.reflection.name}") association = join.instantiate(row) unless row[join.aliased_primary_key].nil? record.send("set_#{join.reflection.name}_target", association) when :belongs_to diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index f37e18df35..956a2891aa 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -38,6 +38,12 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_equal Post.find(1).last_comment, post.last_comment end + def test_loading_with_one_association_with_non_preload + posts = Post.find(:all, :include => :last_comment, :order => 'comments.id DESC') + post = posts.find { |p| p.id == 1 } + assert_equal Post.find(1).last_comment, post.last_comment + end + def test_loading_conditions_with_or posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'") assert_nil posts.detect { |p| p.author_id != authors(:david).id }, diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index ed24794444..b61a3711e3 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -91,4 +91,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase assert_not_nil assert_no_queries {members[0].sponsor_club} end + def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record + Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save! + members = assert_queries(1) do + Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC') #force fallback + end + assert_equal 1, members.size + assert_not_nil assert_no_queries { members[0].sponsor_club } + assert_equal clubs(:crazy_club), members[0].sponsor_club + end + end -- cgit v1.2.3 From 3d2ac918b987ef0cff34f6a7fdd20926b7a9e5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 04:55:57 +0300 Subject: Cache migrated versions list in Migrator and use it to fetch the latest migrated version name [#845 state:resolved] Also optimized Migrator#current_version class method to fetch only the latest version number and not all of them. With this change no matter how many migrations there are the schema_migrations table is only SELECTed from once. Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/migration.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index c7bc76264d..7a1fd7cfbc 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -407,10 +407,9 @@ module ActiveRecord end def current_version - version = Base.connection.select_values( - "SELECT version FROM #{schema_migrations_table_name}" - ).map(&:to_i).max rescue nil - version || 0 + Base.connection.select_value( + "SELECT MAX(CAST(version AS integer)) FROM #{schema_migrations_table_name}" + ).to_i rescue 0 end def proper_table_name(name) @@ -426,7 +425,7 @@ module ActiveRecord end def current_version - self.class.current_version + migrated.last || 0 end def current_migration @@ -518,16 +517,19 @@ module ActiveRecord def migrated sm_table = self.class.schema_migrations_table_name - Base.connection.select_values("SELECT version FROM #{sm_table}").map(&:to_i).sort + @migrated_versions ||= Base.connection.select_values("SELECT version FROM #{sm_table}").map(&:to_i).sort end private def record_version_state_after_migrating(version) sm_table = self.class.schema_migrations_table_name + @migrated_versions ||= [] if down? + @migrated_versions.delete(version.to_i) Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'") else + @migrated_versions.push(version.to_i).sort! Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')") end end -- cgit v1.2.3 From 77b003fb615a1a0b197af9fbb9066622bf489b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 09:14:12 +0300 Subject: Use DECIMAL instead of INTEGER when casting as mysql doesn't work with just "INTEGER" and other databases don't like "UNSIGNED" which mysql requires And don't mask exceptions. Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/migration.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 7a1fd7cfbc..6d101e9db5 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -407,9 +407,14 @@ module ActiveRecord end def current_version - Base.connection.select_value( - "SELECT MAX(CAST(version AS integer)) FROM #{schema_migrations_table_name}" - ).to_i rescue 0 + sm_table = schema_migrations_table_name + if Base.connection.table_exists?(sm_table) + Base.connection.select_value( + "SELECT MAX(CAST(version AS DECIMAL)) FROM #{sm_table}" + ).to_i + else + 0 + end end def proper_table_name(name) -- cgit v1.2.3 From 3beed9cdb7cf2cf75fb043b72ca99cc23fc11556 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 25 Aug 2008 23:32:03 -0700 Subject: ensure tests load sibling Active Support instead of a gem --- activerecord/test/cases/helper.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 0530ba9bd9..f30d58546e 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -1,4 +1,5 @@ $:.unshift(File.dirname(__FILE__) + '/../../lib') +$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib') require 'config' require 'test/unit' -- cgit v1.2.3 From 143f5fbb21b6dfcaab63d67b44afd922dab9dcf5 Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Mon, 25 Aug 2008 19:32:19 -0700 Subject: refactor dynamic finder name matching into its own class Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record.rb | 1 + activerecord/lib/active_record/base.rb | 121 +++++++++------------ .../lib/active_record/dynamic_finder_match.rb | 33 ++++++ activerecord/test/cases/finder_test.rb | 42 +++++++ 4 files changed, 125 insertions(+), 72 deletions(-) create mode 100644 activerecord/lib/active_record/dynamic_finder_match.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index e9767c2d5e..c47ca486c8 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -51,6 +51,7 @@ require 'active_record/calculations' require 'active_record/serialization' require 'active_record/attribute_methods' require 'active_record/dirty' +require 'active_record/dynamic_finder_match' ActiveRecord::Base.class_eval do extend ActiveRecord::QueryCache diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2e139c5cc0..523b619e62 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1354,8 +1354,8 @@ module ActiveRecord #:nodoc: end def respond_to?(method_id, include_private = false) - if match = matches_dynamic_finder?(method_id) || matches_dynamic_finder_with_initialize_or_create?(method_id) - return true if all_attributes_exists?(extract_attribute_names_from_match(match)) + if match = DynamicFinderMatch.match(method_id) + return true if all_attributes_exists?(match.attribute_names) end super end @@ -1674,88 +1674,65 @@ module ActiveRecord #:nodoc: # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future # attempts to use it do not run through method_missing. def method_missing(method_id, *arguments) - if match = matches_dynamic_finder?(method_id) - finder = determine_finder(match) - - attribute_names = extract_attribute_names_from_match(match) + if match = DynamicFinderMatch.match(method_id) + attribute_names = match.attribute_names super unless all_attributes_exists?(attribute_names) - - self.class_eval %{ - def self.#{method_id}(*args) - options = args.extract_options! - attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) - finder_options = { :conditions => attributes } - validate_find_options(options) - set_readonly_option!(options) - - if options[:conditions] - with_scope(:find => finder_options) do - ActiveSupport::Deprecation.silence { send(:#{finder}, options) } + if match.finder? + finder = match.finder + self.class_eval %{ + def self.#{method_id}(*args) + options = args.extract_options! + attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) + finder_options = { :conditions => attributes } + validate_find_options(options) + set_readonly_option!(options) + + if options[:conditions] + with_scope(:find => finder_options) do + ActiveSupport::Deprecation.silence { send(:#{finder}, options) } + end + else + ActiveSupport::Deprecation.silence { send(:#{finder}, options.merge(finder_options)) } end - else - ActiveSupport::Deprecation.silence { send(:#{finder}, options.merge(finder_options)) } - end - end - }, __FILE__, __LINE__ - send(method_id, *arguments) - elsif match = matches_dynamic_finder_with_initialize_or_create?(method_id) - instantiator = determine_instantiator(match) - attribute_names = extract_attribute_names_from_match(match) - super unless all_attributes_exists?(attribute_names) - - self.class_eval %{ - def self.#{method_id}(*args) - guard_protected_attributes = false - - if args[0].is_a?(Hash) - guard_protected_attributes = true - attributes = args[0].with_indifferent_access - find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}]) - else - find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) end + }, __FILE__, __LINE__ + send(method_id, *arguments) + elsif match.instantiator? + instantiator = match.instantiator + self.class_eval %{ + def self.#{method_id}(*args) + guard_protected_attributes = false + + if args[0].is_a?(Hash) + guard_protected_attributes = true + attributes = args[0].with_indifferent_access + find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}]) + else + find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) + end - options = { :conditions => find_attributes } - set_readonly_option!(options) + options = { :conditions => find_attributes } + set_readonly_option!(options) - record = find_initial(options) + record = find_initial(options) - if record.nil? - record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } - #{'yield(record) if block_given?'} - #{'record.save' if instantiator == :create} - record - else - record + if record.nil? + record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } + #{'yield(record) if block_given?'} + #{'record.save' if instantiator == :create} + record + else + record + end end - end - }, __FILE__, __LINE__ - send(method_id, *arguments) + }, __FILE__, __LINE__ + send(method_id, *arguments) + end else super end end - def matches_dynamic_finder?(method_id) - /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_id.to_s) - end - - def matches_dynamic_finder_with_initialize_or_create?(method_id) - /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/.match(method_id.to_s) - end - - def determine_finder(match) - match.captures.first == 'all_by' ? :find_every : :find_initial - end - - def determine_instantiator(match) - match.captures.first == 'initialize' ? :new : :create - end - - def extract_attribute_names_from_match(match) - match.captures.last.split('_and_') - end - def construct_attributes_from_arguments(attribute_names, arguments) attributes = {} attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] } diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb new file mode 100644 index 0000000000..4618e777a4 --- /dev/null +++ b/activerecord/lib/active_record/dynamic_finder_match.rb @@ -0,0 +1,33 @@ +module ActiveRecord + class DynamicFinderMatch + def self.match(method) + df_match = self.new(method) + df_match.finder ? df_match : nil + end + + def initialize(method) + @finder = :find_initial + case method.to_s + when /^find_(all_by|by)_([_a-zA-Z]\w*)$/ + @finder = :find_every if $1 == 'all_by' + names = $2 + when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/ + @instantiator = $1 == 'initialize' ? :new : :create + names = $2 + else + @finder = nil + end + @attribute_names = names && names.split('_and_') + end + + attr_reader :finder, :attribute_names, :instantiator + + def finder? + !@finder.nil? && @instantiator.nil? + end + + def instantiator? + @finder == :find_initial && !@instantiator.nil? + end + end +end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index b97db73b68..875d56f6c1 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -12,6 +12,48 @@ require 'models/customer' require 'models/job' require 'models/categorization' +class DynamicFinderMatchTest < ActiveRecord::TestCase + def test_find_no_match + assert_nil ActiveRecord::DynamicFinderMatch.match("not_a_finder") + end + + def test_find_by + match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location") + assert_not_nil match + assert match.finder? + assert_equal :find_initial, match.finder + assert_equal %w(age sex location), match.attribute_names + end + + def test_find_all_by + match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location") + assert_not_nil match + assert match.finder? + assert_equal :find_every, match.finder + assert_equal %w(age sex location), match.attribute_names + end + + def test_find_or_initialize_by + match = ActiveRecord::DynamicFinderMatch.match("find_or_initialize_by_age_and_sex_and_location") + assert_not_nil match + assert !match.finder? + assert match.instantiator? + assert_equal :find_initial, match.finder + assert_equal :new, match.instantiator + assert_equal %w(age sex location), match.attribute_names + end + + def test_find_or_create_by + match = ActiveRecord::DynamicFinderMatch.match("find_or_create_by_age_and_sex_and_location") + assert_not_nil match + assert !match.finder? + assert match.instantiator? + assert_equal :find_initial, match.finder + assert_equal :create, match.instantiator + assert_equal %w(age sex location), match.attribute_names + end +end + class FinderTest < ActiveRecord::TestCase fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers -- cgit v1.2.3 From 1092c181b5568d06e84f6a3253aaca81c02a2b2c Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Mon, 25 Aug 2008 21:28:53 -0700 Subject: add dynamic finder bang version to raise RecordNotFound [#905 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/base.rb | 4 +++- activerecord/lib/active_record/dynamic_finder_match.rb | 7 +++++++ activerecord/test/cases/finder_test.rb | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 523b619e62..5c30f80555 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1679,6 +1679,7 @@ module ActiveRecord #:nodoc: super unless all_attributes_exists?(attribute_names) if match.finder? finder = match.finder + bang = match.bang? self.class_eval %{ def self.#{method_id}(*args) options = args.extract_options! @@ -1687,13 +1688,14 @@ module ActiveRecord #:nodoc: validate_find_options(options) set_readonly_option!(options) - if options[:conditions] + #{'result = ' if bang}if options[:conditions] with_scope(:find => finder_options) do ActiveSupport::Deprecation.silence { send(:#{finder}, options) } end else ActiveSupport::Deprecation.silence { send(:#{finder}, options.merge(finder_options)) } end + #{'result || raise(RecordNotFound)' if bang} end }, __FILE__, __LINE__ send(method_id, *arguments) diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb index 4618e777a4..b105b919f5 100644 --- a/activerecord/lib/active_record/dynamic_finder_match.rb +++ b/activerecord/lib/active_record/dynamic_finder_match.rb @@ -11,6 +11,9 @@ module ActiveRecord when /^find_(all_by|by)_([_a-zA-Z]\w*)$/ @finder = :find_every if $1 == 'all_by' names = $2 + when /^find_by_([_a-zA-Z]\w*)\!$/ + @bang = true + names = $1 when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/ @instantiator = $1 == 'initialize' ? :new : :create names = $2 @@ -29,5 +32,9 @@ module ActiveRecord def instantiator? @finder == :find_initial && !@instantiator.nil? end + + def bang? + @bang + end end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 875d56f6c1..2ce49ed76f 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -25,6 +25,15 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase assert_equal %w(age sex location), match.attribute_names end + def find_by_bang + match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location!") + assert_not_nil match + assert match.finder? + assert match.bang? + assert_equal :find_initial, match.finder + assert_equal %w(age sex location), match.attribute_names + end + def test_find_all_by match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location") assert_not_nil match @@ -482,6 +491,11 @@ class FinderTest < ActiveRecord::TestCase assert_nil Topic.find_by_title("The First Topic!") end + def test_find_by_one_attribute_bang + assert_equal topics(:first), Topic.find_by_title!("The First Topic") + assert_raises(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") } + end + def test_find_by_one_attribute_caches_dynamic_finder # ensure this test can run independently of order class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_by_title' } -- cgit v1.2.3 From ca48da6300ff0b49ae4125dc582f3c4666481005 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 25 Aug 2008 23:53:31 -0700 Subject: fix tests relying on implicit ordering --- .../cases/associations/has_and_belongs_to_many_associations_test.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 432d245b5b..917c241346 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -647,8 +647,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer.save developer.reload assert_equal 2, developer.projects.length - assert_equal projects(:active_record), developer.projects[0] - assert_equal projects(:action_controller), developer.projects[1] + assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.projects_ids end def test_assign_ids_ignoring_blanks @@ -657,8 +656,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer.save developer.reload assert_equal 2, developer.projects.length - assert_equal projects(:active_record), developer.projects[0] - assert_equal projects(:action_controller), developer.projects[1] + assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.projects_ids end def test_select_limited_ids_list -- cgit v1.2.3 From 842d55cb16eac7e48660ecd4df5ec1daf946f4d1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 26 Aug 2008 00:02:22 -0700 Subject: fix another ordering failure --- activerecord/test/cases/base_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 730d2a89bd..c8111358e3 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -616,7 +616,7 @@ class BasicsTest < ActiveRecord::TestCase end def test_update_counter - category = Category.first + category = categories(:general) assert_nil category.categorizations_count assert_equal 2, category.categorizations.count -- cgit v1.2.3 From 6d66ddaa34ec011a4a20ca5d3f7f695b1a843734 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 26 Aug 2008 00:02:30 -0700 Subject: typo --- .../cases/associations/has_and_belongs_to_many_associations_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 917c241346..cae9fdb090 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -647,7 +647,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer.save developer.reload assert_equal 2, developer.projects.length - assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.projects_ids + assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids end def test_assign_ids_ignoring_blanks @@ -656,7 +656,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer.save developer.reload assert_equal 2, developer.projects.length - assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.projects_ids + assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids end def test_select_limited_ids_list -- cgit v1.2.3 From 52e15abbedab9ef6f3db03ef39fe5cbab9c3ddde Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 26 Aug 2008 00:10:16 -0700 Subject: um.. yeah --- .../cases/associations/has_and_belongs_to_many_associations_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index cae9fdb090..0572418e3b 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -647,7 +647,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer.save developer.reload assert_equal 2, developer.projects.length - assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids + assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort end def test_assign_ids_ignoring_blanks @@ -656,7 +656,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase developer.save developer.reload assert_equal 2, developer.projects.length - assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids + assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort end def test_select_limited_ids_list -- cgit v1.2.3 From fa795ccfade59c4bbab91ada3bb34e5fbfac448c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 10:48:46 +0300 Subject: Include mysql older than 5.1.23 in the 5.1 series in the list of those that can't handle NULL defaults Signed-off-by: Jeremy Kemper --- activerecord/test/cases/defaults_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb index 2ea85417da..3473b846a0 100644 --- a/activerecord/test/cases/defaults_test.rb +++ b/activerecord/test/cases/defaults_test.rb @@ -5,7 +5,7 @@ require 'models/entrant' class DefaultTest < ActiveRecord::TestCase def test_nil_defaults_for_not_null_columns column_defaults = - if current_adapter?(:MysqlAdapter) && Mysql.client_version < 50051 + if current_adapter?(:MysqlAdapter) && (Mysql.client_version < 50051 || (50100..50122).include?(Mysql.client_version)) { 'id' => nil, 'name' => '', 'course_id' => nil } else { 'id' => nil, 'name' => nil, 'course_id' => nil } -- cgit v1.2.3 From 973c0ef26d94b5cf2bd29dce177357d5905be211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 11:45:53 +0300 Subject: Create mysql binary_fields table with latin1 character set as with utf8 all the limits would have to be divided by 3 to get the expected text types Signed-off-by: Jeremy Kemper --- activerecord/test/schema/mysql_specific_schema.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb index 5ae062c97c..f44c33ae67 100644 --- a/activerecord/test/schema/mysql_specific_schema.rb +++ b/activerecord/test/schema/mysql_specific_schema.rb @@ -1,5 +1,5 @@ ActiveRecord::Schema.define do - create_table :binary_fields, :force => true do |t| + create_table :binary_fields, :force => true, :options => 'CHARACTER SET latin1' do |t| t.binary :tiny_blob, :limit => 255 t.binary :normal_blob, :limit => 65535 t.binary :medium_blob, :limit => 16777215 @@ -9,4 +9,4 @@ ActiveRecord::Schema.define do t.text :medium_text, :limit => 16777215 t.text :long_text, :limit => 2147483647 end -end \ No newline at end of file +end -- cgit v1.2.3 From b319e69ecb06692995664dba263813d99bc2145f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 26 Aug 2008 01:48:41 -0700 Subject: PostgreSQL: pg driver expects nil instead of empty string for missing user/pass --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index bc6fd4e722..55c7da5b4f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -23,8 +23,8 @@ module ActiveRecord config = config.symbolize_keys host = config[:host] port = config[:port] || 5432 - username = config[:username].to_s - password = config[:password].to_s + username = config[:username].to_s if config[:username] + password = config[:password].to_s if config[:password] if config.has_key?(:database) database = config[:database] -- cgit v1.2.3 From 0c7bbc72fc043ad5debbff34da81f3d68f516d3f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 26 Aug 2008 02:17:36 -0700 Subject: fix tests relying on implicit ordering --- activerecord/test/cases/query_cache_test.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index eae2104531..171d0e6dae 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -116,8 +116,9 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase def test_cache_is_expired_by_habtm_delete ActiveRecord::Base.connection.expects(:clear_query_cache).times(2) ActiveRecord::Base.cache do - c = Category.find(:first) - p = Post.find(:first) + c = Category.find(1) + p = Post.find(1) + assert p.categories.any? p.categories.delete_all end end -- cgit v1.2.3 From ab1e82b8f7279301e4363decdef84719f9304443 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 26 Aug 2008 02:38:48 -0700 Subject: Include people and readers fixtures to fix test isolation error --- activerecord/test/cases/associations_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 4904feeb7d..0b2731ecd7 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -27,7 +27,7 @@ require 'models/sponsor' class AssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :developers_projects, - :computers + :computers, :people, :readers def test_include_with_order_works assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)} @@ -45,7 +45,7 @@ class AssociationsTest < ActiveRecord::TestCase assert_equal [], person.readers.find(:all) person.save! reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar") - assert_equal [reader], person.readers.find(:all) + assert person.readers.find(reader.id) end def test_force_reload -- cgit v1.2.3 From 00d2165f748bc3d7c235daa959c54266a0a283db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 12:57:33 +0300 Subject: Back to fetching all versions in ruby instead of letting SQL do it as it's difficult to get all databases to convert the text value to a number with the same SQL Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/migration.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 6d101e9db5..1d843fff28 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -406,12 +406,14 @@ module ActiveRecord Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix end + def get_all_versions + Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort + end + def current_version sm_table = schema_migrations_table_name if Base.connection.table_exists?(sm_table) - Base.connection.select_value( - "SELECT MAX(CAST(version AS DECIMAL)) FROM #{sm_table}" - ).to_i + get_all_versions.max || 0 else 0 end @@ -521,8 +523,7 @@ module ActiveRecord end def migrated - sm_table = self.class.schema_migrations_table_name - @migrated_versions ||= Base.connection.select_values("SELECT version FROM #{sm_table}").map(&:to_i).sort + @migrated_versions ||= self.class.get_all_versions end private -- cgit v1.2.3 From ce3c76de7c0deda08a5ed0e064b8d37c26b60cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 13:06:49 +0300 Subject: Just look at sql_type when testing that the correct database-specific type was used Signed-off-by: Michael Koziarski --- activerecord/test/cases/migration_test.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 9639588ef2..c1a8da2270 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -442,10 +442,7 @@ if ActiveRecord::Base.connection.supports_migrations? ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint Person.reset_column_information - Person.create :intelligence_quotient => 300 - jonnyg = Person.find(:first) - assert_equal 127, jonnyg.intelligence_quotient - jonnyg.destroy + assert_match /tinyint/, Person.columns_hash['intelligence_quotient'].sql_type ensure ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil end -- cgit v1.2.3 From 3dfecfe77347421fbb54be70a5a84eb47c30acb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 13:41:41 +0300 Subject: Print the queries that were executed if assert_queries fails --- activerecord/lib/active_record/test_case.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index ca5591ae35..ffaa41282f 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -37,7 +37,7 @@ module ActiveRecord $queries_executed = [] yield ensure - assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed." + assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}" end def assert_no_queries(&block) -- cgit v1.2.3 From eec5eb2e444b2b42206e2d5ccfe7f30306c308cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 14:15:44 +0300 Subject: Fix yet another implicit order dependant test Signed-off-by: Michael Koziarski --- activerecord/test/cases/associations/eager_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 956a2891aa..e78624a98d 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -260,9 +260,9 @@ class EagerAssociationTest < ActiveRecord::TestCase end def test_eager_with_has_many_through - posts_with_comments = people(:michael).posts.find(:all, :include => :comments) - posts_with_author = people(:michael).posts.find(:all, :include => :author ) - posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ]) + posts_with_comments = people(:michael).posts.find(:all, :include => :comments, :order => 'posts.id') + posts_with_author = people(:michael).posts.find(:all, :include => :author, :order => 'posts.id') + posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ], :order => 'posts.id') assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size } assert_equal authors(:david), assert_no_queries { posts_with_author.first.author } assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author } -- cgit v1.2.3 From 8756dd75b257b17ddda92674d4cc0db307d2153b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 26 Aug 2008 14:24:52 -0500 Subject: Performance: reduce garbage created by ActiveRecord::Calculations#column_alias_for --- activerecord/lib/active_record/calculations.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 246f87b7a9..77cc6da251 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -260,7 +260,14 @@ module ActiveRecord # column_alias_for("count(*)") # => "count_all" # column_alias_for("count", "id") # => "count_id" def column_alias_for(*keys) - connection.table_alias_for(keys.join(' ').downcase.gsub(/\*/, 'all').gsub(/\W+/, ' ').strip.gsub(/ +/, '_')) + table_name = keys.join(' ') + table_name.downcase! + table_name.gsub!(/\*/, 'all') + table_name.gsub!(/\W+/, ' ') + table_name.strip! + table_name.gsub!(/ +/, '_') + + connection.table_alias_for(table_name) end def column_for(field) -- cgit v1.2.3 From 0fcd5b5466461e44b6f3b007fa2a2fdf43f55681 Mon Sep 17 00:00:00 2001 From: Marko Seppae Date: Wed, 27 Aug 2008 10:36:00 +0200 Subject: I18n: removed call to #populate from main library files --- activerecord/lib/active_record.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index c47ca486c8..a6bbd6fc82 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -77,7 +77,5 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' -I18n.backend.populate do - I18n.load_translations File.dirname(__FILE__) + '/active_record/locale/en-US.yml' -end +I18n.load_translations File.dirname(__FILE__) + '/active_record/locale/en-US.yml' -- cgit v1.2.3 From 2d03a4c668b0229ad070c04a7c82bd803bc25788 Mon Sep 17 00:00:00 2001 From: Marko Seppae Date: Wed, 27 Aug 2008 10:37:01 +0200 Subject: i18n: fixed failing tests after removing #populate and #store_translations --- activerecord/test/cases/i18n_test.rb | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb index 06036733f5..ea06e377e3 100644 --- a/activerecord/test/cases/i18n_test.rb +++ b/activerecord/test/cases/i18n_test.rb @@ -5,42 +5,37 @@ require 'models/reply' class ActiveRecordI18nTests < Test::Unit::TestCase def setup - reset_translations + I18n.backend = I18n::Backend::Simple.new end def test_translated_model_attributes - I18n.store_translations 'en-US', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } + I18n.backend.store_translations 'en-US', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } assert_equal 'topic title attribute', Topic.human_attribute_name('title') end def test_translated_model_attributes_with_sti - I18n.store_translations 'en-US', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } } + I18n.backend.store_translations 'en-US', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } } assert_equal 'reply title attribute', Reply.human_attribute_name('title') end def test_translated_model_attributes_with_sti_fallback - I18n.store_translations 'en-US', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } + I18n.backend.store_translations 'en-US', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } assert_equal 'topic title attribute', Reply.human_attribute_name('title') end def test_translated_model_names - I18n.store_translations 'en-US', :activerecord => {:models => {:topic => 'topic model'} } + I18n.backend.store_translations 'en-US', :activerecord => {:models => {:topic => 'topic model'} } assert_equal 'topic model', Topic.human_name end def test_translated_model_names_with_sti - I18n.store_translations 'en-US', :activerecord => {:models => {:reply => 'reply model'} } + I18n.backend.store_translations 'en-US', :activerecord => {:models => {:reply => 'reply model'} } assert_equal 'reply model', Reply.human_name end def test_translated_model_names_with_sti_fallback - I18n.store_translations 'en-US', :activerecord => {:models => {:topic => 'topic model'} } + I18n.backend.store_translations 'en-US', :activerecord => {:models => {:topic => 'topic model'} } assert_equal 'topic model', Reply.human_name end - - private - def reset_translations - I18n.backend = I18n::Backend::Simple.new - end end -- cgit v1.2.3 From 9dbde4f5cbd0617ee6cce3e41d41335f9c9ce3fd Mon Sep 17 00:00:00 2001 From: pivotal Date: Tue, 26 Aug 2008 09:20:24 -0700 Subject: Fix two has_one :through errors * Set the association target on assignment; * Reset target to nil on reset, rather than empty array. Signed-off-by: Michael Koziarski [#895 state:committed] --- activerecord/lib/active_record/associations.rb | 5 ++--- .../active_record/associations/has_one_through_association.rb | 4 ++++ .../test/cases/associations/has_one_through_associations_test.rb | 9 +++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index f915daafba..4d935612ca 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1270,10 +1270,9 @@ module ActiveRecord association.create_through_record(new_value) self.send(reflection.name, new_value) else - association.replace(new_value) + association.replace(new_value) + instance_variable_set(ivar, new_value.nil? ? nil : association) end - - instance_variable_set(ivar, new_value.nil? ? nil : association) end define_method("set_#{reflection.name}_target") do |target| diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb index c846956e1f..b78bd5d931 100644 --- a/activerecord/lib/active_record/associations/has_one_through_association.rb +++ b/activerecord/lib/active_record/associations/has_one_through_association.rb @@ -22,6 +22,10 @@ module ActiveRecord def find_target super.first + end + + def reset_target! + @target = nil end end end diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index b61a3711e3..77e3cb1776 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -101,4 +101,13 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase assert_equal clubs(:crazy_club), members[0].sponsor_club end + def test_uninitialized_has_one_through_should_return_nil_for_unsaved_record + assert_nil Member.new.club + end + + def test_assigning_association_correctly_assigns_target + new_member = Member.create(:name => "Chris") + new_member.club = new_club = Club.create(:name => "LRUG") + assert_equal new_club, new_member.club.target + end end -- cgit v1.2.3 From a444c782125e10ead6227f7cc57b2f5c739111f2 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 27 Aug 2008 21:32:51 -0700 Subject: respond_to? passes along splat args to avoid introducing the second arg if it was omitted --- activerecord/lib/active_record/associations/association_proxy.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 99b8748a48..5427681f3c 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -69,8 +69,8 @@ module ActiveRecord @target end - def respond_to?(symbol, include_priv = false) - proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv)) + def respond_to?(*args) + proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args)) end # Explicitly proxy === because the instance method removal above -- cgit v1.2.3 From c2068d14d29ec767c681798b3814f0a8e22fb0ff Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Wed, 27 Aug 2008 22:49:50 -0700 Subject: PostgreSQL: fix quote_string for certain old pg drivers. [#94 state:resolved] --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 55c7da5b4f..0c2532f21d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -380,7 +380,7 @@ module ActiveRecord # There are some incorrectly compiled postgres drivers out there # that don't define PGconn.escape. self.class.instance_eval do - undef_method(:quote_string) + remove_method(:quote_string) end end quote_string(s) -- cgit v1.2.3 From ad562c58eabfb8b44cb8ac9e87b87a7f998325fd Mon Sep 17 00:00:00 2001 From: Tom Lea Date: Mon, 11 Aug 2008 14:12:53 +0100 Subject: Dirty: treat two changes resulting in the original value as being unchanged. [#798 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/dirty.rb | 5 ++++- activerecord/test/cases/dirty_test.rb | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index 63bf8c8f5b..7e246e62ca 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -123,7 +123,10 @@ module ActiveRecord attr = attr.to_s # The attribute already has an unsaved change. - unless changed_attributes.include?(attr) + if changed_attributes.include?(attr) + old = changed_attributes[attr] + changed_attributes.delete(attr) unless field_changed?(attr, old, value) + else old = clone_attribute_value(:read_attribute, attr) changed_attributes[attr] = old if field_changed?(attr, old, value) end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index feb47a15a8..4fe1d79f4d 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -191,6 +191,42 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.changed? end + def test_reverted_changes_are_not_dirty + phrase = "shiver me timbers" + pirate = Pirate.create!(:catchphrase => phrase) + pirate.catchphrase = "*hic*" + assert pirate.changed? + pirate.catchphrase = phrase + assert !pirate.changed? + end + + def test_reverted_changes_are_not_dirty_after_multiple_changes + phrase = "shiver me timbers" + pirate = Pirate.create!(:catchphrase => phrase) + 10.times do |i| + pirate.catchphrase = "*hic*" * i + assert pirate.changed? + end + assert pirate.changed? + pirate.catchphrase = phrase + assert !pirate.changed? + end + + + def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back + pirate = Pirate.create!(:catchphrase => "Yar!") + + pirate.parrot_id = 1 + assert pirate.changed? + assert pirate.parrot_id_changed? + assert !pirate.catchphrase_changed? + + pirate.parrot_id = nil + assert !pirate.changed? + assert !pirate.parrot_id_changed? + assert !pirate.catchphrase_changed? + end + def test_save_should_store_serialized_attributes_even_with_partial_updates with_partial_updates(Topic) do topic = Topic.create!(:content => {:a => "a"}) -- cgit v1.2.3 From 13671cc565aad2327f81a29789154b829ceeda04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 19:00:47 +0300 Subject: Alias included associations if needed when doing a count [#302 state:resolved] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/calculations.rb | 10 ++++++++-- .../cases/associations/has_many_through_associations_test.rb | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 77cc6da251..08306f361a 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -186,11 +186,17 @@ module ActiveRecord sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround sql << " FROM #{connection.quote_table_name(table_name)} " end + + joins = "" + add_joins!(joins, options, scope) + if merged_includes.any? - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins]) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins) sql << join_dependency.join_associations.collect{|join| join.association_join }.join end - add_joins!(sql, options, scope) + + sql << joins unless joins.blank? + add_conditions!(sql, options[:conditions], scope) add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index d51a3c7e1c..353262c81b 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -196,4 +196,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase # due to Unknown column 'comments.id' assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog') end + + def test_count_with_include_should_alias_join_table + assert_equal 2, people(:michael).posts.count(:include => :readers) + end end -- cgit v1.2.3 From 96c6fe084228d570dad80e3100830edb2bc0448d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Tue, 26 Aug 2008 19:29:16 +0300 Subject: Implement count limit/offset support for has_many associations [#348 state:resolved] Signed-off-by: Jeremy Kemper --- .../lib/active_record/associations/has_many_association.rb | 11 ++++++++++- .../test/cases/associations/has_many_associations_test.rb | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index ce62127505..1535995410 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -14,7 +14,16 @@ module ActiveRecord @finder_sql + " AND (#{sanitize_sql(options[:conditions])})" options[:include] ||= @reflection.options[:include] - @reflection.klass.count(column_name, options) + value = @reflection.klass.count(column_name, options) + + limit = @reflection.options[:limit] + offset = @reflection.options[:offset] + + if limit || offset + [ [value - offset.to_i, 0].max, limit.to_i ].min + else + value + end end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index da3c8fb28e..17fd88ce65 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -48,6 +48,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 2, Firm.find(:first).plain_clients.count(:name) end + def test_counting_with_association_limit + firm = companies(:first_firm) + assert_equal firm.limited_clients.length, firm.limited_clients.size + assert_equal firm.limited_clients.length, firm.limited_clients.count + end + def test_finding assert_equal 2, Firm.find(:first).clients.length end -- cgit v1.2.3