diff options
Diffstat (limited to 'activerecord')
41 files changed, 685 insertions, 604 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ee73004810..4aa7ecfc7e 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fixed a bug where column orders for an index weren't written to + db/schema.rb when using the sqlite adapter. + + Fixes #30902. + + *Paul Kuruvilla* + * Remove deprecated method `#sanitize_conditions`. *Rafael Mendonça França* @@ -340,10 +347,6 @@ *Ryuta Kamizono* -* Quote database name in `db:create` grant statement (when database user does not have access to create the database). - - *Rune Philosof* - * Raise error `UnknownMigrationVersionError` on the movement of migrations when the current migration does not exist. diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc index ae53ecd177..ba83a9adb2 100644 --- a/activerecord/README.rdoc +++ b/activerecord/README.rdoc @@ -26,7 +26,7 @@ The Product class is automatically mapped to the table named "products", which might look like this: CREATE TABLE products ( - id int NOT NULL auto_increment, + id bigint NOT NULL auto_increment, name varchar(255), PRIMARY KEY (id) ); diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 573f41d7ad..661605d3e5 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -140,26 +140,6 @@ module ActiveRecord class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection #:nodoc: end - class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc: - def initialize(owner = nil, reflection = nil) - if owner && reflection - super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.") - else - super("Cannot associate new records.") - end - end - end - - class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc: - def initialize(owner = nil, reflection = nil) - if owner && reflection - super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.") - else - super("Cannot dissociate new records.") - end - end - end - class ThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc: def initialize(owner = nil, reflection = nil) if owner && reflection @@ -189,16 +169,6 @@ module ActiveRecord end end - class ReadOnlyAssociation < ActiveRecordError #:nodoc: - def initialize(reflection = nil) - if reflection - super("Cannot add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.") - else - super("Read-only reflection error.") - end - end - end - # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations # (has_many, has_one) when there is at least 1 child associated instance. # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project @@ -483,14 +453,14 @@ module ActiveRecord # The tables for these classes could look something like: # # CREATE TABLE users ( - # id int NOT NULL auto_increment, - # account_id int default NULL, + # id bigint NOT NULL auto_increment, + # account_id bigint default NULL, # name varchar default NULL, # PRIMARY KEY (id) # ) # # CREATE TABLE accounts ( - # id int NOT NULL auto_increment, + # id bigint NOT NULL auto_increment, # name varchar default NULL, # PRIMARY KEY (id) # ) diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index 07bd0a273b..14881cfe17 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -33,7 +33,7 @@ module ActiveRecord elsif join.respond_to? :left join.left.name == name ? 1 : 0 elsif join.is_a?(Hash) - join[name] + join.fetch(name, 0) else # this branch is reached by two tests: # 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 12fcfbcd45..1981da11a2 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 @@ -47,7 +47,7 @@ module ActiveRecord::Associations::Builder # :nodoc: habtm = JoinTableResolver.build lhs_model, association_name, options join_model = Class.new(ActiveRecord::Base) { - class << self; + class << self attr_accessor :left_model attr_accessor :name attr_accessor :table_name_resolver diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index fa32cc5553..b16fca7dc9 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -13,22 +13,38 @@ module ActiveRecord end def associated_records_by_owner(preloader) + already_loaded = owners.first.association(through_reflection.name).loaded? through_scope = through_scope() - preloader.preload(owners, - through_reflection.name, - through_scope) + unless already_loaded + preloader.preload(owners, through_reflection.name, through_scope) + end through_records = owners.map do |owner| center = owner.association(through_reflection.name).target [owner, Array(center)] end - reset_association(owners, through_reflection.name, through_scope) + if already_loaded + if source_type = reflection.options[:source_type] + through_records.map! do |owner, center| + center = center.select do |record| + record[reflection.foreign_type] == source_type + end + [owner, center] + end + end + else + reset_association(owners, through_reflection.name, through_scope) + end middle_records = through_records.flat_map(&:last) - reflection_scope = reflection_scope() if reflection.scope + if preload_scope + reflection_scope = reflection_scope().merge(preload_scope) + elsif reflection.scope + reflection_scope = reflection_scope() + end preloaders = preloader.preload(middle_records, source_reflection.name, @@ -58,6 +74,8 @@ module ActiveRecord rhs_records end end + end.tap do + reset_association(middle_records, source_reflection.name, preload_scope) end end diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 891e556dc4..23d2aef214 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -236,7 +236,7 @@ module ActiveRecord return has_attribute?(name) end - return true + true end # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+. diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb index f3e6516414..88b398ad45 100644 --- a/activerecord/lib/active_record/collection_cache_key.rb +++ b/activerecord/lib/active_record/collection_cache_key.rb @@ -12,6 +12,9 @@ module ActiveRecord timestamp = collection.max_by(×tamp_column)._read_attribute(timestamp_column) end else + if collection.eager_loading? + collection = collection.send(:apply_join_dependency) + end column_type = type_for_attribute(timestamp_column.to_s) column = connection.column_name_from_arel_node(collection.arel_attribute(timestamp_column)) select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp" diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 6698047ad6..9b7345f7c3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -216,7 +216,7 @@ module ActiveRecord # generates: # # CREATE TABLE suppliers ( - # id int auto_increment PRIMARY KEY + # id bigint auto_increment PRIMARY KEY # ) ENGINE=InnoDB DEFAULT CHARSET=utf8 # # ====== Rename the primary key column @@ -228,7 +228,7 @@ module ActiveRecord # generates: # # CREATE TABLE objects ( - # guid int auto_increment PRIMARY KEY, + # guid bigint auto_increment PRIMARY KEY, # name varchar(80) # ) # @@ -255,8 +255,8 @@ module ActiveRecord # generates: # # CREATE TABLE order ( - # product_id integer NOT NULL, - # client_id integer NOT NULL + # product_id bigint NOT NULL, + # client_id bigint NOT NULL # ); # # ALTER TABLE ONLY "orders" @@ -265,15 +265,15 @@ module ActiveRecord # ====== Do not add a primary key column # # create_table(:categories_suppliers, id: false) do |t| - # t.column :category_id, :integer - # t.column :supplier_id, :integer + # t.column :category_id, :bigint + # t.column :supplier_id, :bigint # end # # generates: # # CREATE TABLE categories_suppliers ( - # category_id int, - # supplier_id int + # category_id bigint, + # supplier_id bigint # ) # # ====== Create a temporary table based on a query @@ -361,8 +361,8 @@ module ActiveRecord # generates: # # CREATE TABLE assemblies_parts ( - # assembly_id int NOT NULL, - # part_id int NOT NULL, + # assembly_id bigint NOT NULL, + # part_id bigint NOT NULL, # ) ENGINE=InnoDB DEFAULT CHARSET=utf8 # def create_join_table(table_1, table_2, column_options: {}, **options) @@ -432,7 +432,7 @@ module ActiveRecord # t.references :company # end # - # Creates a <tt>company_id(integer)</tt> column. + # Creates a <tt>company_id(bigint)</tt> column. # # ====== Add a polymorphic foreign key column # @@ -440,7 +440,7 @@ module ActiveRecord # t.belongs_to :company, polymorphic: true # end # - # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns. + # Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns. # # ====== Remove a column # @@ -811,14 +811,14 @@ module ActiveRecord indexes(table_name).detect { |i| i.name == index_name } end - # Adds a reference. The reference column is an integer by default, + # Adds a reference. The reference column is a bigint by default, # the <tt>:type</tt> option can be used to specify a different type. # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided. # #add_reference and #add_belongs_to are acceptable. # # The +options+ hash can include the following keys: # [<tt>:type</tt>] - # The reference column type. Defaults to +:integer+. + # The reference column type. Defaults to +:bigint+. # [<tt>:index</tt>] # Add an appropriate index. Defaults to true. # See #add_index for usage of this option. @@ -829,7 +829,7 @@ module ActiveRecord # [<tt>:null</tt>] # Whether the column allows nulls. Defaults to true. # - # ====== Create a user_id integer column + # ====== Create a user_id bigint column # # add_reference(:products, :user) # diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 28d949b503..5d81de9fe1 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -11,7 +11,7 @@ module ActiveRecord # Instantiates a new column in the table. # - # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int</tt>. + # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id bigint</tt>. # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>. # +sql_type_metadata+ is various information about the type of the column # +null+ determines if this column allows +NULL+ values. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb index c0dbb166b7..84643d20da 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb @@ -5,6 +5,18 @@ module ActiveRecord module PostgreSQL class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc: private + + def extensions(stream) + extensions = @connection.extensions + if extensions.any? + stream.puts " # These are extensions that must be enabled in order to support this database" + extensions.sort.each do |extension| + stream.puts " enable_extension #{extension.inspect}" + end + stream.puts + end + end + def prepare_column_options(column) spec = super spec[:array] = "true" if column.array? diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e9ae861bfb..2c3c1df2a9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -337,18 +337,12 @@ module ActiveRecord end def extension_enabled?(name) - if supports_extensions? - res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA") - res.cast_values.first - end + res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA") + res.cast_values.first end def extensions - if supports_extensions? - exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values - else - super - end + exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values end # Returns the configured supported identifier length supported by PostgreSQL diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb index f0d702136d..58e5138e02 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -23,12 +23,22 @@ module ActiveRecord col["name"] end + # Add info on sort order for columns (only desc order is explicitly specified, asc is + # the default) + orders = {} + if index_sql # index_sql can be null in case of primary key indexes + index_sql.scan(/"(\w+)" DESC/).flatten.each { |order_column| + orders[order_column] = :desc + } + end + IndexDefinition.new( table_name, row["name"], row["unique"] != 0, columns, - where: where + where: where, + orders: orders ) end end diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 5005d58f1c..ee4f818cbf 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -53,7 +53,7 @@ module ActiveRecord unscoped.where(primary_key => object.id).update_all(updates) end - return true + true end # A generic "counter updater" implementation, intended primarily to be diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 8877f762b2..87bfd75bca 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -839,10 +839,6 @@ module ActiveRecord source_reflection.join_scopes(table, predicate_builder) + super end - def source_type_scope - through_reflection.klass.where(foreign_type => options[:source_type]) - end - def has_scope? scope || options[:source_type] || source_reflection.has_scope? || @@ -1002,29 +998,24 @@ module ActiveRecord end class PolymorphicReflection < AbstractReflection # :nodoc: - delegate :klass, :scope, :plural_name, :type, :get_join_keys, to: :@reflection + delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection def initialize(reflection, previous_reflection) @reflection = reflection @previous_reflection = previous_reflection end - def scopes - scopes = @previous_reflection.scopes - scopes << @previous_reflection.source_type_scope - end - def join_scopes(table, predicate_builder) # :nodoc: scopes = @previous_reflection.join_scopes(table, predicate_builder) + super - scopes << @previous_reflection.source_type_scope + scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope) end def constraints - @reflection.constraints + [source_type_info] + @reflection.constraints + [source_type_scope] end private - def source_type_info + def source_type_scope type = @previous_reflection.foreign_type source_type = @previous_reflection.options[:source_type] lambda { |object| where(type => source_type) } diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 997cfe4b5e..7615fb6ee9 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -359,6 +359,11 @@ module ActiveRecord def update_all(updates) raise ArgumentError, "Empty list of attributes to change" if updates.blank? + if eager_loading? + relation = apply_join_dependency + return relation.update_all(updates) + end + stmt = Arel::UpdateManager.new stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates)) @@ -423,6 +428,11 @@ module ActiveRecord raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}") end + if eager_loading? + relation = apply_join_dependency + return relation.delete_all + end + stmt = Arel::DeleteManager.new stmt.from(table) diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 116bddce85..11256ab3d9 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -130,7 +130,7 @@ module ActiveRecord # end def calculate(operation, column_name) if has_include?(column_name) - relation = apply_join_dependency(construct_join_dependency) + relation = apply_join_dependency relation.distinct! if operation.to_s.downcase == "count" relation.calculate(operation, column_name) @@ -180,7 +180,7 @@ module ActiveRecord end if has_include?(column_names.first) - relation = apply_join_dependency(construct_join_dependency) + relation = apply_join_dependency relation.pluck(*column_names) else relation = spawn diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 707245bab2..18566b5662 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -394,7 +394,7 @@ module ActiveRecord ) end - def apply_join_dependency(join_dependency) + def apply_join_dependency(join_dependency = construct_join_dependency) relation = except(:includes, :eager_load, :preload).joins!(join_dependency) if using_limitable_reflections?(join_dependency.reflections) diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 8d0311fabd..66f7d29886 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -82,16 +82,8 @@ HEADER stream.puts "end" end + # extensions are only supported by PostgreSQL def extensions(stream) - return unless @connection.supports_extensions? - extensions = @connection.extensions - if extensions.any? - stream.puts " # These are extensions that must be enabled in order to support this database" - extensions.sort.each do |extension| - stream.puts " enable_extension #{extension.inspect}" - end - stream.puts - end end def tables(stream) diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 6fa096c1fe..310af72c41 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -24,8 +24,14 @@ module ActiveRecord # You can define a scope that applies to all finders using # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope]. def all + current_scope = self.current_scope + if current_scope - current_scope.clone + if self == current_scope.klass + current_scope.clone + else + relation.merge!(current_scope) + end else default_scoped end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index f4ad58c087..4c2c5dd852 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -205,9 +205,7 @@ module ActiveRecord # | # Boom! We now have a duplicate # | # title! # - # This could even happen if you use transactions with the 'serializable' - # isolation level. The best way to work around this problem is to add a unique - # index to the database table using + # The best way to work around this problem is to add a unique index to the database table using # {connection.add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index]. # In the rare case that a race condition occurs, the database will guarantee # the field's uniqueness. diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index a6b83ec377..4e73c557ed 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -9,7 +9,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase def setup ActiveRecord::Base.connection.singleton_class.class_eval do alias_method :execute_without_stub, :execute - def execute(sql, name = nil) return sql end + def execute(sql, name = nil) sql end end end diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb index 050614cade..a25f102bad 100644 --- a/activerecord/test/cases/adapters/postgresql/citext_test.rb +++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb @@ -3,78 +3,76 @@ require "cases/helper" require "support/schema_dumping_helper" -if ActiveRecord::Base.connection.supports_extensions? - class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase - include SchemaDumpingHelper - class Citext < ActiveRecord::Base - self.table_name = "citexts" - end - - def setup - @connection = ActiveRecord::Base.connection +class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + class Citext < ActiveRecord::Base + self.table_name = "citexts" + end - enable_extension!("citext", @connection) + def setup + @connection = ActiveRecord::Base.connection - @connection.create_table("citexts") do |t| - t.citext "cival" - end - end + enable_extension!("citext", @connection) - teardown do - @connection.drop_table "citexts", if_exists: true - disable_extension!("citext", @connection) + @connection.create_table("citexts") do |t| + t.citext "cival" end + end - def test_citext_enabled - assert @connection.extension_enabled?("citext") - end + teardown do + @connection.drop_table "citexts", if_exists: true + disable_extension!("citext", @connection) + end - def test_column - column = Citext.columns_hash["cival"] - assert_equal :citext, column.type - assert_equal "citext", column.sql_type - assert_not column.array? + def test_citext_enabled + assert @connection.extension_enabled?("citext") + end - type = Citext.type_for_attribute("cival") - assert_not type.binary? - end + def test_column + column = Citext.columns_hash["cival"] + assert_equal :citext, column.type + assert_equal "citext", column.sql_type + assert_not column.array? - def test_change_table_supports_json - @connection.transaction do - @connection.change_table("citexts") do |t| - t.citext "username" - end - Citext.reset_column_information - column = Citext.columns_hash["username"] - assert_equal :citext, column.type + type = Citext.type_for_attribute("cival") + assert_not type.binary? + end - raise ActiveRecord::Rollback # reset the schema change + def test_change_table_supports_json + @connection.transaction do + @connection.change_table("citexts") do |t| + t.citext "username" end - ensure Citext.reset_column_information + column = Citext.columns_hash["username"] + assert_equal :citext, column.type + + raise ActiveRecord::Rollback # reset the schema change end + ensure + Citext.reset_column_information + end - def test_write - x = Citext.new(cival: "Some CI Text") - x.save! - citext = Citext.first - assert_equal "Some CI Text", citext.cival + def test_write + x = Citext.new(cival: "Some CI Text") + x.save! + citext = Citext.first + assert_equal "Some CI Text", citext.cival - citext.cival = "Some NEW CI Text" - citext.save! + citext.cival = "Some NEW CI Text" + citext.save! - assert_equal "Some NEW CI Text", citext.reload.cival - end + assert_equal "Some NEW CI Text", citext.reload.cival + end - def test_select_case_insensitive - @connection.execute "insert into citexts (cival) values('Cased Text')" - x = Citext.where(cival: "cased text").first - assert_equal "Cased Text", x.cival - end + def test_select_case_insensitive + @connection.execute "insert into citexts (cival) values('Cased Text')" + x = Citext.where(cival: "cased text").first + assert_equal "Cased Text", x.cival + end - def test_schema_dump_with_shorthand - output = dump_table_schema("citexts") - assert_match %r[t\.citext "cival"], output - end + def test_schema_dump_with_shorthand + output = dump_table_schema("citexts") + assert_match %r[t\.citext "cival"], output end end diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb index e589e3ab1b..df97ab11e7 100644 --- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb +++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb @@ -22,10 +22,6 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase @connection = ActiveRecord::Base.connection - unless @connection.supports_extensions? - return skip("no extension support") - end - @old_schema_migration_table_name = ActiveRecord::SchemaMigration.table_name @old_table_name_prefix = ActiveRecord::Base.table_name_prefix @old_table_name_suffix = ActiveRecord::Base.table_name_suffix diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 97a8a257c5..f09e34b5f2 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -3,378 +3,376 @@ require "cases/helper" require "support/schema_dumping_helper" -if ActiveRecord::Base.connection.supports_extensions? - class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase - include SchemaDumpingHelper - class Hstore < ActiveRecord::Base - self.table_name = "hstores" +class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + class Hstore < ActiveRecord::Base + self.table_name = "hstores" - store_accessor :settings, :language, :timezone - end + store_accessor :settings, :language, :timezone + end - class FakeParameters - def to_unsafe_h - { "hi" => "hi" } - end + class FakeParameters + def to_unsafe_h + { "hi" => "hi" } end + end - def setup - @connection = ActiveRecord::Base.connection + def setup + @connection = ActiveRecord::Base.connection - enable_extension!("hstore", @connection) + enable_extension!("hstore", @connection) - @connection.transaction do - @connection.create_table("hstores") do |t| - t.hstore "tags", default: "" - t.hstore "payload", array: true - t.hstore "settings" - end + @connection.transaction do + @connection.create_table("hstores") do |t| + t.hstore "tags", default: "" + t.hstore "payload", array: true + t.hstore "settings" end - Hstore.reset_column_information - @column = Hstore.columns_hash["tags"] - @type = Hstore.type_for_attribute("tags") - end - - teardown do - @connection.drop_table "hstores", if_exists: true - disable_extension!("hstore", @connection) end + Hstore.reset_column_information + @column = Hstore.columns_hash["tags"] + @type = Hstore.type_for_attribute("tags") + end - def test_hstore_included_in_extensions - assert @connection.respond_to?(:extensions), "connection should have a list of extensions" - assert_includes @connection.extensions, "hstore", "extension list should include hstore" - end + teardown do + @connection.drop_table "hstores", if_exists: true + disable_extension!("hstore", @connection) + end - def test_disable_enable_hstore - assert @connection.extension_enabled?("hstore") - @connection.disable_extension "hstore" - assert_not @connection.extension_enabled?("hstore") - @connection.enable_extension "hstore" - assert @connection.extension_enabled?("hstore") - ensure - # Restore column(s) dropped by `drop extension hstore cascade;` - load_schema - end + def test_hstore_included_in_extensions + assert @connection.respond_to?(:extensions), "connection should have a list of extensions" + assert_includes @connection.extensions, "hstore", "extension list should include hstore" + end - def test_column - assert_equal :hstore, @column.type - assert_equal "hstore", @column.sql_type - assert_not @column.array? + def test_disable_enable_hstore + assert @connection.extension_enabled?("hstore") + @connection.disable_extension "hstore" + assert_not @connection.extension_enabled?("hstore") + @connection.enable_extension "hstore" + assert @connection.extension_enabled?("hstore") + ensure + # Restore column(s) dropped by `drop extension hstore cascade;` + load_schema + end - assert_not @type.binary? - end + def test_column + assert_equal :hstore, @column.type + assert_equal "hstore", @column.sql_type + assert_not @column.array? - def test_default - @connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"' - Hstore.reset_column_information + assert_not @type.binary? + end - assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"]) - assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions) - ensure - Hstore.reset_column_information - end + def test_default + @connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"' + Hstore.reset_column_information - def test_change_table_supports_hstore - @connection.transaction do - @connection.change_table("hstores") do |t| - t.hstore "users", default: "" - end - Hstore.reset_column_information - column = Hstore.columns_hash["users"] - assert_equal :hstore, column.type + assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"]) + assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions) + ensure + Hstore.reset_column_information + end - raise ActiveRecord::Rollback # reset the schema change + def test_change_table_supports_hstore + @connection.transaction do + @connection.change_table("hstores") do |t| + t.hstore "users", default: "" end - ensure Hstore.reset_column_information + column = Hstore.columns_hash["users"] + assert_equal :hstore, column.type + + raise ActiveRecord::Rollback # reset the schema change end + ensure + Hstore.reset_column_information + end - def test_hstore_migration - hstore_migration = Class.new(ActiveRecord::Migration::Current) do - def change - change_table("hstores") do |t| - t.hstore :keys - end + def test_hstore_migration + hstore_migration = Class.new(ActiveRecord::Migration::Current) do + def change + change_table("hstores") do |t| + t.hstore :keys end end - - hstore_migration.new.suppress_messages do - hstore_migration.migrate(:up) - assert_includes @connection.columns(:hstores).map(&:name), "keys" - hstore_migration.migrate(:down) - assert_not_includes @connection.columns(:hstores).map(&:name), "keys" - end end - def test_cast_value_on_write - x = Hstore.new tags: { "bool" => true, "number" => 5 } - assert_equal({ "bool" => true, "number" => 5 }, x.tags_before_type_cast) - assert_equal({ "bool" => "true", "number" => "5" }, x.tags) - x.save - assert_equal({ "bool" => "true", "number" => "5" }, x.reload.tags) + hstore_migration.new.suppress_messages do + hstore_migration.migrate(:up) + assert_includes @connection.columns(:hstores).map(&:name), "keys" + hstore_migration.migrate(:down) + assert_not_includes @connection.columns(:hstores).map(&:name), "keys" end + end - def test_type_cast_hstore - assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\"")) - assert_equal({}, @type.deserialize("")) - assert_equal({ "key" => nil }, @type.deserialize("key => NULL")) - assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b"))) - end + def test_cast_value_on_write + x = Hstore.new tags: { "bool" => true, "number" => 5 } + assert_equal({ "bool" => true, "number" => 5 }, x.tags_before_type_cast) + assert_equal({ "bool" => "true", "number" => "5" }, x.tags) + x.save + assert_equal({ "bool" => "true", "number" => "5" }, x.reload.tags) + end - def test_with_store_accessors - x = Hstore.new(language: "fr", timezone: "GMT") - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + def test_type_cast_hstore + assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\"")) + assert_equal({}, @type.deserialize("")) + assert_equal({ "key" => nil }, @type.deserialize("key => NULL")) + assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b"))) + end - x.save! - x = Hstore.first - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + def test_with_store_accessors + x = Hstore.new(language: "fr", timezone: "GMT") + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - x.language = "de" - x.save! + x.save! + x = Hstore.first + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - x = Hstore.first - assert_equal "de", x.language - assert_equal "GMT", x.timezone - end + x.language = "de" + x.save! - def test_duplication_with_store_accessors - x = Hstore.new(language: "fr", timezone: "GMT") - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + x = Hstore.first + assert_equal "de", x.language + assert_equal "GMT", x.timezone + end - y = x.dup - assert_equal "fr", y.language - assert_equal "GMT", y.timezone - end + def test_duplication_with_store_accessors + x = Hstore.new(language: "fr", timezone: "GMT") + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - def test_yaml_round_trip_with_store_accessors - x = Hstore.new(language: "fr", timezone: "GMT") - assert_equal "fr", x.language - assert_equal "GMT", x.timezone + y = x.dup + assert_equal "fr", y.language + assert_equal "GMT", y.timezone + end - y = YAML.load(YAML.dump(x)) - assert_equal "fr", y.language - assert_equal "GMT", y.timezone - end + def test_yaml_round_trip_with_store_accessors + x = Hstore.new(language: "fr", timezone: "GMT") + assert_equal "fr", x.language + assert_equal "GMT", x.timezone - def test_changes_in_place - hstore = Hstore.create!(settings: { "one" => "two" }) - hstore.settings["three"] = "four" - hstore.save! - hstore.reload + y = YAML.load(YAML.dump(x)) + assert_equal "fr", y.language + assert_equal "GMT", y.timezone + end - assert_equal "four", hstore.settings["three"] - assert_not hstore.changed? - end + def test_changes_in_place + hstore = Hstore.create!(settings: { "one" => "two" }) + hstore.settings["three"] = "four" + hstore.save! + hstore.reload - def test_dirty_from_user_equal - settings = { "alongkey" => "anything", "key" => "value" } - hstore = Hstore.create!(settings: settings) + assert_equal "four", hstore.settings["three"] + assert_not hstore.changed? + end - hstore.settings = { "key" => "value", "alongkey" => "anything" } - assert_equal settings, hstore.settings - refute hstore.changed? - end + def test_dirty_from_user_equal + settings = { "alongkey" => "anything", "key" => "value" } + hstore = Hstore.create!(settings: settings) - def test_hstore_dirty_from_database_equal - settings = { "alongkey" => "anything", "key" => "value" } - hstore = Hstore.create!(settings: settings) - hstore.reload + hstore.settings = { "key" => "value", "alongkey" => "anything" } + assert_equal settings, hstore.settings + refute hstore.changed? + end - assert_equal settings, hstore.settings - hstore.settings = settings - refute hstore.changed? - end + def test_hstore_dirty_from_database_equal + settings = { "alongkey" => "anything", "key" => "value" } + hstore = Hstore.create!(settings: settings) + hstore.reload - def test_gen1 - assert_equal('" "=>""', @type.serialize(" " => "")) - end + assert_equal settings, hstore.settings + hstore.settings = settings + refute hstore.changed? + end - def test_gen2 - assert_equal('","=>""', @type.serialize("," => "")) - end + def test_gen1 + assert_equal('" "=>""', @type.serialize(" " => "")) + end - def test_gen3 - assert_equal('"="=>""', @type.serialize("=" => "")) - end + def test_gen2 + assert_equal('","=>""', @type.serialize("," => "")) + end - def test_gen4 - assert_equal('">"=>""', @type.serialize(">" => "")) - end + def test_gen3 + assert_equal('"="=>""', @type.serialize("=" => "")) + end - def test_parse1 - assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c')) - end + def test_gen4 + assert_equal('">"=>""', @type.serialize(">" => "")) + end - def test_parse2 - assert_equal({ " " => " " }, @type.deserialize("\\ =>\\ ")) - end + def test_parse1 + assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c')) + end - def test_parse3 - assert_equal({ "=" => ">" }, @type.deserialize("==>>")) - end + def test_parse2 + assert_equal({ " " => " " }, @type.deserialize("\\ =>\\ ")) + end - def test_parse4 - assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w')) - end + def test_parse3 + assert_equal({ "=" => ">" }, @type.deserialize("==>>")) + end - def test_parse5 - assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w')) - end + def test_parse4 + assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w')) + end - def test_parse6 - assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w')) - end + def test_parse5 + assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w')) + end - def test_parse7 - assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w')) - end + def test_parse6 + assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w')) + end - def test_rewrite - @connection.execute "insert into hstores (tags) VALUES ('1=>2')" - x = Hstore.first - x.tags = { '"a\'' => "b" } - assert x.save! - end + def test_parse7 + assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w')) + end - def test_select - @connection.execute "insert into hstores (tags) VALUES ('1=>2')" - x = Hstore.first - assert_equal({ "1" => "2" }, x.tags) - end + def test_rewrite + @connection.execute "insert into hstores (tags) VALUES ('1=>2')" + x = Hstore.first + x.tags = { '"a\'' => "b" } + assert x.save! + end - def test_array_cycle - assert_array_cycle([{ "AA" => "BB", "CC" => "DD" }, { "AA" => nil }]) - end + def test_select + @connection.execute "insert into hstores (tags) VALUES ('1=>2')" + x = Hstore.first + assert_equal({ "1" => "2" }, x.tags) + end - def test_array_strings_with_quotes - assert_array_cycle([{ "this has" => 'some "s that need to be escaped"' }]) - end + def test_array_cycle + assert_array_cycle([{ "AA" => "BB", "CC" => "DD" }, { "AA" => nil }]) + end - def test_array_strings_with_commas - assert_array_cycle([{ "this,has" => "many,values" }]) - end + def test_array_strings_with_quotes + assert_array_cycle([{ "this has" => 'some "s that need to be escaped"' }]) + end - def test_array_strings_with_array_delimiters - assert_array_cycle(["{" => "}"]) - end + def test_array_strings_with_commas + assert_array_cycle([{ "this,has" => "many,values" }]) + end - def test_array_strings_with_null_strings - assert_array_cycle([{ "NULL" => "NULL" }]) - end + def test_array_strings_with_array_delimiters + assert_array_cycle(["{" => "}"]) + end - def test_contains_nils - assert_array_cycle([{ "NULL" => nil }]) - end + def test_array_strings_with_null_strings + assert_array_cycle([{ "NULL" => "NULL" }]) + end - def test_select_multikey - @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')" - x = Hstore.first - assert_equal({ "1" => "2", "2" => "3" }, x.tags) - end + def test_contains_nils + assert_array_cycle([{ "NULL" => nil }]) + end - def test_create - assert_cycle("a" => "b", "1" => "2") - end + def test_select_multikey + @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')" + x = Hstore.first + assert_equal({ "1" => "2", "2" => "3" }, x.tags) + end - def test_nil - assert_cycle("a" => nil) - end + def test_create + assert_cycle("a" => "b", "1" => "2") + end - def test_quotes - assert_cycle("a" => 'b"ar', '1"foo' => "2") - end + def test_nil + assert_cycle("a" => nil) + end - def test_whitespace - assert_cycle("a b" => "b ar", '1"foo' => "2") - end + def test_quotes + assert_cycle("a" => 'b"ar', '1"foo' => "2") + end - def test_backslash - assert_cycle('a\\b' => 'b\\ar', '1"foo' => "2") - end + def test_whitespace + assert_cycle("a b" => "b ar", '1"foo' => "2") + end - def test_comma - assert_cycle("a, b" => "bar", '1"foo' => "2") - end + def test_backslash + assert_cycle('a\\b' => 'b\\ar', '1"foo' => "2") + end - def test_arrow - assert_cycle("a=>b" => "bar", '1"foo' => "2") - end + def test_comma + assert_cycle("a, b" => "bar", '1"foo' => "2") + end - def test_quoting_special_characters - assert_cycle("ca" => "cà", "ac" => "àc") - end + def test_arrow + assert_cycle("a=>b" => "bar", '1"foo' => "2") + end - def test_multiline - assert_cycle("a\nb" => "c\nd") - end + def test_quoting_special_characters + assert_cycle("ca" => "cà", "ac" => "àc") + end - class TagCollection - 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_multiline + assert_cycle("a\nb" => "c\nd") + end - class HstoreWithSerialize < Hstore - serialize :tags, TagCollection - end + class TagCollection + 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_hstore_with_serialized_attributes - HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") - record = HstoreWithSerialize.first - assert_instance_of TagCollection, record.tags - assert_equal({ "one" => "two" }, record.tags.to_hash) - record.tags = TagCollection.new("three" => "four") - record.save! - assert_equal({ "three" => "four" }, HstoreWithSerialize.first.tags.to_hash) - end + class HstoreWithSerialize < Hstore + serialize :tags, TagCollection + end - def test_clone_hstore_with_serialized_attributes - HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") - record = HstoreWithSerialize.first - dupe = record.dup - assert_equal({ "one" => "two" }, dupe.tags.to_hash) - end + def test_hstore_with_serialized_attributes + HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") + record = HstoreWithSerialize.first + assert_instance_of TagCollection, record.tags + assert_equal({ "one" => "two" }, record.tags.to_hash) + record.tags = TagCollection.new("three" => "four") + record.save! + assert_equal({ "three" => "four" }, HstoreWithSerialize.first.tags.to_hash) + end - def test_schema_dump_with_shorthand - output = dump_table_schema("hstores") - assert_match %r[t\.hstore "tags",\s+default: {}], output - end + def test_clone_hstore_with_serialized_attributes + HstoreWithSerialize.create! tags: TagCollection.new("one" => "two") + record = HstoreWithSerialize.first + dupe = record.dup + assert_equal({ "one" => "two" }, dupe.tags.to_hash) + end + + def test_schema_dump_with_shorthand + output = dump_table_schema("hstores") + assert_match %r[t\.hstore "tags",\s+default: {}], output + end + + def test_supports_to_unsafe_h_values + assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new)) + end - def test_supports_to_unsafe_h_values - assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new)) + private + def assert_array_cycle(array) + # test creation + x = Hstore.create!(payload: array) + x.reload + assert_equal(array, x.payload) + + # test updating + x = Hstore.create!(payload: []) + x.payload = array + x.save! + x.reload + assert_equal(array, x.payload) end - private - def assert_array_cycle(array) - # test creation - x = Hstore.create!(payload: array) - x.reload - assert_equal(array, x.payload) - - # test updating - x = Hstore.create!(payload: []) - x.payload = array - x.save! - x.reload - assert_equal(array, x.payload) - end + def assert_cycle(hash) + # test creation + x = Hstore.create!(tags: hash) + x.reload + assert_equal(hash, x.tags) - def assert_cycle(hash) - # test creation - x = Hstore.create!(tags: hash) - x.reload - assert_equal(hash, x.tags) - - # test updating - x = Hstore.create!(tags: {}) - x.tags = hash - x.save! - x.reload - assert_equal(hash, x.tags) - end - end + # test updating + x = Hstore.create!(tags: {}) + x.tags = hash + x.save! + x.reload + assert_equal(hash, x.tags) + end end diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb index a3eb4f9e67..fef4b02b04 100644 --- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb @@ -23,7 +23,7 @@ module ActiveRecord assert_equal "bar", cache["foo"] pid = fork { - lookup = cache["foo"]; + lookup = cache["foo"] exit!(!lookup) } diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 466d281e85..c24e0cb330 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -222,68 +222,66 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase connection.execute "DROP FUNCTION IF EXISTS my_uuid_generator();" end - if ActiveRecord::Base.connection.supports_extensions? - def test_id_is_uuid - assert_equal :uuid, UUID.columns_hash["id"].type - assert UUID.primary_key - end + def test_id_is_uuid + assert_equal :uuid, UUID.columns_hash["id"].type + assert UUID.primary_key + end - def test_id_has_a_default - u = UUID.create - assert_not_nil u.id - end + def test_id_has_a_default + u = UUID.create + assert_not_nil u.id + end - def test_auto_create_uuid - u = UUID.create - u.reload - assert_not_nil u.other_uuid - end + def test_auto_create_uuid + u = UUID.create + u.reload + assert_not_nil u.other_uuid + end - def test_pk_and_sequence_for_uuid_primary_key - pk, seq = connection.pk_and_sequence_for("pg_uuids") - assert_equal "id", pk - assert_nil seq - end + def test_pk_and_sequence_for_uuid_primary_key + pk, seq = connection.pk_and_sequence_for("pg_uuids") + assert_equal "id", pk + assert_nil seq + end - def test_schema_dumper_for_uuid_primary_key - schema = dump_table_schema "pg_uuids" - assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: -> { "uuid_generate_v1\(\)" }/, schema) - assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema) - end + def test_schema_dumper_for_uuid_primary_key + schema = dump_table_schema "pg_uuids" + assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: -> { "uuid_generate_v1\(\)" }/, schema) + assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema) + end + + def test_schema_dumper_for_uuid_primary_key_with_custom_default + schema = dump_table_schema "pg_uuids_2" + assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema) + assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema) + end - def test_schema_dumper_for_uuid_primary_key_with_custom_default - schema = dump_table_schema "pg_uuids_2" - assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema) - assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema) + def test_schema_dumper_for_uuid_primary_key_default + schema = dump_table_schema "pg_uuids_3" + if connection.supports_pgcrypto_uuid? + assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema) + else + assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) end + end + + def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration + @verbose_was = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false - def test_schema_dumper_for_uuid_primary_key_default - schema = dump_table_schema "pg_uuids_3" - if connection.supports_pgcrypto_uuid? - assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema) - else - assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) + migration = Class.new(ActiveRecord::Migration[5.0]) do + def version; 101 end + def migrate(x) + create_table("pg_uuids_4", id: :uuid) end - end + end.new + ActiveRecord::Migrator.new(:up, [migration]).migrate - def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration - @verbose_was = ActiveRecord::Migration.verbose - ActiveRecord::Migration.verbose = false - - migration = Class.new(ActiveRecord::Migration[5.0]) do - def version; 101 end - def migrate(x) - create_table("pg_uuids_4", id: :uuid) - end - end.new - ActiveRecord::Migrator.new(:up, [migration]).migrate - - schema = dump_table_schema "pg_uuids_4" - assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) - ensure - drop_table "pg_uuids_4" - ActiveRecord::Migration.verbose = @verbose_was - end + schema = dump_table_schema "pg_uuids_4" + assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema) + ensure + drop_table "pg_uuids_4" + ActiveRecord::Migration.verbose = @verbose_was end end @@ -302,38 +300,36 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase drop_table "pg_uuids" end - if ActiveRecord::Base.connection.supports_extensions? - def test_id_allows_default_override_via_nil - col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default - FROM pg_attribute a - LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum - WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first - assert_nil col_desc["default"] - end + def test_id_allows_default_override_via_nil + col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default + FROM pg_attribute a + LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum + WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first + assert_nil col_desc["default"] + end - def test_schema_dumper_for_uuid_primary_key_with_default_override_via_nil - schema = dump_table_schema "pg_uuids" - assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: nil/, schema) - end + def test_schema_dumper_for_uuid_primary_key_with_default_override_via_nil + schema = dump_table_schema "pg_uuids" + assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: nil/, schema) + end - def test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration - @verbose_was = ActiveRecord::Migration.verbose - ActiveRecord::Migration.verbose = false - - migration = Class.new(ActiveRecord::Migration[5.0]) do - def version; 101 end - def migrate(x) - create_table("pg_uuids_4", id: :uuid, default: nil) - end - end.new - ActiveRecord::Migrator.new(:up, [migration]).migrate - - schema = dump_table_schema "pg_uuids_4" - assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema) - ensure - drop_table "pg_uuids_4" - ActiveRecord::Migration.verbose = @verbose_was - end + def test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration + @verbose_was = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + + migration = Class.new(ActiveRecord::Migration[5.0]) do + def version; 101 end + def migrate(x) + create_table("pg_uuids_4", id: :uuid, default: nil) + end + end.new + ActiveRecord::Migrator.new(:up, [migration]).migrate + + schema = dump_table_schema "pg_uuids_4" + assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema) + ensure + drop_table "pg_uuids_4" + ActiveRecord::Migration.verbose = @verbose_was end end @@ -367,23 +363,21 @@ class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase drop_table "pg_uuid_posts" end - if ActiveRecord::Base.connection.supports_extensions? - def test_collection_association_with_uuid - post = UuidPost.create! - comment = post.uuid_comments.create! - assert post.uuid_comments.find(comment.id) - end + def test_collection_association_with_uuid + post = UuidPost.create! + comment = post.uuid_comments.create! + assert post.uuid_comments.find(comment.id) + end - def test_find_with_uuid - UuidPost.create! - assert_raise ActiveRecord::RecordNotFound do - UuidPost.find(123456) - end + def test_find_with_uuid + UuidPost.create! + assert_raise ActiveRecord::RecordNotFound do + UuidPost.find(123456) end + end - def test_find_by_with_uuid - UuidPost.create! - assert_nil UuidPost.find_by(id: 789) - end + def test_find_by_with_uuid + UuidPost.create! + assert_nil UuidPost.find_by(id: 789) end end diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb index 42b3841d41..61002435a4 100644 --- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb @@ -10,7 +10,7 @@ class SQLite3StatementPoolTest < ActiveRecord::SQLite3TestCase assert_equal "bar", cache["foo"] pid = fork { - lookup = cache["foo"]; + lookup = cache["foo"] exit!(!lookup) } 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 c6a4ac356f..046020e310 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1250,7 +1250,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase TenantMembership.current_member = nil end - def test_has_many_trough_with_scope_that_has_joined_same_table_with_parent_relation + def test_has_many_through_with_scope_that_has_joined_same_table_with_parent_relation assert_equal authors(:david), Author.joins(:comments_for_first_author).take end @@ -1258,6 +1258,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments end + def test_has_many_through_with_scope_should_accept_string_and_hash_join + assert_equal authors(:david), Author.joins({ comments_for_first_author: :post }, "inner join posts posts_alias on authors.id = posts_alias.author_id").eager_load(:categories).take + end + def test_has_many_through_with_scope_should_respect_table_alias family = Family.create! users = 3.times.map { User.create! } diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 3e37e512ca..65d30d011b 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -24,6 +24,11 @@ require "models/category" require "models/categorization" require "models/membership" require "models/essay" +require "models/hotel" +require "models/department" +require "models/chef" +require "models/cake_designer" +require "models/drink_designer" class NestedThroughAssociationsTest < ActiveRecord::TestCase fixtures :authors, :author_addresses, :books, :posts, :subscriptions, :subscribers, :tags, :taggings, @@ -425,6 +430,11 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert authors.empty? end + def test_nested_has_many_through_with_scope_on_polymorphic_reflection + authors = Author.joins(:ordered_posts).where("posts.id" => posts(:misc_by_bob).id) + assert_equal [authors(:mary), authors(:bob)], authors.distinct.sort_by(&:id) + end + def test_has_many_through_with_foreign_key_option_on_through_reflection assert_equal [posts(:welcome), posts(:authorless)], people(:david).agents_posts.order("posts.id") assert_equal [authors(:david)], references(:david_unicyclist).agents_posts_authors @@ -569,6 +579,37 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase assert !c.post_taggings.empty? end + def test_polymorphic_has_many_through_when_through_association_has_not_loaded + cake_designer = CakeDesigner.create!(chef: Chef.new) + drink_designer = DrinkDesigner.create!(chef: Chef.new) + department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef]) + Hotel.create!(departments: [department]) + hotel = Hotel.includes(:cake_designers, :drink_designers).take + + assert_equal [cake_designer], hotel.cake_designers + assert_equal [drink_designer], hotel.drink_designers + end + + def test_polymorphic_has_many_through_when_through_association_has_already_loaded + cake_designer = CakeDesigner.create!(chef: Chef.new) + drink_designer = DrinkDesigner.create!(chef: Chef.new) + department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef]) + Hotel.create!(departments: [department]) + hotel = Hotel.includes(:chefs, :cake_designers, :drink_designers).take + + assert_equal [cake_designer], hotel.cake_designers + assert_equal [drink_designer], hotel.drink_designers + end + + def test_polymorphic_has_many_through_joined_different_table_twice + cake_designer = CakeDesigner.create!(chef: Chef.new) + drink_designer = DrinkDesigner.create!(chef: Chef.new) + department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef]) + hotel = Hotel.create!(departments: [department]) + + assert_equal hotel, Hotel.joins(:cake_designers, :drink_designers).take + end + private def assert_includes_and_joins_equal(query, expected, association) diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb index c137693211..19d6464a22 100644 --- a/activerecord/test/cases/collection_cache_key_test.rb +++ b/activerecord/test/cases/collection_cache_key_test.rb @@ -73,6 +73,16 @@ module ActiveRecord assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3 end + test "cache_key for relation with includes" do + comments = Comment.includes(:post).where("posts.type": "Post") + assert_match(/\Acomments\/query-(\h+)-(\d+)-(\d+)\z/, comments.cache_key) + end + + test "cache_key for loaded relation with includes" do + comments = Comment.includes(:post).where("posts.type": "Post").load + assert_match(/\Acomments\/query-(\h+)-(\d+)-(\d+)\z/, comments.cache_key) + end + test "it triggers at most one query" do developers = Developer.where(name: "David") diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 6cbe18cc8c..f088c064f5 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -94,27 +94,31 @@ class PersistenceTest < ActiveRecord::TestCase end def test_delete_all_with_joins_and_where_part_is_hash - where_args = { toys: { name: "Bone" } } - count = Pet.joins(:toys).where(where_args).count + pets = Pet.joins(:toys).where(toys: { name: "Bone" }) - assert_equal count, 1 - assert_equal count, Pet.joins(:toys).where(where_args).delete_all + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all + end + + def test_delete_all_with_joins_and_where_part_is_not_hash + pets = Pet.joins(:toys).where("toys.name = ?", "Bone") + + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all end def test_delete_all_with_left_joins - where_args = { toys: { name: "Bone" } } - count = Pet.left_joins(:toys).where(where_args).count + pets = Pet.left_joins(:toys).where(toys: { name: "Bone" }) - assert_equal count, 1 - assert_equal count, Pet.left_joins(:toys).where(where_args).delete_all + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all end - def test_delete_all_with_joins_and_where_part_is_not_hash - where_args = ["toys.name = ?", "Bone"] - count = Pet.joins(:toys).where(where_args).count + def test_delete_all_with_includes + pets = Pet.includes(:toys).where(toys: { name: "Bone" }) - assert_equal count, 1 - assert_equal count, Pet.joins(:toys).where(where_args).delete_all + assert_equal true, pets.exists? + assert_equal pets.count, pets.delete_all end def test_increment_attribute @@ -496,17 +500,24 @@ class PersistenceTest < ActiveRecord::TestCase end def test_update_all_with_joins - where_args = { toys: { name: "Bone" } } - count = Pet.left_joins(:toys).where(where_args).count + pets = Pet.joins(:toys).where(toys: { name: "Bone" }) - assert_equal count, Pet.joins(:toys).where(where_args).update_all(name: "Bob") + assert_equal true, pets.exists? + assert_equal pets.count, pets.update_all(name: "Bob") end def test_update_all_with_left_joins - where_args = { toys: { name: "Bone" } } - count = Pet.left_joins(:toys).where(where_args).count + pets = Pet.left_joins(:toys).where(toys: { name: "Bone" }) + + assert_equal true, pets.exists? + assert_equal pets.count, pets.update_all(name: "Bob") + end + + def test_update_all_with_includes + pets = Pet.includes(:toys).where(toys: { name: "Bone" }) - assert_equal count, Pet.left_joins(:toys).where(where_args).update_all(name: "Bob") + assert_equal true, pets.exists? + assert_equal pets.count, pets.update_all(name: "Bob") end def test_update_all_with_non_standard_table_name diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 799a65c61e..ac5092f1c1 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -177,7 +177,7 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_schema_dumps_index_columns_in_right_order index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_index/).first.strip - if current_adapter?(:PostgreSQLAdapter) + if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition elsif current_adapter?(:Mysql2Adapter) if ActiveRecord::Base.connection.supports_index_sort_order? @@ -294,34 +294,32 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{t\.oid\s+"obj_id"$}, output end - if ActiveRecord::Base.connection.supports_extensions? - def test_schema_dump_includes_extensions - connection = ActiveRecord::Base.connection + def test_schema_dump_includes_extensions + connection = ActiveRecord::Base.connection - connection.stubs(:extensions).returns(["hstore"]) - output = perform_schema_dump - assert_match "# These are extensions that must be enabled", output - assert_match %r{enable_extension "hstore"}, output + connection.stubs(:extensions).returns(["hstore"]) + output = perform_schema_dump + assert_match "# These are extensions that must be enabled", output + assert_match %r{enable_extension "hstore"}, output - connection.stubs(:extensions).returns([]) - output = perform_schema_dump - assert_no_match "# These are extensions that must be enabled", output - assert_no_match %r{enable_extension}, output - end + connection.stubs(:extensions).returns([]) + output = perform_schema_dump + assert_no_match "# These are extensions that must be enabled", output + assert_no_match %r{enable_extension}, output + end - def test_schema_dump_includes_extensions_in_alphabetic_order - connection = ActiveRecord::Base.connection + def test_schema_dump_includes_extensions_in_alphabetic_order + connection = ActiveRecord::Base.connection - connection.stubs(:extensions).returns(["hstore", "uuid-ossp", "xml2"]) - output = perform_schema_dump - enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten - assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions + connection.stubs(:extensions).returns(["hstore", "uuid-ossp", "xml2"]) + output = perform_schema_dump + enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten + assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions - connection.stubs(:extensions).returns(["uuid-ossp", "xml2", "hstore"]) - output = perform_schema_dump - enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten - assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions - end + connection.stubs(:extensions).returns(["uuid-ossp", "xml2", "hstore"]) + output = perform_schema_dump + enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten + assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions end end diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index f3b84d88c2..116f8e83aa 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -240,6 +240,20 @@ class RelationScopingTest < ActiveRecord::TestCase assert_nil SpecialComment.current_scope end + def test_scoping_respects_current_class + Comment.unscoped do + assert_equal "a comment...", Comment.all.what_are_you + assert_equal "a special comment...", SpecialComment.all.what_are_you + end + end + + def test_scoping_respects_sti_constraint + Comment.unscoped do + assert_equal comments(:greetings), Comment.find(1) + assert_raises(ActiveRecord::RecordNotFound) { SpecialComment.find(1) } + end + end + def test_circular_joins_with_scoping_does_not_crash posts = Post.joins(comments: :post).scoping do Post.first(10) diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index e57ebf56c8..06a8693a7d 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -110,7 +110,7 @@ module ActiveRecord # FIXME: this needs to be refactored so specific database can add their own # ignored SQL, or better yet, use a different notification for the queries # instead examining the SQL content. - oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im] + oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im, /^\s*select .* from all_sequences/im] mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im] postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im] diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index e9eba9be2e..cb8686f315 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -101,10 +101,12 @@ class Author < ActiveRecord::Base has_many :taggings, through: :posts, source: :taggings has_many :taggings_2, through: :posts, source: :tagging has_many :tags, through: :posts + has_many :ordered_tags, through: :posts has_many :post_categories, through: :posts, source: :categories has_many :tagging_tags, through: :taggings, source: :tag has_many :similar_posts, -> { distinct }, through: :tags, source: :tagged_posts + has_many :ordered_posts, -> { distinct }, through: :ordered_tags, source: :tagged_posts has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, through: :posts, source: :tags has_many :tags_with_primary_key, through: :posts diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index 740aa593ac..5ab433f2d9 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -60,6 +60,10 @@ end class SpecialComment < Comment default_scope { where(deleted_at: nil) } + + def self.what_are_you + "a special comment..." + end end class SubSpecialComment < SpecialComment diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 935a11e811..7f064bf3dd 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -115,6 +115,7 @@ class Post < ActiveRecord::Base has_many :misc_tags, -> { where tags: { name: "Misc" } }, through: :taggings, source: :tag has_many :funky_tags, through: :taggings, source: :tag has_many :super_tags, through: :taggings + has_many :ordered_tags, through: :taggings has_many :tags_with_primary_key, through: :taggings, source: :tag_with_primary_key has_one :tagging, as: :taggable diff --git a/activerecord/test/models/tag.rb b/activerecord/test/models/tag.rb index 4495ac4a09..bc13c3a42d 100644 --- a/activerecord/test/models/tag.rb +++ b/activerecord/test/models/tag.rb @@ -12,4 +12,5 @@ class OrderedTag < Tag self.table_name = "tags" has_many :taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id" + has_many :tagged_posts, through: :taggings, source: "taggable", source_type: "Post" end diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb index fc0af026c5..861fde633f 100644 --- a/activerecord/test/models/tagging.rb +++ b/activerecord/test/models/tagging.rb @@ -8,6 +8,7 @@ class Tagging < ActiveRecord::Base belongs_to :tag, -> { includes(:tagging) } belongs_to :super_tag, class_name: "Tag", foreign_key: "super_tag_id" belongs_to :invalid_tag, class_name: "Tag", foreign_key: "tag_id" + belongs_to :ordered_tag, class_name: "OrderedTag", foreign_key: "tag_id" belongs_to :blue_tag, -> { where tags: { name: "Blue" } }, class_name: "Tag", foreign_key: :tag_id belongs_to :tag_with_primary_key, class_name: "Tag", foreign_key: :tag_id, primary_key: :custom_primary_key belongs_to :taggable, polymorphic: true, counter_cache: :tags_count diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 8f872c38ba..a4505a4892 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -191,6 +191,7 @@ ActiveRecord::Schema.define do t.string :resource_id t.string :resource_type t.integer :developer_id + t.datetime :updated_at t.datetime :deleted_at t.integer :comments end |