aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md56
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb7
-rw-r--r--activerecord/lib/active_record/attribute_set.rb6
-rw-r--r--activerecord/lib/active_record/coders/json.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb11
-rw-r--r--activerecord/lib/active_record/core.rb5
-rw-r--r--activerecord/lib/active_record/enum.rb4
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/reflection.rb25
-rw-r--r--activerecord/lib/active_record/relation.rb5
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb7
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb7
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb37
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb7
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb6
-rw-r--r--activerecord/test/cases/attribute_set_test.rb10
-rw-r--r--activerecord/test/cases/dirty_test.rb16
-rw-r--r--activerecord/test/cases/migration_test.rb10
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb37
-rw-r--r--activerecord/test/cases/test_case.rb17
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb1
-rw-r--r--activerecord/test/schema/schema.rb5
31 files changed, 290 insertions, 53 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 1ba01d3618..25c219a9b0 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,59 @@
+* PostgreSQL invalid `uuid` are convert to nil.
+
+ *Abdelkader Boudih*
+
+* Restore 4.0 behavior for using serialize attributes with `JSON` as coder.
+
+ With 4.1.x, `serialize` started returning a string when `JSON` was passed as
+ the second attribute. It will now return a hash as per previous versions.
+
+ Example:
+
+ class Post < ActiveRecord::Base
+ serialize :comment, JSON
+ end
+
+ class Comment
+ include ActiveModel::Model
+ attr_accessor :category, :text
+ end
+
+ post = Post.create!
+ post.comment = Comment.new(category: "Animals", text: "This is a comment about squirrels.")
+ post.save!
+
+ # 4.0
+ post.comment # => {"category"=>"Animals", "text"=>"This is a comment about squirrels."}
+
+ # 4.1 before
+ post.comment # => "#<Comment:0x007f80ab48ff98>"
+
+ # 4.1 after
+ post.comment # => {"category"=>"Animals", "text"=>"This is a comment about squirrels."}
+
+ When using `JSON` as the coder in `serialize`, Active Record will use the
+ new `ActiveRecord::Coders::JSON` coder which delegates its `dump/load` to
+ `ActiveSupport::JSON.encode/decode`. This ensures special objects are dumped
+ correctly using the `#as_json` hook.
+
+ To keep the previous behaviour, supply a custom coder instead
+ ([example](https://gist.github.com/jenncoop/8c4142bbe59da77daa63)).
+
+ Fixes #15594.
+
+ *Jenn Cooper*
+
+* Do not use `RENAME INDEX` syntax for MariaDB 10.0.
+
+ Fixes #15931.
+
+ *Jeff Browning*
+
+* Calling `#empty?` on a `has_many` association would use the value from the
+ counter cache if one exists.
+
+ *David Verhasselt*
+
* Fix the schema dump generated for tables without constraints and with
primary key with default value of custom PostgreSQL function result.
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 17b00bbaea..9028970a3d 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -97,6 +97,7 @@ module ActiveRecord
module Coders
autoload :YAMLColumn, 'active_record/coders/yaml_column'
+ autoload :JSON, 'active_record/coders/json'
end
module AttributeMethods
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 17f36acf40..6e6dd7204c 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -3,7 +3,7 @@
module ActiveRecord::Associations::Builder
class SingularAssociation < Association #:nodoc:
def valid_options
- super + [:remote, :dependent, :primary_key, :inverse_of, :required]
+ super + [:dependent, :primary_key, :inverse_of, :required]
end
def self.define_accessors(model, reflection)
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 453615ba87..2a97d0ed31 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -41,6 +41,14 @@ module ActiveRecord
end
end
+ def empty?
+ if has_cached_counter?
+ size.zero?
+ else
+ super
+ end
+ end
+
private
# Returns the number of records in this collection.
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index cbe5cf202a..1cdb530815 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -251,7 +251,7 @@ module ActiveRecord
# person.has_attribute?('age') # => true
# person.has_attribute?(:nothing) # => false
def has_attribute?(attr_name)
- @attributes.include?(attr_name.to_s)
+ @attributes.key?(attr_name.to_s)
end
# Returns an array of names for the attributes available on this object.
@@ -386,7 +386,7 @@ module ActiveRecord
def attribute_method?(attr_name) # :nodoc:
# We check defined? because Syck calls respond_to? before actually calling initialize.
- defined?(@attributes) && @attributes.include?(attr_name)
+ defined?(@attributes) && @attributes.key?(attr_name)
end
private
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index e1a86fd3aa..b58295a106 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -34,7 +34,7 @@ module ActiveRecord
# <tt>reload</tt> the record and clears changed attributes.
def reload(*)
super.tap do
- reset_changes
+ clear_changes_information
end
end
@@ -64,7 +64,7 @@ module ActiveRecord
store_original_raw_attributes
end
- def reset_changes
+ def clear_changes_information
super
original_raw_attributes.clear
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 734d94865a..264ce2bdfa 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -37,7 +37,12 @@ module ActiveRecord
# serialize :preferences, Hash
# end
def serialize(attr_name, class_name_or_coder = Object)
- coder = if [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
+ # When ::JSON is used, force it to go through the Active Support JSON encoder
+ # to ensure special objects (e.g. Active Record models) are dumped correctly
+ # using the #as_json hook.
+ coder = if class_name_or_coder == ::JSON
+ Coders::JSON
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
class_name_or_coder
else
Coders::YAMLColumn.new(class_name_or_coder)
diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb
index 64df6f6358..98ac63c7e1 100644
--- a/activerecord/lib/active_record/attribute_set.rb
+++ b/activerecord/lib/active_record/attribute_set.rb
@@ -21,8 +21,8 @@ module ActiveRecord
end
alias_method :to_h, :to_hash
- def include?(name)
- attributes.include?(name) && self[name].initialized?
+ def key?(name)
+ attributes.key?(name) && self[name].initialized?
end
def fetch_value(name, &block)
@@ -53,7 +53,7 @@ module ActiveRecord
end
def reset(key)
- if include?(key)
+ if key?(key)
write_from_database(key, nil)
end
end
diff --git a/activerecord/lib/active_record/coders/json.rb b/activerecord/lib/active_record/coders/json.rb
new file mode 100644
index 0000000000..75d3bfe625
--- /dev/null
+++ b/activerecord/lib/active_record/coders/json.rb
@@ -0,0 +1,13 @@
+module ActiveRecord
+ module Coders # :nodoc:
+ class JSON # :nodoc:
+ def self.dump(obj)
+ ActiveSupport::JSON.encode(obj)
+ end
+
+ def self.load(json)
+ ActiveSupport::JSON.decode(json) unless json.nil?
+ end
+ end
+ end
+end
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 ccb957d2c8..e5417a9556 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -473,7 +473,7 @@ module ActiveRecord
end
def rename_index(table_name, old_name, new_name)
- if (version[0] == 5 && version[1] >= 7) || version[0] >= 6
+ if supports_rename_index?
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
else
super
@@ -774,10 +774,22 @@ module ActiveRecord
private
+ def version
+ @version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
+ end
+
+ def mariadb?
+ full_version =~ /mariadb/i
+ end
+
def supports_views?
version[0] >= 5
end
+ def supports_rename_index?
+ mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
+ end
+
def configure_connection
variables = @config.fetch(:variables, {}).stringify_keys
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 0a14cdfe89..39d52e6349 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -269,8 +269,8 @@ module ActiveRecord
super
end
- def version
- @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
+ def full_version
+ @full_version ||= @connection.info[:version]
end
def set_field_encoding field_name
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index ad07a46e51..a03bc28744 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -470,9 +470,9 @@ module ActiveRecord
rows
end
- # Returns the version of the connected MySQL server.
- def version
- @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
+ # Returns the full version of the connected MySQL server.
+ def full_version
+ @full_version ||= @connection.server_info
end
def set_field_encoding field_name
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
index 89728b0fe2..dd97393eac 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb
@@ -3,12 +3,21 @@ module ActiveRecord
module PostgreSQL
module OID # :nodoc:
class Uuid < Type::Value # :nodoc:
+ RFC_4122 = %r{\A\{?[a-fA-F0-9]{4}-?
+ [a-fA-F0-9]{4}-?
+ [a-fA-F0-9]{4}-?
+ [1-5][a-fA-F0-9]{3}-?
+ [8-Bab][a-fA-F0-9]{3}-?
+ [a-fA-F0-9]{4}-?
+ [a-fA-F0-9]{4}-?
+ [a-fA-F0-9]{4}-?\}?\z}x
+
def type
:uuid
end
def type_cast(value)
- value.presence
+ value.to_s[RFC_4122, 0]
end
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index b11c4f804f..d22806fbdf 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -107,6 +107,11 @@ module ActiveRecord
end
module ClassMethods
+ def allocate
+ define_attribute_methods
+ super
+ end
+
def initialize_find_by_cache
self.find_by_statement_cache = {}.extend(Mutex_m)
end
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 38c6dcf88d..f0ee433d0b 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -69,12 +69,12 @@ module ActiveRecord
#
# Where conditions on an enum attribute must use the ordinal value of an enum.
module Enum
- def self.extended(base)
+ def self.extended(base) # :nodoc:
base.class_attribute(:defined_enums)
base.defined_enums = {}
end
- def inherited(base)
+ def inherited(base) # :nodoc:
base.defined_enums = defined_enums.deep_dup
super
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 4cd5f92207..4306b36ae1 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -2,7 +2,7 @@ require 'erb'
require 'yaml'
require 'zlib'
require 'active_support/dependencies'
-require 'active_support/core_ext/securerandom'
+require 'active_support/core_ext/digest/uuid'
require 'active_record/fixture_set/file'
require 'active_record/errors'
@@ -551,7 +551,7 @@ module ActiveRecord
# Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
def self.identify(label, column_type = :integer)
if column_type == :uuid
- SecureRandom.uuid_v5(SecureRandom::UUID_OID_NAMESPACE, label.to_s)
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
else
Zlib.crc32(label.to_s) % MAX_ID
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 12208aecf7..2eefba2dfe 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -177,12 +177,6 @@ module ActiveRecord
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
attr_reader :name
- # Returns the macro type.
- #
- # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
- # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
- attr_reader :macro
-
attr_reader :scope
# Returns the hash of options used for the macro.
@@ -386,7 +380,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
scope ? [[scope]] : [[]]
end
- alias :source_macro :macro
+ def source_macro; macro; end
def has_inverse?
inverse_name
@@ -408,6 +402,11 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
end
end
+ # Returns the macro type.
+ #
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
+ def macro; raise NotImplementedError; end
+
# Returns whether or not this association reflection is for a collection
# association. Returns +true+ if the +macro+ is either +has_many+ or
# +has_and_belongs_to_many+, +false+ otherwise.
@@ -578,10 +577,11 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
class HasManyReflection < AssociationReflection #:nodoc:
def initialize(name, scope, options, active_record)
- @macro = :has_many
super(name, scope, options, active_record)
end
+ def macro; :has_many; end
+
def collection?
true
end
@@ -589,24 +589,27 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
class HasOneReflection < AssociationReflection #:nodoc:
def initialize(name, scope, options, active_record)
- @macro = :has_one
super(name, scope, options, active_record)
end
+
+ def macro; :has_one; end
end
class BelongsToReflection < AssociationReflection #:nodoc:
def initialize(name, scope, options, active_record)
- @macro = :belongs_to
super(name, scope, options, active_record)
end
+
+ def macro; :belongs_to; end
end
class HasAndBelongsToManyReflection < AssociationReflection #:nodoc:
def initialize(name, scope, options, active_record)
- @macro = :has_and_belongs_to_many
super
end
+ def macro; :has_and_belongs_to_many; end
+
def collection?
true
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index cef40be7ce..ad54d84665 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -242,6 +242,11 @@ module ActiveRecord
@records
end
+ # Serializes the relation objects Array.
+ def encode_with(coder)
+ coder.represent_seq(nil, to_a)
+ end
+
def as_json(options = nil) #:nodoc:
to_a.as_json(options)
end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 4c90d06732..b0759dffde 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -134,12 +134,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
assert_equal [["STRICT_ALL_TABLES"]], result.rows
end
- def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
+ def test_mysql_strict_mode_disabled
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.merge({:strict => false}))
- global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
- session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
- assert_equal global_sql_mode.rows, session_sql_mode.rows
+ result = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal [['']], result.rows
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 65f50e77bb..3b35e69e0d 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -60,12 +60,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
assert_equal [["STRICT_ALL_TABLES"]], result.rows
end
- def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
+ def test_mysql_strict_mode_disabled
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.merge({:strict => false}))
- global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
- session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
- assert_equal global_sql_mode.rows, session_sql_mode.rows
+ result = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal [['']], result.rows
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index ec73ec35aa..9c49599d34 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -6,12 +6,12 @@ module ActiveRecord
class SchemaMigrationsTest < ActiveRecord::TestCase
def test_renaming_index_on_foreign_key
connection.add_index "engines", "car_id"
- connection.execute "ALTER TABLE engines ADD CONSTRAINT fk_engines_cars FOREIGN KEY (car_id) REFERENCES cars(id)"
+ connection.add_foreign_key :engines, :cars, name: "fk_engines_cars"
connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed")
assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name)
ensure
- connection.execute "ALTER TABLE engines DROP FOREIGN KEY fk_engines_cars"
+ connection.remove_foreign_key :engines, name: "fk_engines_cars"
end
def test_initializes_schema_migrations_for_encoding_utf8mb4
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index df8faff546..d7ff81a382 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -60,6 +60,43 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase
assert_equal(nil, UUIDType.last.guid)
end
+ def test_treat_invalid_uuid_as_nil
+ uuid = UUIDType.create! guid: 'foobar'
+ assert_equal(nil, uuid.guid)
+ end
+
+ def test_invalid_uuid_dont_modify_before_type_cast
+ uuid = UUIDType.new guid: 'foobar'
+ assert_equal 'foobar', uuid.guid_before_type_cast
+ end
+
+ def test_rfc_4122_regex
+ # Valid uuids
+ ['A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11',
+ '{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}',
+ 'a0eebc999c0b4ef8bb6d6bb9bd380a11',
+ 'a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11',
+ '{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}'].each do |valid_uuid|
+ uuid = UUIDType.new guid: valid_uuid
+ assert_not_nil uuid.guid
+ end
+
+ # Invalid uuids
+ [['A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'],
+ Hash.new,
+ 0,
+ 0.0,
+ true,
+ 'Z0000C99-9C0B-4EF8-BB6D-6BB9BD380A11',
+ '{a0eebc99-9c0b-4ef8-fb6d-6bb9bd380a11}',
+ 'a0eebc999r0b4ef8ab6d6bb9bd380a11',
+ 'a0ee-bc99------4ef8-bb6d-6bb9-bd38-0a11',
+ '{a0eebc99-bb6d6bb9-bd380a11}'].each do |invalid_uuid|
+ uuid = UUIDType.new guid: invalid_uuid
+ assert_nil uuid.guid
+ end
+ end
+
def test_uuid_formats
["A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",
"{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}",
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 3e5b7e655b..fe961e871c 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -837,6 +837,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_calling_empty_with_counter_cache
+ post = posts(:welcome)
+ assert_queries(0) do
+ assert_not post.comments.empty?
+ end
+ end
+
def test_custom_named_counter_cache
topic = topics(:first)
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 2048e0d5ad..ab67cf4085 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -143,7 +143,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
# Syck calls respond_to? before actually calling initialize
def test_respond_to_with_allocated_object
- topic = Topic.allocate
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'topics'
+ end
+
+ topic = klass.allocate
assert !topic.respond_to?("nothingness")
assert !topic.respond_to?(:nothingness)
assert_respond_to topic, "title"
diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb
index cdbb11fa32..dc20c3c676 100644
--- a/activerecord/test/cases/attribute_set_test.rb
+++ b/activerecord/test/cases/attribute_set_test.rb
@@ -88,15 +88,15 @@ module ActiveRecord
assert_equal [:foo], attributes.keys
end
- test "uninitialized attributes return false for include?" do
+ test "uninitialized attributes return false for key?" do
attributes = attributes_with_uninitialized_key
- assert attributes.include?(:foo)
- assert_not attributes.include?(:bar)
+ assert attributes.key?(:foo)
+ assert_not attributes.key?(:bar)
end
- test "unknown attributes return false for include?" do
+ test "unknown attributes return false for key?" do
attributes = attributes_with_uninitialized_key
- assert_not attributes.include?(:wibble)
+ assert_not attributes.key?(:wibble)
end
test "fetch_value returns the value for the given initialized attribute" do
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index ea73c561e9..69a7f25213 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -169,7 +169,19 @@ class DirtyTest < ActiveRecord::TestCase
pirate = Pirate.create!(:catchphrase => 'Yar!')
pirate.catchphrase = 'Ahoy!'
- pirate.reset_catchphrase!
+ assert_deprecated do
+ pirate.reset_catchphrase!
+ end
+ assert_equal "Yar!", pirate.catchphrase
+ assert_equal Hash.new, pirate.changes
+ assert !pirate.catchphrase_changed?
+ end
+
+ def test_restore_attribute!
+ pirate = Pirate.create!(:catchphrase => 'Yar!')
+ pirate.catchphrase = 'Ahoy!'
+
+ pirate.restore_catchphrase!
assert_equal "Yar!", pirate.catchphrase
assert_equal Hash.new, pirate.changes
assert !pirate.catchphrase_changed?
@@ -398,7 +410,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_dup_objects_should_not_copy_dirty_flag_from_creator
pirate = Pirate.create!(:catchphrase => "shiver me timbers")
pirate_dup = pirate.dup
- pirate_dup.reset_catchphrase!
+ pirate_dup.restore_catchphrase!
pirate.catchphrase = "I love Rum"
assert pirate.catchphrase_changed?
assert !pirate_dup.catchphrase_changed?
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 6b840e16bb..84720585f2 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -895,4 +895,14 @@ class CopyMigrationsTest < ActiveRecord::TestCase
ensure
ActiveRecord::Base.logger = old
end
+
+ private
+
+ def quietly
+ silence_stream(STDOUT) do
+ silence_stream(STDERR) do
+ yield
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 186a1a2ade..f8d87a3661 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -3,10 +3,11 @@ require 'models/topic'
require 'models/reply'
require 'models/person'
require 'models/traffic_light'
+require 'models/post'
require 'bcrypt'
class SerializedAttributeTest < ActiveRecord::TestCase
- fixtures :topics
+ fixtures :topics, :posts
MyObject = Struct.new :attribute1, :attribute2
@@ -67,6 +68,40 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal(orig.content, clone.content)
end
+ def test_serialized_json_attribute_returns_unserialized_value
+ Topic.serialize :content, JSON
+ my_post = posts(:welcome)
+
+ t = Topic.new(content: my_post)
+ t.save!
+ t.reload
+
+ assert_instance_of(Hash, t.content)
+ assert_equal(my_post.id, t.content["id"])
+ assert_equal(my_post.title, t.content["title"])
+ end
+
+ def test_json_read_legacy_null
+ Topic.serialize :content, JSON
+
+ # Force a row to have a JSON "null" instead of a database NULL (this is how
+ # null values are saved on 4.1 and before)
+ id = Topic.connection.insert "INSERT INTO topics (content) VALUES('null')"
+ t = Topic.find(id)
+
+ assert_nil t.content
+ end
+
+ def test_json_read_db_null
+ Topic.serialize :content, JSON
+
+ # Force a row to have a database NULL instead of a JSON "null"
+ id = Topic.connection.insert "INSERT INTO topics (content) VALUES(NULL)"
+ t = Topic.find(id)
+
+ assert_nil t.content
+ end
+
def test_serialized_attribute_declared_in_subclass
hash = { 'important1' => 'value1', 'important2' => 'value2' }
important_topic = ImportantTopic.create("important" => hash)
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index b6c5511849..23a170388e 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -13,6 +13,23 @@ module ActiveRecord
assert_equal expected.to_s, actual.to_s, message
end
+ def capture(stream)
+ stream = stream.to_s
+ captured_stream = Tempfile.new(stream)
+ stream_io = eval("$#{stream}")
+ origin_stream = stream_io.dup
+ stream_io.reopen(captured_stream)
+
+ yield
+
+ stream_io.rewind
+ return captured_stream.read
+ ensure
+ captured_stream.close
+ captured_stream.unlink
+ stream_io.reopen(origin_stream)
+ end
+
def capture_sql
SQLCounter.clear_log
yield
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index 9f1d110ddb..bce59b4fcd 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -1,5 +1,6 @@
require 'cases/helper'
require 'models/topic'
+require 'models/reply'
require 'models/post'
require 'models/author'
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 146190fc19..a8b21904ac 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -860,9 +860,8 @@ ActiveRecord::Schema.define do
create_table :fk_test_has_pk, force: true, primary_key: "pk_id" do |t|
end
- execute "ALTER TABLE fk_test_has_fk ADD CONSTRAINT fk_name FOREIGN KEY (#{quote_column_name 'fk_id'}) REFERENCES #{quote_table_name 'fk_test_has_pk'} (#{quote_column_name 'pk_id'})"
-
- execute "ALTER TABLE lessons_students ADD CONSTRAINT student_id_fk FOREIGN KEY (#{quote_column_name 'student_id'}) REFERENCES #{quote_table_name 'students'} (#{quote_column_name 'id'})"
+ add_foreign_key :fk_test_has_fk, :fk_test_has_pk, column: "fk_id", name: "fk_name", primary_key: "pk_id"
+ add_foreign_key :lessons_students, :students
end
create_table :overloaded_types, force: true do |t|