diff options
Diffstat (limited to 'activerecord')
24 files changed, 261 insertions, 124 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ca072be5e1..e987a0e279 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,17 @@ +* Set polymorphic type column to NULL on `dependent: :nullify` strategy. + + On polymorphic associations both the foreign key and the foreign type columns will be set to NULL. + + *Laerti Papa* + +* Allow `ActionController::Params` as argument of `ActiveRecord::Base#exists?`. + + *Gannon McGibbon* + +* Add support for endless ranges introduces in Ruby 2.6. + + *Greg Navis* + * Deprecate passing `migrations_paths` to `connection.assume_migrated_upto_version`. *Ryuta Kamizono* diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index fb1df00dc8..7bdbd8ce69 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1293,7 +1293,8 @@ module ActiveRecord # # * <tt>:destroy</tt> causes all the associated objects to also be destroyed. # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed). - # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed. + # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified + # on polymorphic associations. Callbacks are not executed. # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records. # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects. # @@ -1436,7 +1437,8 @@ module ActiveRecord # # * <tt>:destroy</tt> causes the associated object to also be destroyed # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute) - # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed. + # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified + # on polymorphic associations. Callbacks are not executed. # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object # diff --git a/activerecord/lib/active_record/associations/foreign_association.rb b/activerecord/lib/active_record/associations/foreign_association.rb index 40010cde03..59af6f54c3 100644 --- a/activerecord/lib/active_record/associations/foreign_association.rb +++ b/activerecord/lib/active_record/associations/foreign_association.rb @@ -9,5 +9,12 @@ module ActiveRecord::Associations false end end + + def nullified_owner_attributes + Hash.new.tap do |attrs| + attrs[reflection.foreign_key] = nil + attrs[reflection.type] = nil if reflection.type.present? + 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 f6fdbcde54..eb22db838c 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -92,7 +92,7 @@ module ActiveRecord if method == :delete_all scope.delete_all else - scope.update_all(reflection.foreign_key => nil) + scope.update_all(nullified_owner_attributes) 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 390bfd8b08..99971286a3 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -33,7 +33,7 @@ module ActiveRecord target.destroy throw(:abort) unless target.destroyed? when :nullify - target.update_columns(reflection.foreign_key => nil) if target.persisted? + target.update_columns(nullified_owner_attributes) if target.persisted? end end end 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 2299fc0214..79d71bfb5d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -123,7 +123,7 @@ module ActiveRecord # +binds+ as the bind substitutes. +name+ is logged along with # the executed +sql+ statement. def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) - sql, binds = sql_for_insert(sql, pk, nil, sequence_name, binds) + sql, binds = sql_for_insert(sql, pk, sequence_name, binds) exec_query(sql, name, binds) end @@ -464,7 +464,7 @@ module ActiveRecord exec_query(sql, name, binds, prepare: true) end - def sql_for_insert(sql, pk, id_value, sequence_name, binds) + def sql_for_insert(sql, pk, sequence_name, binds) [sql, binds] end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 346d4b067a..d1ff32df3f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -6,6 +6,7 @@ require "active_record/connection_adapters/sql_type_metadata" require "active_record/connection_adapters/abstract/schema_dumper" require "active_record/connection_adapters/abstract/schema_creation" require "active_support/concurrency/load_interlock_aware_monitor" +require "active_support/deprecation" require "arel/collectors/bind" require "arel/collectors/composite" require "arel/collectors/sql_string" @@ -76,8 +77,11 @@ module ActiveRecord SIMPLE_INT = /\A\d+\z/ - attr_accessor :visitor, :pool, :prevent_writes - attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock + attr_writer :visitor + deprecate :visitor= + + attr_accessor :pool + attr_reader :schema_cache, :visitor, :owner, :logger, :lock, :prepared_statements, :prevent_writes alias :in_use? :owner set_callback :checkin, :after, :enable_lazy_transactions! @@ -117,6 +121,7 @@ module ActiveRecord @idle_since = Concurrent.monotonic_time @schema_cache = SchemaCache.new self @quoted_column_names, @quoted_table_names = {}, {} + @prevent_writes = false @visitor = arel_visitor @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new @@ -152,11 +157,10 @@ module ActiveRecord # even if you are on a database that can write. `while_preventing_writes` # will prevent writes to the database for the duration of the block. def while_preventing_writes - original = self.prevent_writes - self.prevent_writes = true + original, @prevent_writes = @prevent_writes, true yield ensure - self.prevent_writes = original + @prevent_writes = original end def migrations_paths # :nodoc: @@ -504,15 +508,17 @@ module ActiveRecord @connection end - def case_sensitive_comparison(table, attribute, column, value) # :nodoc: - table[attribute].eq(value) + def case_sensitive_comparison(attribute, value) # :nodoc: + attribute.eq(value) end - def case_insensitive_comparison(table, attribute, column, value) # :nodoc: + def case_insensitive_comparison(attribute, value) # :nodoc: + column = column_for_attribute(attribute) + if can_perform_case_insensitive_comparison_for?(column) - table[attribute].lower.eq(table.lower(value)) + attribute.lower.eq(attribute.relation.lower(value)) else - table[attribute].eq(value) + attribute.eq(value) end end @@ -659,6 +665,11 @@ module ActiveRecord raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}") end + def column_for_attribute(attribute) + table_name = attribute.relation.name + schema_cache.columns_hash(table_name)[attribute.name.to_s] + end + def collector if prepared_statements Arel::Collectors::Composite.new( diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index cccd6e2210..70d281b62b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -476,9 +476,11 @@ module ActiveRecord SQL end - def case_sensitive_comparison(table, attribute, column, value) # :nodoc: + def case_sensitive_comparison(attribute, value) # :nodoc: + column = column_for_attribute(attribute) + if column.collation && !column.case_sensitive? - table[attribute].eq(Arel::Nodes::Bin.new(value)) + attribute.eq(Arel::Nodes::Bin.new(value)) else super end 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 c70a4fa875..41633872e2 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -110,7 +110,7 @@ module ActiveRecord end alias :exec_update :exec_delete - def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc: + def sql_for_insert(sql, pk, sequence_name, binds) # :nodoc: if pk.nil? # Extract the table from the insert sql. Yuck. table_ref = extract_table_ref_from_insert_sql(sql) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index ba221a333b..a863227276 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -44,6 +44,11 @@ module ActiveRecord end def bind_attribute(name, value) # :nodoc: + if reflection = klass._reflect_on_association(name) + name = reflection.foreign_key + value = value.read_attribute(reflection.klass.primary_key) unless value.nil? + end + attr = arel_attribute(name) bind = predicate_builder.build_bind_attribute(attr.name, value) yield attr, bind diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index dc03b196f4..fd84f9c46b 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -312,6 +312,8 @@ module ActiveRecord return false if !conditions || limit_value == 0 + conditions = sanitize_forbidden_attributes(conditions) + if eager_loading? relation = apply_join_dependency(eager_loading: false) return relation.exists?(conditions) diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 5a1dbc8e53..111b6c9a64 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -56,33 +56,21 @@ module ActiveRecord end def build_relation(klass, attribute, value) - if reflection = klass._reflect_on_association(attribute) - attribute = reflection.foreign_key - value = value.attributes[reflection.klass.primary_key] unless value.nil? - end - - if value.nil? - return klass.unscoped.where!(attribute => value) - end - - # the attribute may be an aliased attribute - if klass.attribute_alias?(attribute) - attribute = klass.attribute_alias(attribute) + relation = klass.unscoped + comparison = relation.bind_attribute(attribute, value) do |attr, bind| + return relation.none! unless bind.boundable? + + if bind.nil? + attr.eq(bind) + elsif options[:case_sensitive] + klass.connection.case_sensitive_comparison(attr, bind) + else + # will use SQL LOWER function before comparison, unless it detects a case insensitive collation + klass.connection.case_insensitive_comparison(attr, bind) + end end - attribute_name = attribute.to_s - value = klass.predicate_builder.build_bind_attribute(attribute_name, value) - - table = klass.arel_table - column = klass.columns_hash[attribute_name] - - comparison = if !options[:case_sensitive] - # will use SQL LOWER function before comparison, unless it detects a case insensitive collation - klass.connection.case_insensitive_comparison(table, attribute, column, value) - else - klass.connection.case_sensitive_comparison(table, attribute, column, value) - end - klass.unscoped.where!(comparison) + relation.where!(comparison) end def scope_relation(record, relation) diff --git a/activerecord/lib/arel.rb b/activerecord/lib/arel.rb index dab785738e..7411b5c41b 100644 --- a/activerecord/lib/arel.rb +++ b/activerecord/lib/arel.rb @@ -13,7 +13,6 @@ require "arel/alias_predication" require "arel/order_predications" require "arel/table" require "arel/attributes" -require "arel/compatibility/wheres" require "arel/visitors" require "arel/collectors/sql_string" diff --git a/activerecord/lib/arel/compatibility/wheres.rb b/activerecord/lib/arel/compatibility/wheres.rb deleted file mode 100644 index c8a73f0dae..0000000000 --- a/activerecord/lib/arel/compatibility/wheres.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module Arel # :nodoc: all - module Compatibility # :nodoc: - class Wheres # :nodoc: - include Enumerable - - module Value # :nodoc: - attr_accessor :visitor - def value - visitor.accept self - end - - def name - super.to_sym - end - end - - def initialize(engine, collection) - @engine = engine - @collection = collection - end - - def each - to_sql = Visitors::ToSql.new @engine - - @collection.each { |c| - c.extend(Value) - c.visitor = to_sql - yield c - } - end - end - end -end diff --git a/activerecord/lib/arel/predications.rb b/activerecord/lib/arel/predications.rb index 28679ae892..2a62c53aa3 100644 --- a/activerecord/lib/arel/predications.rb +++ b/activerecord/lib/arel/predications.rb @@ -36,14 +36,14 @@ module Arel # :nodoc: all def between(other) if infinity?(other.begin) - if infinity?(other.end) + if other.end.nil? || infinity?(other.end) not_in([]) elsif other.exclude_end? lt(other.end) else lteq(other.end) end - elsif infinity?(other.end) + elsif other.end.nil? || infinity?(other.end) gteq(other.begin) elsif other.exclude_end? gteq(other.begin).and(lt(other.end)) @@ -82,14 +82,14 @@ Passing a range to `#in` is deprecated. Call `#between`, instead. def not_between(other) if infinity?(other.begin) - if infinity?(other.end) + if other.end.nil? || infinity?(other.end) self.in([]) elsif other.exclude_end? gteq(other.end) else gt(other.end) end - elsif infinity?(other.end) + elsif other.end.nil? || infinity?(other.end) lt(other.begin) else left = lt(other.begin) diff --git a/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb b/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb index 305e033642..79e9efcf06 100644 --- a/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb +++ b/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb @@ -7,22 +7,21 @@ class PostgresqlCaseInsensitiveTest < ActiveRecord::PostgreSQLTestCase def test_case_insensitiveness connection = ActiveRecord::Base.connection - table = Default.arel_table - column = Default.columns_hash["char1"] - comparison = connection.case_insensitive_comparison table, :char1, column, nil + attr = Default.arel_attribute(:char1) + comparison = connection.case_insensitive_comparison(attr, nil) assert_match(/lower/i, comparison.to_sql) - column = Default.columns_hash["char2"] - comparison = connection.case_insensitive_comparison table, :char2, column, nil + attr = Default.arel_attribute(:char2) + comparison = connection.case_insensitive_comparison(attr, nil) assert_match(/lower/i, comparison.to_sql) - column = Default.columns_hash["char3"] - comparison = connection.case_insensitive_comparison table, :char3, column, nil + attr = Default.arel_attribute(:char3) + comparison = connection.case_insensitive_comparison(attr, nil) assert_match(/lower/i, comparison.to_sql) - column = Default.columns_hash["multiline_default"] - comparison = connection.case_insensitive_comparison table, :multiline_default, column, nil + attr = Default.arel_attribute(:multiline_default) + comparison = connection.case_insensitive_comparison(attr, nil) assert_match(/lower/i, comparison.to_sql) end end diff --git a/activerecord/test/cases/arel/attributes/attribute_test.rb b/activerecord/test/cases/arel/attributes/attribute_test.rb index 671e273543..c7bd0a053b 100644 --- a/activerecord/test/cases/arel/attributes/attribute_test.rb +++ b/activerecord/test/cases/arel/attributes/attribute_test.rb @@ -560,7 +560,7 @@ module Arel end end - describe "with a range" do + describe "#between" do it "can be constructed with a standard range" do attribute = Attribute.new nil, nil node = attribute.between(1..3) @@ -628,7 +628,6 @@ module Arel node.must_equal Nodes::NotIn.new(attribute, []) end - it "can be constructed with a range ending at Infinity" do attribute = Attribute.new nil, nil node = attribute.between(0..::Float::INFINITY) @@ -639,6 +638,18 @@ module Arel ) end + if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION) + it "can be constructed with a range implicitly ending at Infinity" do + attribute = Attribute.new nil, nil + node = attribute.between(eval("0..")) # Use eval for compatibility with Ruby < 2.6 parser + + node.must_equal Nodes::GreaterThanOrEqual.new( + attribute, + Nodes::Casted.new(0, attribute) + ) + end + end + it "can be constructed with a quoted range ending at Infinity" do attribute = Attribute.new nil, nil node = attribute.between(quoted_range(0, ::Float::INFINITY, false)) @@ -664,14 +675,6 @@ module Arel ) ]) end - - def quoted_range(begin_val, end_val, exclude) - OpenStruct.new( - begin: Nodes::Quoted.new(begin_val), - end: Nodes::Quoted.new(end_val), - exclude_end?: exclude, - ) - end end describe "#in" do @@ -753,21 +756,23 @@ module Arel end end - describe "with a range" do + describe "#not_between" do it "can be constructed with a standard range" do attribute = Attribute.new nil, nil node = attribute.not_between(1..3) - node.must_equal Nodes::Grouping.new(Nodes::Or.new( - Nodes::LessThan.new( - attribute, - Nodes::Casted.new(1, attribute) - ), - Nodes::GreaterThan.new( - attribute, - Nodes::Casted.new(3, attribute) + node.must_equal Nodes::Grouping.new( + Nodes::Or.new( + Nodes::LessThan.new( + attribute, + Nodes::Casted.new(1, attribute) + ), + Nodes::GreaterThan.new( + attribute, + Nodes::Casted.new(3, attribute) + ) ) - )) + ) end it "can be constructed with a range starting from -Infinity" do @@ -780,6 +785,16 @@ module Arel ) end + it "can be constructed with a quoted range starting from -Infinity" do + attribute = Attribute.new nil, nil + node = attribute.not_between(quoted_range(-::Float::INFINITY, 3, false)) + + node.must_equal Nodes::GreaterThan.new( + attribute, + Nodes::Quoted.new(3) + ) + end + it "can be constructed with an exclusive range starting from -Infinity" do attribute = Attribute.new nil, nil node = attribute.not_between(-::Float::INFINITY...3) @@ -790,6 +805,16 @@ module Arel ) end + it "can be constructed with a quoted exclusive range starting from -Infinity" do + attribute = Attribute.new nil, nil + node = attribute.not_between(quoted_range(-::Float::INFINITY, 3, true)) + + node.must_equal Nodes::GreaterThanOrEqual.new( + attribute, + Nodes::Quoted.new(3) + ) + end + it "can be constructed with an infinite range" do attribute = Attribute.new nil, nil node = attribute.not_between(-::Float::INFINITY..::Float::INFINITY) @@ -797,6 +822,13 @@ module Arel node.must_equal Nodes::In.new(attribute, []) end + it "can be constructed with a quoted infinite range" do + attribute = Attribute.new nil, nil + node = attribute.not_between(quoted_range(-::Float::INFINITY, ::Float::INFINITY, false)) + + node.must_equal Nodes::In.new(attribute, []) + end + it "can be constructed with a range ending at Infinity" do attribute = Attribute.new nil, nil node = attribute.not_between(0..::Float::INFINITY) @@ -807,20 +839,44 @@ module Arel ) end + if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION) + it "can be constructed with a range implicitly ending at Infinity" do + attribute = Attribute.new nil, nil + node = attribute.not_between(eval("0..")) # Use eval for compatibility with Ruby < 2.6 parser + + node.must_equal Nodes::LessThan.new( + attribute, + Nodes::Casted.new(0, attribute) + ) + end + end + + it "can be constructed with a quoted range ending at Infinity" do + attribute = Attribute.new nil, nil + node = attribute.not_between(quoted_range(0, ::Float::INFINITY, false)) + + node.must_equal Nodes::LessThan.new( + attribute, + Nodes::Quoted.new(0) + ) + end + it "can be constructed with an exclusive range" do attribute = Attribute.new nil, nil node = attribute.not_between(0...3) - node.must_equal Nodes::Grouping.new(Nodes::Or.new( - Nodes::LessThan.new( - attribute, - Nodes::Casted.new(0, attribute) - ), - Nodes::GreaterThanOrEqual.new( - attribute, - Nodes::Casted.new(3, attribute) + node.must_equal Nodes::Grouping.new( + Nodes::Or.new( + Nodes::LessThan.new( + attribute, + Nodes::Casted.new(0, attribute) + ), + Nodes::GreaterThanOrEqual.new( + attribute, + Nodes::Casted.new(3, attribute) + ) ) - )) + ) end end @@ -1010,6 +1066,15 @@ module Arel condition.to_sql.must_equal %("foo"."id" = (select 1)) end end + + private + def quoted_range(begin_val, end_val, exclude) + OpenStruct.new( + begin: Nodes::Quoted.new(begin_val), + end: Nodes::Quoted.new(end_val), + exclude_end?: exclude, + ) + end end end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 5921193374..4b9b55f822 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1815,6 +1815,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal num_accounts, Account.count end + def test_depends_and_nullify_on_polymorphic_assoc + author = PersonWithPolymorphicDependentNullifyComments.create!(first_name: "Laertis") + comment = posts(:welcome).comments.first + comment.author = author + comment.save! + + assert_equal comment.author_id, author.id + assert_equal comment.author_type, author.class.name + + author.destroy + comment.reload + + assert_nil comment.author_id + assert_nil comment.author_type + end + def test_restrict_with_exception firm = RestrictedWithExceptionFirm.create!(name: "restrict") firm.companies.create(name: "child") diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index bf574f6637..3e5b5c1275 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -12,6 +12,9 @@ require "models/bulb" require "models/author" require "models/image" require "models/post" +require "models/drink_designer" +require "models/chef" +require "models/department" class HasOneAssociationsTest < ActiveRecord::TestCase self.use_transactional_tests = false unless supports_savepoints? @@ -114,6 +117,21 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_nil Account.find(old_account_id).firm_id end + def test_nullify_on_polymorphic_association + department = Department.create! + designer = DrinkDesignerWithPolymorphicDependentNullifyChef.create! + chef = department.chefs.create!(employable: designer) + + assert_equal chef.employable_id, designer.id + assert_equal chef.employable_type, designer.class.name + + designer.destroy! + chef.reload + + assert_nil chef.employable_id + assert_nil chef.employable_type + end + def test_nullification_on_destroyed_association developer = Developer.create!(name: "Someone") ship = Ship.create!(name: "Planet Caravan", developer: developer) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 961ae03a4c..1c53362bac 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -21,6 +21,7 @@ require "models/dog" require "models/car" require "models/tyre" require "models/subscriber" +require "support/stubs/strong_parameters" class FinderTest < ActiveRecord::TestCase fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :author_addresses, :customers, :categories, :categorizations, :cars @@ -224,6 +225,14 @@ class FinderTest < ActiveRecord::TestCase assert_equal true, Subscriber.exists?(" ") end + def test_exists_with_strong_parameters + assert_equal false, Subscriber.exists?(Parameters.new(nick: "foo")) + + Subscriber.create!(nick: "foo") + + assert_equal true, Subscriber.exists?(Parameters.new(nick: "foo")) + end + def test_exists_passing_active_record_object_is_not_permitted assert_raises(ArgumentError) do Topic.exists?(Topic.new) diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index f6cd4f85ee..fa136fe8da 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -22,7 +22,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase end def test_serialize_does_not_eagerly_load_columns - Topic.reset_column_information + reset_column_information_of(Topic) assert_no_queries do Topic.serialize(:content) end @@ -377,7 +377,8 @@ class SerializedAttributeTest < ActiveRecord::TestCase topic.update group: "1" model.serialize :group, JSON - model.reset_column_information + + reset_column_information_of(model) # This isn't strictly necessary for the test, but a little bit of # knowledge of internals allows us to make failures far more likely. @@ -397,4 +398,12 @@ class SerializedAttributeTest < ActiveRecord::TestCase # raw string ("1"), or raise an exception. assert_equal [1] * threads.size, threads.map(&:value) end + + private + + def reset_column_information_of(topic_class) + topic_class.reset_column_information + # reset original topic to undefine attribute methods + ::Topic.reset_column_information + end end diff --git a/activerecord/test/models/drink_designer.rb b/activerecord/test/models/drink_designer.rb index eb6701b84e..8258408f35 100644 --- a/activerecord/test/models/drink_designer.rb +++ b/activerecord/test/models/drink_designer.rb @@ -4,5 +4,11 @@ class DrinkDesigner < ActiveRecord::Base has_one :chef, as: :employable end +class DrinkDesignerWithPolymorphicDependentNullifyChef < ActiveRecord::Base + self.table_name = "drink_designers" + + has_one :chef, as: :employable, dependent: :nullify +end + class MocktailDesigner < DrinkDesigner end diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index 5cba1e440e..c3d15a571a 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -62,6 +62,11 @@ class PersonWithDependentNullifyJobs < ActiveRecord::Base has_many :jobs, source: :job, through: :references, dependent: :nullify end +class PersonWithPolymorphicDependentNullifyComments < ActiveRecord::Base + self.table_name = "people" + has_many :comments, as: :author, dependent: :nullify +end + class LoosePerson < ActiveRecord::Base self.table_name = "people" self.abstract_class = true diff --git a/activerecord/test/support/stubs/strong_parameters.rb b/activerecord/test/support/stubs/strong_parameters.rb new file mode 100644 index 0000000000..acba3a4504 --- /dev/null +++ b/activerecord/test/support/stubs/strong_parameters.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class Parameters + def initialize(parameters = {}) + @parameters = parameters.with_indifferent_access + end + + def permitted? + true + end + + def to_h + @parameters.to_h + end +end |