From ec0928076529e8f0b5a4ad58c95cfa1fe6ed5b60 Mon Sep 17 00:00:00 2001 From: Ivan Antropov Date: Sun, 10 Nov 2013 12:28:54 +0700 Subject: Fix insertion of records for hmt association with scope, fix #3548 --- activerecord/CHANGELOG.md | 6 ++++++ .../associations/has_many_through_association.rb | 6 +++++- activerecord/lib/active_record/relation.rb | 4 ++-- .../associations/has_many_through_associations_test.rb | 14 +++++++++++++- activerecord/test/cases/relation_test.rb | 4 ++++ activerecord/test/models/club.rb | 2 ++ 6 files changed, 32 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 0bef057836..89fcc129ca 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Fix insertion of records via has_many_through association with scope + + Fixes #3548 + + *Ivan Antropov* + * Checks to see if the record contains the foreign key to set the inverse automatically. *Edo Balvers* 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 31b8d27892..53268372eb 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -84,12 +84,16 @@ module ActiveRecord @through_records[record.object_id] ||= begin ensure_mutable - through_record = through_association.build + through_record = through_association.build through_scope_attributes through_record.send("#{source_reflection.name}=", record) through_record end end + def through_scope_attributes + scope.where_values_hash(through_association.reflection.name.to_s) + end + def save_through_record(record) build_through_record(record).save! ensure diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6e0669a77f..5e38ed632d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -527,9 +527,9 @@ module ActiveRecord # # User.where(name: 'Oscar').where_values_hash # # => {name: "Oscar"} - def where_values_hash + def where_values_hash(relation_table_name = table_name) equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node| - node.left.relation.name == table_name + node.left.relation.name == relation_table_name } binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }] diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 47592f312e..8017ed169c 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1095,7 +1095,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal [posts(:thinking)], person.reload.first_posts end - def test_has_many_through_with_includes_in_through_association_scope + test "has many through with includes in through association scope" do assert_not_empty posts(:welcome).author_address_extra_with_address end + + test "insert records via has_many_through association with scope" do + club = Club.create! + member = Member.create! + Membership.create!(club: club, member: member) + + club.favourites << member + assert_equal [member], club.favourites + + club.reload + assert_equal [member], club.favourites + end end diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 70d113fb39..e83aafc842 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -16,6 +16,10 @@ module ActiveRecord def self.connection Post.connection end + + def self.table_name + 'fake_table' + end end def test_construction diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb index 566e0873f1..902a047f62 100644 --- a/activerecord/test/models/club.rb +++ b/activerecord/test/models/club.rb @@ -6,6 +6,8 @@ class Club < ActiveRecord::Base has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member" belongs_to :category + has_many :favourites, -> { where(memberships: { favourite: true}) }, through: :memberships, source: :member + private def private_method -- cgit v1.2.3 From 933063188870347b59b35d4f96df21864d0f8f0b Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sun, 25 Aug 2013 11:22:36 +0200 Subject: Auto-generate stable fixture UUIDs on PostgreSQL. Fixes: #11524 --- activerecord/CHANGELOG.md | 10 +++++++ activerecord/lib/active_record/fixtures.rb | 33 ++++++++++++++++------ .../test/cases/adapters/postgresql/uuid_test.rb | 8 +----- activerecord/test/cases/fixtures_test.rb | 9 ++++++ activerecord/test/cases/helper.rb | 9 ++++++ activerecord/test/cases/schema_dumper_test.rb | 2 +- activerecord/test/fixtures/uuid_children.yml | 3 ++ activerecord/test/fixtures/uuid_parents.yml | 2 ++ activerecord/test/models/uuid_child.rb | 3 ++ activerecord/test/models/uuid_parent.rb | 3 ++ activerecord/test/schema/schema.rb | 9 ++++++ 11 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 activerecord/test/fixtures/uuid_children.yml create mode 100644 activerecord/test/fixtures/uuid_parents.yml create mode 100644 activerecord/test/models/uuid_child.rb create mode 100644 activerecord/test/models/uuid_parent.rb (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index add5334f39..9f2a4570cd 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Auto-generate stable fixture UUIDs on PostgreSQL. + + Fixes: #11524 + + *Roderick van Domburg* + * `change_table` now uses the current adapter's `update_table_definition` method to retrieve a specific table definition. This ensures that `change_table` and `create_table` will use @@ -752,6 +758,10 @@ *thedarkone* +* Test that PostgreSQL adapter includes `usec` when quoting `DateTime` objects + + *Ben Cherry* + * Re-use `order` argument pre-processing for `reorder`. *Paul Nikitochkin* diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index a7a54483bc..36e134ea17 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -2,6 +2,7 @@ require 'erb' require 'yaml' require 'zlib' require 'active_support/dependencies' +require 'active_support/core_ext/securerandom' require 'active_record/fixture_set/file' require 'active_record/errors' @@ -541,9 +542,13 @@ module ActiveRecord end # Returns a consistent, platform-independent identifier for +label+. - # Identifiers are positive integers less than 2^32. - def self.identify(label) - Zlib.crc32(label.to_s) % MAX_ID + # Integer identifiers are values less than 2^32. UUIDs are RFC 4122 version 5 SHA-1 hashes. + def self.identify(label, column_type = :integer) + if column_type == :uuid + SecureRandom.uuid_v5(SecureRandom::UUID_OID_NAMESPACE, label.to_s) + else + Zlib.crc32(label.to_s) % MAX_ID + end end # Superclass for the evaluation contexts used by ERB fixtures. @@ -634,7 +639,7 @@ module ActiveRecord # generate a primary key if necessary if has_primary_key_column? && !row.include?(primary_key_name) - row[primary_key_name] = ActiveRecord::FixtureSet.identify(label) + row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type) end # If STI is used, find the correct subclass for association reflection @@ -657,7 +662,8 @@ module ActiveRecord row[association.foreign_type] = $1 end - row[fk_name] = ActiveRecord::FixtureSet.identify(value) + fk_type = association.send(:active_record).columns_hash[association.foreign_key].type + row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type) end when :has_many if association.options[:through] @@ -684,6 +690,10 @@ module ActiveRecord def name @association.name end + + def primary_key_type + @association.klass.column_types[@association.klass.primary_key].type + end end class HasManyThroughProxy < ReflectionProxy # :nodoc: @@ -701,17 +711,22 @@ module ActiveRecord @primary_key_name ||= model_class && model_class.primary_key end + def primary_key_type + @primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type + end + def add_join_records(rows, row, association) # This is the case when the join table has no fixtures file if (targets = row.delete(association.name.to_s)) - table_name = association.join_table - lhs_key = association.lhs_key - rhs_key = association.rhs_key + table_name = association.join_table + column_type = association.primary_key_type + lhs_key = association.lhs_key + rhs_key = association.rhs_key targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/) rows[table_name].concat targets.map { |target| { lhs_key => row[primary_key_name], - rhs_key => ActiveRecord::FixtureSet.identify(target) } + rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) } } end end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 3f5d981444..9157e5e1ac 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -11,13 +11,7 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection - - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end - - @connection.reconnect! + enable_uuid_ossp!(@connection) @connection.transaction do @connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index f3a4887a85..1bf977cf8f 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -673,6 +673,12 @@ end class FoxyFixturesTest < ActiveRecord::TestCase fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers, :"admin/accounts", :"admin/users" + if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' + require 'models/uuid_parent' + require 'models/uuid_child' + fixtures :uuid_parents, :uuid_children + end + def test_identifies_strings assert_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("foo")) assert_not_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("FOO")) @@ -685,6 +691,9 @@ class FoxyFixturesTest < ActiveRecord::TestCase def test_identifies_consistently assert_equal 207281424, ActiveRecord::FixtureSet.identify(:ruby) assert_equal 1066363776, ActiveRecord::FixtureSet.identify(:sapphire_2) + + assert_equal 'f92b6bda-0d0d-5fe1-9124-502b18badded', ActiveRecord::FixtureSet.identify(:daddy, :uuid) + assert_equal 'b4b10018-ad47-595d-b42f-d8bdaa6d01bf', ActiveRecord::FixtureSet.identify(:sonny, :uuid) end TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on) diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 3758224b0c..7a7ed9740b 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -106,6 +106,15 @@ def verify_default_timezone_config end end +def enable_uuid_ossp!(connection) + return false unless connection.supports_extensions? + return true if connection.extension_enabled?('uuid-ossp') + + connection.enable_extension 'uuid-ossp' + connection.commit_db_transaction + connection.reconnect! +end + unless ENV['FIXTURE_DEBUG'] module ActiveRecord::TestFixtures::ClassMethods def try_to_load_dependency_with_silence(*args) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 741827446d..0adc18ced1 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -63,7 +63,7 @@ class SchemaDumperTest < ActiveRecord::TestCase next if column_set.empty? lengths = column_set.map do |column| - if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/) + if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid)\s+"/) match[0].length end end diff --git a/activerecord/test/fixtures/uuid_children.yml b/activerecord/test/fixtures/uuid_children.yml new file mode 100644 index 0000000000..a7b15016e2 --- /dev/null +++ b/activerecord/test/fixtures/uuid_children.yml @@ -0,0 +1,3 @@ +sonny: + uuid_parent: daddy + name: Sonny diff --git a/activerecord/test/fixtures/uuid_parents.yml b/activerecord/test/fixtures/uuid_parents.yml new file mode 100644 index 0000000000..0b40225c5c --- /dev/null +++ b/activerecord/test/fixtures/uuid_parents.yml @@ -0,0 +1,2 @@ +daddy: + name: Daddy diff --git a/activerecord/test/models/uuid_child.rb b/activerecord/test/models/uuid_child.rb new file mode 100644 index 0000000000..a3d0962ad6 --- /dev/null +++ b/activerecord/test/models/uuid_child.rb @@ -0,0 +1,3 @@ +class UuidChild < ActiveRecord::Base + belongs_to :uuid_parent +end diff --git a/activerecord/test/models/uuid_parent.rb b/activerecord/test/models/uuid_parent.rb new file mode 100644 index 0000000000..5634f22d0c --- /dev/null +++ b/activerecord/test/models/uuid_parent.rb @@ -0,0 +1,3 @@ +class UuidParent < ActiveRecord::Base + has_many :uuid_children +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index ddfc1ac0d6..0fb2c6269f 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -17,6 +17,15 @@ ActiveRecord::Schema.define do ActiveRecord::Base.connection.create_table(*args, &block) ActiveRecord::Base.connection.execute "SET GENERATOR #{args.first}_seq TO 10000" end + when "PostgreSQL" + enable_uuid_ossp!(ActiveRecord::Base.connection) + create_table :uuid_parents, id: :uuid, force: true do |t| + t.string :name + end + create_table :uuid_children, id: :uuid, force: true do |t| + t.string :name + t.uuid :uuid_parent_id + end end -- cgit v1.2.3 From 1f31488499111fdfce79d8dc1cc8fb008f7cdb25 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Wed, 9 Apr 2014 21:23:43 -0300 Subject: Make the reflections cache work with strings as its keys. --- activerecord/lib/active_record/reflection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 5465a7bfd7..9e4c70dabf 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -22,7 +22,7 @@ module ActiveRecord end def self.add_reflection(ar, name, reflection) - ar.reflections = ar.reflections.merge(name => reflection) + ar.reflections = ar.reflections.merge(name.to_s => reflection) end def self.add_aggregate_reflection(ar, name, reflection) @@ -72,7 +72,7 @@ module ActiveRecord # Invoice.reflect_on_association(:line_items).macro # returns :has_many # def reflect_on_association(association) - reflections[association] + reflections[association.to_s] end # Returns an array of AssociationReflection objects for all associations which have :autosave enabled. -- cgit v1.2.3 From 4c6ba7432dc5288708817d5b03e7ed13458ca58d Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Wed, 9 Apr 2014 21:25:39 -0300 Subject: No need to call `to_sym` on reflection name, since the cache now works with strings with string keys. Related #14668. --- activerecord/lib/active_record/reflection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 9e4c70dabf..eddc0cf51c 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -617,11 +617,11 @@ module ActiveRecord # # => [:tag, :tags] # def source_reflection_names - (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq + (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n }.uniq end def source_reflection_name # :nodoc: - return @source_reflection_name.to_sym if @source_reflection_name + return @source_reflection_name if @source_reflection_name names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq names = names.find_all { |n| -- cgit v1.2.3 From e7630b62a604a0bc11a56283140f11a80e4cc1b0 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Wed, 9 Apr 2014 21:34:48 -0300 Subject: Make the aggregate_reflections cache work with strings as its keys. --- activerecord/lib/active_record/reflection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index eddc0cf51c..1ce477f38b 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -26,7 +26,7 @@ module ActiveRecord end def self.add_aggregate_reflection(ar, name, reflection) - ar.aggregate_reflections = ar.aggregate_reflections.merge(name => reflection) + ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection) end # \Reflection enables to interrogate Active Record classes and objects @@ -48,7 +48,7 @@ module ActiveRecord # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection # def reflect_on_aggregation(aggregation) - aggregate_reflections[aggregation] + aggregate_reflections[aggregation.to_s] end # Returns an array of AssociationReflection objects for all the -- cgit v1.2.3 From 213ef567ae2ab92f1e1145cd385da0c5b9534422 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Wed, 9 Apr 2014 21:56:40 -0300 Subject: Make sure the reflection test is passing a String to the reflection cache. --- activerecord/test/cases/reflection_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index ad77472333..fed199f6e9 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -192,7 +192,7 @@ class ReflectionTest < ActiveRecord::TestCase end def test_reflection_should_not_raise_error_when_compared_to_other_object - assert_nothing_raised { Firm.reflections[:clients] == Object.new } + assert_nothing_raised { Firm.reflections['clients'] == Object.new } end def test_has_many_through_reflection -- cgit v1.2.3 From cf0e44c771319bcd8ec0038f8502fe0baa5fa738 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 9 Apr 2014 23:30:47 -0300 Subject: Remove extra collect call --- activerecord/lib/active_record/reflection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 1ce477f38b..22ee2238ce 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -617,7 +617,7 @@ module ActiveRecord # # => [:tag, :tags] # def source_reflection_names - (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n }.uniq + (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).uniq end def source_reflection_name # :nodoc: -- cgit v1.2.3 From 9a8af0c15089fbdc18fa75115586ab6febcd4d06 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 9 Apr 2014 23:31:10 -0300 Subject: Only call uniq on the conditional that actually needs it --- activerecord/lib/active_record/reflection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 22ee2238ce..1724ea95b0 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -617,7 +617,7 @@ module ActiveRecord # # => [:tag, :tags] # def source_reflection_names - (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).uniq + options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq end def source_reflection_name # :nodoc: -- cgit v1.2.3 From afd4d8205e6a3264c30a29e4a2de0f1e71ef0717 Mon Sep 17 00:00:00 2001 From: Simon Woker Date: Thu, 10 Apr 2014 16:58:39 +0000 Subject: Fix error for aggregate methods with select, see issue #13648 --- activerecord/CHANGELOG.md | 7 +++++++ activerecord/lib/active_record/relation.rb | 5 ++--- activerecord/test/cases/relations_test.rb | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index d39e808f5b..a23981a22b 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -264,4 +264,11 @@ *Yves Senn* +* Fixed error for aggregate methods (empty?, any?, count) with select() + which created invalid SQL + + Fixes #13648 + + *Simon Woker* + Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 4d37ac6e2b..7cdae176a1 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -238,7 +238,7 @@ module ActiveRecord # Returns size of the records. def size - loaded? ? @records.length : count + loaded? ? @records.length : count(:all) end # Returns true if there are no records. @@ -248,8 +248,7 @@ module ActiveRecord if limit_value == 0 true else - # FIXME: This count is not compatible with #select('authors.*') or other select narrows - c = count + c = count(:all) c.respond_to?(:zero?) ? c.zero? : c.empty? end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 2aa6d643a5..da932b7663 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -824,6 +824,16 @@ class RelationTest < ActiveRecord::TestCase assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all } end + def test_select_with_aggregates + posts = Post.select(:title, :body) + + assert_equal 11, posts.count(:all) + assert_equal 11, posts.size + assert posts.any? + assert posts.many? + assert ! posts.empty? + end + def test_select_takes_a_variable_list_of_args david = developers(:david) -- cgit v1.2.3 From 7edb204598c3a2382a870ecd7018fc1a3a804138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 10 Apr 2014 15:43:51 -0300 Subject: No need to call send --- activerecord/lib/active_record/fixtures.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 60ece6bd13..47d32fae05 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -661,7 +661,7 @@ module ActiveRecord row[association.foreign_type] = $1 end - fk_type = association.send(:active_record).columns_hash[association.foreign_key].type + fk_type = association.active_record.columns_hash[association.foreign_key].type row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type) end when :has_many -- cgit v1.2.3 From 1938c9646b7edcae88817a9b3b0e5d8b897ecc77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 10 Apr 2014 15:58:08 -0300 Subject: Remove warning of unused variable --- activerecord/test/cases/adapters/postgresql/bytea_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb index 84fa199f17..e3478856c8 100644 --- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb @@ -72,7 +72,7 @@ class PostgresqlByteaTest < ActiveRecord::TestCase def test_via_to_sql data = "'\u001F\\" - record = ByteaDataType.create(payload: data) + ByteaDataType.create(payload: data) sql = ByteaDataType.where(payload: data).select(:payload).to_sql result = @connection.query(sql) assert_equal([[data]], result) -- cgit v1.2.3 From 856ffbe7058065a34a708fd5b398c0553f9f1f97 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 11 Apr 2014 07:53:39 +0200 Subject: docs, make association `autosave: true` examples runnable. Closes #14700 [ci skip] The examples are written in a way you expect them to be executable. However one snippet assumed there to be two comments when only one was created above. The defined models did not extend `ActiveRecord::Base` The example used `comments.last.mark_for_destruction`. This does no longer load the whole collection but just the last record. It is then refetcht on subsequent calls to `last`. This breaks the example. --- activerecord/lib/active_record/autosave_association.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index e9622ca0c1..f149d8f127 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -35,7 +35,7 @@ module ActiveRecord # # === One-to-one Example # - # class Post + # class Post < ActiveRecord::Base # has_one :author, autosave: true # end # @@ -76,7 +76,7 @@ module ActiveRecord # # When :autosave is not declared new children are saved when their parent is saved: # - # class Post + # class Post < ActiveRecord::Base # has_many :comments # :autosave option is not declared # end # @@ -95,20 +95,23 @@ module ActiveRecord # When :autosave is true all children are saved, no matter whether they # are new records or not: # - # class Post + # class Post < ActiveRecord::Base # has_many :comments, autosave: true # end # # post = Post.create(title: 'ruby rocks') # post.comments.create(body: 'hello world') # post.comments[0].body = 'hi everyone' - # post.save # => saves both post and comment, with 'hi everyone' as body + # post.comments.build(body: "good morning.") + # post.title += "!" + # post.save # => saves both post and comments. # # Destroying one of the associated models as part of the parent's save action # is as simple as marking it for destruction: # - # post.comments.last.mark_for_destruction - # post.comments.last.marked_for_destruction? # => true + # post.comments # => [#, # + # post.comments[1].mark_for_destruction + # post.comments[1].marked_for_destruction? # => true # post.comments.length # => 2 # # Note that the model is _not_ yet removed from the database: -- cgit v1.2.3 From 0c9bbc632642895676684ed7e2978ab69b04de80 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 10 Apr 2014 20:25:40 +0200 Subject: PostgreSQL, adapter automatically reloads it's type map. Closes #14678. [Yves Senn & Matthew Draper] --- activerecord/CHANGELOG.md | 7 ++++++ .../postgresql/database_statements.rb | 5 +--- .../postgresql/schema_statements.rb | 4 +--- .../connection_adapters/postgresql_adapter.rb | 23 +++++++++++++++--- .../test/cases/adapters/postgresql/domain_test.rb | 3 --- .../test/cases/adapters/postgresql/enum_test.rb | 2 -- .../adapters/postgresql/postgresql_adapter_test.rb | 28 ++++++++++++++++++++++ .../test/cases/adapters/postgresql/range_test.rb | 1 - 8 files changed, 57 insertions(+), 16 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index abf6373030..c0264df8e1 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* PostgreSQL adapter automatically reloads it's type map when encountering + unknown OIDs. + + Fixes #14678. + + *Matthew Draper*, *Yves Senn* + * Fix insertion of records via `has_many :through` association with scope. Fixes #3548. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 51ee2829b2..a5fb048b1e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -142,10 +142,7 @@ module ActiveRecord fields.each_with_index do |fname, i| ftype = result.ftype i fmod = result.fmod i - types[fname] = type_map.fetch(ftype, fmod) { |oid, mod| - warn "unknown OID: #{fname}(#{oid}) (#{sql})" - OID::Identity.new - } + types[fname] = get_oid_type(ftype, fmod, fname) end ret = ActiveRecord::Result.new(fields, result.values, types) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 50a73aa666..1229b4851a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -182,9 +182,7 @@ module ActiveRecord def columns(table_name) # Limit, precision, and scale are all handled by the superclass. column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod| - oid = type_map.fetch(oid.to_i, fmod.to_i) { - OID::Identity.new - } + oid = get_oid_type(oid.to_i, fmod.to_i, column_name) PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f') end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 3510e4f3b0..207e1c4fed 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -559,6 +559,17 @@ module ActiveRecord @type_map end + def get_oid_type(oid, fmod, column_name) + if !type_map.key?(oid) + initialize_type_map(type_map, [oid]) + end + + type_map.fetch(oid, fmod) { + warn "unknown OID #{oid}: failed to recognize type of #{column_name}" + OID::Identity.new + } + end + def reload_type_map type_map.clear initialize_type_map(type_map) @@ -583,19 +594,25 @@ module ActiveRecord type_map end - def initialize_type_map(type_map) + def initialize_type_map(type_map, oids = nil) if supports_ranges? - result = execute(<<-SQL, 'SCHEMA') + query = <<-SQL SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype FROM pg_type as t LEFT JOIN pg_range as r ON oid = rngtypid SQL else - result = execute(<<-SQL, 'SCHEMA') + query = <<-SQL SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype FROM pg_type as t SQL end + + if oids + query += "WHERE t.oid::integer IN (%s)" % oids.join(", ") + end + + result = execute(query, 'SCHEMA') ranges, nodes = result.partition { |row| row['typtype'] == 'r' } enums, nodes = nodes.partition { |row| row['typtype'] == 'e' } domains, nodes = nodes.partition { |row| row['typtype'] == 'd' } diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb index 214e89dd7f..5286a847a4 100644 --- a/activerecord/test/cases/adapters/postgresql/domain_test.rb +++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb @@ -19,9 +19,6 @@ class PostgresqlDomainTest < ActiveRecord::TestCase t.column :price, :custom_money end end - - # reload type map after creating the enum type - @connection.send(:reload_type_map) end teardown do diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb index 73da5a74ab..4146b117f6 100644 --- a/activerecord/test/cases/adapters/postgresql/enum_test.rb +++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb @@ -21,8 +21,6 @@ class PostgresqlEnumTest < ActiveRecord::TestCase t.column :current_mood, :mood end end - # reload type map after creating the enum type - @connection.send(:reload_type_map) end teardown do diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index 49d8ec238d..d319bd0b80 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -1,11 +1,13 @@ # encoding: utf-8 require "cases/helper" require 'support/ddl_helper' +require 'support/connection_helper' module ActiveRecord module ConnectionAdapters class PostgreSQLAdapterTest < ActiveRecord::TestCase include DdlHelper + include ConnectionHelper def setup @connection = ActiveRecord::Base.connection @@ -357,6 +359,32 @@ module ActiveRecord end end + def test_reload_type_map_for_newly_defined_types + @connection.execute "CREATE TYPE feeling AS ENUM ('good', 'bad')" + result = @connection.select_all "SELECT 'good'::feeling" + assert_instance_of(PostgreSQLAdapter::OID::Enum, + result.column_types["feeling"]) + ensure + @connection.execute "DROP TYPE IF EXISTS feeling" + reset_connection + end + + def test_only_reload_type_map_once_for_every_unknown_type + silence_warnings do + assert_queries 2, ignore_none: true do + @connection.select_all "SELECT NULL::anyelement" + end + assert_queries 1, ignore_none: true do + @connection.select_all "SELECT NULL::anyelement" + end + assert_queries 2, ignore_none: true do + @connection.select_all "SELECT NULL::anyarray" + end + end + ensure + reset_connection + end + private def insert(ctx, data) binds = data.map { |name, value| diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index 57c7da2657..060b17d071 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -34,7 +34,6 @@ _SQL @connection.add_column 'postgresql_ranges', 'float_range', 'floatrange' end - @connection.send :reload_type_map PostgresqlRange.reset_column_information rescue ActiveRecord::StatementInvalid skip "do not test on PG without range" -- cgit v1.2.3 From 9f62344d9552f0a58e4aa43ca6f1a7fec273a04e Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 10 Apr 2014 20:39:21 +0200 Subject: PostgreSQL, warn once per connection per missing OID. Closes #14275. [Yves Senn & Matthew Draper] --- activerecord/CHANGELOG.md | 6 ++++++ .../active_record/connection_adapters/postgresql_adapter.rb | 4 ++-- .../test/cases/adapters/postgresql/postgresql_adapter_test.rb | 11 +++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c0264df8e1..00ea49fa9d 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* PostgreSQL adapter only warns once for every missing OID per connection. + + Fixes #14275. + + *Matthew Draper*, *Yves Senn* + * PostgreSQL adapter automatically reloads it's type map when encountering unknown OIDs. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 207e1c4fed..0485093123 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -565,8 +565,8 @@ module ActiveRecord end type_map.fetch(oid, fmod) { - warn "unknown OID #{oid}: failed to recognize type of #{column_name}" - OID::Identity.new + warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String." + type_map[oid] = OID::Identity.new } end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index d319bd0b80..b7791078db 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -385,6 +385,17 @@ module ActiveRecord reset_connection end + def test_only_warn_on_first_encounter_of_unknown_oid + warning = capture(:stderr) { + @connection.select_all "SELECT NULL::anyelement" + @connection.select_all "SELECT NULL::anyelement" + @connection.select_all "SELECT NULL::anyelement" + } + assert_match(/\Aunknown OID \d+: failed to recognize type of 'anyelement'. It will be treated as String.\n\z/, warning) + ensure + reset_connection + end + private def insert(ctx, data) binds = data.map { |name, value| -- cgit v1.2.3 From 70fffaccfc9761498cb50537d8b29c2e5d0585d1 Mon Sep 17 00:00:00 2001 From: Simon Woker Date: Fri, 11 Apr 2014 17:54:03 +0000 Subject: fix exception on polymorphic associations with predicates --- activerecord/lib/active_record/association_relation.rb | 8 ++++++++ activerecord/test/cases/associations/eager_test.rb | 8 ++++++++ 2 files changed, 16 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/association_relation.rb b/activerecord/lib/active_record/association_relation.rb index 20516bba0c..a17c36ccd9 100644 --- a/activerecord/lib/active_record/association_relation.rb +++ b/activerecord/lib/active_record/association_relation.rb @@ -8,7 +8,15 @@ module ActiveRecord def proxy_association @association end + + def size + @association.size + end + def empty? + @association.empty? + end + private def exec_queries diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 8c9797861c..3133f60bcf 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1197,7 +1197,15 @@ class EagerAssociationTest < ActiveRecord::TestCase author = Author.includes(:posts).references(:posts).reorder(:name).find_by('posts.title IS NOT NULL') assert_equal authors(:bob), author end + + test "preloading with a polymorphic association and using the existential predicate but also using a select" do + assert_equal authors(:david), authors(:david).essays.includes(:writer).first.writer + assert_nothing_raised do + authors(:david).essays.includes(:writer).select(:name).any? + end + end + test "preloading with a polymorphic association and using the existential predicate" do assert_equal authors(:david), authors(:david).essays.includes(:writer).first.writer -- cgit v1.2.3 From 8a08f5cb236f4ca5458c6bbf87dbde75bfc667be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 11 Apr 2014 15:46:04 -0300 Subject: :scissors: --- activerecord/lib/active_record/association_relation.rb | 4 ++-- activerecord/test/cases/associations/eager_test.rb | 4 ++-- activerecord/test/cases/relations_test.rb | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/association_relation.rb b/activerecord/lib/active_record/association_relation.rb index a17c36ccd9..ef9650d482 100644 --- a/activerecord/lib/active_record/association_relation.rb +++ b/activerecord/lib/active_record/association_relation.rb @@ -8,7 +8,7 @@ module ActiveRecord def proxy_association @association end - + def size @association.size end @@ -16,7 +16,7 @@ module ActiveRecord def empty? @association.empty? end - + private def exec_queries diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 3133f60bcf..7eaa5adc86 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1197,7 +1197,7 @@ class EagerAssociationTest < ActiveRecord::TestCase author = Author.includes(:posts).references(:posts).reorder(:name).find_by('posts.title IS NOT NULL') assert_equal authors(:bob), author end - + test "preloading with a polymorphic association and using the existential predicate but also using a select" do assert_equal authors(:david), authors(:david).essays.includes(:writer).first.writer @@ -1205,7 +1205,7 @@ class EagerAssociationTest < ActiveRecord::TestCase authors(:david).essays.includes(:writer).select(:name).any? end end - + test "preloading with a polymorphic association and using the existential predicate" do assert_equal authors(:david), authors(:david).essays.includes(:writer).first.writer diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index da932b7663..561f6451cd 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -826,14 +826,14 @@ class RelationTest < ActiveRecord::TestCase def test_select_with_aggregates posts = Post.select(:title, :body) - + assert_equal 11, posts.count(:all) assert_equal 11, posts.size assert posts.any? assert posts.many? - assert ! posts.empty? + assert ! posts.empty? end - + def test_select_takes_a_variable_list_of_args david = developers(:david) -- cgit v1.2.3 From bf98261da6eeff2676e29df3c74e4cd913078d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 11 Apr 2014 15:46:20 -0300 Subject: New CHANGELOG entries are in to top of file --- activerecord/CHANGELOG.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index b8956034d9..dead555cca 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fixed error for aggregate methods (`empty?`, `any?`, `count`) with `select` + which created invalid SQL. + + Fixes #13648. + + *Simon Woker* + * PostgreSQL adapter only warns once for every missing OID per connection. Fixes #14275. @@ -289,11 +296,4 @@ *Yves Senn* -* Fixed error for aggregate methods (empty?, any?, count) with select() - which created invalid SQL - - Fixes #13648 - - *Simon Woker* - Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activerecord/CHANGELOG.md) for previous changes. -- cgit v1.2.3 From 8d11fbf679e17c277ddf5bf52311f0e824ebae46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 11 Apr 2014 15:46:49 -0300 Subject: Use assert_not --- activerecord/test/cases/relations_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 561f6451cd..562cfe6796 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -831,7 +831,7 @@ class RelationTest < ActiveRecord::TestCase assert_equal 11, posts.size assert posts.any? assert posts.many? - assert ! posts.empty? + assert_not posts.empty? end def test_select_takes_a_variable_list_of_args -- cgit v1.2.3 From d6840f914a32bff4d73f23c3f5c64c5397f8b400 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Thu, 3 Apr 2014 22:07:03 -0300 Subject: The comparison between `Relation` and `CollectionProxy` should be consistent. Example: author.posts == Post.where(author_id: author.id) # => true Post.where(author_id: author.id) == author.posts # => true Fixes #13506 --- activerecord/CHANGELOG.md | 13 +++++++++++++ activerecord/lib/active_record/relation.rb | 2 ++ activerecord/test/cases/base_test.rb | 17 +++++++++++++++++ 3 files changed, 32 insertions(+) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index dead555cca..8bbd27bb58 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,16 @@ +* The comparison between `Relation` and `CollectionProxy` should be consistent. + + Example: + + author.posts == Post.where(author_id: author.id) + # => true + Post.where(author_id: author.id) == author.posts + # => true + + Fixes #13506. + + *Lauro Caetano* + * Fixed error for aggregate methods (`empty?`, `any?`, `count`) with `select` which created invalid SQL. diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index d1764a2bb2..4adc8a3862 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -569,6 +569,8 @@ module ActiveRecord # Compares two relations for equality. def ==(other) case other + when Associations::CollectionProxy + self == other.to_a when Relation other.to_sql == to_sql when Array diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 4969344763..d253b94b10 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -551,6 +551,23 @@ class BasicsTest < ActiveRecord::TestCase assert_equal one, two end + def test_equality_of_relation_and_collection_proxy + car = Car.create! + car.bulbs.build + car.save + + assert car.bulbs == Bulb.where(car_id: car.id), 'CollectionProxy should be comparable with Relation' + assert Bulb.where(car_id: car.id) == car.bulbs, 'Relation should be comparable with CollectionProxy' + end + + def test_equality_of_relation_and_array + car = Car.create! + car.bulbs.build + car.save + + assert Bulb.where(car_id: car.id) == car.bulbs.to_a, 'Relation should be comparable with Array' + end + def test_hashing assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ] end -- cgit v1.2.3 From e533855383ea187d8a157d49afc438f893c4b8ae Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 11 Apr 2014 16:22:01 -0700 Subject: please use Ruby, not ActiveSupport --- .../active_record/connection_adapters/abstract/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index da25e640c1..47f2ad9b10 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -381,7 +381,7 @@ module ActiveRecord end def binds_from_relation(relation, binds) - if relation.is_a?(Relation) && binds.blank? + if relation.is_a?(Relation) && binds.empty? relation, binds = relation.arel, relation.bind_values end [relation, binds] -- cgit v1.2.3 From 24052f925f5f52f74646610a843eb3b98845ac77 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Fri, 11 Apr 2014 19:51:38 -0300 Subject: Make the comparison between 'Relation' and 'AssociationRelation' consistent. --- activerecord/lib/active_record/association_relation.rb | 4 ++++ activerecord/lib/active_record/relation.rb | 2 +- activerecord/test/cases/base_test.rb | 9 +++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/association_relation.rb b/activerecord/lib/active_record/association_relation.rb index ef9650d482..45f1b07f69 100644 --- a/activerecord/lib/active_record/association_relation.rb +++ b/activerecord/lib/active_record/association_relation.rb @@ -17,6 +17,10 @@ module ActiveRecord @association.empty? end + def ==(other) + other == to_a + end + private def exec_queries diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 4adc8a3862..709edbee88 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -569,7 +569,7 @@ module ActiveRecord # Compares two relations for equality. def ==(other) case other - when Associations::CollectionProxy + when Associations::CollectionProxy, AssociationRelation self == other.to_a when Relation other.to_sql == to_sql diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index d253b94b10..b428605b9d 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -568,6 +568,15 @@ class BasicsTest < ActiveRecord::TestCase assert Bulb.where(car_id: car.id) == car.bulbs.to_a, 'Relation should be comparable with Array' end + def test_equality_of_relation_and_association_relation + car = Car.create! + car.bulbs.build + car.save + + assert_equal Bulb.where(car_id: car.id), car.bulbs.includes(:car), 'Relation should be comparable with AssociationRelation' + assert_equal car.bulbs.includes(:car), Bulb.where(car_id: car.id), 'AssociationRelation should be comparable with Relation' + end + def test_hashing assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ] end -- cgit v1.2.3 From 783982ab2df7a65e97d098ac1ab624436eb7c278 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Fri, 11 Apr 2014 20:09:30 -0300 Subject: Ensure that the comparison between 'CollectionProxy' and 'AssociationRelation' is consistent. --- activerecord/test/cases/base_test.rb | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index b428605b9d..2e5b8cffa6 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -577,6 +577,15 @@ class BasicsTest < ActiveRecord::TestCase assert_equal car.bulbs.includes(:car), Bulb.where(car_id: car.id), 'AssociationRelation should be comparable with Relation' end + def test_equality_of_collection_proxy_and_association_relation + car = Car.create! + car.bulbs.build + car.save + + assert_equal car.bulbs, car.bulbs.includes(:car), 'CollectionProxy should be comparable with AssociationRelation' + assert_equal car.bulbs.includes(:car), car.bulbs, 'AssociationRelation should be comparable with CollectionProxy' + end + def test_hashing assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ] end -- cgit v1.2.3 From 0405d5a7e95776f9adf5b8ff064300898d89b43a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 12 Apr 2014 09:42:02 -0700 Subject: remove branching logic from calls to find_nth --- .../lib/active_record/relation/finder_methods.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index c2b9dc08fe..6274ca15ca 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -131,7 +131,7 @@ module ActiveRecord if limit find_nth_with_limit(offset_value, limit) else - find_nth(:first, offset_value) + find_nth(:first, offset_index) end end @@ -181,7 +181,7 @@ module ActiveRecord # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4) # Person.where(["user_name = :u", { u: user_name }]).second def second - find_nth(:second, offset_value ? offset_value + 1 : 1) + find_nth(:second, offset_index + 1) end # Same as +second+ but raises ActiveRecord::RecordNotFound if no record @@ -197,7 +197,7 @@ module ActiveRecord # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5) # Person.where(["user_name = :u", { u: user_name }]).third def third - find_nth(:third, offset_value ? offset_value + 2 : 2) + find_nth(:third, offset_index + 2) end # Same as +third+ but raises ActiveRecord::RecordNotFound if no record @@ -213,7 +213,7 @@ module ActiveRecord # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6) # Person.where(["user_name = :u", { u: user_name }]).fourth def fourth - find_nth(:fourth, offset_value ? offset_value + 3 : 3) + find_nth(:fourth, offset_index + 3) end # Same as +fourth+ but raises ActiveRecord::RecordNotFound if no record @@ -229,7 +229,7 @@ module ActiveRecord # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7) # Person.where(["user_name = :u", { u: user_name }]).fifth def fifth - find_nth(:fifth, offset_value ? offset_value + 4 : 4) + find_nth(:fifth, offset_index + 4) end # Same as +fifth+ but raises ActiveRecord::RecordNotFound if no record @@ -245,7 +245,7 @@ module ActiveRecord # Person.offset(3).forty_two # returns the fifth object from OFFSET 3 (which is OFFSET 44) # Person.where(["user_name = :u", { u: user_name }]).forty_two def forty_two - find_nth(:forty_two, offset_value ? offset_value + 41 : 41) + find_nth(:forty_two, offset_index + 41) end # Same as +forty_two+ but raises ActiveRecord::RecordNotFound if no record @@ -334,6 +334,10 @@ module ActiveRecord private + def offset_index + offset_value || 0 + end + def find_with_associations join_dependency = construct_join_dependency -- cgit v1.2.3 From 8c2b79d0c8f3533f8045baf647317c4ff33b0f26 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 12 Apr 2014 09:45:39 -0700 Subject: only add the offset and index when we need to --- .../lib/active_record/relation/finder_methods.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 6274ca15ca..8daea2c13e 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -131,7 +131,7 @@ module ActiveRecord if limit find_nth_with_limit(offset_value, limit) else - find_nth(:first, offset_index) + find_nth(0, offset_index) end end @@ -181,7 +181,7 @@ module ActiveRecord # Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4) # Person.where(["user_name = :u", { u: user_name }]).second def second - find_nth(:second, offset_index + 1) + find_nth(1, offset_index) end # Same as +second+ but raises ActiveRecord::RecordNotFound if no record @@ -197,7 +197,7 @@ module ActiveRecord # Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5) # Person.where(["user_name = :u", { u: user_name }]).third def third - find_nth(:third, offset_index + 2) + find_nth(2, offset_index) end # Same as +third+ but raises ActiveRecord::RecordNotFound if no record @@ -213,7 +213,7 @@ module ActiveRecord # Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6) # Person.where(["user_name = :u", { u: user_name }]).fourth def fourth - find_nth(:fourth, offset_index + 3) + find_nth(3, offset_index) end # Same as +fourth+ but raises ActiveRecord::RecordNotFound if no record @@ -229,7 +229,7 @@ module ActiveRecord # Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7) # Person.where(["user_name = :u", { u: user_name }]).fifth def fifth - find_nth(:fifth, offset_index + 4) + find_nth(4, offset_index) end # Same as +fifth+ but raises ActiveRecord::RecordNotFound if no record @@ -245,7 +245,7 @@ module ActiveRecord # Person.offset(3).forty_two # returns the fifth object from OFFSET 3 (which is OFFSET 44) # Person.where(["user_name = :u", { u: user_name }]).forty_two def forty_two - find_nth(:forty_two, offset_index + 41) + find_nth(41, offset_index) end # Same as +forty_two+ but raises ActiveRecord::RecordNotFound if no record @@ -472,10 +472,11 @@ module ActiveRecord end end - def find_nth(ordinal, offset) + def find_nth(index, offset) if loaded? - @records.send(ordinal) + @records[index] else + offset += index @offsets[offset] ||= find_nth_with_limit(offset, 1).first end end -- cgit v1.2.3 From 711a882f106385e248135c84ea9a6a827f0bd1e1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 12 Apr 2014 10:14:12 -0700 Subject: don't bother with an offset if the offset is zero we're guaranteed to pass a numeric value for offset, so if it's zero, just don't add an offset to the query --- activerecord/lib/active_record/relation/finder_methods.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 8daea2c13e..7af4b29ebc 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -129,7 +129,7 @@ module ActiveRecord # def first(limit = nil) if limit - find_nth_with_limit(offset_value, limit) + find_nth_with_limit(offset_index, limit) else find_nth(0, offset_index) end @@ -482,11 +482,14 @@ module ActiveRecord end def find_nth_with_limit(offset, limit) - if order_values.empty? && primary_key - order(arel_table[primary_key].asc).limit(limit).offset(offset).to_a - else - limit(limit).offset(offset).to_a - end + relation = if order_values.empty? && primary_key + order(arel_table[primary_key].asc) + else + self + end + + relation = relation.offset(offset) unless offset.zero? + relation.limit(limit).to_a end def find_last -- cgit v1.2.3 From 5d5beccc8ce0e844dca3ebac6e0ec8dd3fcc1b91 Mon Sep 17 00:00:00 2001 From: Robin Dupret Date: Sun, 13 Apr 2014 11:13:19 +0200 Subject: Add a changelog entry for #14546 [ci skip] --- activerecord/CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index dead555cca..3893292cee 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,17 @@ +* Calling `delete_all` on an unloaded `CollectionProxy` no longer + generates a SQL statement containing each id of the collection: + + Before: + + DELETE FROM `model` WHERE `model`.`parent_id` = 1 + AND `model`.`id` IN (1, 2, 3...) + + After: + + DELETE FROM `model` WHERE `model`.`parent_id` = 1 + + *Eileen M. Uchitelle*, *Aaron Patterson* + * Fixed error for aggregate methods (`empty?`, `any?`, `count`) with `select` which created invalid SQL. -- cgit v1.2.3 From dd063f6ef436b5e6a594e70eeb50532a09ef7a57 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Sun, 13 Apr 2014 12:03:54 -0400 Subject: Write the failing test case for concurrent counter cache --- .../associations/belongs_to_associations_test.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 27f6fa575d..76a61d492a 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -502,6 +502,27 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 4, topic.replies.size end + def test_concurrent_counter_cache_double_destroy + topic = Topic.create :title => "Zoom-zoom-zoom" + + 5.times do + topic.replies.create(:title => "re: zoom", :content => "speedy quick!") + end + + assert_equal 5, topic.reload[:replies_count] + assert_equal 5, topic.replies.size + + reply = topic.replies.first + reply_clone = Reply.find(reply.id) + + reply.destroy + assert_equal 4, topic.reload[:replies_count] + + reply_clone.destroy + assert_equal 4, topic.reload[:replies_count] + assert_equal 4, topic.replies.size + end + def test_custom_counter_cache reply = Reply.create(:title => "re: zoom", :content => "speedy quick!") assert_equal 0, reply[:replies_count] -- cgit v1.2.3 From 655a3667aa99ae3f7e68024b3971b5783d6396bf Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Sun, 13 Apr 2014 12:24:59 -0400 Subject: Make counter cache decrementation on destroy idempotent --- .../associations/builder/belongs_to.rb | 9 +++++---- activerecord/lib/active_record/counter_cache.rb | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index 5ccaa55a32..11be92ae01 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -37,13 +37,14 @@ module ActiveRecord::Associations::Builder end end - def belongs_to_counter_cache_before_destroy(reflection) + def belongs_to_counter_cache_after_destroy(reflection) foreign_key = reflection.foreign_key.to_sym unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key record = send reflection.name - if record && !self.destroyed? + if record && self.actually_destroyed? cache_column = reflection.counter_cache_column record.class.decrement_counter(cache_column, record.id) + self.clear_destroy_state end end end @@ -77,8 +78,8 @@ module ActiveRecord::Associations::Builder record.belongs_to_counter_cache_after_create(reflection) } - model.before_destroy lambda { |record| - record.belongs_to_counter_cache_before_destroy(reflection) + model.after_destroy lambda { |record| + record.belongs_to_counter_cache_after_destroy(reflection) } model.after_update lambda { |record| diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index dcbdf75627..a5897edf03 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -118,5 +118,26 @@ module ActiveRecord update_counters(id, counter_name => -1) end end + + protected + + def actually_destroyed? + @_actually_destroyed + end + + def clear_destroy_state + @_actually_destroyed = nil + end + + private + + def destroy_row + affected_rows = super + + @_actually_destroyed = affected_rows > 0 + + affected_rows + end + end end -- cgit v1.2.3 From feb88bda5484c2af13daec768f47d46e9ab4879e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 13 Apr 2014 15:44:01 -0700 Subject: read_attribute is public, so we should just call it --- activerecord/lib/active_record/associations/has_many_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 3e4b7902c0..aac85a36c8 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -58,7 +58,7 @@ module ActiveRecord # the loaded flag is set to true as well. def count_records count = if has_cached_counter? - owner.send(:read_attribute, cached_counter_attribute_name) + owner.read_attribute cached_counter_attribute_name else scope.count end -- cgit v1.2.3 From af549a1ad6692d7e2c756750651f0e1b293f5185 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 13 Apr 2014 15:46:50 -0700 Subject: again, read_attribute is public, so just call it --- .../lib/active_record/associations/has_many_through_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index c2eff0d8ce..73baefb8e1 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -18,7 +18,7 @@ module ActiveRecord # SELECT query if you use #length. def size if has_cached_counter? - owner.send(:read_attribute, cached_counter_attribute_name) + owner.read_attribute cached_counter_attribute_name(reflection) elsif loaded? target.size else -- cgit v1.2.3 From a16f33045ae6fac1ba66980bb0a2b0b1e5847103 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Mon, 14 Apr 2014 00:04:08 -0300 Subject: Just call read_attribute, no need to use `send`. Follow up to af549a1ad6692d7e2c756750651f0e1b293f5185 --- .../test/cases/associations/belongs_to_associations_test.rb | 6 +++--- activerecord/test/cases/attribute_methods_test.rb | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 27f6fa575d..bcd2b6dfaa 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -233,13 +233,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase def test_belongs_to_counter debate = Topic.create("title" => "debate") - assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet" + assert_equal 0, debate.read_attribute("replies_count"), "No replies yet" trash = debate.replies.create("title" => "blah!", "content" => "world around!") - assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created" + assert_equal 1, Topic.find(debate.id).read_attribute("replies_count"), "First reply created" trash.destroy - assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted" + assert_equal 0, Topic.find(debate.id).read_attribute("replies_count"), "First reply deleted" end def test_belongs_to_counter_with_assigning_nil diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 952baaca36..38e93288e4 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -288,10 +288,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase def test_read_attribute topic = Topic.new topic.title = "Don't change the topic" - assert_equal "Don't change the topic", topic.send(:read_attribute, "title") + assert_equal "Don't change the topic", topic.read_attribute("title") assert_equal "Don't change the topic", topic["title"] - assert_equal "Don't change the topic", topic.send(:read_attribute, :title) + assert_equal "Don't change the topic", topic.read_attribute(:title) assert_equal "Don't change the topic", topic[:title] end @@ -358,10 +358,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase super(attr_name).upcase end - assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, "title") + assert_equal "STOP CHANGING THE TOPIC", topic.read_attribute("title") assert_equal "STOP CHANGING THE TOPIC", topic["title"] - assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, :title) + assert_equal "STOP CHANGING THE TOPIC", topic.read_attribute(:title) assert_equal "STOP CHANGING THE TOPIC", topic[:title] end -- cgit v1.2.3 From 2e000ab23d187e425a1be1ce8ce43a0d4c6f5be7 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 14 Apr 2014 14:47:28 +0200 Subject: docs, double meaning of `serialize` argument. Closes #14284. The second argument to serialize has double meaning: * `class_name` of the Type to serialize * `coder` to use then serializing. The new variable name and the docs better describe that behavior. --- .../attribute_methods/serialization.rb | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 67abbbc2a0..c3466153d6 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -30,7 +30,8 @@ module ActiveRecord # ==== Parameters # # * +attr_name+ - The field name that should be serialized. - # * +class_name+ - Optional, class name that the object type should be equal to. + # * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump` + # or a class name that the object type should be equal to. # # ==== Example # @@ -38,13 +39,23 @@ module ActiveRecord # class User < ActiveRecord::Base # serialize :preferences # end - def serialize(attr_name, class_name = Object) + # + # # Serialize preferences using JSON as coder. + # class User < ActiveRecord::Base + # serialize :preferences, JSON + # end + # + # # Serialize preferences as Hash using YAML coder. + # class User < ActiveRecord::Base + # serialize :preferences, Hash + # end + def serialize(attr_name, class_name_or_coder = Object) include Behavior - coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) } - class_name + coder = if [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) } + class_name_or_coder else - Coders::YAMLColumn.new(class_name) + Coders::YAMLColumn.new(class_name_or_coder) end # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy -- cgit v1.2.3