diff options
Diffstat (limited to 'activerecord')
17 files changed, 132 insertions, 48 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 03609a7a05..9f95b30122 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -4,6 +4,10 @@ *Aaron Wortham +* Add `binary` fixture helper method. + + *Atsushi Yoshida* + * When using `Relation#or`, extract the common conditions and put them before the OR condition. *Maxime Handfield Lapointe* diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 38e7ba39d3..ceedf150e3 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -55,13 +55,16 @@ module ActiveRecord pk_type = reflection.association_primary_key_type ids = Array(ids).reject(&:blank?) ids.map! { |i| pk_type.cast(i) } - records = klass.where(reflection.association_primary_key => ids).index_by do |r| - r.send(reflection.association_primary_key) + + primary_key = reflection.association_primary_key + records = klass.where(primary_key => ids).index_by do |r| + r.public_send(primary_key) end.values_at(*ids).compact + if records.size != ids.size - found_ids = records.map { |record| record.send(reflection.association_primary_key) } + found_ids = records.map { |record| record.public_send(primary_key) } not_found_ids = ids - found_ids - klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key, not_found_ids) + klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids) else replace(records) end diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index acd47629dd..ebc2baed34 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -70,7 +70,7 @@ module ActiveRecord end decorate_attribute_type(attr_name, :serialize) do |type| - if type_incompatible_with_serialize?(type) + if type_incompatible_with_serialize?(type, class_name_or_coder) raise ColumnNotSerializableError.new(attr_name, type) end @@ -80,12 +80,9 @@ module ActiveRecord private - def type_incompatible_with_serialize?(type) - type.is_a?(ActiveRecord::Type::Json) || - ( - defined?(ActiveRecord::ConnectionAdapters::PostgreSQL) && - type.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array) - ) + def type_incompatible_with_serialize?(type, class_name) + type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON || + type.respond_to?(:type_cast_array, true) && class_name == ::Array end end end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index ef302fc0a0..4940e122f4 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -1065,6 +1065,10 @@ class ActiveRecord::FixtureSet::RenderContext # :nodoc: def get_binding binding() end + + def binary(path) + %(!!binary "#{Base64.strict_encode64(File.read(path))}") + end end end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 7f1601805c..b2c62cd686 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -329,6 +329,10 @@ module ActiveRecord def join_pk(_) foreign_key end + + def primary_key(klass) + klass.primary_key || raise(UnknownPrimaryKey.new(klass)) + end end # Base class for AggregateReflection and AssociationReflection. Objects of @@ -697,10 +701,6 @@ module ActiveRecord def derive_join_table ModelSchema.derive_join_table_name active_record.table_name, klass.table_name end - - def primary_key(klass) - klass.primary_key || raise(UnknownPrimaryKey.new(klass)) - end end class HasManyReflection < AssociationReflection # :nodoc: @@ -715,6 +715,10 @@ module ActiveRecord Associations::HasManyAssociation end end + + def association_primary_key(klass = nil) + primary_key(klass || self.klass) + end end class HasOneReflection < AssociationReflection # :nodoc: @@ -1016,10 +1020,6 @@ module ActiveRecord end end - def primary_key(klass) - klass.primary_key || raise(UnknownPrimaryKey.new(klass)) - end - def inverse_name; delegate_reflection.send(:inverse_name); end def derive_class_name diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 141ad176ea..fa19c679cf 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -47,7 +47,12 @@ module ActiveRecord # handle from 10000 and beyond by setting the +:start+ and +:finish+ # option on each worker. # - # # Let's process from record 10_000 on. + # # In worker 1, let's process until 9999 records. + # Person.find_each(finish: 9_999) do |person| + # person.party_all_night! + # end + # + # # In worker 2, let's process from record 10_000 and onwards. # Person.find_each(start: 10_000) do |person| # person.party_all_night! # end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 2677fade18..baeb653c61 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -8,6 +8,10 @@ module ActiveRecord raise ArgumentError, "#{options[:conditions]} was passed as :conditions but is not callable. " \ "Pass a callable instead: `conditions: -> { where(approved: true) }`" end + unless Array(options[:scope]).all? { |scope| scope.respond_to?(:to_sym) } + raise ArgumentError, "#{options[:scope]} is not supported format for :scope option. " \ + "Pass a symbol or an array of symbols instead: `scope: :user_id`" + end super({ case_sensitive: true }.merge!(options)) @klass = options[:class] end diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index 08b17f37e2..0e9e86f425 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -47,7 +47,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase assert ratings_column.array? end - def test_not_compatible_with_serialize + def test_not_compatible_with_serialize_array new_klass = Class.new(PgArray) do serialize :tags, Array end @@ -56,6 +56,30 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase end end + class MyTags + def initialize(tags); @tags = tags end + def to_a; @tags end + def self.load(tags); new(tags) end + def self.dump(object); object.to_a end + end + + def test_array_with_serialized_attributes + new_klass = Class.new(PgArray) do + serialize :tags, MyTags + end + + new_klass.create!(tags: MyTags.new(["one", "two"])) + record = new_klass.first + + assert_instance_of MyTags, record.tags + assert_equal ["one", "two"], record.tags.to_a + + record.tags = MyTags.new(["three", "four"]) + record.save! + + assert_equal ["three", "four"], record.reload.tags.to_a + end + def test_default @connection.add_column "pg_arrays", "score", :integer, array: true, default: [4, 4, 2] PgArray.reset_column_information diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index cedb621b4f..383c7314e1 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -63,7 +63,7 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase assert_equal 2, subscriber.subscriptions.size end - assert_equal subscriber.subscriptions, Subscription.where(subscriber_id: "webster132") + assert_equal Subscription.where(subscriber_id: "webster132"), subscriber.subscriptions end def test_association_primary_key_on_new_record_should_fetch_with_query @@ -74,12 +74,23 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase assert_equal 1, author.essays.size end - assert_equal author.essays, Essay.where(writer_id: "David") + assert_equal Essay.where(writer_id: "David"), author.essays end def test_has_many_custom_primary_key david = authors(:david) - assert_equal david.essays, Essay.where(writer_id: "David") + assert_equal Essay.where(writer_id: "David"), david.essays + end + + def test_ids_on_unloaded_association_with_custom_primary_key + david = people(:david) + assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids + end + + def test_ids_on_loaded_association_with_custom_primary_key + david = people(:david) + david.essays.load + assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids end def test_has_many_assignment_with_custom_primary_key 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 f4e907ba90..968c0eb944 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -880,13 +880,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end end - def test_collection_singular_ids_setter_with_changed_primary_key - company = companies(:first_firm) - client = companies(:first_client) - company.clients_using_primary_key_ids = [client.name] - assert_equal [client], company.clients_using_primary_key - end - def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set company = companies(:rails_core) ids = [Developer.first.id, -9999] @@ -895,13 +888,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal(msg, e.message) end - def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set_with_changed_primary_key - company = companies(:first_firm) - ids = [Client.first.name, "unknown client"] - e = assert_raises(ActiveRecord::RecordNotFound) { company.clients_using_primary_key_ids = ids } - assert_match(/Couldn't find all Clients with 'name'/, e.message) - end - def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set author = authors(:david) ids = [categories(:general).name, "Unknown"] diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 9d3d5353ff..3abdcf3564 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -99,11 +99,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class - post = SubStiPost.create title: "SubStiPost", body: "SubStiPost body" - assert_instance_of SubStiPost, post + post = SubAbstractStiPost.create title: "SubAbstractStiPost", body: "SubAbstractStiPost body" + assert_instance_of SubAbstractStiPost, post tagging = tags(:misc).taggings.create(taggable: post) - assert_equal "SubStiPost", tagging.taggable_type + assert_equal "SubAbstractStiPost", tagging.taggable_type end def test_polymorphic_has_many_going_through_join_model_with_inheritance diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 6b014bcb3d..b0b63f5203 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -132,7 +132,7 @@ class FixturesTest < ActiveRecord::TestCase def test_no_args_record_returns_all_without_array all_binaries = binaries assert_kind_of(Array, all_binaries) - assert_equal 1, binaries.length + assert_equal 2, binaries.length end def test_nil_raises @@ -313,6 +313,7 @@ class FixturesTest < ActiveRecord::TestCase data.force_encoding("ASCII-8BIT") data.freeze assert_equal data, @flowers.data + assert_equal data, @binary_helper.data end def test_serialized_fixtures diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index a263106f6d..c931f7d21c 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -147,12 +147,16 @@ class InheritanceTest < ActiveRecord::TestCase # Concrete subclass of AR::Base. assert Post.descends_from_active_record? + # Concrete subclasses of a concrete class which has a type column. + assert !StiPost.descends_from_active_record? + assert !SubStiPost.descends_from_active_record? + # Abstract subclass of a concrete class which has a type column. # This is pathological, as you'll never have Sub < Abstract < Concrete. - assert !StiPost.descends_from_active_record? + assert !AbstractStiPost.descends_from_active_record? - # Concrete subclasses an abstract class which has a type column. - assert !SubStiPost.descends_from_active_record? + # Concrete subclass of an abstract class which has a type column. + assert !SubAbstractStiPost.descends_from_active_record? end def test_company_descends_from_active_record @@ -172,7 +176,8 @@ class InheritanceTest < ActiveRecord::TestCase assert_equal Post, Post.base_class assert_equal Post, SpecialPost.base_class assert_equal Post, StiPost.base_class - assert_equal SubStiPost, SubStiPost.base_class + assert_equal Post, SubStiPost.base_class + assert_equal SubAbstractStiPost, SubAbstractStiPost.base_class end def test_abstract_inheritance_base_class diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb index 952194c6dc..56ec8c8a82 100644 --- a/activerecord/test/cases/json_shared_test_cases.rb +++ b/activerecord/test/cases/json_shared_test_cases.rb @@ -216,7 +216,7 @@ module JSONSharedTestCases assert_equal true, json.payload end - def test_not_compatible_with_serialize_macro + def test_not_compatible_with_serialize_json new_klass = Class.new(klass) do serialize :payload, JSON end @@ -225,6 +225,30 @@ module JSONSharedTestCases end end + class MySettings + def initialize(hash); @hash = hash end + def to_hash; @hash end + def self.load(hash); new(hash) end + def self.dump(object); object.to_hash end + end + + def test_json_with_serialized_attributes + new_klass = Class.new(klass) do + serialize :settings, MySettings + end + + new_klass.create!(settings: MySettings.new("one" => "two")) + record = new_klass.first + + assert_instance_of MySettings, record.settings + assert_equal({ "one" => "two" }, record.settings.to_hash) + + record.settings = MySettings.new("three" => "four") + record.save! + + assert_equal({ "three" => "four" }, record.reload.settings.to_hash) + end + private def klass JsonDataType diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb index fad55916c7..a10567f066 100644 --- a/activerecord/test/cases/validations/uniqueness_validation_test.rb +++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb @@ -156,6 +156,13 @@ class UniquenessValidationTest < ActiveRecord::TestCase assert r3.valid?, "Saving r3" end + def test_validate_uniqueness_with_scope_invalid_syntax + error = assert_raises(ArgumentError) do + Reply.validates_uniqueness_of(:content, scope: { parent_id: false }) + end + assert_match(/Pass a symbol or an array of symbols instead/, error.to_s) + end + def test_validate_uniqueness_with_object_scope Reply.validates_uniqueness_of(:content, scope: :topic) diff --git a/activerecord/test/fixtures/binaries.yml b/activerecord/test/fixtures/binaries.yml index ec8f2facdc..53b7883369 100644 --- a/activerecord/test/fixtures/binaries.yml +++ b/activerecord/test/fixtures/binaries.yml @@ -131,3 +131,7 @@ flowers: SgCUASgCUASgCUASgAC74PbXOTvE5/En7jpSoLE8/wBn7uPJjKyj46T9D/NT pKsXyQzxNpdNP0/akB5484WkMKh4RfXG4UafNmH7b0UxWMrb7Nxg6rl9Z/Im w+vWq0iscQwxQroiUIvkKsRZQBKAJQBKAJQB/9k= + +binary_helper: + id: 2 + data: <%= binary(ASSETS_ROOT + "/flowers.jpg") %> diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 4b3576fce8..4c8e847354 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -184,14 +184,19 @@ end class SpecialPost < Post; end class StiPost < Post - self.abstract_class = true has_one :special_comment, class_name: "SpecialComment" end +class AbstractStiPost < Post + self.abstract_class = true +end + class SubStiPost < StiPost self.table_name = Post.table_name end +class SubAbstractStiPost < AbstractStiPost; end + class FirstPost < ActiveRecord::Base self.inheritance_column = :disabled self.table_name = "posts" |