aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md61
-rw-r--r--activerecord/activerecord.gemspec2
-rw-r--r--activerecord/lib/active_record/associations.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb4
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb31
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb17
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb15
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb1
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb1
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb11
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb15
-rw-r--r--activerecord/lib/active_record/attribute_set/builder.rb21
-rw-r--r--activerecord/lib/active_record/attributes.rb23
-rw-r--r--activerecord/lib/active_record/autosave_association.rb4
-rw-r--r--activerecord/lib/active_record/callbacks.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb33
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb57
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb17
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_handling.rb2
-rw-r--r--activerecord/lib/active_record/core.rb29
-rw-r--r--activerecord/lib/active_record/errors.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb38
-rw-r--r--activerecord/lib/active_record/gem_version.rb2
-rw-r--r--activerecord/lib/active_record/inheritance.rb16
-rw-r--r--activerecord/lib/active_record/migration.rb8
-rw-r--r--activerecord/lib/active_record/model_schema.rb12
-rw-r--r--activerecord/lib/active_record/persistence.rb32
-rw-r--r--activerecord/lib/active_record/querying.rb11
-rw-r--r--activerecord/lib/active_record/railties/databases.rake3
-rw-r--r--activerecord/lib/active_record/reflection.rb36
-rw-r--r--activerecord/lib/active_record/relation.rb24
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb1
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb42
-rw-r--r--activerecord/lib/active_record/relation/merger.rb30
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb13
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb46
-rw-r--r--activerecord/lib/active_record/result.rb13
-rw-r--r--activerecord/lib/active_record/sanitization.rb5
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/scoping/named.rb4
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb28
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb1
-rw-r--r--activerecord/lib/active_record/transactions.rb6
-rw-r--r--activerecord/lib/active_record/type.rb1
-rw-r--r--activerecord/lib/active_record/type/big_integer.rb13
-rw-r--r--activerecord/lib/active_record/type/boolean.rb13
-rw-r--r--activerecord/lib/active_record/type/decimal_without_scale.rb4
-rw-r--r--activerecord/lib/active_record/type/integer.rb25
-rw-r--r--activerecord/lib/active_record/type/string.rb8
-rw-r--r--activerecord/lib/active_record/validations/associated.rb8
-rw-r--r--activerecord/lib/active_record/validations/presence.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb9
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb9
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/enum_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb54
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb11
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/sql_types_test.rb18
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb39
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb29
-rw-r--r--activerecord/test/cases/associations/eager_test.rb36
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb11
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb34
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb6
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb6
-rw-r--r--activerecord/test/cases/attributes_test.rb24
-rw-r--r--activerecord/test/cases/autosave_association_test.rb9
-rw-r--r--activerecord/test/cases/base_test.rb10
-rw-r--r--activerecord/test/cases/calculations_test.rb1
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb89
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb4
-rw-r--r--activerecord/test/cases/finder_test.rb37
-rw-r--r--activerecord/test/cases/fixtures_test.rb19
-rw-r--r--activerecord/test/cases/helper.rb19
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb19
-rw-r--r--activerecord/test/cases/migration/references_index_test.rb4
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb14
-rw-r--r--activerecord/test/cases/migrator_test.rb6
-rw-r--r--activerecord/test/cases/readonly_test.rb12
-rw-r--r--activerecord/test/cases/relation/where_chain_test.rb28
-rw-r--r--activerecord/test/cases/relation/where_test.rb1
-rw-r--r--activerecord/test/cases/result_test.rb4
-rw-r--r--activerecord/test/cases/sanitize_test.rb4
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb9
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb7
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb20
-rw-r--r--activerecord/test/cases/serialization_test.rb9
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb5
-rw-r--r--activerecord/test/cases/type/integer_test.rb106
-rw-r--r--activerecord/test/cases/type/string_test.rb4
-rw-r--r--activerecord/test/cases/types_test.rb57
-rw-r--r--activerecord/test/config.example.yml6
-rw-r--r--activerecord/test/fixtures/computers.yml5
-rw-r--r--activerecord/test/fixtures/developers.yml1
-rw-r--r--activerecord/test/models/author.rb2
-rw-r--r--activerecord/test/models/developer.rb2
-rw-r--r--activerecord/test/models/member.rb3
-rw-r--r--activerecord/test/models/post.rb13
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb15
-rw-r--r--activerecord/test/schema/schema.rb12
123 files changed, 1299 insertions, 523 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 92e1575484..2c66ddb983 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,9 +1,66 @@
-* Cache CollectionAssociation#reader proxies separately before and after
+* Cache `CollectionAssociation#reader` proxies separately before and after
the owner has been saved so that the proxy is not cached without the
owner's id.
*Ben Woosley*
+* `ActiveRecord::ReadOnlyRecord` now has a descriptive message.
+
+ *Franky W.*
+
+* Fix preloading of associations which unscope a default scope.
+
+ Fixes #11036.
+
+ *Byron Bischoff*
+
+* Added SchemaDumper support for tables with jsonb columns.
+
+ *Ted O'Meara*
+
+* Deprecate `sanitize_sql_hash_for_conditions` without replacement. Using a
+ `Relation` for performing queries and updates is the prefered API.
+
+ *Sean Griffin*
+
+* Queries now properly type cast values that are part of a join statement,
+ even when using type decorators such as `serialize`.
+
+ *Melanie Gilman & Sean Griffin*
+
+* MySQL enum type lookups, with values matching another type, no longer result
+ in an endless loop.
+
+ Fixes #17402.
+
+ *Yves Senn*
+
+* Raise `ArgumentError` when the body of a scope is not callable.
+
+ *Mauro George*
+
+* Use type column first in multi-column indexes created with `add-reference`.
+
+ *Derek Prior*
+
+* Fix `Relation.rewhere` to work with Range values.
+
+ *Dan Olson*
+
+* `AR::UnknownAttributeError` now includes the class name of a record.
+
+ User.new(name: "Yuki Nishijima", project_attributes: {name: "kaminari"})
+ # => ActiveRecord::UnknownAttributeError: unknown attribute 'name' for User.
+
+ *Yuki Nishijima*
+
+* Fix regression causing `after_create` callbacks to run before associated
+ records are autosaved.
+
+ Fixes #17209.
+
+ *Agis Anastasopoulos*
+
* Honor overridden `rack.test` in Rack environment for the connection
management middleware.
@@ -219,7 +276,7 @@
*Kenn Ejima*
-* Add support for Postgresql JSONB.
+* Add support for PostgreSQL JSONB.
Example:
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index 6231851be5..bc10e96244 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -24,5 +24,5 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
s.add_dependency 'activemodel', version
- s.add_dependency 'arel', '>= 6.0.0.beta1', '< 6.1'
+ s.add_dependency 'arel', '>= 6.0.0.beta2', '< 6.1'
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 12ca3a48a9..d2bcdc55bf 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -46,6 +46,12 @@ module ActiveRecord
end
end
+ class HasOneAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
+ def initialize(owner_class_name, reflection)
+ super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
+ end
+ end
+
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
def initialize(reflection)
through_reflection = reflection.through_reflection
@@ -1694,7 +1700,7 @@ module ActiveRecord
hm_options[:through] = middle_reflection.name
hm_options[:source] = join_model.right_reflection.name
- [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table].each do |k|
+ [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name].each do |k|
hm_options[k] = options[k] if options.key? k
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 34a555dfd4..357b28ac94 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
@@ -11,7 +11,7 @@ module ActiveRecord::Associations::Builder
end
def join_table
- @join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
+ @join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
end
private
@@ -98,7 +98,7 @@ module ActiveRecord::Associations::Builder
def middle_options(join_model)
middle_options = {}
- middle_options[:class] = join_model
+ middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
middle_options[:source] = join_model.left_reflection.name
if options.key? :foreign_key
middle_options[:foreign_key] = options[:foreign_key]
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index c5dff7ee1a..93f611dd8d 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -413,7 +413,13 @@ module ActiveRecord
private
def get_records
- return scope.to_a if reflection.scope_chain.any?(&:any?) || scope.eager_loading?
+ if reflection.scope_chain.any?(&:any?) ||
+ scope.eager_loading? ||
+ klass.current_scope ||
+ klass.default_scopes.any?
+
+ return scope.to_a
+ end
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 8c7b0b4be9..060b2278d9 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -355,14 +355,15 @@ module ActiveRecord
@association.replace(other_array)
end
- # Deletes all the records from the collection. For +has_many+ associations,
- # the deletion is done according to the strategy specified by the <tt>:dependent</tt>
- # option.
+ # Deletes all the records from the collection according to the strategy
+ # specified by the +:dependent+ option. If no +:dependent+ option is given,
+ # then it will follow the default strategy.
#
- # If no <tt>:dependent</tt> option is given, then it will follow the
- # default strategy. The default strategy is <tt>:nullify</tt>. This
- # sets the foreign keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>,
- # the default strategy is +delete_all+.
+ # For +has_many :through+ associations, the default deletion strategy is
+ # +:delete_all+.
+ #
+ # For +has_many+ associations, the default deletion strategy is +:nullify+.
+ # This sets the foreign keys to +NULL+.
#
# class Person < ActiveRecord::Base
# has_many :pets # dependent: :nullify option by default
@@ -393,9 +394,9 @@ module ActiveRecord
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
# # ]
#
- # If it is set to <tt>:destroy</tt> all the objects from the collection
- # are removed by calling their +destroy+ method. See +destroy+ for more
- # information.
+ # Both +has_many+ and +has_many :through+ dependencies default to the
+ # +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
+ # Records are not instantiated and callbacks will not be fired.
#
# class Person < ActiveRecord::Base
# has_many :pets, dependent: :destroy
@@ -410,11 +411,6 @@ module ActiveRecord
# # ]
#
# person.pets.delete_all
- # # => [
- # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
- # # #<Pet id: 2, name: "Spook", person_id: 1>,
- # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
- # # ]
#
# Pet.find(1, 2, 3)
# # => ActiveRecord::RecordNotFound
@@ -443,8 +439,9 @@ module ActiveRecord
end
# Deletes the records of the collection directly from the database
- # ignoring the +:dependent+ option. It invokes +before_remove+,
- # +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
+ # ignoring the +:dependent+ option. Records are instantiated and it
+ # invokes +before_remove+, +after_remove+ , +before_destroy+ and
+ # +after_destroy+ callbacks.
#
# class Person < ActiveRecord::Base
# has_many :pets
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 455a540bdb..bde23fc116 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/string/filters'
+
module ActiveRecord
# = Active Record Has Many Through Association
module Associations
@@ -63,11 +65,12 @@ module ActiveRecord
save_through_record(record)
if has_cached_counter? && !through_reflection_updates_counter_cache?
- ActiveSupport::Deprecation.warn \
- "Automatic updating of counter caches on through associations has been " \
- "deprecated, and will be removed in Rails 5.0. Instead, please set the " \
- "appropriate counter_cache options on the has_many and belongs_to for " \
- "your associations to #{through_reflection.name}."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Automatic updating of counter caches on through associations has been
+ deprecated, and will be removed in Rails 5. Instead, please set the
+ appropriate `counter_cache` options on the `has_many` and `belongs_to`
+ for your associations to #{through_reflection.name}.
+ MSG
update_counter_in_database(1)
end
@@ -158,8 +161,8 @@ module ActiveRecord
if scope.klass.primary_key
count = scope.destroy_all.length
else
- scope.to_a.each do |record|
- record.run_destroy_callbacks
+ scope.each do |record|
+ record._run_destroy_callbacks
end
arel = scope.arel
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index ec5c189cd3..c5c4edd090 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -142,11 +142,20 @@ module ActiveRecord
parents = model_cache[join_root]
column_aliases = aliases.column_aliases join_root
- result_set.each { |row_hash|
- parent = parents[row_hash[primary_key]] ||= join_root.instantiate(row_hash, column_aliases)
- construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
+ message_bus = ActiveSupport::Notifications.instrumenter
+
+ payload = {
+ record_count: result_set.length,
+ class_name: join_root.base_klass.name
}
+ message_bus.instrument('instantiation.active_record', payload) do
+ result_set.each { |row_hash|
+ parent = parents[row_hash[primary_key]] ||= join_root.instantiate(row_hash, column_aliases)
+ construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
+ }
+ end
+
parents.values
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index c0639742be..496c426986 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -159,6 +159,7 @@ module ActiveRecord
scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
end
+ scope.unscope_values = Array(values[:unscope])
klass.default_scoped.merge(scope)
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index d57da366bd..12bf3ef138 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -81,6 +81,7 @@ module ActiveRecord
unless reflection_scope.where_values.empty?
scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
scope.where_values = reflection_scope.values[:where]
+ scope.bind_values = reflection_scope.bind_values
end
scope.references! reflection_scope.values[:references]
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index b9326b9683..c44242a0f0 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -39,7 +39,13 @@ module ActiveRecord
end
def get_records
- return scope.limit(1).to_a if reflection.scope_chain.any?(&:any?) || scope.eager_loading?
+ if reflection.scope_chain.any?(&:any?) ||
+ scope.eager_loading? ||
+ klass.current_scope ||
+ klass.default_scopes.any?
+
+ return scope.limit(1).to_a
+ end
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index f4a4e3f605..34ec397aee 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/enumerable'
+require 'active_support/core_ext/string/filters'
require 'mutex_m'
require 'thread_safe'
@@ -90,7 +91,7 @@ module ActiveRecord
def undefine_attribute_methods # :nodoc:
generated_attribute_methods.synchronize do
- super if @attribute_methods_generated
+ super if defined?(@attribute_methods_generated) && @attribute_methods_generated
@attribute_methods_generated = false
end
end
@@ -205,9 +206,11 @@ module ActiveRecord
def column_for_attribute(name)
column = columns_hash[name.to_s]
if column.nil?
- ActiveSupport::Deprecation.warn \
- "`column_for_attribute` will return a null object for non-existent columns " \
- "in Rails 5.0. Use `has_attribute?` if you need to check for an attribute's existence."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `#column_for_attribute` will return a null object for non-existent
+ columns in Rails 5. Use `#has_attribute?` if you need to check for
+ an attribute's existence.
+ MSG
end
column
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 100d6d4229..e5ec5ddca5 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/string/filters'
+
module ActiveRecord
module AttributeMethods
module Serialization
@@ -8,11 +10,8 @@ module ActiveRecord
# object, and retrieved as the same object, then specify the name of that
# attribute using this method and it will be handled automatically. The
# serialization is done through YAML. If +class_name+ is specified, the
- # serialized object must be of that class on retrieval or
- # <tt>SerializationTypeMismatch</tt> will be raised.
- #
- # A notable side effect of serialized attributes is that the model will
- # be updated on every save, even if it is not dirty.
+ # serialized object must be of that class on assignment and retrieval.
+ # Otherwise <tt>SerializationTypeMismatch</tt> will be raised.
#
# ==== Parameters
#
@@ -54,8 +53,10 @@ module ActiveRecord
end
def serialized_attributes
- ActiveSupport::Deprecation.warn "`serialized_attributes` is deprecated " \
- "without replacement, and will be removed in Rails 5.0."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `serialized_attributes` is deprecated without replacement, and will
+ be removed in Rails 5.0.
+ MSG
@serialized_attributes ||= Hash[
columns.select { |t| t.cast_type.is_a?(Type::Serialized) }.map { |c|
diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb
index d4a787f2fe..0a62c68bfb 100644
--- a/activerecord/lib/active_record/attribute_set/builder.rb
+++ b/activerecord/lib/active_record/attribute_set/builder.rb
@@ -8,18 +8,33 @@ module ActiveRecord
end
def build_from_database(values = {}, additional_types = {})
- attributes = build_attributes_from_values(values, additional_types)
+ build_from_database_pairs(values.keys, values.values, additional_types)
+ end
+
+ def build_from_database_pairs(columns, values, additional_types)
+ attributes = build_attributes_from_values(columns, values, additional_types)
add_uninitialized_attributes(attributes)
AttributeSet.new(attributes)
end
private
- def build_attributes_from_values(values, additional_types)
- values.each_with_object({}) do |(name, value), hash|
+ def build_attributes_from_values(columns, values, additional_types)
+ # We are performing manual iteration here as this method is a performance
+ # hotspot
+ hash = {}
+ index = 0
+ length = columns.length
+
+ while index < length
+ name = columns[index]
+ value = values[index]
type = additional_types.fetch(name, types[name])
hash[name] = Attribute.from_database(name, value, type)
+ index += 1
end
+
+ hash
end
def add_uninitialized_attributes(attributes)
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 890a1314d9..3288108a6a 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -6,7 +6,9 @@ module ActiveRecord
included do
class_attribute :user_provided_columns, instance_accessor: false # :internal:
+ class_attribute :user_provided_defaults, instance_accessor: false # :internal:
self.user_provided_columns = {}
+ self.user_provided_defaults = {}
end
module ClassMethods # :nodoc:
@@ -77,7 +79,11 @@ module ActiveRecord
name = name.to_s
clear_caches_calculated_from_columns
# Assign a new hash to ensure that subclasses do not share a hash
- self.user_provided_columns = user_provided_columns.merge(name => connection.new_column(name, options[:default], cast_type))
+ self.user_provided_columns = user_provided_columns.merge(name => cast_type)
+
+ if options.key?(:default)
+ self.user_provided_defaults = user_provided_defaults.merge(name => options[:default])
+ end
end
# Returns an array of column objects for the table associated with this class.
@@ -99,11 +105,18 @@ module ActiveRecord
def add_user_provided_columns(schema_columns)
existing_columns = schema_columns.map do |column|
- user_provided_columns[column.name] || column
+ new_type = user_provided_columns[column.name]
+ if new_type
+ column.with_type(new_type)
+ else
+ column
+ end
end
existing_column_names = existing_columns.map(&:name)
- new_columns = user_provided_columns.except(*existing_column_names).values
+ new_columns = user_provided_columns.except(*existing_column_names).map do |(name, type)|
+ connection.new_column(name, nil, type)
+ end
existing_columns + new_columns
end
@@ -117,6 +130,10 @@ module ActiveRecord
@content_columns = nil
@default_attributes = nil
end
+
+ def raw_default_values
+ super.merge(user_provided_defaults)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index c384e8c413..a0d70435fa 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -184,7 +184,9 @@ module ActiveRecord
before_save :before_save_collection_association
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
- after_save save_method
+ # Doesn't use after_save as that would save associations added in after_create/after_update twice
+ after_create save_method
+ after_update save_method
elsif reflection.has_one?
define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
# Configures two callbacks instead of a single after_save so that
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 1aa760157a..523d492a48 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -289,25 +289,25 @@ module ActiveRecord
end
def destroy #:nodoc:
- run_destroy_callbacks { super }
+ _run_destroy_callbacks { super }
end
def touch(*) #:nodoc:
- run_touch_callbacks { super }
+ _run_touch_callbacks { super }
end
private
def create_or_update #:nodoc:
- run_save_callbacks { super }
+ _run_save_callbacks { super }
end
def _create_record #:nodoc:
- run_create_callbacks { super }
+ _run_create_callbacks { super }
end
def _update_record(*) #:nodoc:
- run_update_callbacks { super }
+ _run_update_callbacks { super }
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index da43e5bb10..46812b75bb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -2,6 +2,7 @@ require 'thread'
require 'thread_safe'
require 'monitor'
require 'set'
+require 'active_support/core_ext/string/filters'
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -360,7 +361,7 @@ module ActiveRecord
synchronize do
owner = conn.owner
- conn.run_checkin_callbacks do
+ conn._run_checkin_callbacks do
conn.expire
end
@@ -449,7 +450,7 @@ module ActiveRecord
end
def checkout_and_verify(c)
- c.run_checkout_callbacks do
+ c._run_checkout_callbacks do
c.verify!
end
c
@@ -518,10 +519,11 @@ module ActiveRecord
end
def connection_pools
- ActiveSupport::Deprecation.warn(
- "In the next release, this will return the same as #connection_pool_list. " \
- "(An array of pools, rather than a hash mapping specs to pools.)"
- )
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ In the next release, this will return the same as `#connection_pool_list`.
+ (An array of pools, rather than a hash mapping specs to pools.)
+ MSG
+
Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
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 ff8b3e9890..1a3ed28d66 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -337,8 +337,9 @@ module ActiveRecord
# Returns an ActiveRecord::Result instance.
def select(sql, name = nil, binds = [])
+ exec_query(sql, name, binds)
end
- undef_method :select
+
# Returns the last auto-generated ID from the affected table.
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index eb88845913..679878d860 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -108,7 +108,7 @@ module ActiveRecord
when Numeric, ActiveSupport::Duration then value.to_s
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
- when Class then "'#{value.to_s}'"
+ when Class then "'#{value}'"
else
"'#{quote_string(YAML.dump(value))}'"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index fe00f9d750..dd4c4ae9fc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -60,11 +60,11 @@ module ActiveRecord
def emit_warning_if_null_unspecified(options)
return if options.key?(:null)
- ActiveSupport::Deprecation.warn \
- "`timestamp` was called without specifying an option for `null`. In Rails " \
- "5.0, this behavior will change to `null: false`. You should manually " \
- "specify `null: true` to prevent the behavior of your existing migrations " \
- "from changing."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ `#timestamp` was called without specifying an option for `null`. In Rails 5,
+ this behavior will change to `null: false`. You should manually specify
+ `null: true` to prevent the behavior of your existing migrations from changing.
+ MSG
end
end
@@ -124,8 +124,8 @@ module ActiveRecord
# which is one of the following:
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
# <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
- # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
- # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
#
# You may use a type not in this list as long as it is supported by your
# database (for example, "polygon" in MySQL), but this will not be database
@@ -256,7 +256,7 @@ module ActiveRecord
name = name.to_s
type = type.to_sym
- if primary_key_column_name == name
+ if @columns_hash[name] && @columns_hash[name].primary_key?
raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
end
@@ -270,7 +270,7 @@ module ActiveRecord
@columns_hash.delete name.to_s
end
- [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
+ [:string, :text, :integer, :bigint, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
define_method column_type do |*args|
options = args.extract_options!
column_names = args
@@ -312,13 +312,13 @@ module ActiveRecord
args.each do |col|
column("#{col}_id", type, options)
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
- index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
+ index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
end
end
alias :belongs_to :references
def new_column_definition(name, type, options) # :nodoc:
- type = aliased_types[type] || type
+ type = aliased_types(type.to_s, type)
column = create_column_definition name, type
limit = options.fetch(:limit) do
native[type][:limit] if native[type].is_a?(Hash)
@@ -340,19 +340,12 @@ module ActiveRecord
ColumnDefinition.new name, type
end
- def primary_key_column_name
- primary_key_column = columns.detect { |c| c.primary_key? }
- primary_key_column && primary_key_column.name
- end
-
def native
@native
end
- def aliased_types
- HashWithIndifferentAccess.new(
- timestamp: :datetime,
- )
+ def aliased_types(name, fallback)
+ 'timestamp' == name ? :datetime : fallback
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index b05a4f8440..6eab11b88b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -8,7 +8,7 @@ module ActiveRecord
module ColumnDumper
def column_spec(column, types)
spec = prepare_column_options(column, types)
- (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.to_s}: ")}
+ (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k}: ")}
spec
end
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 7105df1ee4..cbf87df356 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -629,7 +629,7 @@ module ActiveRecord
type = options.delete(:type) || :integer
add_column(table_name, "#{ref_name}_id", type, options)
add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
- add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
+ add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
end
alias :add_belongs_to :add_reference
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index a0d9086875..582dd360f0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -14,10 +14,7 @@ module ActiveRecord
module ConnectionAdapters # :nodoc:
extend ActiveSupport::Autoload
- autoload_at 'active_record/connection_adapters/column' do
- autoload :Column
- autoload :NullColumn
- end
+ autoload :Column
autoload :ConnectionSpecification
autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
@@ -267,7 +264,7 @@ module ActiveRecord
# Returns a bind substitution value given a bind +index+ and +column+
# NOTE: The column param is currently being used by the sqlserver-adapter
- def substitute_at(column, index)
+ def substitute_at(column, index = 0)
Arel::Nodes::BindParam.new '?'
end
@@ -386,6 +383,10 @@ module ActiveRecord
type_map.lookup(sql_type)
end
+ def column_name_for_operation(operation, node) # :nodoc:
+ node.to_sql
+ end
+
protected
def initialize_type_map(m) # :nodoc:
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 e4cfe843a8..26015528e5 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -91,6 +91,13 @@ module ActiveRecord
collation && !collation.match(/_ci$/)
end
+ def ==(other)
+ super &&
+ collation == other.collation &&
+ strict == other.strict &&
+ extra == other.extra
+ end
+
private
# MySQL misreports NOT NULL column default when none is given.
@@ -109,6 +116,10 @@ module ActiveRecord
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
end
end
+
+ def attributes_for_hash
+ super + [collation, strict, extra]
+ end
end
##
@@ -485,7 +496,7 @@ module ActiveRecord
end
end
- def change_column_default(table_name, column_name, default)
+ def change_column_default(table_name, column_name, default) #:nodoc:
column = column_for(table_name, column_name)
change_column table_name, column_name, column.sql_type, :default => default
end
@@ -574,14 +585,6 @@ module ActiveRecord
end
end
- def add_column_position!(sql, options)
- if options[:first]
- sql << " FIRST"
- elsif options[:after]
- sql << " AFTER #{quote_column_name(options[:after])}"
- end
- end
-
# SHOW VARIABLES LIKE 'name'
def show_variable(name)
variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
@@ -645,11 +648,7 @@ module ActiveRecord
def initialize_type_map(m) # :nodoc:
super
- m.register_type(%r(enum)i) do |sql_type|
- limit = sql_type[/^enum\((.+)\)/i, 1]
- .split(',').map{|enum| enum.strip.length - 2}.max
- Type::String.new(limit: limit)
- end
+ register_class_with_limit m, %r(char)i, MysqlString
m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
@@ -671,6 +670,12 @@ module ActiveRecord
m.alias_type %r(set)i, 'varchar'
m.alias_type %r(year)i, 'integer'
m.alias_type %r(bit)i, 'binary'
+
+ m.register_type(%r(enum)i) do |sql_type|
+ limit = sql_type[/^enum\((.+)\)/i, 1]
+ .split(',').map{|enum| enum.strip.length - 2}.max
+ MysqlString.new(limit: limit)
+ end
end
# MySQL is too stupid to create a temporary table for use subquery, so we have
@@ -825,9 +830,9 @@ module ActiveRecord
# Gather up all of the SET variables...
variable_assignments = variables.map do |k, v|
if v == ':default' || v == :default
- "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
+ "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
elsif !v.nil?
- "@@SESSION.#{k.to_s} = #{quote(v)}"
+ "@@SESSION.#{k} = #{quote(v)}"
end
# or else nil; compact to clear nils out
end.compact.join(', ')
@@ -844,6 +849,26 @@ module ActiveRecord
end
end
end
+
+ class MysqlString < Type::String # :nodoc:
+ def type_cast_for_database(value)
+ case value
+ when true then "1"
+ when false then "0"
+ else super
+ end
+ end
+
+ private
+
+ def cast_value(value)
+ case value
+ when true then "1"
+ when false then "0"
+ else super
+ end
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 5f9cc6edd0..dd303c73d5 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -56,6 +56,26 @@ module ActiveRecord
clone.instance_variable_set('@cast_type', type)
end
end
+
+ def ==(other)
+ other.name == name &&
+ other.default == default &&
+ other.cast_type == cast_type &&
+ other.sql_type == sql_type &&
+ other.null == null &&
+ other.default_function == default_function
+ end
+ alias :eql? :==
+
+ def hash
+ attributes_for_hash.hash
+ end
+
+ private
+
+ def attributes_for_hash
+ [self.class, name, default, cast_type, sql_type, null, default_function]
+ end
end
end
# :startdoc:
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index e02824b33d..609ec7dabd 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -1,4 +1,5 @@
require 'uri'
+require 'active_support/core_ext/string/filters'
module ActiveRecord
module ConnectionAdapters
@@ -221,8 +222,12 @@ module ActiveRecord
# this ambiguous behaviour and in the future this function
# can be removed in favor of resolve_url_connection.
if configurations.key?(spec) || spec !~ /:/
- ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \
- "for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead"
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing a string to ActiveRecord::Base.establish_connection for a
+ configuration lookup is deprecated, please pass a symbol
+ (#{spec.to_sym.inspect}) instead.
+ MSG
+
resolve_symbol_connection(spec)
else
resolve_url_connection(spec)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 38bdddefba..5b83131f0e 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -232,11 +232,6 @@ module ActiveRecord
alias exec_without_stmt exec_query
- # Returns an ActiveRecord::Result instance.
- def select(sql, name = nil, binds = [])
- exec_query(sql, name)
- end
-
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
super
id_value || @connection.last_id
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index da3aecf69a..3357ed52e5 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -88,7 +88,7 @@ module ActiveRecord
end
def clear
- cache.values.each do |hash|
+ cache.each_value do |hash|
hash[:stmt].close
end
cache.clear
@@ -465,7 +465,7 @@ module ActiveRecord
def select(sql, name = nil, binds = [])
@connection.query_with_result = true
- rows = exec_query(sql, name, binds)
+ rows = super
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
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 89a7257d77..cf379ab210 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -156,7 +156,7 @@ module ActiveRecord
end
end
- def substitute_at(column, index)
+ def substitute_at(column, index = 0)
Arel::Nodes::BindParam.new "$#{index + 1}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
index a53b4ee8e2..222f10fa8f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
@@ -12,15 +12,15 @@ module ActiveRecord
# If the subnet mask is equal to /32, don't output it
if subnet_mask == (2**32 - 1)
- "\"#{value.to_s}\""
+ "\"#{value}\""
else
- "\"#{value.to_s}/#{subnet_mask.to_s(2).count('1')}\""
+ "\"#{value}/#{subnet_mask.to_s(2).count('1')}\""
end
end
def type_cast_for_database(value)
if IPAddr === value
- "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
+ "#{value}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
else
value
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index 84b9490ba3..961e6224c4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/string/filters'
+
module ActiveRecord
module ConnectionAdapters
module PostgreSQL
@@ -25,10 +27,11 @@ module ActiveRecord
if !infinity?(from) && extracted[:exclude_start]
if from.respond_to?(:succ)
from = from.succ
- ActiveSupport::Deprecation.warn \
- "Excluding the beginning of a Range is only partialy supported " \
- "through `#succ`. This is not reliable and will be removed in " \
- "the future."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Excluding the beginning of a Range is only partialy supported
+ through `#succ`. This is not reliable and will be removed in
+ the future.
+ MSG
else
raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')"
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
index e396ff4a1e..35e699eeda 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb
@@ -4,7 +4,7 @@ module ActiveRecord
module OID # :nodoc:
# This class uses the data from PostgreSQL pg_type table to build
# the OID -> Type mapping.
- # - OID is and integer representing the type.
+ # - OID is an integer representing the type.
# - Type is an OID::Type object.
# This class has side effects on the +store+ passed during initialization.
class TypeMapInitializer # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index cf5c8d288e..f95f45c689 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -21,7 +21,7 @@ module ActiveRecord
case value
when Float
if value.infinite? || value.nan?
- "'#{value.to_s}'"
+ "'#{value}'"
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 799aafbd99..51853c6812 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -293,6 +293,23 @@ module ActiveRecord
result.rows.first.first
end
+ # Sets the sequence of a table's primary key to the specified value.
+ def set_pk_sequence!(table, value) #:nodoc:
+ pk, sequence = pk_and_sequence_for(table)
+
+ if pk
+ if sequence
+ quoted_sequence = quote_table_name(sequence)
+
+ select_value <<-end_sql, 'SCHEMA'
+ SELECT setval('#{quoted_sequence}', #{value})
+ end_sql
+ else
+ @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
+ end
+ end
+ end
+
# Resets the sequence of a table's primary key to the maximum value.
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
unless pk and sequence
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 0d74cb6707..9941f74766 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -78,6 +78,7 @@ module ActiveRecord
NATIVE_DATABASE_TYPES = {
primary_key: "serial primary key",
+ bigserial: "bigserial",
string: { name: "character varying" },
text: { name: "text" },
integer: { name: "integer" },
@@ -94,6 +95,7 @@ module ActiveRecord
int8range: { name: "int8range" },
binary: { name: "bytea" },
boolean: { name: "boolean" },
+ bigint: { name: "bigint" },
xml: { name: "xml" },
tsvector: { name: "tsvector" },
hstore: { name: "hstore" },
@@ -102,6 +104,7 @@ module ActiveRecord
macaddr: { name: "macaddr" },
uuid: { name: "uuid" },
json: { name: "json" },
+ jsonb: { name: "jsonb" },
ltree: { name: "ltree" },
citext: { name: "citext" },
point: { name: "point" },
@@ -391,6 +394,16 @@ module ActiveRecord
super(oid)
end
+ def column_name_for_operation(operation, node) # :nodoc:
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
+ end
+
+ OPERATION_ALIASES = { # :nodoc:
+ "maximum" => "max",
+ "minimum" => "min",
+ "average" => "avg",
+ }
+
protected
# Returns the version of the connected PostgreSQL server.
@@ -677,9 +690,9 @@ module ActiveRecord
variables.map do |k, v|
if v == ':default' || v == :default
# Sets the value to the global or compile default
- execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
+ execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
elsif !v.nil?
- execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
+ execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
end
end
end
@@ -697,12 +710,6 @@ module ActiveRecord
exec_query("SELECT currval('#{sequence_name}')", 'SQL')
end
- # Executes a SELECT query and returns the results, performing any data type
- # conversions that are required to be performed here instead of in PostgreSQLColumn.
- def select(sql, name = nil, binds = [])
- exec_query(sql, name, binds)
- end
-
# Returns the list of a table's column names, data types, and default values.
#
# The underlying query is roughly:
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index ebb311df57..1b5e3bdbac 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -115,7 +115,7 @@ module ActiveRecord
end
def clear
- cache.values.each do |hash|
+ cache.each_value do |hash|
dealloc hash[:stmt]
end
cache.clear
@@ -514,10 +514,6 @@ module ActiveRecord
register_class_with_limit m, %r(char)i, SQLite3String
end
- def select(sql, name = nil, binds = []) #:nodoc:
- exec_query(sql, name, binds)
- end
-
def table_structure(table_name)
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 31e7390bf7..8f51590c99 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -1,6 +1,6 @@
module ActiveRecord
module ConnectionHandling
- RAILS_ENV = -> { Rails.env if defined?(Rails) }
+ RAILS_ENV = -> { (Rails.env if defined?(Rails)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
# Establishes the connection to the database. Accepts a hash as input where
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 5571a2d297..952aeaa703 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -1,6 +1,7 @@
+require 'thread'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/object/duplicable'
-require 'thread'
+require 'active_support/core_ext/string/filters'
module ActiveRecord
module Core
@@ -88,8 +89,10 @@ module ActiveRecord
mattr_accessor :maintain_test_schema, instance_accessor: false
def self.disable_implicit_join_references=(value)
- ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \
- "Make sure to remove this configuration because it does nothing.")
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Implicit join references were removed with Rails 4.1.
+ Make sure to remove this configuration because it does nothing.
+ MSG
end
class_attribute :default_connection_handler, instance_writer: false
@@ -135,8 +138,10 @@ module ActiveRecord
id = ids.first
if ActiveRecord::Base === id
id = id.id
- ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `find`." \
- "Please pass the id of the object by calling `.id`"
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ You are passing an instance of ActiveRecord::Base to `find`.
+ Please pass the id of the object by calling `.id`
+ MSG
end
key = primary_key
@@ -150,6 +155,8 @@ module ActiveRecord
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
end
record
+ rescue RangeError
+ raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
end
def find_by(*args)
@@ -180,6 +187,8 @@ module ActiveRecord
s.execute(hash.values, self, connection).first
rescue TypeError => e
raise ActiveRecord::StatementInvalid.new(e.message, e)
+ rescue RangeError
+ nil
end
end
@@ -261,7 +270,7 @@ module ActiveRecord
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
def initialize(attributes = nil, options = {})
- @attributes = self.class.default_attributes.dup
+ @attributes = self.class._default_attributes.dup
init_internals
initialize_internals_callback
@@ -272,7 +281,7 @@ module ActiveRecord
init_attributes(attributes, options) if attributes
yield self if block_given?
- run_initialize_callbacks
+ _run_initialize_callbacks
end
# Initialize an empty model object from +coder+. +coder+ must contain
@@ -294,8 +303,8 @@ module ActiveRecord
self.class.define_attribute_methods
- run_find_callbacks
- run_initialize_callbacks
+ _run_find_callbacks
+ _run_initialize_callbacks
self
end
@@ -331,7 +340,7 @@ module ActiveRecord
@attributes = @attributes.dup
@attributes.reset(self.class.primary_key)
- run_initialize_callbacks
+ _run_initialize_callbacks
@aggregation_cache = {}
@association_cache = {}
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 52c70977ef..5b3fdf16f5 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -167,7 +167,7 @@ module ActiveRecord
def initialize(record, attribute)
@record = record
@attribute = attribute.to_s
- super("unknown attribute: #{attribute}")
+ super("unknown attribute '#{attribute}' for #{@record.class}.")
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 44cc1a079f..db6421dacb 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -649,7 +649,7 @@ module ActiveRecord
model_class
end
- reflection_class._reflections.values.each do |association|
+ reflection_class._reflections.each_value do |association|
case association.macro
when :belongs_to
# Do not replace association name with association foreign key if they are named the same
@@ -870,34 +870,9 @@ module ActiveRecord
end
self.fixture_table_names |= fixture_set_names
- require_fixture_classes(fixture_set_names, self.config)
setup_fixture_accessors(fixture_set_names)
end
- def try_to_load_dependency(file_name)
- require_dependency file_name
- rescue LoadError => e
- unless fixture_class_names.key?(file_name.pluralize)
- if ActiveRecord::Base.logger
- ActiveRecord::Base.logger.warn("Unable to load #{file_name}, make sure you added it to ActiveSupport::TestCase.set_fixture_class")
- ActiveRecord::Base.logger.warn("underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
- end
- end
- end
-
- def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
- if fixture_set_names
- fixture_set_names = fixture_set_names.map { |n| n.to_s }
- else
- fixture_set_names = fixture_table_names
- end
-
- fixture_set_names.each do |file_name|
- file_name = file_name.singularize if config.pluralize_table_names
- try_to_load_dependency(file_name)
- end
- end
-
def setup_fixture_accessors(fixture_set_names = nil)
fixture_set_names = Array(fixture_set_names || fixture_table_names)
methods = Module.new do
@@ -974,7 +949,7 @@ module ActiveRecord
end
# Instantiate fixtures for every test if requested.
- instantiate_fixtures(config) if use_instantiated_fixtures
+ instantiate_fixtures if use_instantiated_fixtures
end
def teardown_fixtures
@@ -1001,16 +976,9 @@ module ActiveRecord
Hash[fixtures.map { |f| [f.name, f] }]
end
- # for pre_loaded_fixtures, only require the classes once. huge speed improvement
- @@required_fixture_classes = false
-
- def instantiate_fixtures(config)
+ def instantiate_fixtures
if pre_loaded_fixtures
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
- unless @@required_fixture_classes
- self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
- @@required_fixture_classes = true
- end
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
else
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
diff --git a/activerecord/lib/active_record/gem_version.rb b/activerecord/lib/active_record/gem_version.rb
index 91747d2ccf..e820835626 100644
--- a/activerecord/lib/active_record/gem_version.rb
+++ b/activerecord/lib/active_record/gem_version.rb
@@ -8,7 +8,7 @@ module ActiveRecord
MAJOR = 4
MINOR = 2
TINY = 0
- PRE = "beta2"
+ PRE = "beta4"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 251d682a02..4aad3217cb 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -80,12 +80,12 @@ module ActiveRecord
end
def symbolized_base_class
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_base_class is deprecated and will be removed without replacement.")
+ ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_base_class` is deprecated and will be removed without replacement.')
@symbolized_base_class ||= base_class.to_s.to_sym
end
def symbolized_sti_name
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_sti_name is deprecated and will be removed without replacement.")
+ ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_sti_name` is deprecated and will be removed without replacement.')
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
end
@@ -165,15 +165,19 @@ module ActiveRecord
# record instance. For single-table inheritance, we check the record
# for a +type+ column and return the corresponding class.
def discriminate_class_for_record(record)
- if using_single_table_inheritance?(record)
- find_sti_class(record[inheritance_column])
+ discriminate_class_for_value(record[inheritance_column])
+ end
+
+ def discriminate_class_for_value(value)
+ if using_single_table_inheritance?(value)
+ find_sti_class(value)
else
super
end
end
- def using_single_table_inheritance?(record)
- record[inheritance_column].present? && columns_hash.include?(inheritance_column)
+ def using_single_table_inheritance?(value)
+ value.present? && columns_hash.include?(inheritance_column)
end
def find_sti_class(type_name)
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index d0d9304a36..2024b225e4 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -394,8 +394,8 @@ module ActiveRecord
end
def load_schema_if_pending!
- if ActiveRecord::Migrator.needs_migration?
- ActiveRecord::Tasks::DatabaseTasks.load_schema_current
+ if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_current_if_exists
check_pending!
end
end
@@ -848,6 +848,10 @@ module ActiveRecord
(migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
end
+ def any_migrations?
+ migrations(migrations_paths).any?
+ end
+
def last_version
last_migration.version
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 171a6f4391..a444aac23c 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -63,7 +63,7 @@ module ActiveRecord
# records, artists => artists_records
# music_artists, music_records => music_artists_records
def self.derive_join_table_name(first_table, second_table) # :nodoc:
- [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
+ [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
end
module ClassMethods
@@ -247,12 +247,12 @@ module ActiveRecord
# Returns a hash where the keys are column names and the values are
# default values when instantiating the AR object for this table.
def column_defaults
- default_attributes.to_hash
+ _default_attributes.to_hash
end
- def default_attributes # :nodoc:
+ def _default_attributes # :nodoc:
@default_attributes ||= attributes_builder.build_from_database(
- columns_hash.transform_values(&:default))
+ raw_default_values)
end
# Returns an array of column names as strings.
@@ -331,6 +331,10 @@ module ActiveRecord
base.table_name
end
end
+
+ def raw_default_values
+ columns_hash.transform_values(&:default)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 755ff2b2f1..06c8bceb30 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -65,19 +65,41 @@ module ActiveRecord
# how this "single-table" inheritance mapping is implemented.
def instantiate(attributes, column_types = {})
klass = discriminate_class_for_record(attributes)
- attributes = klass.attributes_builder.build_from_database(attributes, column_types)
- klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
+ klass.instantiate_pairs(attributes.keys, attributes.values, column_types)
+ end
+
+ def instantiate_pairs(columns, values, column_types = {}) # :nodoc:
+ attributes = attributes_builder.build_from_database_pairs(columns, values, column_types)
+ allocate.init_with('attributes' => attributes, 'new_record' => false)
+ end
+
+ def instantiate_result_set(result_set, column_types = {}) # :nodoc:
+ inheritance_column_index = inheritance_column && result_set.columns.find_index(inheritance_column)
+
+ result_set.each_pair.map do |columns, values|
+ inheritance_value = inheritance_column_index && values[inheritance_column_index]
+ klass = discriminate_class_for_value(inheritance_value)
+ klass.instantiate_pairs(columns, values, column_types)
+ end
end
private
# Called by +instantiate+ to decide which class to use for a new
# record instance.
#
- # See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
+ # See +ActiveRecord::Inheritance#discriminate_class_for_value+ for
# the single-table inheritance discriminator.
+ def discriminate_class_for_value(*)
+ self
+ end
+
def discriminate_class_for_record(record)
self
end
+
+ def inheritance_column
+ nil
+ end
end
# Returns true if this object hasn't been saved yet -- that is, a record
@@ -166,7 +188,7 @@ module ActiveRecord
# and <tt>destroy</tt> returns +false+. See
# ActiveRecord::Callbacks for further details.
def destroy
- raise ReadOnlyRecord if readonly?
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
destroy_associations
destroy_row if persisted?
@destroyed = true
@@ -497,7 +519,7 @@ module ActiveRecord
end
def create_or_update
- raise ReadOnlyRecord if readonly?
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
result = new_record? ? _create_record : _update_record
result != false
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 45b6b1c925..9d4df81b07 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -39,7 +39,16 @@ module ActiveRecord
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
column_types = result_set.column_types.dup
columns_hash.each_key { |k| column_types.delete k }
- result_set.map { |record| instantiate(record, column_types) }
+ message_bus = ActiveSupport::Notifications.instrumenter
+
+ payload = {
+ record_count: result_set.length,
+ class_name: name
+ }
+
+ message_bus.instrument('instantiation.active_record', payload) do
+ instantiate_result_set(result_set, column_types)
+ end
end
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 44765bd050..3ec25f9f17 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -196,7 +196,8 @@ db_namespace = namespace :db do
fixture_files = if ENV['FIXTURES']
ENV['FIXTURES'].split(',')
else
- Pathname.glob("#{fixtures_dir}/**/*.yml").map {|f| f.basename.sub_ext('').to_s }
+ # The use of String#[] here is to support namespaced fixtures
+ Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }
end
ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files)
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 6b5a592ee5..4b58f2deb7 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,4 +1,5 @@
require 'thread'
+require 'active_support/core_ext/string/filters'
module ActiveRecord
# = Active Record Reflection
@@ -153,8 +154,11 @@ module ActiveRecord
end
def source_macro
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.source_macro is deprecated and " \
- "will be removed without replacement.")
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveRecord::Base.source_macro is deprecated and will be removed
+ without replacement.
+ MSG
+
macro
end
end
@@ -339,13 +343,14 @@ module ActiveRecord
return unless scope
if scope.arity > 0
- ActiveSupport::Deprecation.warn \
- "The association scope '#{name}' is instance dependent (the scope " \
- "block takes an argument). Preloading happens before the individual " \
- "instances are created. This means that there is no instance being " \
- "passed to the association scope. This will most likely result in " \
- "broken or incorrect behavior. Joining, Preloading and eager loading " \
- "of these associations is deprecated and will be removed in the future."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ The association scope '#{name}' is instance dependent (the scope
+ block takes an argument). Preloading happens before the individual
+ instances are created. This means that there is no instance being
+ passed to the association scope. This will most likely result in
+ broken or incorrect behavior. Joining, Preloading and eager loading
+ of these associations is deprecated and will be removed in the future.
+ MSG
end
end
alias :check_eager_loadable! :check_preloadable!
@@ -746,8 +751,11 @@ module ActiveRecord
# The macro used by the source association
def source_macro
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.source_macro is deprecated and " \
- "will be removed without replacement.")
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ ActiveRecord::Base.source_macro is deprecated and will be removed
+ without replacement.
+ MSG
+
source_reflection.source_macro
end
@@ -821,7 +829,11 @@ module ActiveRecord
end
if through_reflection.polymorphic?
- raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
+ if has_one?
+ raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
+ else
+ raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
+ end
end
if source_reflection.nil?
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ad54d84665..03bce4f5b7 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -4,8 +4,6 @@ require 'arel/collectors/bind'
module ActiveRecord
# = Active Record Relation
class Relation
- JoinOperation = Struct.new(:relation, :join_class, :on)
-
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
:order, :joins, :where, :having, :bind, :references,
:extending, :unscope]
@@ -81,22 +79,27 @@ module ActiveRecord
scope.unscope!(where: @klass.inheritance_column)
end
- um = scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
+ relation = scope.where(@klass.primary_key => (id_was || id))
+ bvs = binds + relation.bind_values
+ um = relation
+ .arel
+ .compile_update(substitutes, @klass.primary_key)
+ reorder_bind_params(um.ast, bvs)
@klass.connection.update(
um,
'SQL',
- binds)
+ bvs,
+ )
end
def substitute_values(values) # :nodoc:
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
- binds = substitutes.map do |arel_attr, value|
+ binds = values.map do |arel_attr, value|
[@klass.columns_hash[arel_attr.name], value]
end
- substitutes.each_with_index do |tuple, i|
- tuple[1] = @klass.connection.substitute_at(binds[i][0], i)
+ substitutes = values.each_with_index.map do |(arel_attr, _), i|
+ [arel_attr, @klass.connection.substitute_at(binds[i][0], i)]
end
[substitutes, binds]
@@ -305,7 +308,8 @@ module ActiveRecord
# Updates all records with details given if they match a set of conditions supplied, limits and order can
# also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
# database. It does not instantiate the involved models and it does not trigger Active Record callbacks
- # or validations.
+ # or validations. Values passed to `update_all` will not go through ActiveRecord's type-casting behavior.
+ # It should receive only values that can be passed as-is to the SQL database.
#
# ==== Parameters
#
@@ -338,7 +342,7 @@ module ActiveRecord
stmt.wheres = arel.constraints
end
- bvs = bind_values + arel.bind_values
+ bvs = arel.bind_values + bind_values
@klass.connection.update stmt, 'SQL', bvs
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index eaaa409636..c8ebb41131 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -254,6 +254,7 @@ module ActiveRecord
select_value = operation_over_aggregate_column(column, operation, distinct)
column_alias = select_value.alias
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
relation.select_values = [select_value]
query_builder = relation.arel
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index ed56369f86..eacae73ebb 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -1,4 +1,5 @@
require 'active_support/deprecation'
+require 'active_support/core_ext/string/filters'
module ActiveRecord
module FinderMethods
@@ -81,12 +82,16 @@ module ActiveRecord
# Post.find_by "published_at < ?", 2.weeks.ago
def find_by(*args)
where(*args).take
+ rescue RangeError
+ nil
end
# Like <tt>find_by</tt>, except that if no record is found, raises
# an <tt>ActiveRecord::RecordNotFound</tt> error.
def find_by!(*args)
where(*args).take!
+ rescue RangeError
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
end
# Gives a record (or N records if a parameter is supplied) without any implied
@@ -138,7 +143,7 @@ module ActiveRecord
# Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found. Note that <tt>first!</tt> accepts no arguments.
def first!
- first or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
+ find_nth! 0
end
# Find the last record (or last N records if a parameter is supplied).
@@ -187,7 +192,7 @@ module ActiveRecord
# Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def second!
- second or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
+ find_nth! 1
end
# Find the third record.
@@ -203,7 +208,7 @@ module ActiveRecord
# Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def third!
- third or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
+ find_nth! 2
end
# Find the fourth record.
@@ -219,7 +224,7 @@ module ActiveRecord
# Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def fourth!
- fourth or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
+ find_nth! 3
end
# Find the fifth record.
@@ -235,7 +240,7 @@ module ActiveRecord
# Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def fifth!
- fifth or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
+ find_nth! 4
end
# Find the forty-second record. Also known as accessing "the reddit".
@@ -251,7 +256,7 @@ module ActiveRecord
# Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
# is found.
def forty_two!
- forty_two or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
+ find_nth! 41
end
# Returns +true+ if a record exists in the table that matches the +id+ or
@@ -284,8 +289,10 @@ module ActiveRecord
def exists?(conditions = :none)
if Base === conditions
conditions = conditions.id
- ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `exists?`." \
- "Please pass the id of the object by calling `.id`"
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ You are passing an instance of ActiveRecord::Base to `exists?`.
+ Please pass the id of the object by calling `.id`
+ MSG
end
return false if !conditions
@@ -430,19 +437,20 @@ module ActiveRecord
else
find_some(ids)
end
+ rescue RangeError
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
end
def find_one(id)
if ActiveRecord::Base === id
id = id.id
- ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `find`." \
- "Please pass the id of the object by calling `.id`"
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ You are passing an instance of ActiveRecord::Base to `find`.
+ Please pass the id of the object by calling `.id`
+ MSG
end
- column = columns_hash[primary_key]
- substitute = connection.substitute_at(column, bind_values.length)
- relation = where(table[primary_key].eq(substitute))
- relation.bind_values += [[column, id]]
+ relation = where(primary_key => id)
record = relation.take
raise_record_not_found_exception!(id, 0, 1) unless record
@@ -451,7 +459,7 @@ module ActiveRecord
end
def find_some(ids)
- result = where(table[primary_key].in(ids)).to_a
+ result = where(primary_key => ids).to_a
expected_size =
if limit_value && ids.size > limit_value
@@ -489,6 +497,10 @@ module ActiveRecord
end
end
+ def find_nth!(index)
+ find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
+ end
+
def find_nth_with_limit(offset, limit)
relation = if order_values.empty? && primary_key
order(arel_table[primary_key].asc)
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index ac41d0aa80..a8febf6a18 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -13,7 +13,7 @@ module ActiveRecord
@hash = hash
end
- def merge
+ def merge #:nodoc:
Merger.new(relation, other).merge
end
@@ -83,12 +83,12 @@ module ActiveRecord
private
def merge_joins
- return if values[:joins].blank?
+ return if other.joins_values.blank?
if other.klass == relation.klass
- relation.joins!(*values[:joins])
+ relation.joins!(*other.joins_values)
else
- joins_dependency, rest = values[:joins].partition do |join|
+ joins_dependency, rest = other.joins_values.partition do |join|
case join
when Hash, Symbol, Array
true
@@ -108,10 +108,10 @@ module ActiveRecord
def merge_multi_values
lhs_wheres = relation.where_values
- rhs_wheres = values[:where] || []
+ rhs_wheres = other.where_values
lhs_binds = relation.bind_values
- rhs_binds = values[:bind] || []
+ rhs_binds = other.bind_values
removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
@@ -133,23 +133,23 @@ module ActiveRecord
relation.where_values = where_values
relation.bind_values = bind_values
- if values[:reordering]
+ if other.reordering_value
# override any order specified in the original relation
- relation.reorder! values[:order]
- elsif values[:order]
+ relation.reorder! other.order_values
+ elsif other.order_values
# merge in order_values from relation
- relation.order! values[:order]
+ relation.order! other.order_values
end
- relation.extend(*values[:extending]) unless values[:extending].blank?
+ relation.extend(*other.extending_values) unless other.extending_values.blank?
end
def merge_single_values
- relation.from_value = values[:from] unless relation.from_value
- relation.lock_value = values[:lock] unless relation.lock_value
+ relation.from_value = other.from_value unless relation.from_value
+ relation.lock_value = other.lock_value unless relation.lock_value
- unless values[:create_with].blank?
- relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with])
+ unless other.create_with_value.blank?
+ relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 3df0df40ee..e4b6b49087 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -109,7 +109,7 @@ module ActiveRecord
# FIXME: I think we need to deprecate this behavior
register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
- register_handler(Range, ->(attribute, value) { attribute.in(value) })
+ register_handler(Range, ->(attribute, value) { attribute.between(value) })
register_handler(Relation, RelationHandler.new)
register_handler(Array, ArrayHandler.new)
diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index b8d9240bf8..b8f3285c3e 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/string/filters'
+
module ActiveRecord
class PredicateBuilder
class ArrayHandler # :nodoc:
@@ -6,9 +8,12 @@ module ActiveRecord
nils, values = values.partition(&:nil?)
if values.any? { |val| val.is_a?(Array) }
- ActiveSupport::Deprecation.warn "Passing a nested array to Active Record " \
- "finder methods is deprecated and will be removed. Flatten your array " \
- "before using it for 'IN' conditions."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing a nested array to Active Record finder methods is
+ deprecated and will be removed. Flatten your array before using
+ it for 'IN' conditions.
+ MSG
+
values = values.flatten
end
@@ -27,7 +32,7 @@ module ActiveRecord
values_predicate = values_predicate.or(attribute.eq(nil))
end
- array_predicates = ranges.map { |range| attribute.in(range) }
+ array_predicates = ranges.map { |range| attribute.between(range) }
array_predicates.unshift(values_predicate)
array_predicates.inject { |composite, predicate| composite.or(predicate) }
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
index 618fa3cdd9..063150958a 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb
@@ -6,7 +6,7 @@ module ActiveRecord
value = value.select(value.klass.arel_table[value.klass.primary_key])
end
- attribute.in(value.arel.ast)
+ attribute.in(value.arel)
end
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index bbddd28ccc..a686e3263b 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/array/wrap'
+require 'active_support/core_ext/string/filters'
require 'active_model/forbidden_attributes_protection'
module ActiveRecord
@@ -94,8 +95,10 @@ module ActiveRecord
def check_cached_relation # :nodoc:
if defined?(@arel) && @arel
@arel = nil
- ActiveSupport::Deprecation.warn "Modifying already cached Relation. The " \
- "cache will be reset. Use a cloned Relation to prevent this warning."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Modifying already cached Relation. The cache will be reset. Use a
+ cloned Relation to prevent this warning.
+ MSG
end
end
@@ -436,7 +439,7 @@ module ActiveRecord
self
end
- def bind(value)
+ def bind(value) # :nodoc:
spawn.bind!(value)
end
@@ -880,12 +883,15 @@ module ActiveRecord
# Reorder bind indexes if joins produced bind values
bvs = arel.bind_values + bind_values
- arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
+ reorder_bind_params(arel.ast, bvs)
+ arel
+ end
+
+ def reorder_bind_params(ast, bvs)
+ ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
column = bvs[i].first
bp.replace connection.substitute_at(column, i)
end
-
- arel
end
def symbol_unscoping(scope)
@@ -913,7 +919,7 @@ module ActiveRecord
where_values.reject! do |rel|
case rel
- when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
+ when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual
subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
subrelation.name == target_value
end
@@ -955,8 +961,7 @@ module ActiveRecord
when Hash
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
- bv_len = bind_values.length
- tmp_opts, bind_values = create_binds(opts, bv_len)
+ tmp_opts, bind_values = create_binds(opts)
self.bind_values += bind_values
attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
@@ -968,7 +973,7 @@ module ActiveRecord
end
end
- def create_binds(opts, idx)
+ def create_binds(opts)
bindable, non_binds = opts.partition do |column, value|
case value
when String, Integer, ActiveRecord::StatementCache::Substitute
@@ -978,12 +983,23 @@ module ActiveRecord
end
end
+ association_binds, non_binds = non_binds.partition do |column, value|
+ value.is_a?(Hash) && association_for_table(column)
+ end
+
new_opts = {}
binds = []
- bindable.each_with_index do |(column,value), index|
+ bindable.each do |(column,value)|
binds.push [@klass.columns_hash[column.to_s], value]
- new_opts[column] = connection.substitute_at(column, index + idx)
+ new_opts[column] = connection.substitute_at(column)
+ end
+
+ association_binds.each do |(column, value)|
+ association_relation = association_for_table(column).klass.send(:relation)
+ association_new_opts, association_bind = association_relation.send(:create_binds, value)
+ new_opts[column] = association_new_opts
+ binds += association_bind
end
non_binds.each { |column,value| new_opts[column] = value }
@@ -991,6 +1007,12 @@ module ActiveRecord
[new_opts, binds]
end
+ def association_for_table(table_name)
+ table_name = table_name.to_s
+ @klass._reflect_on_association(table_name) ||
+ @klass._reflect_on_association(table_name.singularize)
+ end
+
def build_from
opts, name = from_value
case opts
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 8405fdaeb9..c84ad586e2 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -42,6 +42,10 @@ module ActiveRecord
@column_types = column_types
end
+ def length
+ @rows.length
+ end
+
def each
if block_given?
hash_rows.each { |row| yield row }
@@ -50,6 +54,15 @@ module ActiveRecord
end
end
+ def each_pair
+ return to_enum(__method__) unless block_given?
+
+ columns = @columns.map { |c| c.dup.freeze }
+ @rows.each do |row|
+ yield columns, row
+ end
+ end
+
def to_hash
hash_rows
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index ff70cbed0f..6a130c145b 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -87,6 +87,9 @@ module ActiveRecord
# { address: Address.new("123 abc st.", "chicago") }
# # => "address_street='123 abc st.' and address_city='chicago'"
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
+ ActiveSupport::Deprecation.warn(<<-EOWARN)
+sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0
+ EOWARN
attrs = PredicateBuilder.resolve_column_aliases self, attrs
attrs = expand_hash_conditions_for_aggregates(attrs)
@@ -134,7 +137,7 @@ module ActiveRecord
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
bound = values.dup
c = connection
- statement.gsub('?') do
+ statement.gsub(/\?/) do
replace_bind_variable(bound.shift, c)
end
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 82c5ca291c..261fb9d992 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -118,6 +118,8 @@ HEADER
if pkcol
if pk != 'id'
tbl.print %Q(, primary_key: "#{pk}")
+ elsif pkcol.sql_type == 'bigint'
+ tbl.print ", id: :bigserial"
elsif pkcol.sql_type == 'uuid'
tbl.print ", id: :uuid"
tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 49cadb66d0..ec1edf0e01 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -139,6 +139,10 @@ module ActiveRecord
# Article.published.featured.latest_article
# Article.featured.titles
def scope(name, body, &block)
+ unless body.respond_to?:call
+ raise ArgumentError, 'The scope body needs to be callable.'
+ end
+
if dangerous_class_method?(name)
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
"on the model \"#{self.name}\", but Active Record already defined " \
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index f9b54139d5..1228de2bfd 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/string/filters'
+
module ActiveRecord
module Tasks # :nodoc:
class DatabaseAlreadyExists < StandardError; end # :nodoc:
@@ -187,25 +189,35 @@ module ActiveRecord
end
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
- ActiveSupport::Deprecation.warn \
- "This method will act on a specific connection in the future. " \
- "To act on the current connection, use `load_schema_current` instead."
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ This method will act on a specific connection in the future.
+ To act on the current connection, use `load_schema_current` instead.
+ MSG
load_schema_current(format, file)
end
+ def schema_file(format = ActiveSupport::Base.schema_format)
+ case format
+ when :ruby
+ File.join(db_dir, "schema.rb")
+ when :sql
+ File.join(db_dir, "structure.sql")
+ end
+ end
+
# This method is the successor of +load_schema+. We should rename it
# after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
+ file ||= schema_file(format)
+
case format
when :ruby
- file ||= File.join(db_dir, "schema.rb")
check_schema_file(file)
purge(configuration)
ActiveRecord::Base.establish_connection(configuration)
load(file)
when :sql
- file ||= File.join(db_dir, "structure.sql")
check_schema_file(file)
purge(configuration)
structure_load(configuration, file)
@@ -214,6 +226,12 @@ module ActiveRecord
end
end
+ def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
+ if File.exist?(file || schema_file(format))
+ load_schema_current(format, file, environment)
+ end
+ end
+
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
each_current_configuration(environment) { |configuration|
load_schema_for configuration, format, file
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index d890196f47..eafbb2c249 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -31,6 +31,7 @@ module ActiveRecord
end
establish_connection configuration
else
+ $stderr.puts error.inspect
$stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
$stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index e53297d0ab..f92e1de03b 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -11,7 +11,7 @@ module ActiveRecord
"\n" \
"You can opt into the new behavior and remove this warning by setting:\n" \
"\n" \
- " config.active_record.raise_in_transactional_callbacks = true"
+ " config.active_record.raise_in_transactional_callbacks = true\n\n"
included do
define_callbacks :commit, :rollback,
@@ -309,7 +309,7 @@ module ActiveRecord
# Ensure that it is not called if the object was never persisted (failed create),
# but call it after the commit of a destroyed object.
def committed!(should_run_callbacks = true) #:nodoc:
- run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
+ _run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
ensure
force_clear_transaction_record_state
end
@@ -317,7 +317,7 @@ module ActiveRecord
# Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
# state should be rolled back to the beginning or just to the last savepoint.
def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc:
- run_rollback_callbacks if should_run_callbacks
+ _run_rollback_callbacks if should_run_callbacks
ensure
restore_transaction_record_state(force_restore_state)
clear_transaction_record_state
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index e3d6c5957e..e5acbbb6b3 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -4,6 +4,7 @@ require 'active_record/type/numeric'
require 'active_record/type/time_value'
require 'active_record/type/value'
+require 'active_record/type/big_integer'
require 'active_record/type/binary'
require 'active_record/type/boolean'
require 'active_record/type/date'
diff --git a/activerecord/lib/active_record/type/big_integer.rb b/activerecord/lib/active_record/type/big_integer.rb
new file mode 100644
index 0000000000..0c72d8914f
--- /dev/null
+++ b/activerecord/lib/active_record/type/big_integer.rb
@@ -0,0 +1,13 @@
+require 'active_record/type/integer'
+
+module ActiveRecord
+ module Type
+ class BigInteger < Integer # :nodoc:
+ private
+
+ def max_value
+ ::Float::INFINITY
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/type/boolean.rb b/activerecord/lib/active_record/type/boolean.rb
index 06dd17ed28..978d16d524 100644
--- a/activerecord/lib/active_record/type/boolean.rb
+++ b/activerecord/lib/active_record/type/boolean.rb
@@ -10,8 +10,19 @@ module ActiveRecord
def cast_value(value)
if value == ''
nil
+ elsif ConnectionAdapters::Column::TRUE_VALUES.include?(value)
+ true
else
- ConnectionAdapters::Column::TRUE_VALUES.include?(value)
+ if !ConnectionAdapters::Column::FALSE_VALUES.include?(value)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ You attempted to assign a value which is not explicitly `true` or `false`
+ to a boolean column. Currently this value casts to `false`. This will
+ change to match Ruby's semantics, and will cast to `true` in Rails 5.
+ If you would like to maintain the current behavior, you should
+ explicitly handle the values you would like cast to `false`.
+ MSG
+ end
+ false
end
end
end
diff --git a/activerecord/lib/active_record/type/decimal_without_scale.rb b/activerecord/lib/active_record/type/decimal_without_scale.rb
index cabdcecdd7..ff5559e300 100644
--- a/activerecord/lib/active_record/type/decimal_without_scale.rb
+++ b/activerecord/lib/active_record/type/decimal_without_scale.rb
@@ -1,8 +1,8 @@
-require 'active_record/type/integer'
+require 'active_record/type/big_integer'
module ActiveRecord
module Type
- class DecimalWithoutScale < Integer # :nodoc:
+ class DecimalWithoutScale < BigInteger # :nodoc:
def type
:decimal
end
diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb
index 08477d1303..d69e5b3f28 100644
--- a/activerecord/lib/active_record/type/integer.rb
+++ b/activerecord/lib/active_record/type/integer.rb
@@ -3,21 +3,44 @@ module ActiveRecord
class Integer < Value # :nodoc:
include Numeric
+ def initialize(*)
+ super
+ @range = -max_value...max_value
+ end
+
def type
:integer
end
alias type_cast_for_database type_cast
+ protected
+
+ attr_reader :range
+
private
def cast_value(value)
case value
when true then 1
when false then 0
- else value.to_i rescue nil
+ else
+ result = value.to_i rescue nil
+ ensure_in_range(result) if result
+ result
end
end
+
+ def ensure_in_range(value)
+ unless range.cover?(value)
+ raise RangeError, "#{value} is too large for #{self.class} with limit #{limit || 4}"
+ end
+ end
+
+ def max_value
+ limit = self.limit || 4
+ 1 << (limit * 8 - 1) # 8 bits per byte with one bit for sign
+ end
end
end
end
diff --git a/activerecord/lib/active_record/type/string.rb b/activerecord/lib/active_record/type/string.rb
index 150defb106..fbc0af2c5a 100644
--- a/activerecord/lib/active_record/type/string.rb
+++ b/activerecord/lib/active_record/type/string.rb
@@ -15,8 +15,8 @@ module ActiveRecord
case value
when ::Numeric, ActiveSupport::Duration then value.to_s
when ::String then ::String.new(value)
- when true then "1"
- when false then "0"
+ when true then "t"
+ when false then "f"
else super
end
end
@@ -25,8 +25,8 @@ module ActiveRecord
def cast_value(value)
case value
- when true then "1"
- when false then "0"
+ when true then "t"
+ when false then "f"
# String.new is slightly faster than dup
else ::String.new(value.to_s)
end
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index b4785d3ba4..47ccef31a5 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -29,9 +29,11 @@ module ActiveRecord
# Configuration options:
#
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</tt>)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb
index e586744818..c7aa814ba8 100644
--- a/activerecord/lib/active_record/validations/presence.rb
+++ b/activerecord/lib/active_record/validations/presence.rb
@@ -44,9 +44,11 @@ module ActiveRecord
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
- # and <tt>:update</tt>.
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
+ # Runs in all validation contexts by default (nil). You can pass a symbol
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
+ # <tt>on: :custom_validation_context</tt> or
+ # <tt>on: [:create, :custom_validation_context]</tt>)
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
# the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
# <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index ff553c3f1a..fa7bebf08b 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -254,6 +254,15 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
end
end
+ def test_assigning_non_array_value
+ record = PgArray.new(tags: "not-an-array")
+ assert_equal "not-an-array", record.tags
+ e = assert_raises(ActiveRecord::StatementInvalid) do
+ record.save!
+ end
+ assert_instance_of PG::InvalidTextRepresentation, e.original_exception
+ end
+
private
def assert_cycle field, array
# test creation
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 7872f91943..aeebec034d 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -17,7 +17,6 @@ class PostgresqlByteaTest < ActiveRecord::TestCase
end
end
@column = ByteaDataType.columns_hash['payload']
- assert(@column.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLColumn))
end
teardown do
@@ -25,9 +24,17 @@ class PostgresqlByteaTest < ActiveRecord::TestCase
end
def test_column
+ assert @column.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLColumn)
assert_equal :binary, @column.type
end
+ def test_binary_columns_are_limitless_the_upper_limit_is_one_GB
+ assert_equal 'bytea', @connection.type_to_sql(:binary, 100_000)
+ assert_raise ActiveRecord::ActiveRecordError do
+ @connection.type_to_sql :binary, 4294967295
+ end
+ end
+
def test_type_cast_binary_converts_the_encoding
assert @column
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index a0a34e4b87..2c4839338c 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -94,6 +94,13 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
assert @first_oid.reload
assert_equal new_value, @first_oid.obj_id
end
+
+ def test_text_columns_are_limitless_the_upper_limit_is_one_GB
+ assert_equal 'text', @connection.type_to_sql(:text, 100_000)
+ assert_raise ActiveRecord::ActiveRecordError do
+ @connection.type_to_sql :text, 4294967295
+ end
+ end
end
class PostgresqlInternalDataTypeTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb
index d99c4a292e..83cedc5a7b 100644
--- a/activerecord/test/cases/adapters/postgresql/enum_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
require "cases/helper"
require 'support/connection_helper'
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlEnumTest < ActiveRecord::TestCase
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 1296eb72c0..6a9c6483fe 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -1,41 +1,38 @@
# encoding: utf-8
-
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
-class PostgresqlHstoreTest < ActiveRecord::TestCase
- class Hstore < ActiveRecord::Base
- self.table_name = 'hstores'
+if ActiveRecord::Base.connection.supports_extensions?
+ class PostgresqlHstoreTest < ActiveRecord::TestCase
+ class Hstore < ActiveRecord::Base
+ self.table_name = 'hstores'
- store_accessor :settings, :language, :timezone
- end
+ store_accessor :settings, :language, :timezone
+ end
- def setup
- @connection = ActiveRecord::Base.connection
+ def setup
+ @connection = ActiveRecord::Base.connection
- unless @connection.extension_enabled?('hstore')
- @connection.enable_extension 'hstore'
- @connection.commit_db_transaction
- end
+ unless @connection.extension_enabled?('hstore')
+ @connection.enable_extension 'hstore'
+ @connection.commit_db_transaction
+ end
- @connection.reconnect!
+ @connection.reconnect!
- @connection.transaction do
- @connection.create_table('hstores') do |t|
- t.hstore 'tags', :default => ''
- t.hstore 'payload', array: true
- t.hstore 'settings'
+ @connection.transaction do
+ @connection.create_table('hstores') do |t|
+ t.hstore 'tags', :default => ''
+ t.hstore 'payload', array: true
+ t.hstore 'settings'
+ end
end
+ @column = Hstore.columns_hash['tags']
end
- @column = Hstore.columns_hash['tags']
- end
- teardown do
- @connection.execute 'drop table if exists hstores'
- end
+ teardown do
+ @connection.execute 'drop table if exists hstores'
+ end
- if ActiveRecord::Base.connection.supports_extensions?
def test_hstore_included_in_extensions
assert @connection.respond_to?(:extensions), "connection should have a list of extensions"
assert @connection.extensions.include?('hstore'), "extension list should include hstore"
@@ -315,10 +312,8 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
dupe = record.dup
assert_equal({"one" => "two"}, dupe.tags.to_hash)
end
- end
-
- private
+ private
def assert_array_cycle(array)
# test creation
x = Hstore.create!(payload: array)
@@ -346,4 +341,5 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
x.reload
assert_equal(hash, x.tags)
end
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 86ba849445..340ca29c0e 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -1,10 +1,10 @@
# encoding: utf-8
-
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
+require 'support/schema_dumping_helper'
module PostgresqlJSONSharedTestCases
+ include SchemaDumpingHelper
+
class JsonDataType < ActiveRecord::Base
self.table_name = 'json_data_type'
@@ -64,6 +64,11 @@ module PostgresqlJSONSharedTestCases
JsonDataType.reset_column_information
end
+ def test_schema_dumping
+ output = dump_table_schema("json_data_type")
+ assert_match(/t.#{column_type.to_s}\s+"payload",\s+default: {}/, output)
+ end
+
def test_cast_value_on_write
x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar}
assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast)
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 6f40b0d2de..350cb3e065 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -388,6 +388,14 @@ class SchemaTest < ActiveRecord::TestCase
assert_equal "1", @connection.select_value("SELECT nextval('#{sequence_name}')")
end
+ def test_set_pk_sequence
+ table_name = "#{SCHEMA_NAME}.#{PK_TABLE_NAME}"
+ _, sequence_name = @connection.pk_and_sequence_for table_name
+ @connection.set_pk_sequence! table_name, 123
+ assert_equal "124", @connection.select_value("SELECT nextval('#{sequence_name}')")
+ @connection.reset_pk_sequence! table_name
+ end
+
private
def columns(table_name)
@connection.send(:column_definitions, table_name).map do |name, type, default|
diff --git a/activerecord/test/cases/adapters/postgresql/sql_types_test.rb b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb
deleted file mode 100644
index d7d40f6385..0000000000
--- a/activerecord/test/cases/adapters/postgresql/sql_types_test.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require "cases/helper"
-
-class SqlTypesTest < ActiveRecord::TestCase
- def test_binary_types
- assert_equal 'bytea', type_to_sql(:binary, 100_000)
- assert_raise ActiveRecord::ActiveRecordError do
- type_to_sql :binary, 4294967295
- end
- assert_equal 'text', type_to_sql(:text, 100_000)
- assert_raise ActiveRecord::ActiveRecordError do
- type_to_sql :text, 4294967295
- end
- end
-
- def type_to_sql(*args)
- ActiveRecord::Base.connection.type_to_sql(*args)
- end
-end
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index ae5b409f99..5753da1173 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -1,8 +1,6 @@
# encoding: utf-8
-
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
+require 'support/schema_dumping_helper'
module PostgresqlUUIDHelper
def connection
@@ -110,8 +108,29 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase
end
end
+class PostgresqlLargeKeysTest < ActiveRecord::TestCase
+ include PostgresqlUUIDHelper
+ include SchemaDumpingHelper
+
+ def setup
+ connection.create_table('big_serials', id: :bigserial) do |t|
+ t.string 'name'
+ end
+ end
+
+ def test_omg
+ schema = dump_table_schema "big_serials"
+ assert_match "create_table \"big_serials\", id: :bigserial, force: true", schema
+ end
+
+ def teardown
+ drop_table "big_serials"
+ end
+end
+
class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase
include PostgresqlUUIDHelper
+ include SchemaDumpingHelper
class UUID < ActiveRecord::Base
self.table_name = 'pg_uuids'
@@ -171,17 +190,15 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase
end
def test_schema_dumper_for_uuid_primary_key
- schema = StringIO.new
- ActiveRecord::SchemaDumper.dump(connection, schema)
- assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: "uuid_generate_v1\(\)"/, schema.string)
- assert_match(/t\.uuid "other_uuid", default: "uuid_generate_v4\(\)"/, schema.string)
+ 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 = StringIO.new
- ActiveRecord::SchemaDumper.dump(connection, schema)
- assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: "my_uuid_generator\(\)"/, schema.string)
- assert_match(/t\.uuid "other_uuid_2", default: "my_uuid_generator\(\)"/, schema.string)
+ 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
end
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 25555bd75c..bcd0cf82d5 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -57,6 +57,35 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_default_scope_on_relations_is_not_cached
+ counter = 0
+
+ comments = Class.new(ActiveRecord::Base) {
+ self.table_name = 'comments'
+ self.inheritance_column = 'not_there'
+
+ posts = Class.new(ActiveRecord::Base) {
+ self.table_name = 'posts'
+ self.inheritance_column = 'not_there'
+
+ default_scope -> {
+ counter += 1
+ where("id = :inc", :inc => counter)
+ }
+
+ has_many :comments, :class => comments
+ }
+ belongs_to :post, :class => posts, :inverse_of => false
+ }
+
+ assert_equal 0, counter
+ comment = comments.first
+ assert_equal 0, counter
+ sql = capture_sql { comment.post }
+ comment.reload
+ assert_not_equal sql, capture_sql { comment.post }
+ end
+
def test_proxy_assignment
account = Account.find(1)
assert_nothing_raised { account.firm = account.firm }
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index b852bd3536..8234ee95be 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -935,6 +935,42 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal 3, authors(:david).posts_with_comments.where("length(comments.body) > 15").references(:comments).count
end
+ def test_association_loading_notification
+ notifications = messages_for('instantiation.active_record') do
+ Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+ end
+
+ message = notifications.first
+ payload = message.last
+ count = Developer.all.merge!(:includes => 'projects', :where => { 'developers_projects.access_level' => 1 }, :limit => 5).to_a.size
+
+ # eagerloaded row count should be greater than just developer count
+ assert_operator payload[:record_count], :>, count
+ assert_equal Developer.name, payload[:class_name]
+ end
+
+ def test_base_messages
+ notifications = messages_for('instantiation.active_record') do
+ Developer.all.to_a
+ end
+ message = notifications.first
+ payload = message.last
+
+ assert_equal Developer.all.to_a.count, payload[:record_count]
+ assert_equal Developer.name, payload[:class_name]
+ end
+
+ def messages_for(name)
+ notifications = []
+ ActiveSupport::Notifications.subscribe(name) do |*args|
+ notifications << args
+ end
+ yield
+ notifications
+ ensure
+ ActiveSupport::Notifications.unsubscribe(name)
+ end
+
def test_load_with_sti_sharing_association
assert_queries(2) do #should not do 1 query per subclass
Comment.includes(:post).to_a
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 859310575e..9a57683ee3 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
@@ -1,5 +1,6 @@
require "cases/helper"
require 'models/developer'
+require 'models/computer'
require 'models/project'
require 'models/company'
require 'models/customer'
@@ -80,7 +81,7 @@ end
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
- :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings
+ :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers
def setup_data_for_habtm_case
ActiveRecord::Base.connection.execute('delete from countries_treaties')
@@ -883,4 +884,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
child.special_projects << SpecialProject.new("name" => "Special Project")
assert child.save, 'child object should be saved'
end
+
+ def test_habtm_with_reflection_using_class_name_and_fixtures
+ assert_not_nil Developer._reflections['shared_computers']
+ # Checking the fixture for named association is important here, because it's the only way
+ # we've been able to reproduce this bug
+ assert_not_nil File.read(File.expand_path("../../../fixtures/developers.yml", __FILE__)).index("shared_computers")
+ assert_equal developers(:david).shared_computers.first, computers(:laptop)
+ 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 c72d9342a9..69f7bde4bc 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -76,6 +76,32 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
dev.developer_projects.map(&:project_id).sort
end
+ def test_default_scope_on_relations_is_not_cached
+ counter = 0
+ posts = Class.new(ActiveRecord::Base) {
+ self.table_name = 'posts'
+ self.inheritance_column = 'not_there'
+ post = self
+
+ comments = Class.new(ActiveRecord::Base) {
+ self.table_name = 'comments'
+ self.inheritance_column = 'not_there'
+ belongs_to :post, :class => post
+ default_scope -> {
+ counter += 1
+ where("id = :inc", :inc => counter)
+ }
+ }
+ has_many :comments, :class => comments, :foreign_key => 'post_id'
+ }
+ assert_equal 0, counter
+ post = posts.first
+ assert_equal 0, counter
+ sql = capture_sql { post.comments.to_a }
+ post.comments.reset
+ assert_not_equal sql, capture_sql { post.comments.to_a }
+ end
+
def test_has_many_build_with_options
college = College.create(name: 'UFMT')
Student.create(active: true, college_id: college.id, name: 'Sarah')
@@ -1902,6 +1928,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb1, bulb2], car.all_bulbs.sort_by(&:id)
end
+ test 'unscopes the default scope of associated model when used with include' do
+ car = Car.create!
+ bulb = Bulb.create! name: "other", car: car
+
+ assert_equal bulb, Car.find(car.id).all_bulbs.first
+ assert_equal bulb, Car.includes(:all_bulbs).find(car.id).all_bulbs.first
+ end
+
test "raises RecordNotDestroyed when replaced child can't be destroyed" do
car = Car.create!
original_child = FailedBulb.create!(car: car)
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index 089cb0a3a2..19d1aa87a8 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -289,6 +289,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_has_one_through_polymorphic_association
+ assert_raise(ActiveRecord::HasOneAssociationPolymorphicThroughError) do
+ @member.premium_club
+ end
+ end
+
def test_has_one_through_belongs_to_should_update_when_the_through_foreign_key_changes
minivan = minivans(:cool_first)
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 7ca7349528..c3e8ceb0da 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -736,11 +736,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_bulk_update_raise_unknown_attribute_error
error = assert_raises(ActiveRecord::UnknownAttributeError) {
- @target.new(:hello => "world")
+ Topic.new(hello: "world")
}
- assert_instance_of @target, error.record
+ assert_instance_of Topic, error.record
assert_equal "hello", error.attribute
- assert_equal "unknown attribute: hello", error.message
+ assert_equal "unknown attribute 'hello' for Topic.", error.message
end
def test_methods_override_in_multi_level_subclass
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 79ef0502cb..dbe1eb48db 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -20,7 +20,7 @@ end
module ActiveRecord
class CustomPropertiesTest < ActiveRecord::TestCase
- def test_overloading_types
+ test "overloading types" do
data = OverloadedType.new
data.overloaded_float = "1.1"
@@ -30,7 +30,7 @@ module ActiveRecord
assert_equal 1.1, data.unoverloaded_float
end
- def test_overloaded_properties_save
+ test "overloaded properties save" do
data = OverloadedType.new
data.overloaded_float = "2.2"
@@ -43,18 +43,18 @@ module ActiveRecord
assert_kind_of Float, UnoverloadedType.last.overloaded_float
end
- def test_properties_assigned_in_constructor
+ test "properties assigned in constructor" do
data = OverloadedType.new(overloaded_float: '3.3')
assert_equal 3, data.overloaded_float
end
- def test_overloaded_properties_with_limit
+ test "overloaded properties with limit" do
assert_equal 50, OverloadedType.columns_hash['overloaded_string_with_limit'].limit
assert_equal 255, UnoverloadedType.columns_hash['overloaded_string_with_limit'].limit
end
- def test_nonexistent_attribute
+ test "nonexistent attribute" do
data = OverloadedType.new(non_existent_decimal: 1)
assert_equal BigDecimal.new(1), data.non_existent_decimal
@@ -63,7 +63,7 @@ module ActiveRecord
end
end
- def test_changing_defaults
+ test "changing defaults" do
data = OverloadedType.new
unoverloaded_data = UnoverloadedType.new
@@ -71,24 +71,28 @@ module ActiveRecord
assert_equal 'the original default', unoverloaded_data.string_with_default
end
- def test_children_inherit_custom_properties
+ test "defaults are not touched on the columns" do
+ assert_equal 'the original default', OverloadedType.columns_hash['string_with_default'].default
+ end
+
+ test "children inherit custom properties" do
data = ChildOfOverloadedType.new(overloaded_float: '4.4')
assert_equal 4, data.overloaded_float
end
- def test_children_can_override_parents
+ test "children can override parents" do
data = GrandchildOfOverloadedType.new(overloaded_float: '4.4')
assert_equal 4.4, data.overloaded_float
end
- def test_overloading_properties_does_not_change_column_order
+ test "overloading properties does not change column order" do
column_names = OverloadedType.column_names
assert_equal %w(id overloaded_float unoverloaded_float overloaded_string_with_limit string_with_default non_existent_decimal), column_names
end
- def test_caches_are_cleared
+ test "caches are cleared" do
klass = Class.new(OverloadedType)
assert_equal 6, klass.columns.length
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index b2a7d3956d..734fd5fe18 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -1,5 +1,6 @@
require 'cases/helper'
require 'models/bird'
+require 'models/comment'
require 'models/company'
require 'models/customer'
require 'models/developer'
@@ -616,6 +617,14 @@ class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
firm.save!
assert !account.persisted?
end
+
+ def test_autosave_new_record_with_after_create_callback
+ post = PostWithAfterCreateCallback.new(title: 'Captain Murphy', body: 'is back')
+ post.comments.build(body: 'foo')
+ post.save!
+
+ assert_not_nil post.author_id
+ end
end
class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index fb535e74fc..3675401555 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -522,6 +522,10 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal Topic.find(['1-meowmeow', '2-hello']), Topic.find([1, 2])
end
+ def test_find_by_slug_with_range
+ assert_equal Topic.where(id: '1-meowmeow'..'2-hello'), Topic.where(id: 1..2)
+ end
+
def test_equality_of_new_records
assert_not_equal Topic.new, Topic.new
assert_equal false, Topic.new == Topic.new
@@ -985,7 +989,6 @@ class BasicsTest < ActiveRecord::TestCase
class NumericData < ActiveRecord::Base
self.table_name = 'numeric_data'
- attribute :world_population, Type::Integer.new
attribute :my_house_population, Type::Integer.new
attribute :atoms_in_universe, Type::Integer.new
end
@@ -1383,7 +1386,10 @@ class BasicsTest < ActiveRecord::TestCase
c1 = Post.connection.schema_cache.columns('posts')
ActiveRecord::Base.clear_cache!
c2 = Post.connection.schema_cache.columns('posts')
- assert_not_equal c1, c2
+ c1.each_with_index do |v, i|
+ assert_not_same v, c2[i]
+ end
+ assert_equal c1, c2
end
def test_current_scope_is_reset
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index ec6a319ab5..e886268a72 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -461,7 +461,6 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 7, Company.includes(:contracts).sum(:developer_id)
end
-
def test_from_option_with_specified_index
if Edge.connection.adapter_name == 'MySQL' or Edge.connection.adapter_name == 'Mysql2'
assert_equal Edge.count(:all), Edge.from('edges USE INDEX(unique_edge_index)').count(:all)
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
index e1b2804a18..37ad469476 100644
--- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -5,10 +5,14 @@ module ActiveRecord
class MergeAndResolveDefaultUrlConfigTest < ActiveRecord::TestCase
def setup
@previous_database_url = ENV.delete("DATABASE_URL")
+ @previous_rack_env = ENV.delete("RACK_ENV")
+ @previous_rails_env = ENV.delete("RAILS_ENV")
end
teardown do
ENV["DATABASE_URL"] = @previous_database_url
+ ENV["RACK_ENV"] = @previous_rack_env
+ ENV["RAILS_ENV"] = @previous_rails_env
end
def resolve_config(config)
@@ -27,6 +31,26 @@ module ActiveRecord
assert_equal expected, actual
end
+ def test_resolver_with_database_uri_and_current_env_symbol_key_and_rails_env
+ ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV['RAILS_ENV'] = "foo"
+
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = resolve_spec(:foo, config)
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ assert_equal expected, actual
+ end
+
+ def test_resolver_with_database_uri_and_current_env_symbol_key_and_rack_env
+ ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV['RACK_ENV'] = "foo"
+
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = resolve_spec(:foo, config)
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ assert_equal expected, actual
+ end
+
def test_resolver_with_database_uri_and_and_current_env_string_key
ENV['DATABASE_URL'] = "postgres://localhost/foo"
config = { "default_env" => { "adapter" => "not_postgres", "database" => "not_foo" } }
@@ -35,6 +59,26 @@ module ActiveRecord
assert_equal expected, actual
end
+ def test_resolver_with_database_uri_and_and_current_env_string_key_and_rails_env
+ ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV['RAILS_ENV'] = "foo"
+
+ config = { "not_production" => {"adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = assert_deprecated { resolve_spec("foo", config) }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ assert_equal expected, actual
+ end
+
+ def test_resolver_with_database_uri_and_and_current_env_string_key_and_rack_env
+ ENV['DATABASE_URL'] = "postgres://localhost/foo"
+ ENV['RACK_ENV'] = "foo"
+
+ config = { "not_production" => {"adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = assert_deprecated { resolve_spec("foo", config) }
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ assert_equal expected, actual
+ end
+
def test_resolver_with_database_uri_and_known_key
ENV['DATABASE_URL'] = "postgres://localhost/foo"
config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
@@ -139,6 +183,51 @@ module ActiveRecord
assert_equal nil, actual["production"]
assert_equal nil, actual["development"]
assert_equal nil, actual["test"]
+ assert_equal nil, actual[:default_env]
+ assert_equal nil, actual[:production]
+ assert_equal nil, actual[:development]
+ assert_equal nil, actual[:test]
+ end
+
+ def test_blank_with_database_url_with_rails_env
+ ENV['RAILS_ENV'] = "not_production"
+ ENV['DATABASE_URL'] = "postgres://localhost/foo"
+
+ config = {}
+ actual = resolve_config(config)
+ expected = { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost" }
+
+ assert_equal expected, actual["not_production"]
+ assert_equal nil, actual["production"]
+ assert_equal nil, actual["default_env"]
+ assert_equal nil, actual["development"]
+ assert_equal nil, actual["test"]
+ assert_equal nil, actual[:default_env]
+ assert_equal nil, actual[:not_production]
+ assert_equal nil, actual[:production]
+ assert_equal nil, actual[:development]
+ assert_equal nil, actual[:test]
+ end
+
+ def test_blank_with_database_url_with_rack_env
+ ENV['RACK_ENV'] = "not_production"
+ ENV['DATABASE_URL'] = "postgres://localhost/foo"
+
+ config = {}
+ actual = resolve_config(config)
+ expected = { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost" }
+
+ assert_equal expected, actual["not_production"]
+ assert_equal nil, actual["production"]
+ assert_equal nil, actual["default_env"]
+ assert_equal nil, actual["development"]
+ assert_equal nil, actual["test"]
+ assert_equal nil, actual[:default_env]
+ assert_equal nil, actual[:not_production]
assert_equal nil, actual[:production]
assert_equal nil, actual[:development]
assert_equal nil, actual[:test]
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
index d4d67487db..80244d1439 100644
--- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -22,6 +22,10 @@ module ActiveRecord
assert_lookup_type :string, "SET('one', 'two', 'three')"
end
+ def test_enum_type_with_value_matching_other_type
+ assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
+ end
+
def test_binary_types
assert_lookup_type :binary, 'bit'
assert_lookup_type :binary, 'BIT'
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index ac570ecbe0..dc73faa5be 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -99,21 +99,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_exists_fails_when_parameter_has_invalid_type
- if current_adapter?(:PostgreSQLAdapter, :MysqlAdapter)
- assert_raises ActiveRecord::StatementInvalid do
- Topic.exists?(("9"*53).to_i) # number that's bigger than int
- end
- else
+ assert_raises(RangeError) do
assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int
end
-
- if current_adapter?(:PostgreSQLAdapter)
- assert_raises ActiveRecord::StatementInvalid do
- Topic.exists?("foo")
- end
- else
- assert_equal false, Topic.exists?("foo")
- end
+ assert_equal false, Topic.exists?("foo")
end
def test_exists_does_not_select_columns_without_alias
@@ -209,6 +198,28 @@ class FinderTest < ActiveRecord::TestCase
assert_equal 2, last_devs.size
end
+ def test_find_with_large_number
+ assert_raises(ActiveRecord::RecordNotFound) { Topic.find('9999999999999999999999999999999') }
+ end
+
+ def test_find_by_with_large_number
+ assert_nil Topic.find_by(id: '9999999999999999999999999999999')
+ end
+
+ def test_find_by_id_with_large_number
+ assert_nil Topic.find_by_id('9999999999999999999999999999999')
+ end
+
+ def test_find_on_relation_with_large_number
+ assert_nil Topic.where('1=1').find_by(id: 9999999999999999999999999999999)
+ end
+
+ def test_find_by_bang_on_relation_with_large_number
+ assert_raises(ActiveRecord::RecordNotFound) do
+ Topic.where('1=1').find_by!(id: 9999999999999999999999999999999)
+ end
+ end
+
def test_find_an_empty_array
assert_equal [], Topic.find([])
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 1050047a43..7141d3ee7f 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -822,25 +822,6 @@ class ActiveSupportSubclassWithFixturesTest < ActiveRecord::TestCase
end
end
-class FixtureLoadingTest < ActiveRecord::TestCase
- def test_logs_message_for_failed_dependency_load
- ActiveRecord::Base.logger.expects(:warn).twice
- ActiveRecord::TestCase.try_to_load_dependency('does_not_exist')
- end
-
- def test_does_not_logs_message_for_dependency_that_has_been_defined_with_set_fixture_class
- ActiveRecord::TestCase.set_fixture_class unknown_dead_parrots: DeadParrot
- ActiveRecord::Base.logger.expects(:warn).never
- ActiveRecord::TestCase.try_to_load_dependency('unknown_dead_parrot')
- end
-
- def test_does_not_logs_message_for_successful_dependency_load
- ActiveRecord::TestCase.expects(:require_dependency).with('works_out_fine')
- ActiveRecord::Base.logger.expects(:warn).never
- ActiveRecord::TestCase.try_to_load_dependency('works_out_fine')
- end
-end
-
class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
ActiveRecord::FixtureSet.reset_cache
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index be635aeef9..80ac57ec7c 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -95,7 +95,7 @@ EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES = false
def verify_default_timezone_config
if Time.zone != EXPECTED_ZONE
$stderr.puts <<-MSG
-\n#{self.to_s}
+\n#{self}
Global state `Time.zone` was leaked.
Expected: #{EXPECTED_ZONE}
Got: #{Time.zone}
@@ -103,7 +103,7 @@ def verify_default_timezone_config
end
if ActiveRecord::Base.default_timezone != EXPECTED_DEFAULT_TIMEZONE
$stderr.puts <<-MSG
-\n#{self.to_s}
+\n#{self}
Global state `ActiveRecord::Base.default_timezone` was leaked.
Expected: #{EXPECTED_DEFAULT_TIMEZONE}
Got: #{ActiveRecord::Base.default_timezone}
@@ -111,7 +111,7 @@ def verify_default_timezone_config
end
if ActiveRecord::Base.time_zone_aware_attributes != EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES
$stderr.puts <<-MSG
-\n#{self.to_s}
+\n#{self}
Global state `ActiveRecord::Base.time_zone_aware_attributes` was leaked.
Expected: #{EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES}
Got: #{ActiveRecord::Base.time_zone_aware_attributes}
@@ -136,19 +136,6 @@ def disable_extension!(extension, connection)
connection.reconnect!
end
-unless ENV['FIXTURE_DEBUG']
- module ActiveRecord::TestFixtures::ClassMethods
- def try_to_load_dependency_with_silence(*args)
- old = ActiveRecord::Base.logger.level
- ActiveRecord::Base.logger.level = ActiveSupport::Logger::ERROR
- try_to_load_dependency_without_silence(*args)
- ActiveRecord::Base.logger.level = old
- end
-
- alias_method_chain :try_to_load_dependency, :silence
- end
-end
-
require "cases/validations_repair_helper"
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index bd3dd29f4d..d91e7142b3 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -97,6 +97,25 @@ module ActiveRecord
end
end
+ def test_create_table_with_bigint
+ connection.create_table :testings do |t|
+ t.bigint :eight_int
+ end
+ columns = connection.columns(:testings)
+ eight = columns.detect { |c| c.name == "eight_int" }
+
+ if current_adapter?(:OracleAdapter)
+ assert_equal 'NUMBER(8)', eight.sql_type
+ elsif current_adapter?(:SQLite3Adapter)
+ assert_equal 'bigint', eight.sql_type
+ else
+ assert_equal :integer, eight.type
+ assert_equal 8, eight.limit
+ end
+ ensure
+ connection.drop_table :testings
+ end
+
def test_create_table_with_limits
connection.create_table :testings do |t|
t.column :foo, :string, :limit => 255
diff --git a/activerecord/test/cases/migration/references_index_test.rb b/activerecord/test/cases/migration/references_index_test.rb
index 4485701a4e..ad6b828d0b 100644
--- a/activerecord/test/cases/migration/references_index_test.rb
+++ b/activerecord/test/cases/migration/references_index_test.rb
@@ -55,7 +55,7 @@ module ActiveRecord
t.references :foo, :polymorphic => true, :index => true
end
- assert connection.index_exists?(table_name, [:foo_id, :foo_type], :name => :index_testings_on_foo_id_and_foo_type)
+ assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
end
end
@@ -93,7 +93,7 @@ module ActiveRecord
t.references :foo, :polymorphic => true, :index => true
end
- assert connection.index_exists?(table_name, [:foo_id, :foo_type], :name => :index_testings_on_foo_id_and_foo_type)
+ assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
end
end
end
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index b8b4fa1135..988bd9c89f 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -42,7 +42,7 @@ module ActiveRecord
def test_creates_polymorphic_index
add_reference table_name, :taggable, polymorphic: true, index: true
- assert index_exists?(table_name, [:taggable_id, :taggable_type])
+ assert index_exists?(table_name, [:taggable_type, :taggable_id])
end
def test_creates_reference_type_column_with_default
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 270e446c69..6b7a0a9000 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -15,7 +15,6 @@ class BigNumber < ActiveRecord::Base
unless current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter)
attribute :value_of_e, Type::Integer.new
end
- attribute :world_population, Type::Integer.new
attribute :my_house_population, Type::Integer.new
end
@@ -101,6 +100,19 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Migrator.migrations_paths = old_path
end
+ def test_any_migrations
+ old_path = ActiveRecord::Migrator.migrations_paths
+ ActiveRecord::Migrator.migrations_paths = MIGRATIONS_ROOT + "/valid"
+
+ assert ActiveRecord::Migrator.any_migrations?
+
+ ActiveRecord::Migrator.migrations_paths = MIGRATIONS_ROOT + "/empty"
+
+ assert_not ActiveRecord::Migrator.any_migrations?
+ ensure
+ ActiveRecord::Migrator.migrations_paths = old_path
+ end
+
def test_migration_version
ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947)
end
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 9809d83315..f05ca900aa 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -357,11 +357,11 @@ class MigratorTest < ActiveRecord::TestCase
end
private
- def m(name, version, &block)
+ def m(name, version)
x = Sensor.new name, version
x.extend(Module.new {
- define_method(:up) { block.call(:up, x); super() }
- define_method(:down) { block.call(:down, x); super() }
+ define_method(:up) { yield(:up, x); super() }
+ define_method(:down) { yield(:down, x); super() }
}) if block_given?
end
diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb
index 2afd25c989..5b903e4f2c 100644
--- a/activerecord/test/cases/readonly_test.rb
+++ b/activerecord/test/cases/readonly_test.rb
@@ -22,9 +22,15 @@ class ReadOnlyTest < ActiveRecord::TestCase
assert !dev.save
dev.name = 'Forbidden.'
end
- assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save }
- assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save! }
- assert_raise(ActiveRecord::ReadOnlyRecord) { dev.destroy }
+
+ e = assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save }
+ assert_equal "Developer is marked as readonly", e.message
+
+ e = assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save! }
+ assert_equal "Developer is marked as readonly", e.message
+
+ e = assert_raise(ActiveRecord::ReadOnlyRecord) { dev.destroy }
+ assert_equal "Developer is marked as readonly", e.message
end
diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb
index b9e69bdb08..619055f1e7 100644
--- a/activerecord/test/cases/relation/where_chain_test.rb
+++ b/activerecord/test/cases/relation/where_chain_test.rb
@@ -149,5 +149,33 @@ module ActiveRecord
assert_bound_ast value, Post.arel_table['title'], Arel::Nodes::Equality
assert_equal 'alone', bind.last
end
+
+ def test_rewhere_with_range
+ relation = Post.where(comments_count: 1..3).rewhere(comments_count: 3..5)
+
+ assert_equal 1, relation.where_values.size
+ assert_equal Post.where(comments_count: 3..5), relation
+ end
+
+ def test_rewhere_with_infinite_upper_bound_range
+ relation = Post.where(comments_count: 1..Float::INFINITY).rewhere(comments_count: 3..5)
+
+ assert_equal 1, relation.where_values.size
+ assert_equal Post.where(comments_count: 3..5), relation
+ end
+
+ def test_rewhere_with_infinite_lower_bound_range
+ relation = Post.where(comments_count: -Float::INFINITY..1).rewhere(comments_count: 3..5)
+
+ assert_equal 1, relation.where_values.size
+ assert_equal Post.where(comments_count: 3..5), relation
+ end
+
+ def test_rewhere_with_infinite_range
+ relation = Post.where(comments_count: -Float::INFINITY..Float::INFINITY).rewhere(comments_count: 3..5)
+
+ assert_equal 1, relation.where_values.size
+ assert_equal Post.where(comments_count: 3..5), relation
+ end
end
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 39c8afdd23..a453203e15 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -7,6 +7,7 @@ require 'models/comment'
require 'models/edge'
require 'models/topic'
require 'models/binary'
+require 'models/vertex'
module ActiveRecord
class WhereTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/result_test.rb b/activerecord/test/cases/result_test.rb
index d6decafad9..dec01dfa76 100644
--- a/activerecord/test/cases/result_test.rb
+++ b/activerecord/test/cases/result_test.rb
@@ -10,6 +10,10 @@ module ActiveRecord
])
end
+ test "length" do
+ assert_equal 3, result.length
+ end
+
test "to_hash returns row_hashes" do
assert_equal [
{'col_1' => 'row 1 col 1', 'col_2' => 'row 1 col 2'},
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index dca85fb5eb..f477cf7d92 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -13,7 +13,9 @@ class SanitizeTest < ActiveRecord::TestCase
quoted_table_name = ActiveRecord::Base.connection.quote_table_name("adorable_animals")
expected_value = "#{quoted_table_name}.#{quoted_column_name} = #{quoted_bambi}"
- assert_equal expected_value, Binary.send(:sanitize_sql_hash, {adorable_animals: {name: 'Bambi'}})
+ assert_deprecated do
+ assert_equal expected_value, Binary.send(:sanitize_sql_hash, {adorable_animals: {name: 'Bambi'}})
+ end
end
def test_sanitize_sql_array_handles_string_interpolation
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 93fb502410..6303393c22 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -19,7 +19,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_dump_schema_information_outputs_lexically_ordered_versions
versions = %w{ 20100101010101 20100201010101 20100301010101 }
- versions.reverse.each do |v|
+ versions.reverse_each do |v|
ActiveRecord::SchemaMigration.create!(:version => v)
end
@@ -271,13 +271,6 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
end
- def test_schema_dump_includes_json_shorthand_definition
- output = standard_dump
- if %r{create_table "postgresql_json_data_type"} =~ output
- assert_match %r|t.json "json_data", default: {}|, output
- end
- end
-
def test_schema_dump_includes_inet_shorthand_definition
output = standard_dump
if %r{create_table "postgresql_network_addresses"} =~ output
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index d3546bd471..c2816c3670 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -132,6 +132,13 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5)
end
+ def test_scopes_body_is_a_callable
+ e = assert_raises ArgumentError do
+ Class.new(Post).class_eval { scope :containing_the_letter_z, where("body LIKE '%z%'") }
+ end
+ assert_equal "The scope body needs to be callable.", e.message
+ end
+
def test_active_records_have_scope_named__all__
assert !Topic.all.empty?
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index 8e512e118a..73835c85a8 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -15,6 +15,26 @@ class RelationScopingTest < ActiveRecord::TestCase
developers(:david)
end
+ def test_unscoped_breaks_caching
+ author = authors :mary
+ assert_nil author.first_post
+ post = FirstPost.unscoped do
+ author.reload.first_post
+ end
+ assert post
+ end
+
+ def test_scope_breaks_caching_on_collections
+ author = authors :david
+ ids = author.reload.special_posts_with_default_scope.map(&:id)
+ assert_equal [1,5,6], ids.sort
+ scoped_posts = SpecialPostWithDefaultScope.unscoped do
+ author = authors :david
+ author.reload.special_posts_with_default_scope.to_a
+ end
+ assert_equal author.posts.map(&:id).sort, scoped_posts.map(&:id).sort
+ end
+
def test_reverse_order
assert_equal Developer.order("id DESC").to_a.reverse, Developer.order("id DESC").reverse_order
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 3f52e80e11..35b13ea247 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -2,6 +2,8 @@ require "cases/helper"
require 'models/contact'
require 'models/topic'
require 'models/book'
+require 'models/author'
+require 'models/post'
class SerializationTest < ActiveRecord::TestCase
fixtures :books
@@ -92,4 +94,11 @@ class SerializationTest < ActiveRecord::TestCase
book = klazz.find(books(:awdr).id)
assert_equal 'paperback', book.read_attribute_for_serialization(:format)
end
+
+ def test_find_records_by_serialized_attributes_through_join
+ author = Author.create!(name: "David")
+ author.serialized_posts.create!(title: "Hello")
+
+ assert_equal 1, Author.joins(:serialized_posts).where(name: "David", serialized_posts: { title: "Hello" }).length
+ end
end
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index c5fb491b10..66f345d805 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -140,11 +140,10 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal 1, Topic.where(:content => nil).count
end
- def test_serialized_attribute_should_raise_exception_on_save_with_wrong_type
+ def test_serialized_attribute_should_raise_exception_on_assignment_with_wrong_type
Topic.serialize(:content, Hash)
assert_raise(ActiveRecord::SerializationTypeMismatch) do
- topic = Topic.new(content: 'string')
- topic.save
+ Topic.new(content: 'string')
end
end
diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb
new file mode 100644
index 0000000000..53d6a3a6aa
--- /dev/null
+++ b/activerecord/test/cases/type/integer_test.rb
@@ -0,0 +1,106 @@
+require "cases/helper"
+require "models/company"
+
+module ActiveRecord
+ module Type
+ class IntegerTest < ActiveRecord::TestCase
+ test "simple values" do
+ type = Type::Integer.new
+ assert_equal 1, type.type_cast_from_user(1)
+ assert_equal 1, type.type_cast_from_user('1')
+ assert_equal 1, type.type_cast_from_user('1ignore')
+ assert_equal 0, type.type_cast_from_user('bad1')
+ assert_equal 0, type.type_cast_from_user('bad')
+ assert_equal 1, type.type_cast_from_user(1.7)
+ assert_equal 0, type.type_cast_from_user(false)
+ assert_equal 1, type.type_cast_from_user(true)
+ assert_nil type.type_cast_from_user(nil)
+ end
+
+ test "random objects cast to nil" do
+ type = Type::Integer.new
+ assert_nil type.type_cast_from_user([1,2])
+ assert_nil type.type_cast_from_user({1 => 2})
+ assert_nil type.type_cast_from_user((1..2))
+ end
+
+ test "casting ActiveRecord models" do
+ type = Type::Integer.new
+ firm = Firm.create(:name => 'Apple')
+ assert_nil type.type_cast_from_user(firm)
+ end
+
+ test "casting objects without to_i" do
+ type = Type::Integer.new
+ assert_nil type.type_cast_from_user(::Object.new)
+ end
+
+ test "casting nan and infinity" do
+ type = Type::Integer.new
+ assert_nil type.type_cast_from_user(::Float::NAN)
+ assert_nil type.type_cast_from_user(1.0/0.0)
+ end
+
+ test "changed?" do
+ type = Type::Integer.new
+
+ assert type.changed?(5, 5, '5wibble')
+ assert_not type.changed?(5, 5, '5')
+ assert_not type.changed?(5, 5, '5.0')
+ assert_not type.changed?(nil, nil, nil)
+ end
+
+ test "values below int min value are out of range" do
+ assert_raises(::RangeError) do
+ Integer.new.type_cast_from_user("-2147483649")
+ end
+ end
+
+ test "values above int max value are out of range" do
+ assert_raises(::RangeError) do
+ Integer.new.type_cast_from_user("2147483648")
+ end
+ end
+
+ test "very small numbers are out of range" do
+ assert_raises(::RangeError) do
+ Integer.new.type_cast_from_user("-9999999999999999999999999999999")
+ end
+ end
+
+ test "very large numbers are in range" do
+ assert_raises(::RangeError) do
+ Integer.new.type_cast_from_user("9999999999999999999999999999999")
+ end
+ end
+
+ test "normal numbers are in range" do
+ type = Integer.new
+ assert_equal(0, type.type_cast_from_user("0"))
+ assert_equal(-1, type.type_cast_from_user("-1"))
+ assert_equal(1, type.type_cast_from_user("1"))
+ end
+
+ test "int max value is in range" do
+ assert_equal(2147483647, Integer.new.type_cast_from_user("2147483647"))
+ end
+
+ test "int min value is in range" do
+ assert_equal(-2147483648, Integer.new.type_cast_from_user("-2147483648"))
+ end
+
+ test "columns with a larger limit have larger ranges" do
+ type = Integer.new(limit: 8)
+
+ assert_equal(9223372036854775807, type.type_cast_from_user("9223372036854775807"))
+ assert_equal(-9223372036854775808, type.type_cast_from_user("-9223372036854775808"))
+ assert_raises(::RangeError) do
+ type.type_cast_from_user("-9999999999999999999999999999999")
+ end
+ assert_raises(::RangeError) do
+ type.type_cast_from_user("9999999999999999999999999999999")
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/type/string_test.rb b/activerecord/test/cases/type/string_test.rb
index 420177ed49..4d78f287f1 100644
--- a/activerecord/test/cases/type/string_test.rb
+++ b/activerecord/test/cases/type/string_test.rb
@@ -4,8 +4,8 @@ module ActiveRecord
class StringTypeTest < ActiveRecord::TestCase
test "type casting" do
type = Type::String.new
- assert_equal "1", type.type_cast_from_user(true)
- assert_equal "0", type.type_cast_from_user(false)
+ assert_equal "t", type.type_cast_from_user(true)
+ assert_equal "f", type.type_cast_from_user(false)
assert_equal "123", type.type_cast_from_user(123)
end
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
index db4f78d354..b0979cbe1f 100644
--- a/activerecord/test/cases/types_test.rb
+++ b/activerecord/test/cases/types_test.rb
@@ -1,5 +1,4 @@
require "cases/helper"
-require 'models/company'
module ActiveRecord
module ConnectionAdapters
@@ -29,56 +28,12 @@ module ActiveRecord
assert_equal false, type.type_cast_from_user('FALSE')
assert_equal false, type.type_cast_from_user('off')
assert_equal false, type.type_cast_from_user('OFF')
- assert_equal false, type.type_cast_from_user(' ')
- assert_equal false, type.type_cast_from_user("\u3000\r\n")
- assert_equal false, type.type_cast_from_user("\u0000")
- assert_equal false, type.type_cast_from_user('SOMETHING RANDOM')
- end
-
- def test_type_cast_integer
- type = Type::Integer.new
- assert_equal 1, type.type_cast_from_user(1)
- assert_equal 1, type.type_cast_from_user('1')
- assert_equal 1, type.type_cast_from_user('1ignore')
- assert_equal 0, type.type_cast_from_user('bad1')
- assert_equal 0, type.type_cast_from_user('bad')
- assert_equal 1, type.type_cast_from_user(1.7)
- assert_equal 0, type.type_cast_from_user(false)
- assert_equal 1, type.type_cast_from_user(true)
- assert_nil type.type_cast_from_user(nil)
- end
-
- def test_type_cast_non_integer_to_integer
- type = Type::Integer.new
- assert_nil type.type_cast_from_user([1,2])
- assert_nil type.type_cast_from_user({1 => 2})
- assert_nil type.type_cast_from_user((1..2))
- end
-
- def test_type_cast_activerecord_to_integer
- type = Type::Integer.new
- firm = Firm.create(:name => 'Apple')
- assert_nil type.type_cast_from_user(firm)
- end
-
- def test_type_cast_object_without_to_i_to_integer
- type = Type::Integer.new
- assert_nil type.type_cast_from_user(Object.new)
- end
-
- def test_type_cast_nan_and_infinity_to_integer
- type = Type::Integer.new
- assert_nil type.type_cast_from_user(Float::NAN)
- assert_nil type.type_cast_from_user(1.0/0.0)
- end
-
- def test_changing_integers
- type = Type::Integer.new
-
- assert type.changed?(5, 5, '5wibble')
- assert_not type.changed?(5, 5, '5')
- assert_not type.changed?(5, 5, '5.0')
- assert_not type.changed?(nil, nil, nil)
+ assert_deprecated do
+ assert_equal false, type.type_cast_from_user(' ')
+ assert_equal false, type.type_cast_from_user("\u3000\r\n")
+ assert_equal false, type.type_cast_from_user("\u0000")
+ assert_equal false, type.type_cast_from_user('SOMETHING RANDOM')
+ end
end
def test_type_cast_float
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index ce30cff9e7..e3b55d640e 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -69,12 +69,6 @@ connections:
username: rails
encoding: utf8
- openbase:
- arunit:
- username: admin
- arunit2:
- username: admin
-
oracle:
arunit:
adapter: oracle_enhanced
diff --git a/activerecord/test/fixtures/computers.yml b/activerecord/test/fixtures/computers.yml
index 7281a4d768..ad5ae2ec71 100644
--- a/activerecord/test/fixtures/computers.yml
+++ b/activerecord/test/fixtures/computers.yml
@@ -3,3 +3,8 @@ workstation:
system: 'Linux'
developer: 1
extendedWarranty: 1
+
+laptop:
+ system: 'MacOS 1'
+ developer: 1
+ extendedWarranty: 1
diff --git a/activerecord/test/fixtures/developers.yml b/activerecord/test/fixtures/developers.yml
index 1a74563dc6..54b74e7a74 100644
--- a/activerecord/test/fixtures/developers.yml
+++ b/activerecord/test/fixtures/developers.yml
@@ -2,6 +2,7 @@ david:
id: 1
name: David
salary: 80000
+ shared_computers: laptop
jamis:
id: 2
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 8949cf5826..3da3a1fd59 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -1,5 +1,6 @@
class Author < ActiveRecord::Base
has_many :posts
+ has_many :serialized_posts
has_one :post
has_many :very_special_comments, :through => :posts
has_many :posts_with_comments, -> { includes(:comments) }, :class_name => "Post"
@@ -44,6 +45,7 @@ class Author < ActiveRecord::Base
has_many :special_posts
has_many :special_post_comments, :through => :special_posts, :source => :comments
+ has_many :special_posts_with_default_scope, :class_name => 'SpecialPostWithDefaultScope'
has_many :sti_posts, :class_name => 'StiPost'
has_many :sti_post_comments, :through => :sti_posts, :source => :comments
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 3627cfdd09..d2a5a7fc49 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -15,6 +15,8 @@ class Developer < ActiveRecord::Base
accepts_nested_attributes_for :projects
+ has_and_belongs_to_many :shared_computers, class_name: "Computer"
+
has_and_belongs_to_many :projects_extended_by_name,
-> { extending(DeveloperProjectsAssociationExtension) },
:class_name => "Project",
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 72095f9236..dc0566d8a7 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -27,6 +27,9 @@ class Member < ActiveRecord::Base
has_many :clubs, :through => :current_memberships
has_one :club_through_many, :through => :current_memberships, :source => :club
+
+ belongs_to :admittable, polymorphic: true
+ has_one :premium_club, through: :admittable
end
class SelfMember < ActiveRecord::Base
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 256b720c9a..36cf221d45 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -219,8 +219,21 @@ class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
end
end
+class PostWithAfterCreateCallback < ActiveRecord::Base
+ self.table_name = 'posts'
+ has_many :comments, foreign_key: :post_id
+
+ after_create do |post|
+ update_attribute(:author_id, comments.first.id)
+ end
+end
+
class PostWithCommentWithDefaultScopeReferencesAssociation < ActiveRecord::Base
self.table_name = 'posts'
has_many :comment_with_default_scope_references_associations, foreign_key: :post_id
has_one :first_comment, class_name: "CommentWithDefaultScopeReferencesAssociation", foreign_key: :post_id
end
+
+class SerializedPost < ActiveRecord::Base
+ serialize :title
+end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index e9294a11b9..7c3b170c08 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,7 +1,9 @@
ActiveRecord::Schema.define do
- %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_uuids postgresql_ltrees
- postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type postgresql_citext).each do |table_name|
+ %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times
+ postgresql_network_addresses postgresql_uuids postgresql_ltrees postgresql_oids postgresql_xml_data_type defaults
+ geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent
+ postgresql_citext).each do |table_name|
execute "DROP TABLE IF EXISTS #{quote_table_name table_name}"
end
@@ -108,15 +110,6 @@ _SQL
_SQL
end
- if 't' == select_value("select 'json'=ANY(select typname from pg_type)")
- execute <<_SQL
- CREATE TABLE postgresql_json_data_type (
- id SERIAL PRIMARY KEY,
- json_data json default '{}'::json
- );
-_SQL
- end
-
execute <<_SQL
CREATE TABLE postgresql_numbers (
id SERIAL PRIMARY KEY,
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 10de3d34cb..720a127585 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -228,6 +228,11 @@ ActiveRecord::Schema.define do
t.integer :extendedWarranty, null: false
end
+ create_table :computers_developers, id: false, force: true do |t|
+ t.references :computer
+ t.references :developer
+ end
+
create_table :contracts, force: true do |t|
t.integer :developer_id
t.integer :company_id
@@ -311,7 +316,7 @@ ActiveRecord::Schema.define do
end
create_table :cold_jokes, force: true do |t|
- t.string :name
+ t.string :cold_name
end
create_table :friendships, force: true do |t|
@@ -585,6 +590,11 @@ ActiveRecord::Schema.define do
t.integer :tags_with_nullify_count, default: 0
end
+ create_table :serialized_posts, force: true do |t|
+ t.integer :author_id
+ t.string :title, null: false
+ end
+
create_table :price_estimates, force: true do |t|
t.string :estimate_of_type
t.integer :estimate_of_id