aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md23
-rw-r--r--activerecord/Rakefile46
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/merger.rb2
-rw-r--r--activerecord/lib/active_record/store.rb19
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb12
-rw-r--r--activerecord/test/cases/relation/merging_test.rb5
-rw-r--r--activerecord/test/cases/store_test.rb16
-rw-r--r--activerecord/test/models/comment.rb8
-rw-r--r--activerecord/test/models/post.rb9
-rw-r--r--activerecord/test/models/publisher.rb2
-rw-r--r--activerecord/test/models/publisher/article.rb3
-rw-r--r--activerecord/test/models/publisher/magazine.rb3
-rw-r--r--activerecord/test/schema/schema.rb11
16 files changed, 117 insertions, 54 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f6cd5efb45..4588ef1ac9 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,26 @@
+* Fix how to calculate associated class name when using namespaced `has_and_belongs_to_many`
+ association.
+
+ Fixes #14709.
+
+ *Kassio Borges*
+
+* `ActiveRecord::Relation::Merger#filter_binds` now compares equivalent symbols and
+ strings in column names as equal.
+
+ This fixes a rare case in which more bind values are passed than there are
+ placeholders for them in the generated SQL statement, which can make PostgreSQL
+ throw a `StatementInvalid` exception.
+
+ *Nat Budin*
+
+* Fix `stored_attributes` to correctly merge the details of stored
+ attributes defined in parent classes.
+
+ Fixes #14672.
+
+ *Brad Bennett*, *Jessica Yao*, *Lakshmi Parthasarathy*
+
* `change_column_default` allows `[]` as argument to `change_column_default`.
Fixes #11586.
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 6f8948f987..84856774f2 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -142,53 +142,7 @@ task :drop_postgresql_databases => 'postgresql:drop_databases'
task :rebuild_postgresql_databases => 'postgresql:rebuild_databases'
-namespace :frontbase do
- desc 'Build the FrontBase test databases'
- task :build_databases => :rebuild_frontbase_databases
-
- desc 'Rebuild the FrontBase test databases'
- task :rebuild_databases do
- build_frontbase_database = Proc.new do |db_name, sql_definition_file|
- %(
- STOP DATABASE #{db_name};
- DELETE DATABASE #{db_name};
- CREATE DATABASE #{db_name};
-
- CONNECT TO #{db_name} AS SESSION_NAME USER _SYSTEM;
- SET COMMIT FALSE;
-
- CREATE USER RAILS;
- CREATE SCHEMA RAILS AUTHORIZATION RAILS;
- COMMIT;
-
- SET SESSION AUTHORIZATION RAILS;
- SCRIPT '#{sql_definition_file}';
-
- COMMIT;
-
- DISCONNECT ALL;
- )
- end
- config = ARTest.config['connections']['frontbase']
- create_activerecord_unittest = build_frontbase_database[config['arunit']['database'], File.join(SCHEMA_ROOT, 'frontbase.sql')]
- create_activerecord_unittest2 = build_frontbase_database[config['arunit2']['database'], File.join(SCHEMA_ROOT, 'frontbase2.sql')]
- execute_frontbase_sql = Proc.new do |sql|
- system(<<-SHELL)
- /Library/FrontBase/bin/sql92 <<-SQL
- #{sql}
- SQL
- SHELL
- end
- execute_frontbase_sql[create_activerecord_unittest]
- execute_frontbase_sql[create_activerecord_unittest2]
- end
-end
-
-task :build_frontbase_databases => 'frontbase:build_databases'
-task :rebuild_frontbase_databases => 'frontbase:rebuild_databases'
-
spec = eval(File.read('activerecord.gemspec'))
-
Gem::PackageTask.new(spec) do |p|
p.gem_spec = spec
end
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index e472277374..a297439214 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -23,7 +23,13 @@ module ActiveRecord::Associations::Builder
KnownTable.new options[:join_table].to_s
else
class_name = options.fetch(:class_name) {
- name.to_s.camelize.singularize
+ model_name = name.to_s.camelize.singularize
+
+ if parent_name = lhs_class.parent_name
+ model_name = model_name.prepend("#{parent_name}::")
+ end
+
+ model_name
}
KnownClass.new lhs_class, class_name
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 7fd7accc6b..8bd51dc71f 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -359,6 +359,8 @@ module ActiveRecord
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
#
+ # Note: +:id+ is always present.
+ #
# Alias for the <tt>read_attribute</tt> method.
#
# class Person < ActiveRecord::Base
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index fcb28a18f6..ac41d0aa80 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -156,7 +156,7 @@ module ActiveRecord
def filter_binds(lhs_binds, removed_wheres)
return lhs_binds if removed_wheres.empty?
- set = Set.new removed_wheres.map { |x| x.left.name }
+ set = Set.new removed_wheres.map { |x| x.left.name.to_s }
lhs_binds.dup.delete_if { |col,_| set.include? col.name }
end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 79a6ccbda0..7014bc6d45 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -66,8 +66,9 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :stored_attributes, instance_accessor: false
- self.stored_attributes = {}
+ class << self
+ attr_accessor :local_stored_attributes
+ end
end
module ClassMethods
@@ -93,9 +94,9 @@ module ActiveRecord
# assign new store attribute and create new hash to ensure that each class in the hierarchy
# has its own hash of stored attributes.
- self.stored_attributes = {} if self.stored_attributes.blank?
- self.stored_attributes[store_attribute] ||= []
- self.stored_attributes[store_attribute] |= keys
+ self.local_stored_attributes ||= {}
+ self.local_stored_attributes[store_attribute] ||= []
+ self.local_stored_attributes[store_attribute] |= keys
end
def _store_accessors_module
@@ -105,6 +106,14 @@ module ActiveRecord
mod
end
end
+
+ def stored_attributes
+ parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
+ if self.local_stored_attributes
+ parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
+ end
+ parent
+ end
end
protected
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 897b603321..ea433d391f 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -138,7 +138,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
def test_number_values
assert_equal 123.456, @first_number.single
assert_equal 123456.789, @first_number.double
- assert_equal -::Float::INFINITY, @second_number.single
+ assert_equal(-::Float::INFINITY, @second_number.single)
assert_equal ::Float::INFINITY, @second_number.double
assert_same ::Float::NAN, @third_number.double
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index cfdfff6af9..878f1877db 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -22,6 +22,9 @@ require 'models/sponsor'
require 'models/country'
require 'models/treaty'
require 'models/vertex'
+require 'models/publisher'
+require 'models/publisher/article'
+require 'models/publisher/magazine'
require 'active_support/core_ext/string/conversions'
class ProjectWithAfterCreateHook < ActiveRecord::Base
@@ -848,4 +851,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_custom_join_table
assert_equal 'edges', Vertex.reflect_on_association(:sources).join_table
end
+
+ def test_namespaced_habtm
+ magazine = Publisher::Magazine.create
+ article = Publisher::Article.create
+ magazine.articles << article
+ magazine.save
+
+ assert_includes magazine.articles, article
+ end
end
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index 48f45d45b1..2b5c2fd5a4 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -108,6 +108,11 @@ class RelationMergingTest < ActiveRecord::TestCase
merged = left.merge(right)
assert_equal post, merged.first
end
+
+ def test_merging_compares_symbols_and_strings_as_equal
+ post = PostThatLoadsCommentsInAnAfterSaveHook.create!(title: "First Post", body: "Blah blah blah.")
+ assert_equal "First comment!", post.comments.where(body: "First comment!").first_or_create.body
+ end
end
class MergingDifferentRelationsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 978cee9cfb..6a34c55011 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -163,6 +163,22 @@ class StoreTest < ActiveRecord::TestCase
assert_equal [:width, :height], second_model.stored_attributes[:data]
end
+ test "stored_attributes are tracked per subclass" do
+ first_model = Class.new(ActiveRecord::Base) do
+ store_accessor :data, :color
+ end
+ second_model = Class.new(first_model) do
+ store_accessor :data, :width, :height
+ end
+ third_model = Class.new(first_model) do
+ store_accessor :data, :area, :volume
+ end
+
+ assert_equal [:color], first_model.stored_attributes[:data]
+ assert_equal [:color, :width, :height], second_model.stored_attributes[:data]
+ assert_equal [:color, :area, :volume], third_model.stored_attributes[:data]
+ end
+
test "YAML coder initializes the store when a Nil value is given" do
assert_equal({}, @john.params)
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index f82df417ce..bf0162d09b 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -40,3 +40,11 @@ end
class VerySpecialComment < Comment
end
+
+class CommentThatAutomaticallyAltersPostBody < Comment
+ belongs_to :post, class_name: "PostThatLoadsCommentsInAnAfterSaveHook", foreign_key: :post_id
+
+ after_save do |comment|
+ comment.post.update_attributes(body: "Automatically altered")
+ end
+end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index b1e56c14d1..5f01ab0a82 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -208,3 +208,12 @@ class SpecialPostWithDefaultScope < ActiveRecord::Base
self.table_name = 'posts'
default_scope { where(:id => [1, 5,6]) }
end
+
+class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
+ self.table_name = 'posts'
+ has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id
+
+ after_save do |post|
+ post.comments.load
+ end
+end
diff --git a/activerecord/test/models/publisher.rb b/activerecord/test/models/publisher.rb
new file mode 100644
index 0000000000..0d4a7f9235
--- /dev/null
+++ b/activerecord/test/models/publisher.rb
@@ -0,0 +1,2 @@
+module Publisher
+end
diff --git a/activerecord/test/models/publisher/article.rb b/activerecord/test/models/publisher/article.rb
new file mode 100644
index 0000000000..03a277bbdd
--- /dev/null
+++ b/activerecord/test/models/publisher/article.rb
@@ -0,0 +1,3 @@
+class Publisher::Article < ActiveRecord::Base
+ has_and_belongs_to_many :magazines
+end
diff --git a/activerecord/test/models/publisher/magazine.rb b/activerecord/test/models/publisher/magazine.rb
new file mode 100644
index 0000000000..82e1a14008
--- /dev/null
+++ b/activerecord/test/models/publisher/magazine.rb
@@ -0,0 +1,3 @@
+class Publisher::Magazine < ActiveRecord::Base
+ has_and_belongs_to_many :articles
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index da3074e90f..a78074d530 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -62,6 +62,14 @@ ActiveRecord::Schema.define do
t.string :name
end
+ create_table :articles, force: true do |t|
+ end
+
+ create_table :articles_magazines, force: true do |t|
+ t.references :article
+ t.references :magazine
+ end
+
create_table :audit_logs, force: true do |t|
t.column :message, :string, null: false
t.column :developer_id, :integer, null: false
@@ -385,6 +393,9 @@ ActiveRecord::Schema.define do
t.column :custom_lock_version, :integer
end
+ create_table :magazines, force: true do |t|
+ end
+
create_table :mateys, id: false, force: true do |t|
t.column :pirate_id, :integer
t.column :target_id, :integer