From 9b852af49783096124aa34c4638b99768f701a78 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 20:33:50 -0700 Subject: Explicitly require builder for to_xml --- activerecord/lib/active_record/validations.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index d2d12b80c9..e6b61e0b35 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1,3 +1,5 @@ +require 'builder' + module ActiveRecord # Raised by save! and create! when the record is invalid. Use the # +record+ method to retrieve the record which did not validate. -- cgit v1.2.3 From dc9f53b05b650614520086ae780ca5e58efe65b1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 21:02:11 -0700 Subject: Selectively require rake sshpublisher so full gem isn't required --- activerecord/Rakefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index b50008c971..5905c67e29 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -4,7 +4,6 @@ require 'rake/testtask' require 'rake/rdoctask' require 'rake/packagetask' require 'rake/gempackagetask' -require 'rake/contrib/sshpublisher' require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version') require File.expand_path(File.dirname(__FILE__)) + "/test/config" @@ -231,12 +230,14 @@ end desc "Publish the beta gem" task :pgem => [:package] do + require 'rake/contrib/sshpublisher' Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'` end desc "Publish the API documentation" task :pdoc => [:rdoc] do + require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload end -- cgit v1.2.3 From b193f233908823cccd2f1d5fcb4146787ed8c4ed Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Feb 2009 15:24:59 -0500 Subject: Use table_exists? in #initialize_schema_migrations_table [#1923 state:resolved] Signed-off-by: Pratik Naik --- .../active_record/connection_adapters/abstract/schema_statements.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 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 c29c1562b4..ff63ea3a2e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -316,7 +316,7 @@ module ActiveRecord def initialize_schema_migrations_table sm_table = ActiveRecord::Migrator.schema_migrations_table_name - unless tables.detect { |t| t == sm_table } + unless table_exists?(sm_table) create_table(sm_table, :id => false) do |schema_migrations_table| schema_migrations_table.column :version, :string, :null => false end @@ -327,7 +327,7 @@ module ActiveRecord # migrated up to that point: si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix - if tables.detect { |t| t == si_table } + if table_exists?(si_table) old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i assume_migrated_upto_version(old_version) -- cgit v1.2.3 From 5501b99a19a2a67a9a920fd3c7bff071a2ecf058 Mon Sep 17 00:00:00 2001 From: steve Date: Fri, 1 May 2009 14:58:10 +0100 Subject: Ensure ActiveRecord::Base.connection_pool.with_connection creates a new connection only when needed [#1752 state:resolved] Signed-off-by: Pratik Naik --- .../abstract/connection_pool.rb | 11 ++++---- activerecord/test/cases/pooled_connections_test.rb | 29 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index aac84cc5f4..e8e736bf38 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -107,13 +107,14 @@ module ActiveRecord checkin conn if conn end - # Reserve a connection, and yield it to a block. Ensure the connection is - # checked back in when finished. + # If a connection already exists yield it to the block. If no connection + # exists checkout a connection, yield it to the block, and checkin the + # connection when finished. def with_connection - conn = checkout - yield conn + fresh_connection = true unless @reserved_connections[current_connection_id] + yield connection ensure - checkin conn + release_connection if fresh_connection end # Returns true if a connection has already been opened. diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 2649a9358a..bbd943fc6e 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -1,4 +1,6 @@ require "cases/helper" +require "models/project" +require "timeout" class PooledConnectionsTest < ActiveRecord::TestCase def setup @@ -89,6 +91,33 @@ class PooledConnectionsTest < ActiveRecord::TestCase ensure ActiveRecord::Base.connection_handler = old_handler end + + def test_with_connection_nesting_safety + ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1, :wait_timeout => 0.1})) + + before_count = Project.count + + add_record('one') + + ActiveRecord::Base.connection.transaction do + add_record('two') + # Have another thread try to screw up the transaction + Thread.new do + raise ActiveRecord::Rollback + ActiveRecord::Base.connection_pool.release_connection + end.join rescue nil + add_record('three') + end + + after_count = Project.count + assert_equal 3, after_count - before_count + end + + private + + def add_record(name) + ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name } + end end unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name class AllowConcurrencyDeprecatedTest < ActiveRecord::TestCase -- cgit v1.2.3 From 60a6284a461afda5911ecadf3a147d4da6ed5d22 Mon Sep 17 00:00:00 2001 From: steve Date: Fri, 1 May 2009 16:00:09 +0100 Subject: Tests should use ActiveRecord::Base.connection.rollback_db_transaction to rollback a transaction Signed-off-by: Pratik Naik --- activerecord/test/cases/pooled_connections_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index bbd943fc6e..bb9013c2a1 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -103,7 +103,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase add_record('two') # Have another thread try to screw up the transaction Thread.new do - raise ActiveRecord::Rollback + ActiveRecord::Base.connection.rollback_db_transaction ActiveRecord::Base.connection_pool.release_connection end.join rescue nil add_record('three') -- cgit v1.2.3 From db0bfe4ede3cdfc2e4ccdb2a89525a914e6d0913 Mon Sep 17 00:00:00 2001 From: Alexander Podgorbunsky Date: Thu, 26 Mar 2009 15:00:12 +0300 Subject: Default scope :order should be overridden by named scopes. [#2346 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/named_scope.rb | 4 ++-- activerecord/test/cases/method_scoping_test.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 1f3ef300f2..3df7089096 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -114,7 +114,7 @@ module ActiveRecord end end - delegate :scopes, :with_scope, :to => :proxy_scope + delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope def initialize(proxy_scope, options, &block) options ||= {} @@ -178,7 +178,7 @@ module ActiveRecord else with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do method = :new if method == :build - if current_scoped_methods_when_defined + if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined) with_scope current_scoped_methods_when_defined do proxy_scope.send(method, *args, &block) end diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 2165d2f7e8..c479859ee0 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -628,9 +628,9 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end - def test_named_scope - expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.salary } + def test_named_scope_overwrites_default + expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name } assert_equal expected, received end -- cgit v1.2.3 From 99c103be1165da9c8299bc0977188ecf167e06a5 Mon Sep 17 00:00:00 2001 From: Ruy Asan Date: Fri, 1 May 2009 13:09:29 -0700 Subject: Fixed bug with polymorphic has_one :as pointing to an STI record [#2594 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_record/associations/has_one_association.rb | 2 +- .../test/cases/associations/has_one_associations_test.rb | 9 ++++++++- activerecord/test/fixtures/organizations.yml | 4 +++- activerecord/test/fixtures/sponsors.yml | 4 +++- activerecord/test/models/organization.rb | 4 ++++ activerecord/test/schema/schema.rb | 3 ++- 6 files changed, 21 insertions(+), 5 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 b92cbbdeab..1464227bb0 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -88,7 +88,7 @@ module ActiveRecord when @reflection.options[:as] @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.name.to_s)}" else @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 1ddb3f49bf..3984945f9f 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -2,9 +2,11 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' +require 'models/sponsor' +require 'models/organization' class HasOneAssociationsTest < ActiveRecord::TestCase - fixtures :accounts, :companies, :developers, :projects, :developers_projects + fixtures :accounts, :companies, :developers, :projects, :developers_projects, :organizations, :sponsors def setup Account.destroyed_account_ids.clear @@ -306,4 +308,9 @@ class HasOneAssociationsTest < ActiveRecord::TestCase Firm.find(@firm.id, :include => :account).save! end end + + def test_polymorphic_sti + assert_equal organizations(:sponsorable), sponsors(:org_sponsor).sponsorable + assert_equal sponsors(:org_sponsor), organizations(:sponsorable).sponsor + end end diff --git a/activerecord/test/fixtures/organizations.yml b/activerecord/test/fixtures/organizations.yml index 25295bff87..05d620fbc6 100644 --- a/activerecord/test/fixtures/organizations.yml +++ b/activerecord/test/fixtures/organizations.yml @@ -2,4 +2,6 @@ nsa: name: No Such Agency discordians: name: Discordians - +sponsorable: + name: We Need Money + type: SponsorableOrganization diff --git a/activerecord/test/fixtures/sponsors.yml b/activerecord/test/fixtures/sponsors.yml index 42df8957d1..97a7784047 100644 --- a/activerecord/test/fixtures/sponsors.yml +++ b/activerecord/test/fixtures/sponsors.yml @@ -6,4 +6,6 @@ boring_club_sponsor_for_groucho: sponsorable: some_other_guy (Member) crazy_club_sponsor_for_groucho: sponsor_club: crazy_club - sponsorable: some_other_guy (Member) \ No newline at end of file + sponsorable: some_other_guy (Member) +org_sponsor: + sponsorable: sponsorable (SponsorableOrganization) \ No newline at end of file diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb index d79d5037c8..5d1308354d 100644 --- a/activerecord/test/models/organization.rb +++ b/activerecord/test/models/organization.rb @@ -1,4 +1,8 @@ class Organization < ActiveRecord::Base has_many :member_details has_many :members, :through => :member_details +end + +class SponsorableOrganization < Organization + has_one :sponsor, :as => :sponsorable end \ No newline at end of file diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 98e6d192a5..6918a4fcab 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -283,6 +283,7 @@ ActiveRecord::Schema.define do create_table :organizations, :force => true do |t| t.string :name + t.string :type end create_table :owners, :primary_key => :owner_id ,:force => true do |t| @@ -388,7 +389,7 @@ ActiveRecord::Schema.define do create_table :sponsors, :force => true do |t| t.integer :club_id t.integer :sponsorable_id - t.string :sponsorable_type + t.string :sponsorable_type end create_table :subscribers, :force => true, :id => false do |t| -- cgit v1.2.3 From 945bf9c254b5bfb56df874c1a3f7f0f1e89dc8b8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 2 May 2009 15:16:12 -0700 Subject: Check for sibling Active Support first --- activerecord/lib/active_record.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 500a90d6cb..1982ffc791 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -21,15 +21,10 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -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 +"#{File.dirname(__FILE__)}/../../activesupport/lib".tap do |path| + $:.unshift(path) if File.directory?(path) end +require 'active_support' require 'active_support/core/all' module ActiveRecord -- cgit v1.2.3 From d3ee8756c8b09b8524a9599926b02ededd27319c Mon Sep 17 00:00:00 2001 From: Chris Kampmeier Date: Sun, 3 May 2009 18:09:06 -0700 Subject: Don't use #tap before Active Support is available, since older versions of ruby don't have native implementations [#2603 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 1982ffc791..06d6c87090 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -21,9 +21,8 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -"#{File.dirname(__FILE__)}/../../activesupport/lib".tap do |path| - $:.unshift(path) if File.directory?(path) -end +activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" +$:.unshift(activesupport_path) if File.directory?(activesupport_path) require 'active_support' require 'active_support/core/all' -- cgit v1.2.3 From ccea98389abbf150b886c9f964b1def47f00f237 Mon Sep 17 00:00:00 2001 From: Murray Steele Date: Fri, 1 May 2009 16:01:13 +0100 Subject: Providing support for :inverse_of as an option to associations. You can now add an :inverse_of option to has_one, has_many and belongs_to associations. This is best described with an example: class Man < ActiveRecord::Base has_one :face, :inverse_of => :man end class Face < ActiveRecord::Base belongs_to :man, :inverse_of => :face end m = Man.first f = m.face Without :inverse_of m and f.man would be different instances of the same object (f.man being pulled from the database again). With these new :inverse_of options m and f.man are the same in memory instance. Currently :inverse_of supports has_one and has_many (but not the :through variants) associations. It also supplies inverse support for belongs_to associations where the inverse is a has_one and it's not a polymorphic. Signed-off-by: Murray Steele Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations.rb | 12 +- .../associations/association_collection.rb | 8 +- .../associations/association_proxy.rb | 14 ++ .../associations/belongs_to_association.rb | 12 +- .../associations/has_many_association.rb | 5 + .../associations/has_many_through_association.rb | 10 +- .../associations/has_one_association.rb | 11 +- activerecord/lib/active_record/reflection.rb | 21 ++ .../associations/inverse_associations_test.rb | 252 +++++++++++++++++++++ activerecord/test/fixtures/faces.yml | 7 + activerecord/test/fixtures/interests.yml | 29 +++ activerecord/test/fixtures/men.yml | 5 + activerecord/test/fixtures/zines.yml | 5 + activerecord/test/models/face.rb | 5 + activerecord/test/models/interest.rb | 4 + activerecord/test/models/man.rb | 7 + activerecord/test/models/zine.rb | 3 + activerecord/test/schema/schema.rb | 20 ++ 18 files changed, 418 insertions(+), 12 deletions(-) create mode 100644 activerecord/test/cases/associations/inverse_associations_test.rb create mode 100644 activerecord/test/fixtures/faces.yml create mode 100644 activerecord/test/fixtures/interests.yml create mode 100644 activerecord/test/fixtures/men.yml create mode 100644 activerecord/test/fixtures/zines.yml create mode 100644 activerecord/test/models/face.rb create mode 100644 activerecord/test/models/interest.rb create mode 100644 activerecord/test/models/man.rb create mode 100644 activerecord/test/models/zine.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 2115878e32..0952b087d1 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1,4 +1,10 @@ module ActiveRecord + class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: + def initialize(reflection) + super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name})") + end + end + class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc: def initialize(owner_class_name, reflection) super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}") @@ -1488,7 +1494,7 @@ module ActiveRecord :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove, :extend, :readonly, - :validate + :validate, :inverse_of ] def create_has_many_reflection(association_id, options, &extension) @@ -1502,7 +1508,7 @@ module ActiveRecord @@valid_keys_for_has_one_association = [ :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly, - :validate, :primary_key + :validate, :primary_key, :inverse_of ] def create_has_one_reflection(association_id, options) @@ -1521,7 +1527,7 @@ module ActiveRecord @@valid_keys_for_belongs_to_association = [ :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly, - :validate, :touch + :validate, :touch, :inverse_of ] def create_belongs_to_reflection(association_id, options) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 3aef1b21e9..26987dde97 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -399,11 +399,14 @@ module ActiveRecord find(:all) end - @reflection.options[:uniq] ? uniq(records) : records + records = @reflection.options[:uniq] ? uniq(records) : records + records.each do |record| + set_inverse_instance(record, @owner) + end + records end private - def create_record(attrs) attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash) ensure_owner_is_not_new @@ -433,6 +436,7 @@ module ActiveRecord @target ||= [] unless loaded? @target << record unless @reflection.options[:uniq] && @target.include?(record) callback(:after_add, record) + set_inverse_instance(record, @owner) record end diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 241b9bfee0..e36b04ea95 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -53,6 +53,7 @@ module ActiveRecord def initialize(owner, reflection) @owner, @reflection = owner, reflection + reflection.check_validity! Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) } reset end @@ -274,6 +275,19 @@ module ActiveRecord def owner_quoted_id @owner.quoted_id end + + def set_inverse_instance(record, instance) + return if record.nil? || !we_can_set_the_inverse_on_this?(record) + inverse_relationship = @reflection.inverse_of + unless inverse_relationship.nil? + record.send(:"set_#{inverse_relationship.name}_target", instance) + end + end + + # Override in subclasses + def we_can_set_the_inverse_on_this?(record) + false + end end end end diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index f05c6be075..c88575048a 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -31,6 +31,8 @@ module ActiveRecord @updated = true end + set_inverse_instance(record, @owner) + loaded record end @@ -41,18 +43,26 @@ module ActiveRecord private def find_target - @reflection.klass.find( + the_target = @reflection.klass.find( @owner[@reflection.primary_key_name], :select => @reflection.options[:select], :conditions => conditions, :include => @reflection.options[:include], :readonly => @reflection.options[:readonly] ) + set_inverse_instance(the_target, @owner) + the_target end def foreign_key_present !@owner[@reflection.primary_key_name].nil? end + + # NOTE - for now, we're only supporting inverse setting from belongs_to back onto + # has_one associations. + def we_can_set_the_inverse_on_this?(record) + @reflection.has_inverse? && @reflection.inverse_of.macro == :has_one + end end end end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index a2cbabfe0c..73dd50dd07 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -116,6 +116,11 @@ module ActiveRecord :create => create_scoping } end + + def we_can_set_the_inverse_on_this?(record) + inverse = @reflection.inverse_of + return !inverse.nil? + end end end end 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 1c091e7d5a..2dca84b911 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -1,11 +1,6 @@ module ActiveRecord module Associations class HasManyThroughAssociation < HasManyAssociation #:nodoc: - def initialize(owner, reflection) - reflection.check_validity! - super - end - alias_method :new, :build def create!(attrs = nil) @@ -251,6 +246,11 @@ module ActiveRecord def cached_counter_attribute_name "#{@reflection.name}_count" end + + # NOTE - not sure that we can actually cope with inverses here + def we_can_set_the_inverse_on_this?(record) + false + end end end end diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 1464227bb0..4908005d2e 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -74,13 +74,15 @@ module ActiveRecord private def find_target - @reflection.klass.find(:first, + the_target = @reflection.klass.find(:first, :conditions => @finder_sql, :select => @reflection.options[:select], :order => @reflection.options[:order], :include => @reflection.options[:include], :readonly => @reflection.options[:readonly] ) + set_inverse_instance(the_target, @owner) + the_target end def construct_sql @@ -117,8 +119,15 @@ module ActiveRecord self.target = record end + set_inverse_instance(record, @owner) + record end + + def we_can_set_the_inverse_on_this?(record) + inverse = @reflection.inverse_of + return !inverse.nil? + end end end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 2d4c1d5507..ec0175497d 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -212,6 +212,13 @@ module ActiveRecord end def check_validity! + check_validity_of_inverse! + end + + def check_validity_of_inverse! + if has_inverse? && inverse_of.nil? + raise InverseOfAssociationNotFoundError.new(self) + end end def through_reflection @@ -225,6 +232,18 @@ module ActiveRecord nil end + def has_inverse? + !@options[:inverse_of].nil? + end + + def inverse_of + if has_inverse? + @inverse_of ||= klass.reflect_on_association(options[:inverse_of]) + else + nil + end + end + private def derive_class_name class_name = name.to_s.camelize @@ -300,6 +319,8 @@ module ActiveRecord unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil? raise HasManyThroughSourceAssociationMacroError.new(self) end + + check_validity_of_inverse! end def through_reflection_primary_key diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb new file mode 100644 index 0000000000..616f8dfbbe --- /dev/null +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -0,0 +1,252 @@ +require "cases/helper" +require 'models/man' +require 'models/face' +require 'models/interest' +require 'models/zine' +require 'models/club' +require 'models/sponsor' + +class InverseAssociationTests < ActiveRecord::TestCase + def test_should_allow_for_inverse_of_options_in_associations + assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_many') do + Class.new(ActiveRecord::Base).has_many(:wheels, :inverse_of => :car) + end + + assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_one') do + Class.new(ActiveRecord::Base).has_one(:engine, :inverse_of => :car) + end + + assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on belongs_to') do + Class.new(ActiveRecord::Base).belongs_to(:car, :inverse_of => :driver) + end + end + + def test_should_be_able_to_ask_a_reflection_if_it_has_an_inverse + has_one_with_inverse_ref = Man.reflect_on_association(:face) + assert has_one_with_inverse_ref.respond_to?(:has_inverse?) + assert has_one_with_inverse_ref.has_inverse? + + has_many_with_inverse_ref = Man.reflect_on_association(:interests) + assert has_many_with_inverse_ref.respond_to?(:has_inverse?) + assert has_many_with_inverse_ref.has_inverse? + + belongs_to_with_inverse_ref = Face.reflect_on_association(:man) + assert belongs_to_with_inverse_ref.respond_to?(:has_inverse?) + assert belongs_to_with_inverse_ref.has_inverse? + + has_one_without_inverse_ref = Club.reflect_on_association(:sponsor) + assert has_one_without_inverse_ref.respond_to?(:has_inverse?) + assert !has_one_without_inverse_ref.has_inverse? + + has_many_without_inverse_ref = Club.reflect_on_association(:memberships) + assert has_many_without_inverse_ref.respond_to?(:has_inverse?) + assert !has_many_without_inverse_ref.has_inverse? + + belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club) + assert belongs_to_without_inverse_ref.respond_to?(:has_inverse?) + assert !belongs_to_without_inverse_ref.has_inverse? + end + + def test_should_be_able_to_ask_a_reflection_what_it_is_the_inverse_of + has_one_ref = Man.reflect_on_association(:face) + assert has_one_ref.respond_to?(:inverse_of) + + has_many_ref = Man.reflect_on_association(:interests) + assert has_many_ref.respond_to?(:inverse_of) + + belongs_to_ref = Face.reflect_on_association(:man) + assert belongs_to_ref.respond_to?(:inverse_of) + end + + def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of + has_one_ref = Man.reflect_on_association(:face) + assert_equal Face.reflect_on_association(:man), has_one_ref.inverse_of + + has_many_ref = Man.reflect_on_association(:interests) + assert_equal Interest.reflect_on_association(:man), has_many_ref.inverse_of + + belongs_to_ref = Face.reflect_on_association(:man) + assert_equal Man.reflect_on_association(:face), belongs_to_ref.inverse_of + end + + def test_associations_with_no_inverse_of_should_return_nil + has_one_ref = Club.reflect_on_association(:sponsor) + assert_nil has_one_ref.inverse_of + + has_many_ref = Club.reflect_on_association(:memberships) + assert_nil has_many_ref.inverse_of + + belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club) + assert_nil belongs_to_ref.inverse_of + end +end + +class InverseHasOneTests < ActiveRecord::TestCase + fixtures :men, :faces + + def test_parent_instance_should_be_shared_with_child_on_find + m = Man.find(:first) + f = m.face + assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance" + f.man.name = 'Mungo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance" + end + + def test_parent_instance_should_be_shared_with_newly_built_child + m = Man.find(:first) + f = m.build_face(:description => 'haunted') + assert_not_nil f.man + assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance" + f.man.name = 'Mungo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to just-built-child-owned instance" + end + + def test_parent_instance_should_be_shared_with_newly_created_child + m = Man.find(:first) + f = m.create_face(:description => 'haunted') + assert_not_nil f.man + assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance" + f.man.name = 'Mungo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance" + end + + def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error + assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).dirty_face } + end +end + +class InverseHasManyTests < ActiveRecord::TestCase + fixtures :men, :interests + + def test_parent_instance_should_be_shared_with_every_child_on_find + m = Man.find(:first) + is = m.interests + is.each do |i| + assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance" + i.man.name = 'Mungo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance" + end + end + + def test_parent_instance_should_be_shared_with_newly_built_child + m = Man.find(:first) + i = m.interests.build(:topic => 'Industrial Revolution Re-enactment') + assert_not_nil i.man + assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance" + i.man.name = 'Mungo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to just-built-child-owned instance" + end + + def test_parent_instance_should_be_shared_with_newly_created_child + m = Man.find(:first) + i = m.interests.create(:topic => 'Industrial Revolution Re-enactment') + assert_not_nil i.man + assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance" + i.man.name = 'Mungo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance" + end + + def test_parent_instance_should_be_shared_with_poked_in_child + m = Man.find(:first) + i = Interest.create(:topic => 'Industrial Revolution Re-enactment') + m.interests << i + assert_not_nil i.man + assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance" + i.man.name = 'Mungo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance" + end + + def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error + assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).secret_interests } + end +end + +class InverseBelongsToTests < ActiveRecord::TestCase + fixtures :men, :faces, :interests + + def test_child_instance_should_be_shared_with_parent_on_find + f = Face.find(:first) + m = f.man + assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance" + f.description = 'gormless' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance" + m.face.description = 'pleasing' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance" + end + + def test_child_instance_should_be_shared_with_newly_built_parent + f = Face.find(:first) + m = f.build_man(:name => 'Charles') + assert_not_nil m.face + assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance" + f.description = 'gormless' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance" + m.face.description = 'pleasing' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to just-built-parent-owned instance" + end + + def test_child_instance_should_be_shared_with_newly_created_parent + f = Face.find(:first) + m = f.create_man(:name => 'Charles') + assert_not_nil m.face + assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance" + f.description = 'gormless' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance" + m.face.description = 'pleasing' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to newly-created-parent-owned instance" + end + + def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many + i = Interest.find(:first) + m = i.man + assert_not_nil m.interests + iz = m.interests.detect {|iz| iz.id == i.id} + assert_not_nil iz + assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child" + i.topic = 'Eating cheese with a spoon' + assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child" + iz.topic = 'Cow tipping' + assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance" + end + + def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error + assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_man } + end +end + +# NOTE - these tests might not be meaningful, ripped as they were from the parental_control plugin +# which would guess the inverse rather than look for an explicit configuration option. +class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase + fixtures :men, :interests, :zines + + def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_different_models + assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do + i = Interest.find(:first) + z = i.zine + m = i.man + end + end + + def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models + assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do + i = Interest.find(:first) + i.build_zine(:title => 'Get Some in Winter! 2008') + i.build_man(:name => 'Gordon') + i.save! + end + end +end diff --git a/activerecord/test/fixtures/faces.yml b/activerecord/test/fixtures/faces.yml new file mode 100644 index 0000000000..1dd2907cf7 --- /dev/null +++ b/activerecord/test/fixtures/faces.yml @@ -0,0 +1,7 @@ +trusting: + description: trusting + man: gordon + +weather_beaten: + description: weather beaten + man: steve diff --git a/activerecord/test/fixtures/interests.yml b/activerecord/test/fixtures/interests.yml new file mode 100644 index 0000000000..ec71890ab6 --- /dev/null +++ b/activerecord/test/fixtures/interests.yml @@ -0,0 +1,29 @@ +trainspotting: + topic: Trainspotting + zine: staying_in + man: gordon + +birdwatching: + topic: Birdwatching + zine: staying_in + man: gordon + +stamp_collecting: + topic: Stamp Collecting + zine: staying_in + man: gordon + +hunting: + topic: Hunting + zine: going_out + man: steve + +woodsmanship: + topic: Woodsmanship + zine: going_out + man: steve + +survial: + topic: Survival + zine: going_out + man: steve diff --git a/activerecord/test/fixtures/men.yml b/activerecord/test/fixtures/men.yml new file mode 100644 index 0000000000..c67429f925 --- /dev/null +++ b/activerecord/test/fixtures/men.yml @@ -0,0 +1,5 @@ +gordon: + name: Gordon + +steve: + name: Steve diff --git a/activerecord/test/fixtures/zines.yml b/activerecord/test/fixtures/zines.yml new file mode 100644 index 0000000000..07dce4db7e --- /dev/null +++ b/activerecord/test/fixtures/zines.yml @@ -0,0 +1,5 @@ +staying_in: + title: Staying in '08 + +going_out: + title: Outdoor Pursuits 2k+8 diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb new file mode 100644 index 0000000000..1540dbf741 --- /dev/null +++ b/activerecord/test/models/face.rb @@ -0,0 +1,5 @@ +class Face < ActiveRecord::Base + belongs_to :man, :inverse_of => :face + # This is a "broken" inverse_of for the purposes of testing + belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face +end diff --git a/activerecord/test/models/interest.rb b/activerecord/test/models/interest.rb new file mode 100644 index 0000000000..d8291d00cc --- /dev/null +++ b/activerecord/test/models/interest.rb @@ -0,0 +1,4 @@ +class Interest < ActiveRecord::Base + belongs_to :man, :inverse_of => :interests + belongs_to :zine, :inverse_of => :interests +end diff --git a/activerecord/test/models/man.rb b/activerecord/test/models/man.rb new file mode 100644 index 0000000000..f40bc9d0fc --- /dev/null +++ b/activerecord/test/models/man.rb @@ -0,0 +1,7 @@ +class Man < ActiveRecord::Base + has_one :face, :inverse_of => :man + has_many :interests, :inverse_of => :man + # These are "broken" inverse_of associations for the purposes of testing + has_one :dirty_face, :class_name => 'Face', :inverse_of => :dirty_man + has_many :secret_interests, :class_name => 'Interest', :inverse_of => :secret_man +end diff --git a/activerecord/test/models/zine.rb b/activerecord/test/models/zine.rb new file mode 100644 index 0000000000..c2d0fdaf25 --- /dev/null +++ b/activerecord/test/models/zine.rb @@ -0,0 +1,3 @@ +class Zine < ActiveRecord::Base + has_many :interests, :inverse_of => :zine +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 6918a4fcab..3b0e17c867 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -468,6 +468,26 @@ ActiveRecord::Schema.define do end end + # NOTE - the following 4 tables are used by models that have :inverse_of options on the associations + create_table :men, :force => true do |t| + t.string :name + end + + create_table :faces, :force => true do |t| + t.string :description + t.integer :man_id + end + + create_table :interests, :force => true do |t| + t.string :topic + t.integer :man_id + t.integer :zine_id + end + + create_table :zines, :force => true do |t| + t.string :title + end + except 'SQLite' do # fk_test_has_fk should be before fk_test_has_pk create_table :fk_test_has_fk, :force => true do |t| -- cgit v1.2.3 From ef6f22ab2aadf619c993527150490d6d48a719c6 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Thu, 7 May 2009 01:03:52 +0100 Subject: honour inverse_of when preloading associations Signed-off-by: Michael Koziarski --- .../lib/active_record/association_preload.rb | 4 ++- .../associations/inverse_associations_test.rb | 33 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index e4ab69aa1b..967fff4d6f 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -126,6 +126,7 @@ module ActiveRecord association_proxy = parent_record.send(reflection_name) association_proxy.loaded association_proxy.target.push(*[associated_record].flatten) + association_proxy.__send__(:set_inverse_instance, associated_record, parent_record) end end @@ -152,7 +153,8 @@ module ActiveRecord seen_keys[associated_record[key].to_s] = true mapped_records = id_to_record_map[associated_record[key].to_s] mapped_records.each do |mapped_record| - mapped_record.send("set_#{reflection_name}_target", associated_record) + association_proxy = mapped_record.send("set_#{reflection_name}_target", associated_record) + association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record) end end end diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index 616f8dfbbe..d123837efd 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -94,6 +94,17 @@ class InverseHasOneTests < ActiveRecord::TestCase assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance" end + + def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find + m = Man.find(:first, :include => :face) + f = m.face + assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance" + f.man.name = 'Mungo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance" + end + def test_parent_instance_should_be_shared_with_newly_built_child m = Man.find(:first) f = m.build_face(:description => 'haunted') @@ -136,6 +147,18 @@ class InverseHasManyTests < ActiveRecord::TestCase end end + def test_parent_instance_should_be_shared_with_eager_loaded_children + m = Man.find(:first, :include => :interests) + is = m.interests + is.each do |i| + assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance" + i.man.name = 'Mungo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance" + end + end + def test_parent_instance_should_be_shared_with_newly_built_child m = Man.find(:first) i = m.interests.build(:topic => 'Industrial Revolution Re-enactment') @@ -188,6 +211,16 @@ class InverseBelongsToTests < ActiveRecord::TestCase assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance" end + def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find + f = Face.find(:first, :include => :man) + m = f.man + assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance" + f.description = 'gormless' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance" + m.face.description = 'pleasing' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance" + end + def test_child_instance_should_be_shared_with_newly_built_parent f = Face.find(:first) m = f.build_man(:name => 'Charles') -- cgit v1.2.3 From 235775de291787bba6b7bc3c58e791216c3b5090 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Thu, 7 May 2009 01:43:15 +0100 Subject: honour :inverse_of for joins based include Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/associations.rb | 10 ++++++-- .../associations/inverse_associations_test.rb | 28 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 0952b087d1..2928b0bf83 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1916,21 +1916,27 @@ module ActiveRecord return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil? association = join.instantiate(row) collection.target.push(association) + collection.__send__(:set_inverse_instance, association, record) 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) + set_target_and_inverse(join, association, record) when :belongs_to return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil? association = join.instantiate(row) - record.send("set_#{join.reflection.name}_target", association) + set_target_and_inverse(join, association, record) else raise ConfigurationError, "unknown macro: #{join.reflection.macro}" end return association end + def set_target_and_inverse(join, association, record) + association_proxy = record.send("set_#{join.reflection.name}_target", association) + association_proxy.__send__(:set_inverse_instance, association, record) + end + class JoinBase # :nodoc: attr_reader :active_record, :table_joins delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index d123837efd..47f83db112 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -103,6 +103,14 @@ class InverseHasOneTests < ActiveRecord::TestCase assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance" f.man.name = 'Mungo' assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance" + + m = Man.find(:first, :include => :face, :order => 'faces.id') + f = m.face + assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance" + f.man.name = 'Mungo' + assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance" end def test_parent_instance_should_be_shared_with_newly_built_child @@ -157,6 +165,17 @@ class InverseHasManyTests < ActiveRecord::TestCase i.man.name = 'Mungo' assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance" end + + m = Man.find(:first, :include => :interests, :order => 'interests.id') + is = m.interests + is.each do |i| + assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance" + m.name = 'Bongo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance" + i.man.name = 'Mungo' + assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance" + end + end def test_parent_instance_should_be_shared_with_newly_built_child @@ -219,6 +238,15 @@ class InverseBelongsToTests < ActiveRecord::TestCase assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance" m.face.description = 'pleasing' assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance" + + + f = Face.find(:first, :include => :man, :order => 'men.id') + m = f.man + assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance" + f.description = 'gormless' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance" + m.face.description = 'pleasing' + assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance" end def test_child_instance_should_be_shared_with_newly_built_parent -- cgit v1.2.3 From 026b78f9076216990bddb1aa5d83d23a647c02a5 Mon Sep 17 00:00:00 2001 From: Anthony Crumley Date: Mon, 4 May 2009 09:49:43 -0500 Subject: Fixed eager load error on find with include => [:table_name] and hash conditions like {:table_name => {:column => 'value'}} Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/associations.rb | 20 ++++++++++++++++---- activerecord/test/cases/associations/eager_test.rb | 12 ++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 2928b0bf83..781a0290e8 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1671,17 +1671,29 @@ module ActiveRecord string.scan(/([\.a-zA-Z_]+).?\./).flatten end + def tables_in_hash(hash) + return [] if hash.blank? + tables = hash.map do |key, value| + if value.is_a?(Hash) + key.to_s + else + tables_in_string(key) if key.is_a?(String) + end + end + tables.flatten.compact + end + def conditions_tables(options) # look in both sets of conditions conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond| case cond when nil then all - when Array then all << cond.first - when Hash then all << cond.keys - else all << cond + when Array then all << tables_in_string(cond.first) + when Hash then all << tables_in_hash(cond) + else all << tables_in_string(cond) end end - tables_in_string(conditions.join(' ')) + conditions.flatten end def order_tables(options) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 40723814c5..d23f86b700 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -223,6 +223,18 @@ class EagerAssociationTest < ActiveRecord::TestCase end end + def test_eager_association_loading_with_belongs_to_and_conditions_hash + comments = [] + assert_nothing_raised do + comments = Comment.find(:all, :include => :post, :conditions => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id') + end + assert_equal 3, comments.length + assert_equal [5,6,7], comments.collect { |c| c.id } + assert_no_queries do + comments.first.post + end + end + def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id') assert_nothing_raised do -- cgit v1.2.3 From 9010ed27559ed5ab89ea71b4b16f4c8e56d03dbb Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Sun, 10 May 2009 15:02:00 +1200 Subject: Allow you to pass :all_blank to :reject_if option to automatically create a Proc that will reject any record with blank attributes. --- activerecord/lib/active_record/nested_attributes.rb | 11 ++++++++++- activerecord/test/cases/nested_attributes_test.rb | 18 +++++++++++++++++- activerecord/test/models/pirate.rb | 2 ++ activerecord/test/schema/schema.rb | 1 + 4 files changed, 30 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index e3122d195a..dfad2901c5 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -180,10 +180,14 @@ module ActiveRecord # and the Proc should return either +true+ or +false+. When no Proc # is specified a record will be built for all attribute hashes that # do not have a _delete that evaluates to true. + # Passing :all_blank instead of a Proc will create a proc + # that will reject a record where all the attributes are blank. # # Examples: # # creates avatar_attributes= # accepts_nested_attributes_for :avatar, :reject_if => proc { |attributes| attributes['name'].blank? } + # # creates avatar_attributes= + # accepts_nested_attributes_for :avatar, :reject_if => :all_blank # # creates avatar_attributes= and posts_attributes= # accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true def accepts_nested_attributes_for(*attr_names) @@ -201,7 +205,12 @@ module ActiveRecord end reflection.options[:autosave] = true - self.reject_new_nested_attributes_procs[association_name.to_sym] = options[:reject_if] + + self.reject_new_nested_attributes_procs[association_name.to_sym] = if options[:reject_if] == :all_blank + proc { |attributes| attributes.all? {|k,v| v.blank?} } + else + options[:reject_if] + end # def pirate_attributes=(attributes) # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, false) diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index cd6277c24b..f1741ed54d 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -31,11 +31,27 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase end def test_should_add_a_proc_to_reject_new_nested_attributes_procs - [:parrots, :birds].each do |name| + [:parrots, :birds, :birds_with_reject_all_blank].each do |name| assert_instance_of Proc, Pirate.reject_new_nested_attributes_procs[name] end end + def test_should_not_build_a_new_record_if_reject_all_blank_returns_false + pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?") + pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}] + pirate.save! + + assert pirate.birds_with_reject_all_blank.empty? + end + + def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false + pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?") + pirate.birds_with_reject_all_blank_attributes = [{:name => 'Tweetie', :color => ''}] + pirate.save! + + assert_equal 1, pirate.birds_with_reject_all_blank.count + end + def test_should_raise_an_ArgumentError_for_non_existing_associations assert_raise_with_message ArgumentError, "No association found for name `honesty'. Has it been defined yet?" do Pirate.accepts_nested_attributes_for :honesty diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index 238917bf30..acf53fce8b 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -28,11 +28,13 @@ class Pirate < ActiveRecord::Base :after_add => proc {|p,b| p.ship_log << "after_adding_proc_bird_#{b.id || ''}"}, :before_remove => proc {|p,b| p.ship_log << "before_removing_proc_bird_#{b.id}"}, :after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"} + has_many :birds_with_reject_all_blank, :class_name => "Bird" accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks, :birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true + accepts_nested_attributes_for :birds_with_reject_all_blank, :reject_if => :all_blank validates_presence_of :catchphrase diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 3b0e17c867..6fb918c60e 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -57,6 +57,7 @@ ActiveRecord::Schema.define do create_table :birds, :force => true do |t| t.string :name + t.string :color t.integer :pirate_id end -- cgit v1.2.3 From ddbeb15a5e7e0c3c5f316ccf65b557bc5311a6c4 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 11 May 2009 12:01:08 -0700 Subject: Revert "Fixed bug with polymorphic has_one :as pointing to an STI record" [#2594 state:open] This reverts commit 99c103be1165da9c8299bc0977188ecf167e06a5. --- .../lib/active_record/associations/has_one_association.rb | 2 +- .../test/cases/associations/has_one_associations_test.rb | 9 +-------- activerecord/test/fixtures/organizations.yml | 4 +--- activerecord/test/fixtures/sponsors.yml | 4 +--- activerecord/test/models/organization.rb | 4 ---- activerecord/test/schema/schema.rb | 3 +-- 6 files changed, 5 insertions(+), 21 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 4908005d2e..b72b84343b 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -90,7 +90,7 @@ module ActiveRecord when @reflection.options[:as] @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.name.to_s)}" + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" else @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 3984945f9f..1ddb3f49bf 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -2,11 +2,9 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' -require 'models/sponsor' -require 'models/organization' class HasOneAssociationsTest < ActiveRecord::TestCase - fixtures :accounts, :companies, :developers, :projects, :developers_projects, :organizations, :sponsors + fixtures :accounts, :companies, :developers, :projects, :developers_projects def setup Account.destroyed_account_ids.clear @@ -308,9 +306,4 @@ class HasOneAssociationsTest < ActiveRecord::TestCase Firm.find(@firm.id, :include => :account).save! end end - - def test_polymorphic_sti - assert_equal organizations(:sponsorable), sponsors(:org_sponsor).sponsorable - assert_equal sponsors(:org_sponsor), organizations(:sponsorable).sponsor - end end diff --git a/activerecord/test/fixtures/organizations.yml b/activerecord/test/fixtures/organizations.yml index 05d620fbc6..25295bff87 100644 --- a/activerecord/test/fixtures/organizations.yml +++ b/activerecord/test/fixtures/organizations.yml @@ -2,6 +2,4 @@ nsa: name: No Such Agency discordians: name: Discordians -sponsorable: - name: We Need Money - type: SponsorableOrganization + diff --git a/activerecord/test/fixtures/sponsors.yml b/activerecord/test/fixtures/sponsors.yml index 97a7784047..42df8957d1 100644 --- a/activerecord/test/fixtures/sponsors.yml +++ b/activerecord/test/fixtures/sponsors.yml @@ -6,6 +6,4 @@ boring_club_sponsor_for_groucho: sponsorable: some_other_guy (Member) crazy_club_sponsor_for_groucho: sponsor_club: crazy_club - sponsorable: some_other_guy (Member) -org_sponsor: - sponsorable: sponsorable (SponsorableOrganization) \ No newline at end of file + sponsorable: some_other_guy (Member) \ No newline at end of file diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb index 5d1308354d..d79d5037c8 100644 --- a/activerecord/test/models/organization.rb +++ b/activerecord/test/models/organization.rb @@ -1,8 +1,4 @@ class Organization < ActiveRecord::Base has_many :member_details has_many :members, :through => :member_details -end - -class SponsorableOrganization < Organization - has_one :sponsor, :as => :sponsorable end \ No newline at end of file diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 6fb918c60e..a776cd974b 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -284,7 +284,6 @@ ActiveRecord::Schema.define do create_table :organizations, :force => true do |t| t.string :name - t.string :type end create_table :owners, :primary_key => :owner_id ,:force => true do |t| @@ -390,7 +389,7 @@ ActiveRecord::Schema.define do create_table :sponsors, :force => true do |t| t.integer :club_id t.integer :sponsorable_id - t.string :sponsorable_type + t.string :sponsorable_type end create_table :subscribers, :force => true, :id => false do |t| -- cgit v1.2.3 From a2875bec9a31702a385d2f34e66843ddbe4e9db2 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 11 May 2009 22:23:47 -0400 Subject: Use DependencyModule for included hooks in ActiveRecord --- activerecord/lib/active_record/aggregations.rb | 4 +-- .../lib/active_record/association_preload.rb | 4 +-- activerecord/lib/active_record/associations.rb | 6 ++-- .../lib/active_record/attribute_methods.rb | 22 ++++++++----- .../lib/active_record/autosave_association.rb | 13 ++++---- activerecord/lib/active_record/batches.rb | 4 +-- activerecord/lib/active_record/calculations.rb | 5 ++- activerecord/lib/active_record/callbacks.rb | 12 ++++--- activerecord/lib/active_record/dirty.rb | 21 ++++++------ activerecord/lib/active_record/fixtures.rb | 38 ++++++++++------------ .../lib/active_record/locking/optimistic.rb | 16 ++++----- activerecord/lib/active_record/named_scope.rb | 9 +++-- .../lib/active_record/nested_attributes.rb | 9 ++--- activerecord/lib/active_record/observer.rb | 4 +-- activerecord/lib/active_record/reflection.rb | 4 +-- .../active_record/serializers/json_serializer.rb | 7 ++-- activerecord/lib/active_record/timestamp.rb | 12 ++++--- activerecord/lib/active_record/transactions.rb | 12 +++---- activerecord/lib/active_record/validations.rb | 15 ++++----- .../associations/eager_load_nested_include_test.rb | 13 ++++---- activerecord/test/cases/repair_helper.rb | 6 +--- 21 files changed, 111 insertions(+), 125 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 1eefebb3b3..359e70f5ed 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -1,8 +1,6 @@ module ActiveRecord module Aggregations # :nodoc: - def self.included(base) - base.extend(ClassMethods) - end + extend ActiveSupport::DependencyModule def clear_aggregation_cache #:nodoc: self.class.reflect_on_all_aggregations.to_a.each do |assoc| diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 967fff4d6f..5df76bb183 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -1,9 +1,7 @@ module ActiveRecord # See ActiveRecord::AssociationPreload::ClassMethods for documentation. module AssociationPreload #:nodoc: - def self.included(base) - base.extend(ClassMethods) - end + extend ActiveSupport::DependencyModule # Implements the details of eager loading of ActiveRecord associations. # Application developers should not use this module directly. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 781a0290e8..e2dd36158f 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -77,6 +77,8 @@ module ActiveRecord # See ActiveRecord::Associations::ClassMethods for documentation. module Associations # :nodoc: + extend ActiveSupport::DependencyModule + # These classes will be loaded when associations are created. # So there is no need to eager load them. autoload :AssociationCollection, 'active_record/associations/association_collection' @@ -89,10 +91,6 @@ module ActiveRecord autoload :HasOneAssociation, 'active_record/associations/has_one_association' autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association' - def self.included(base) - base.extend(ClassMethods) - end - # Clears out the association cache def clear_association_cache #:nodoc: self.class.reflect_on_all_associations.to_a.each do |assoc| diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 3ffc48941c..8d68d77eac 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -1,17 +1,21 @@ module ActiveRecord module AttributeMethods #:nodoc: + extend ActiveSupport::DependencyModule + DEFAULT_SUFFIXES = %w(= ? _before_type_cast) ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date] - def self.included(base) - base.extend ClassMethods - base.attribute_method_suffix(*DEFAULT_SUFFIXES) - base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false - base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT - base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false - base.time_zone_aware_attributes = false - base.class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false - base.skip_time_zone_conversion_for_attributes = [] + included do + attribute_method_suffix(*DEFAULT_SUFFIXES) + + cattr_accessor :attribute_types_cached_by_default, :instance_writer => false + self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT + + cattr_accessor :time_zone_aware_attributes, :instance_writer => false + self.time_zone_aware_attributes = false + + class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false + self.skip_time_zone_conversion_for_attributes = [] end # Declare and check for suffixed attribute methods. diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 9717ca3d8b..4ab2818282 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -125,16 +125,15 @@ module ActiveRecord # post.author.name = '' # post.save(false) # => true module AutosaveAssociation + extend ActiveSupport::DependencyModule + ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many } - def self.included(base) - base.class_eval do - base.extend(ClassMethods) - alias_method_chain :reload, :autosave_associations + included do + alias_method_chain :reload, :autosave_associations - ASSOCIATION_TYPES.each do |type| - base.send("valid_keys_for_#{type}_association") << :autosave - end + ASSOCIATION_TYPES.each do |type| + send("valid_keys_for_#{type}_association") << :autosave end end diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb index 5a6cecd4ad..4836601297 100644 --- a/activerecord/lib/active_record/batches.rb +++ b/activerecord/lib/active_record/batches.rb @@ -1,8 +1,6 @@ module ActiveRecord module Batches # :nodoc: - def self.included(base) - base.extend(ClassMethods) - end + extend ActiveSupport::DependencyModule # When processing large numbers of records, it's often a good idea to do # so in batches to prevent memory ballooning. diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index f077818d3b..7afa7c49bd 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -1,9 +1,8 @@ module ActiveRecord module Calculations #:nodoc: + extend ActiveSupport::DependencyModule + CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from] - def self.included(base) - base.extend(ClassMethods) - end module ClassMethods # Count operates using three different approaches. diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index e375037b5b..a77fdb1c13 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -211,21 +211,23 @@ module ActiveRecord # needs to be aware of it because an ordinary +save+ will raise such exception # instead of quietly returning +false+. module Callbacks + extend ActiveSupport::DependencyModule + CALLBACKS = %w( after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation after_validation before_validation_on_create after_validation_on_create before_validation_on_update after_validation_on_update before_destroy after_destroy ) - def self.included(base) #:nodoc: - base.extend Observable + included do + extend Observable [:create_or_update, :valid?, :create, :update, :destroy].each do |method| - base.send :alias_method_chain, method, :callbacks + alias_method_chain method, :callbacks end - base.send :include, ActiveSupport::Callbacks - base.define_callbacks *CALLBACKS + include ActiveSupport::Callbacks + define_callbacks *CALLBACKS end # Is called when the object was instantiated by one of the finders, like Base.find. diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index 4a2510aa63..fac6ca40d3 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -34,20 +34,21 @@ module ActiveRecord # person.name << 'by' # person.name_change # => ['uncle bob', 'uncle bobby'] module Dirty + extend ActiveSupport::DependencyModule + DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was'] - def self.included(base) - base.attribute_method_suffix *DIRTY_SUFFIXES - base.alias_method_chain :write_attribute, :dirty - base.alias_method_chain :save, :dirty - base.alias_method_chain :save!, :dirty - base.alias_method_chain :update, :dirty - base.alias_method_chain :reload, :dirty + included do + attribute_method_suffix *DIRTY_SUFFIXES - base.superclass_delegating_accessor :partial_updates - base.partial_updates = true + alias_method_chain :write_attribute, :dirty + alias_method_chain :save, :dirty + alias_method_chain :save!, :dirty + alias_method_chain :update, :dirty + alias_method_chain :reload, :dirty - base.send(:extend, ClassMethods) + superclass_delegating_accessor :partial_updates + self.partial_updates = true end # Do any attributes have unsaved changes? diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index c6501113bf..91b4b4e182 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -805,27 +805,25 @@ end module ActiveRecord module TestFixtures - def self.included(base) - base.class_eval do - setup :setup_fixtures - teardown :teardown_fixtures - - superclass_delegating_accessor :fixture_path - superclass_delegating_accessor :fixture_table_names - superclass_delegating_accessor :fixture_class_names - superclass_delegating_accessor :use_transactional_fixtures - superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances - superclass_delegating_accessor :pre_loaded_fixtures - - self.fixture_table_names = [] - self.use_transactional_fixtures = false - self.use_instantiated_fixtures = true - self.pre_loaded_fixtures = false - - self.fixture_class_names = {} - end + extend ActiveSupport::DependencyModule + + included do + setup :setup_fixtures + teardown :teardown_fixtures + + superclass_delegating_accessor :fixture_path + superclass_delegating_accessor :fixture_table_names + superclass_delegating_accessor :fixture_class_names + superclass_delegating_accessor :use_transactional_fixtures + superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances + superclass_delegating_accessor :pre_loaded_fixtures + + self.fixture_table_names = [] + self.use_transactional_fixtures = false + self.use_instantiated_fixtures = true + self.pre_loaded_fixtures = false - base.extend ClassMethods + self.fixture_class_names = {} end module ClassMethods diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 7fa7e267d8..cf4f8864c6 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -42,17 +42,17 @@ module ActiveRecord # To override the name of the lock_version column, invoke the set_locking_column method. # This method uses the same syntax as set_table_name module Optimistic - def self.included(base) #:nodoc: - base.extend ClassMethods + extend ActiveSupport::DependencyModule - base.cattr_accessor :lock_optimistically, :instance_writer => false - base.lock_optimistically = true + included do + cattr_accessor :lock_optimistically, :instance_writer => false + self.lock_optimistically = true - base.alias_method_chain :update, :lock - base.alias_method_chain :destroy, :lock - base.alias_method_chain :attributes_from_column_definition, :lock + alias_method_chain :update, :lock + alias_method_chain :destroy, :lock + alias_method_chain :attributes_from_column_definition, :lock - class << base + class << self alias_method :locking_column=, :set_locking_column end end diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 3df7089096..32bb36c07c 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -1,5 +1,7 @@ module ActiveRecord module NamedScope + extend ActiveSupport::DependencyModule + # All subclasses of ActiveRecord::Base have one named scope: # * scoped - which allows for the creation of anonymous \scopes, on the fly: Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions) # @@ -7,11 +9,8 @@ module ActiveRecord # intermediate values (scopes) around as first-class objects is convenient. # # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. - def self.included(base) - base.class_eval do - extend ClassMethods - named_scope :scoped, lambda { |scope| scope } - end + included do + named_scope :scoped, lambda { |scope| scope } end module ClassMethods diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index dfad2901c5..1ea2f53fd8 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -1,9 +1,10 @@ module ActiveRecord module NestedAttributes #:nodoc: - def self.included(base) - base.extend(ClassMethods) - base.class_inheritable_accessor :reject_new_nested_attributes_procs, :instance_writer => false - base.reject_new_nested_attributes_procs = {} + extend ActiveSupport::DependencyModule + + included do + class_inheritable_accessor :reject_new_nested_attributes_procs, :instance_writer => false + self.reject_new_nested_attributes_procs = {} end # == Nested Attributes diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index b35e407cc1..1ca76c7b2f 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -3,9 +3,7 @@ require 'set' module ActiveRecord module Observing # :nodoc: - def self.included(base) - base.extend ClassMethods - end + extend ActiveSupport::DependencyModule module ClassMethods # Activates the observers assigned. Examples: diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index ec0175497d..3747ba449d 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1,8 +1,6 @@ module ActiveRecord module Reflection # :nodoc: - def self.included(base) - base.extend(ClassMethods) - end + extend ActiveSupport::DependencyModule # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations. # This information can, for example, be used in a form builder that took an Active Record object and created input diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb index 48df15d2c0..d376fd5e1b 100644 --- a/activerecord/lib/active_record/serializers/json_serializer.rb +++ b/activerecord/lib/active_record/serializers/json_serializer.rb @@ -2,9 +2,10 @@ require 'active_support/json' module ActiveRecord #:nodoc: module Serialization - def self.included(base) - base.cattr_accessor :include_root_in_json, :instance_writer => false - base.extend ClassMethods + extend ActiveSupport::DependencyModule + + included do + cattr_accessor :include_root_in_json, :instance_writer => false end # Returns a JSON string representing the model. Some configuration is diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index d9e1ef351f..3734e170af 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -8,12 +8,14 @@ module ActiveRecord # Timestamps are in the local timezone by default but you can use UTC by setting # ActiveRecord::Base.default_timezone = :utc module Timestamp - def self.included(base) #:nodoc: - base.alias_method_chain :create, :timestamps - base.alias_method_chain :update, :timestamps + extend ActiveSupport::DependencyModule - base.class_inheritable_accessor :record_timestamps, :instance_writer => false - base.record_timestamps = true + included do + alias_method_chain :create, :timestamps + alias_method_chain :update, :timestamps + + class_inheritable_accessor :record_timestamps, :instance_writer => false + self.record_timestamps = true end # Saves the record with the updated_at/on attributes set to the current time. diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index b059eb7f6f..471a81dfb5 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -3,16 +3,14 @@ require 'thread' module ActiveRecord # See ActiveRecord::Transactions::ClassMethods for documentation. module Transactions + extend ActiveSupport::DependencyModule + class TransactionError < ActiveRecordError # :nodoc: end - def self.included(base) - base.extend(ClassMethods) - - base.class_eval do - [:destroy, :save, :save!].each do |method| - alias_method_chain method, :transactions - end + included do + [:destroy, :save, :save!].each do |method| + alias_method_chain method, :transactions end end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index e6b61e0b35..9907a3c9b7 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -301,17 +301,16 @@ module ActiveRecord # # An Errors object is automatically created for every Active Record. module Validations + extend ActiveSupport::DependencyModule + VALIDATIONS = %w( validate validate_on_create validate_on_update ) - def self.included(base) # :nodoc: - base.extend ClassMethods - base.class_eval do - alias_method_chain :save, :validation - alias_method_chain :save!, :validation - end + included do + alias_method_chain :save, :validation + alias_method_chain :save!, :validation - base.send :include, ActiveSupport::Callbacks - base.define_callbacks *VALIDATIONS + include ActiveSupport::Callbacks + define_callbacks *VALIDATIONS end # Active Record classes can implement validations in several ways. The highest level, easiest to read, 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 677226ec89..5f824f9c74 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -6,13 +6,12 @@ require 'models/category' require 'models/categorization' 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 + extend ActiveSupport::DependencyModule + + included do + after_create :remember + protected + def remember; self.class.remembered << self; end end module ClassMethods diff --git a/activerecord/test/cases/repair_helper.rb b/activerecord/test/cases/repair_helper.rb index 0155668811..686bfee46d 100644 --- a/activerecord/test/cases/repair_helper.rb +++ b/activerecord/test/cases/repair_helper.rb @@ -1,11 +1,7 @@ module ActiveRecord module Testing module RepairHelper - def self.included(base) - base.class_eval do - extend ClassMethods - end - end + extend ActiveSupport::DependencyModule module Toolbox def self.record_validations(*model_classes) -- cgit v1.2.3 From a7ccc7c79fa69cea3b7d19bcaa4df3c2932238d8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 12 May 2009 17:58:37 -0700 Subject: Remove support for deprecated validation message interpolation format --- activerecord/lib/active_record.rb | 1 - .../i18n_interpolation_deprecation.rb | 26 ----------------- activerecord/test/cases/validations_i18n_test.rb | 34 +++++----------------- 3 files changed, 8 insertions(+), 53 deletions(-) delete mode 100644 activerecord/lib/active_record/i18n_interpolation_deprecation.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 06d6c87090..c9e9a84ce7 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -75,5 +75,4 @@ module ActiveRecord end end -require 'active_record/i18n_interpolation_deprecation' I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml' diff --git a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb deleted file mode 100644 index cd634e1b8d..0000000000 --- a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb +++ /dev/null @@ -1,26 +0,0 @@ -# Deprecates the use of the former message interpolation syntax in activerecord -# as in "must have %d characters". The new syntax uses explicit variable names -# as in "{{value}} must have {{count}} characters". - -require 'i18n/backend/simple' -module I18n - module Backend - class Simple - DEPRECATED_INTERPOLATORS = { '%d' => '{{count}}', '%s' => '{{value}}' } - - protected - def interpolate_with_deprecated_syntax(locale, string, values = {}) - return string unless string.is_a?(String) - - string = string.gsub(/%d|%s/) do |s| - instead = DEPRECATED_INTERPOLATORS[s] - ActiveSupport::Deprecation.warn "using #{s} in messages is deprecated; use #{instead} instead." - instead - end - - interpolate_without_deprecated_syntax(locale, string, values) - end - alias_method_chain :interpolate, :deprecated_syntax - end - end -end \ No newline at end of file diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 66982346e9..20d1369a64 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -38,35 +38,17 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase end end - def test_default_error_messages_is_deprecated - assert_deprecated('ActiveRecord::Errors.default_error_messages') do - ActiveRecord::Errors.default_error_messages + def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated + assert_not_deprecated do + default = "%s interpolation syntax was deprecated" + assert_equal default, I18n.t(:does_not_exist, :default => default, :value => 'this') end end - def test_percent_s_interpolation_syntax_in_error_messages_still_works - ActiveSupport::Deprecation.silence do - result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this' - assert_equal result, "this interpolation syntax is deprecated" - end - end - - def test_percent_s_interpolation_syntax_in_error_messages_is_deprecated - assert_deprecated('using %s in messages') do - I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this' - end - end - - def test_percent_d_interpolation_syntax_in_error_messages_still_works - ActiveSupport::Deprecation.silence do - result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2 - assert_equal result, "2 interpolation syntaxes are deprecated" - end - end - - def test_percent_d_interpolation_syntax_in_error_messages_is_deprecated - assert_deprecated('using %d in messages') do - I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2 + def test_percent_d_interpolation_syntax_in_error_messages_was_deprecated + assert_not_deprecated do + default = "%d interpolation syntaxes are deprecated" + assert_equal default, I18n.t(:does_not_exist, :default => default, :count => 2) end end -- cgit v1.2.3 From fa5da8ad54d68ea0484825845eb6f6a8e8bca361 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 13 May 2009 01:06:53 -0700 Subject: isolated_test task --- activerecord/Rakefile | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 5905c67e29..892d52f30d 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -33,20 +33,28 @@ task :test => defined?(JRUBY_VERSION) ? %w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) : %w(test_mysql test_sqlite3 test_postgresql) -for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ) +%w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter| Rake::TestTask.new("test_#{adapter}") { |t| - if adapter =~ /jdbc/ - t.libs << "test" << "test/connections/jdbc_#{adapter}" - else - t.libs << "test" << "test/connections/native_#{adapter}" - end + connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}" adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/] + t.libs << "test" << connection_path t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort t.verbose = true } + task "isolated_test_#{adapter}" do + connection_path = "test/connections/#{adapter =~ /jdbc/ ? 'jdbc' : 'native'}_#{adapter}" + adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/] + puts [adapter, adapter_short, connection_path].inspect + ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) + Dir["test/cases/**/*_test{,_#{adapter_short}}.rb"].all? do |file| + system(ruby, "-Ilib:test:#{connection_path}", file) + end or raise "Failures" + end + namespace adapter do task :test => "test_#{adapter}" + task :isolated_test => "isolated_test_#{adapter}" end end -- cgit v1.2.3 From e8550ee0329586b32de425e905c7af7e65bc78a8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 13 May 2009 01:10:37 -0700 Subject: Cherry-pick core extensions --- activerecord/Rakefile | 3 +++ activerecord/lib/active_record.rb | 1 - activerecord/lib/active_record/associations.rb | 2 ++ activerecord/lib/active_record/attribute_methods.rb | 2 ++ activerecord/lib/active_record/base.rb | 21 +++++++++++++++------ .../connection_adapters/abstract/connection_pool.rb | 1 + .../connection_adapters/abstract/quoting.rb | 2 ++ .../connection_adapters/abstract_adapter.rb | 2 ++ .../connection_adapters/mysql_adapter.rb | 1 + .../connection_adapters/postgresql_adapter.rb | 1 + .../connection_adapters/sqlite3_adapter.rb | 6 +++--- .../connection_adapters/sqlite_adapter.rb | 1 + activerecord/lib/active_record/dirty.rb | 4 +++- activerecord/lib/active_record/fixtures.rb | 1 + activerecord/lib/active_record/migration.rb | 10 +++++----- activerecord/lib/active_record/named_scope.rb | 3 +++ activerecord/lib/active_record/nested_attributes.rb | 3 +++ activerecord/lib/active_record/schema_dumper.rb | 4 ++-- activerecord/lib/active_record/serialization.rb | 17 +++++++++-------- .../lib/active_record/serializers/xml_serializer.rb | 5 ++++- activerecord/lib/active_record/validations.rb | 5 ++--- activerecord/test/cases/aggregations_test.rb | 1 + .../associations/eager_load_nested_include_test.rb | 1 + .../has_and_belongs_to_many_associations_test.rb | 1 + activerecord/test/cases/base_test.rb | 1 + activerecord/test/cases/finder_test.rb | 5 +++-- activerecord/test/cases/helper.rb | 5 +++++ activerecord/test/cases/named_scope_test.rb | 3 ++- activerecord/test/cases/nested_attributes_test.rb | 1 + activerecord/test/models/company_in_module.rb | 2 ++ 30 files changed, 82 insertions(+), 33 deletions(-) (limited to 'activerecord') diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 892d52f30d..0b3f50d17e 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -32,6 +32,9 @@ desc 'Run mysql, sqlite, and postgresql tests' task :test => defined?(JRUBY_VERSION) ? %w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) : %w(test_mysql test_sqlite3 test_postgresql) +task :isolated_test => defined?(JRUBY_VERSION) ? + %w(isolated_test_jdbcmysql isolated_test_jdbcsqlite3 isolated_test_jdbcpostgresql) : + %w(isolated_test_mysql isolated_test_sqlite3 isolated_test_postgresql) %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter| Rake::TestTask.new("test_#{adapter}") { |t| diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index c9e9a84ce7..2d98239052 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -24,7 +24,6 @@ activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" $:.unshift(activesupport_path) if File.directory?(activesupport_path) require 'active_support' -require 'active_support/core/all' module ActiveRecord # TODO: Review explicit loads to see if they will automatically be handled by the initilizer. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index e2dd36158f..c5e4df4950 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/delegation' + module ActiveRecord class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc: def initialize(reflection) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 8d68d77eac..55d9a4d15d 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/enumerable' + module ActiveRecord module AttributeMethods #:nodoc: extend ActiveSupport::DependencyModule diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 97c36a675d..b9ba727a3f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1,6 +1,15 @@ require 'yaml' require 'set' require 'active_support/dependencies' +require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/class/delegating_attributes' +require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/array/extract_options' +require 'active_support/core_ext/hash/deep_merge' +require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/string/behavior' +require 'active_support/core/time' module ActiveRecord #:nodoc: # Generic Active Record exception class. @@ -1888,7 +1897,7 @@ module ActiveRecord #:nodoc: else find(:#{finder}, options.merge(finder_options)) end - #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang} + #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect { |pair| pair.join(\' = \') }.join(\', \')}")' if bang} end }, __FILE__, __LINE__ send(method_id, *arguments) @@ -2610,11 +2619,11 @@ module ActiveRecord #:nodoc: # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either # instance will affect the other. def becomes(klass) - returning klass.new do |became| - became.instance_variable_set("@attributes", @attributes) - became.instance_variable_set("@attributes_cache", @attributes_cache) - became.instance_variable_set("@new_record", new_record?) - end + became = klass.new + became.instance_variable_set("@attributes", @attributes) + became.instance_variable_set("@attributes_cache", @attributes_cache) + became.instance_variable_set("@new_record", new_record?) + became end # Updates a single attribute and saves the record without going through the normal validation procedure. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index e8e736bf38..500dafdc2e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -1,5 +1,6 @@ require 'monitor' require 'set' +require 'active_support/core_ext/module/synchronization' module ActiveRecord # Raised when a connection could not be obtained within the connection diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 3a7bf35248..720fba29e9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/big_decimal/conversions' + module ActiveRecord module ConnectionAdapters # :nodoc: module Quoting diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index a8cd9f033b..91b111ab55 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -12,6 +12,8 @@ require 'active_record/connection_adapters/abstract/connection_pool' require 'active_record/connection_adapters/abstract/connection_specification' require 'active_record/connection_adapters/abstract/query_cache' +require 'active_support/core_ext/benchmark' + module ActiveRecord module ConnectionAdapters # :nodoc: # ActiveRecord supports multiple database systems. AbstractAdapter and diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 9300df28ee..d5536e4d67 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -1,4 +1,5 @@ require 'active_record/connection_adapters/abstract_adapter' +require 'active_support/core_ext/kernel/requires' require 'set' module MysqlCompat #:nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 4961793866..002696d2c4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -1,4 +1,5 @@ require 'active_record/connection_adapters/abstract_adapter' +require 'active_support/core_ext/kernel/requires' begin require_library_or_gem 'pg' diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 75420f69aa..5eef692d05 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -25,9 +25,9 @@ module ActiveRecord module ConnectionAdapters #:nodoc: class SQLite3Adapter < SQLiteAdapter # :nodoc: def table_structure(table_name) - returning structure = @connection.table_info(quote_table_name(table_name)) do - raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? - end + structure = @connection.table_info(quote_table_name(table_name)) + raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty? + structure end end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 05334a830a..c9d0c9574f 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -1,4 +1,5 @@ require 'active_record/connection_adapters/abstract_adapter' +require 'active_support/core_ext/kernel/requires' module ActiveRecord class Base diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index fac6ca40d3..ac84f6b209 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -168,7 +168,9 @@ module ActiveRecord module ClassMethods def self.extended(base) - base.metaclass.alias_method_chain(:alias_attribute, :dirty) + class << base + alias_method_chain :alias_attribute, :dirty + end end def alias_attribute_with_dirty(new_name, old_name) diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 91b4b4e182..e30fcf9a4f 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -3,6 +3,7 @@ require 'yaml' require 'csv' require 'active_support/dependencies' require 'active_support/test_case' +require 'active_support/core_ext/logger' if RUBY_VERSION < '1.9' module YAML #:nodoc: diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 657acd6dc0..a7be3539d5 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -511,11 +511,11 @@ module ActiveRecord raise DuplicateMigrationNameError.new(name.camelize) end - klasses << returning(MigrationProxy.new) do |migration| - migration.name = name.camelize - migration.version = version - migration.filename = file - end + migration = MigrationProxy.new + migration.name = name.camelize + migration.version = version + migration.filename = file + klasses << migration end migrations = migrations.sort_by(&:version) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 32bb36c07c..07f98dc743 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/array' +require 'active_support/core_ext/hash/except' + module ActiveRecord module NamedScope extend ActiveSupport::DependencyModule diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 1ea2f53fd8..c532d3dfa3 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/hash/except' +require 'active_support/core_ext/object/try' + module ActiveRecord module NestedAttributes #:nodoc: extend ActiveSupport::DependencyModule diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 557a554966..de530a3456 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -1,5 +1,5 @@ require 'stringio' -require 'bigdecimal' +require 'active_support/core_ext/big_decimal' module ActiveRecord # This class is used to dump the database schema for some connection to some @@ -176,4 +176,4 @@ HEADER end end end -end \ No newline at end of file +end diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index 78f66c3a73..7959f2b510 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -1,5 +1,3 @@ -require 'active_support/json' - module ActiveRecord #:nodoc: module Serialization class Serializer #:nodoc: @@ -73,16 +71,19 @@ module ActiveRecord #:nodoc: end def serializable_record - returning(serializable_record = {}) do - serializable_names.each { |name| serializable_record[name] = @record.send(name) } - add_includes do |association, records, opts| + record = {} + serializable_names.each { |name| record[name] = @record.send(name) } + + add_includes do |association, records, opts| + record[association] = if records.is_a?(Enumerable) - serializable_record[association] = records.collect { |r| self.class.new(r, opts).serializable_record } + records.collect { |r| self.class.new(r, opts).serializable_record } else - serializable_record[association] = self.class.new(records, opts).serializable_record + self.class.new(records, opts).serializable_record end - end end + + record end def serialize diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index fa75874603..4eaf9531e2 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/hash/conversions' + module ActiveRecord #:nodoc: module Serialization # Builds an XML document to represent the model. Some configuration is @@ -165,8 +167,9 @@ module ActiveRecord #:nodoc: class XmlSerializer < ActiveRecord::Serialization::Serializer #:nodoc: def builder @builder ||= begin + require 'builder' unless defined? ::Builder options[:indent] ||= 2 - builder = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) + builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) unless options[:skip_instruct] builder.instruct! diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 9907a3c9b7..b6e848fa79 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1,5 +1,3 @@ -require 'builder' - module ActiveRecord # Raised by save! and create! when the record is invalid. Use the # +record+ method to retrieve the record which did not validate. @@ -247,9 +245,10 @@ module ActiveRecord # # Address can't be blank # # def to_xml(options={}) + require 'builder' unless defined? ::Builder options[:root] ||= "errors" options[:indent] ||= 2 - options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) + options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) options[:builder].instruct! unless options.delete(:skip_instruct) options[:builder].errors do |e| diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb index 4e0e1c7f15..8b6ec04018 100644 --- a/activerecord/test/cases/aggregations_test.rb +++ b/activerecord/test/cases/aggregations_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/customer' +require 'active_support/core_ext/exception' class AggregationsTest < ActiveRecord::TestCase fixtures :customers 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 5f824f9c74..cb7fe9698b 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -4,6 +4,7 @@ require 'models/author' require 'models/comment' require 'models/category' require 'models/categorization' +require 'active_support/core_ext/array/random_access' module Remembered extend ActiveSupport::DependencyModule 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 5e8b2cadfc..8dc95806b9 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 @@ -24,6 +24,7 @@ require 'models/club' require 'models/member' require 'models/membership' require 'models/sponsor' +require 'active_support/core_ext/string/conversions' class ProjectWithAfterCreateHook < ActiveRecord::Base set_table_name 'projects' diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 7ca2807f7e..59aa6953e3 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -18,6 +18,7 @@ require 'models/minimalistic' require 'models/warehouse_thing' require 'models/parrot' require 'rexml/document' +require 'active_support/core_ext/exception' class Category < ActiveRecord::Base; end class Categorization < ActiveRecord::Base; end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 28eb311618..ad4588db69 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -485,8 +485,9 @@ class FinderTest < ActiveRecord::TestCase assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', []) end - def test_bind_string - assert_equal ActiveRecord::Base.connection.quote(''), bind('?', '') + def test_bind_empty_string + quoted_empty = ActiveRecord::Base.connection.quote('') + assert_equal quoted_empty, bind('?', '') end def test_bind_chars diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 1ec52ac24d..05e92433cd 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -15,6 +15,11 @@ require 'connection' require 'cases/repair_helper' +begin + require 'ruby-debug' +rescue LoadError +end + # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index ae6a54a5bd..7dcea6d42e 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -1,4 +1,5 @@ require "cases/helper" +require 'active_support/core_ext/array/random_access' require 'models/post' require 'models/topic' require 'models/comment' @@ -265,7 +266,7 @@ class NamedScopeTest < ActiveRecord::TestCase end def test_rand_should_select_a_random_object_from_proxy - assert Topic.approved.rand.is_a?(Topic) + assert_kind_of Topic, Topic.approved.rand end def test_should_use_where_in_query_for_named_scope diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index f1741ed54d..f31275163d 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -4,6 +4,7 @@ require "models/ship" require "models/bird" require "models/parrot" require "models/treasure" +require 'active_support/hash_with_indifferent_access' module AssertRaiseWithMessage def assert_raise_with_message(expected_exception, expected_message) diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 7f02403d5a..3c34efbe16 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/misc' + module MyApplication module Business class Company < ActiveRecord::Base -- cgit v1.2.3 From 4f291fa528e5faad03def69ae7ac98224ab859db Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 13 May 2009 11:55:19 -0700 Subject: Simple examples for require profiling --- activerecord/examples/simple.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 activerecord/examples/simple.rb (limited to 'activerecord') diff --git a/activerecord/examples/simple.rb b/activerecord/examples/simple.rb new file mode 100644 index 0000000000..c12f746992 --- /dev/null +++ b/activerecord/examples/simple.rb @@ -0,0 +1,14 @@ +$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" +require 'active_record' + +class Person < ActiveRecord::Base + establish_connection :adapter => 'sqlite3', :database => 'foobar.db' + connection.create_table table_name, :force => true do |t| + t.string :name + end +end + +bob = Person.create!(:name => 'bob') +puts Person.all.inspect +bob.destroy +puts Person.all.inspect -- cgit v1.2.3 From afcbdfc15f919a470e4cfca97fb0084eebd2ab1f Mon Sep 17 00:00:00 2001 From: Peter Marklund Date: Thu, 14 May 2009 09:30:16 +0200 Subject: Changed ActiveRecord::Base#exists? to invoke find_initial so that it is compatible with, and doesn't lose, :include scopes (references to eager loaded tables) Signed-off-by: Michael Koziarski [#2543 state:committed] --- activerecord/lib/active_record/base.rb | 11 +++-------- activerecord/test/cases/finder_test.rb | 6 ++++++ 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b9ba727a3f..54ec0e841f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -697,14 +697,9 @@ module ActiveRecord #:nodoc: # Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists? def exists?(id_or_conditions = {}) - connection.select_all( - construct_finder_sql( - :select => "#{quoted_table_name}.#{primary_key}", - :conditions => expand_id_conditions(id_or_conditions), - :limit => 1 - ), - "#{name} Exists" - ).size > 0 + find_initial( + :select => "#{quoted_table_name}.#{primary_key}", + :conditions => expand_id_conditions(id_or_conditions)) ? true : false end # Creates an object (or multiple objects) and saves it to the database, if validations pass. diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index ad4588db69..d0d7094e30 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -119,6 +119,12 @@ class FinderTest < ActiveRecord::TestCase Address.new(existing_address.street + "1", existing_address.city, existing_address.country)) end + def test_exists_with_scoped_include + Developer.with_scope(:find => { :include => :projects, :order => "projects.name" }) do + assert Developer.exists? + end + end + def test_find_by_array_of_one_id assert_kind_of(Array, Topic.find([ 1 ])) assert_equal(1, Topic.find([ 1 ]).length) -- cgit v1.2.3