aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2014-06-04 11:45:53 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2014-06-04 11:45:53 -0700
commit1ce0cc0d38c9f5058cb484612fab625db823c4e4 (patch)
tree7c856d0c2f6534c349ff2e81a01046acaa868c31 /activerecord
parent3f7e482aed8f67137c6c8382f6c558d7e856b341 (diff)
parentb8f586a094c104006d29a87fee0d8b48d0af2d14 (diff)
downloadrails-1ce0cc0d38c9f5058cb484612fab625db823c4e4.tar.gz
rails-1ce0cc0d38c9f5058cb484612fab625db823c4e4.tar.bz2
rails-1ce0cc0d38c9f5058cb484612fab625db823c4e4.zip
Merge branch 'master' of github.com:rails/rails
* 'master' of github.com:rails/rails: For our build, stick with mail 2.5.x for now Correct result, previously showing wrong result Collapse PG default extractoin of most types to single regex Change wording of explanation about precision & scale of decimal numbers [ci skip] Cleaned up duplicated CHANGELOG entry [ci skip] reuse available belongs_to? method Convert StrongParameters cache to a hash. This fixes an unbounded memory leak demonstrated on @tenderlove's latest blog post: Partially revert deprecation of *_filter Pluralize params Add default_i18n_subject to the guides Fix regression on eager loading association based on SQL query rather than existing column. Relax mail gem constraint from ~> 2.5.4 to ~> 2.5, >= 2.5.4 Keep column defaults in type cast form Return a null column when no column exists for an attribute Refactor XML serializer to not depend on `serialized_attributes` Test the serialized types of virtual columns in XML implement ActiveRecord::Base#pretty_print + changelog Remove duplicated HashWithIndifferentAccess#with_indifferent_access.
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md20
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb6
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb16
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb54
-rw-r--r--activerecord/lib/active_record/core.rb25
-rw-r--r--activerecord/lib/active_record/model_schema.rb9
-rw-r--r--activerecord/lib/active_record/properties.rb1
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb8
-rw-r--r--activerecord/test/cases/associations/eager_test.rb4
-rw-r--r--activerecord/test/cases/core_test.rb68
-rw-r--r--activerecord/test/cases/defaults_test.rb5
-rw-r--r--activerecord/test/cases/dirty_test.rb28
-rw-r--r--activerecord/test/cases/reflection_test.rb19
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb5
-rw-r--r--activerecord/test/models/owner.rb12
22 files changed, 228 insertions, 82 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 02fa504c78..4f0b1a76df 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,18 @@
+* Fix regression on eager loading association based on SQL query rather than
+ existing column.
+
+ Fixes #15480.
+
+ *Lauro Caetano*, *Carlos Antonio da Silva*
+
+* Return a null column from `column_for_attribute` when no column exists.
+
+ *Sean Griffin*
+
+* Implemented ActiveRecord::Base#pretty_print to work with PP.
+
+ *Ethan*
+
* Preserve type when dumping PostgreSQL point, bit, bit varying and money
columns.
@@ -577,12 +592,11 @@
*arthurnn*
-* Passing an Active Record object to `find` is now deprecated. Call `.id`
- on the object first.
-
* Passing an Active Record object to `find` or `exists?` is now deprecated.
Call `.id` on the object first.
+ *Aaron Patterson*
+
* Only use BINARY for MySQL case sensitive uniqueness check when column has a case insensitive collation.
*Ryuta Kamizono*
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 35ad512537..954128064d 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -116,7 +116,7 @@ module ActiveRecord
end
def target_reflection_has_associated_record?
- !(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?)
+ !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
end
def update_through_counter?(method)
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 63773bd5e1..1b83700613 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -104,11 +104,13 @@ module ActiveRecord
end
def association_key_type
- @klass.column_types[association_key_name.to_s].type
+ column = @klass.column_types[association_key_name.to_s]
+ column && column.type
end
def owner_key_type
- @model.column_types[owner_key_name.to_s].type
+ column = @model.column_types[owner_key_name.to_s]
+ column && column.type
end
def load_slices(slices)
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index f8a85b8a6f..fcf3b219d4 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -63,14 +63,13 @@ module ActiveRecord
# Note: this does not capture all cases, for example it would be crazy to try to
# properly support stale-checking for nested associations.
def stale_state
- if through_reflection.macro == :belongs_to
+ if through_reflection.belongs_to?
owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
end
end
def foreign_key_present?
- through_reflection.macro == :belongs_to &&
- !owner[through_reflection.foreign_key].nil?
+ through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
end
def ensure_mutable
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index b6520b9b3d..e56a4cc805 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -350,10 +350,12 @@ module ActiveRecord
# # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
#
# person.column_for_attribute(:nothing)
- # # => nil
+ # # => #<ActiveRecord::ConnectionAdapters::Column:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
def column_for_attribute(name)
- # FIXME: should this return a null object for columns that don't exist?
- self.class.columns_hash[name.to_s]
+ name = name.to_s
+ self.class.columns_hash.fetch(name) do
+ ConnectionAdapters::Column.new(name, nil, Type::Value.new)
+ end
end
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
@@ -438,16 +440,16 @@ module ActiveRecord
# Filters the primary keys and readonly attributes from the attribute names.
def attributes_for_update(attribute_names)
- attribute_names.select do |name|
- column_for_attribute(name) && !readonly_attribute?(name)
+ attribute_names.reject do |name|
+ readonly_attribute?(name)
end
end
# Filters out the primary keys, from the attribute names, when the primary
# key is to be generated (e.g. the id attribute has no value).
def attributes_for_create(attribute_names)
- attribute_names.select do |name|
- column_for_attribute(name) && !(pk_attribute?(name) && id.nil?)
+ attribute_names.reject do |name|
+ pk_attribute?(name) && id.nil?
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 6cd4e43ddd..4e32b78e34 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -94,8 +94,7 @@ module ActiveRecord
end
def _field_changed?(attr, old, value)
- column = column_for_attribute(attr) || Type::Value.new
- column.changed?(old, value)
+ column_for_attribute(attr).changed?(old, value)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index c3e601a208..5203b30462 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -72,18 +72,20 @@ module ActiveRecord
@attributes.delete(attr_name)
column = column_for_attribute(attr_name)
+ unless has_attribute?(attr_name) || self.class.columns_hash.key?(attr_name)
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
+ end
+
# If we're dealing with a binary column, write the data to the cache
# so we don't attempt to typecast multiple times.
- if column && column.binary?
+ if column.binary?
@attributes[attr_name] = value
end
- if column && should_type_cast
+ if should_type_cast
@raw_attributes[attr_name] = column.type_cast_for_write(value)
- elsif !should_type_cast || @raw_attributes.has_key?(attr_name)
- @raw_attributes[attr_name] = value
else
- raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
+ @raw_attributes[attr_name] = value
end
end
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 117c0f0969..a9b3e9cfb9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -102,8 +102,8 @@ module ActiveRecord
# * <tt>:index</tt> -
# Create an index for the column. Can be either <tt>true</tt> or an options hash.
#
- # For clarity's sake: the precision is the number of significant digits,
- # while the scale is the number of digits that can be stored following
+ # Note: The precision is the total number of significant digits
+ # and the scale is the number of digits that can be stored following
# the decimal point. For example, the number 123.45 has a precision of 5
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
# range from -999.99 to 999.99.
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 34054589d0..3b0dcbc6a7 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -52,7 +52,7 @@ module ActiveRecord
end
def extract_default(default)
- type_cast_for_write(type_cast(default))
+ type_cast(default)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 67570dad3c..0f20d52879 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -511,58 +511,16 @@ module ActiveRecord
# makes this method very very slow.
return default unless default
- # TODO: The default extraction is related to the cast-type.
- # we should probably make a type_map lookup and cast the default-
- # expression accordingly
- if oid.type == :enum && default =~ /\A'(.*)'::/
- return $1
- end
-
case default
- when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
- $1
+ # Quoted types
+ when /\A[\(B]?'(.*)'::/m
+ $1.gsub(/''/, "'")
+ # Boolean types
+ when 'true', 'false'
+ default
# Numeric types
when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
$1
- # Character types
- when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
- $1.gsub(/''/, "'")
- # Binary data types
- when /\A'(.*)'::bytea\z/m
- $1
- # Date/time types
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
- $1
- when /\A'(.*)'::interval\z/
- $1
- # Boolean type
- when 'true'
- true
- when 'false'
- false
- # Geometric types
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
- $1
- # Network address types
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
- $1
- # Bit string types
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
- $1
- # XML type
- when /\A'(.*)'::xml\z/m
- $1
- # Arrays
- when /\A'(.*)'::"?\D+"?\[\]\z/
- $1
- # Hstore
- when /\A'(.*)'::hstore\z/
- $1
- # JSON
- when /\A'(.*)'::json\z/
- $1
- when /\A'(.*)'::money\z/
- $1
# Object identifier types
when /\A-?\d+\z/
$1
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 1d75eeda69..d6849fef2e 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -249,7 +249,7 @@ module ActiveRecord
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
def initialize(attributes = nil, options = {})
- defaults = self.class.column_defaults.dup
+ defaults = self.class.raw_column_defaults.dup
defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
@raw_attributes = defaults
@@ -432,6 +432,29 @@ module ActiveRecord
"#<#{self.class} #{inspection}>"
end
+ # Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
+ # when pp is required.
+ def pretty_print(pp)
+ pp.object_address_group(self) do
+ if defined?(@attributes) && @attributes
+ column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
+ pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
+ column_value = read_attribute(column_name)
+ pp.breakable ' '
+ pp.group(1) do
+ pp.text column_name
+ pp.text ':'
+ pp.breakable
+ pp.pp column_value
+ end
+ end
+ else
+ pp.breakable ' '
+ pp.text 'not initialized'
+ end
+ end
+ end
+
# Returns a hash of the given methods with their names as keys and returned values as values.
def slice(*methods)
Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index f0ddf202c3..baf2b5fbf8 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -241,6 +241,14 @@ module ActiveRecord
@column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
end
+ # Returns a hash where the keys are the column names and the values
+ # are the default values suitable for use in `@raw_attriubtes`
+ def raw_column_defaults # :nodoc:
+ @raw_column_defauts ||= Hash[column_defaults.map { |name, default|
+ [name, columns_hash[name].type_cast_for_write(default)]
+ }]
+ end
+
# Returns an array of column names as strings.
def column_names
@column_names ||= columns.map { |column| column.name }
@@ -285,6 +293,7 @@ module ActiveRecord
@arel_engine = nil
@column_defaults = nil
+ @raw_column_defauts = nil
@column_names = nil
@column_types = nil
@content_columns = nil
diff --git a/activerecord/lib/active_record/properties.rb b/activerecord/lib/active_record/properties.rb
index 5fd51e09fa..48ee42aaca 100644
--- a/activerecord/lib/active_record/properties.rb
+++ b/activerecord/lib/active_record/properties.rb
@@ -113,6 +113,7 @@ module ActiveRecord
@columns_hash = nil
@column_types = nil
@column_defaults = nil
+ @raw_column_defaults = nil
@column_names = nil
@content_columns = nil
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 9ee6422329..11ab1b4595 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -278,7 +278,7 @@ module ActiveRecord
if group_attrs.first.respond_to?(:to_sym)
association = @klass._reflect_on_association(group_attrs.first.to_sym)
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
+ associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
group_fields = Array(associated ? association.foreign_key : group_attrs)
else
group_fields = group_attrs
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index 1a766093d0..019fe2218e 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -180,12 +180,12 @@ module ActiveRecord #:nodoc:
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
def compute_type
klass = @serializable.class
- type = if klass.serialized_attributes.key?(name)
+ column = klass.columns_hash[name] || Type::Value.new
+
+ type = if column.serialized?
super
- elsif klass.columns_hash.key?(name)
- klass.columns_hash[name].type
else
- NilClass
+ column.type
end
{ :text => :string,
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 4bd4486b41..910067666a 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1239,6 +1239,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
}
end
+ test "including association based on sql condition and no database column" do
+ assert_equal pets(:parrot), Owner.including_last_pet.first.last_pet
+ end
+
test "include instance dependent associations is deprecated" do
message = "association scope 'posts_with_signature' is"
assert_deprecated message do
diff --git a/activerecord/test/cases/core_test.rb b/activerecord/test/cases/core_test.rb
index 2a52bf574c..715d92af99 100644
--- a/activerecord/test/cases/core_test.rb
+++ b/activerecord/test/cases/core_test.rb
@@ -1,6 +1,8 @@
require 'cases/helper'
require 'models/person'
require 'models/topic'
+require 'pp'
+require 'active_support/core_ext/string/strip'
class NonExistentTable < ActiveRecord::Base; end
@@ -30,4 +32,70 @@ class CoreTest < ActiveRecord::TestCase
def test_inspect_class_without_table
assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
end
+
+ def test_pretty_print_new
+ topic = Topic.new
+ actual = ''
+ PP.pp(topic, StringIO.new(actual))
+ expected = <<-PRETTY.strip_heredoc
+ #<Topic:0xXXXXXX
+ id: nil,
+ title: nil,
+ author_name: nil,
+ author_email_address: "test@test.com",
+ written_on: nil,
+ bonus_time: nil,
+ last_read: nil,
+ content: nil,
+ important: nil,
+ approved: true,
+ replies_count: 0,
+ unique_replies_count: 0,
+ parent_id: nil,
+ parent_title: nil,
+ type: nil,
+ group: nil,
+ created_at: nil,
+ updated_at: nil>
+ PRETTY
+ assert actual.start_with?(expected.split('XXXXXX').first)
+ assert actual.end_with?(expected.split('XXXXXX').last)
+ end
+
+ def test_pretty_print_persisted
+ topic = topics(:first)
+ actual = ''
+ PP.pp(topic, StringIO.new(actual))
+ expected = <<-PRETTY.strip_heredoc
+ #<Topic:0x\\w+
+ id: 1,
+ title: "The First Topic",
+ author_name: "David",
+ author_email_address: "david@loudthinking.com",
+ written_on: 2003-07-16 14:28:11 UTC,
+ bonus_time: 2000-01-01 14:28:00 UTC,
+ last_read: Thu, 15 Apr 2004,
+ content: "Have a nice day",
+ important: nil,
+ approved: false,
+ replies_count: 1,
+ unique_replies_count: 0,
+ parent_id: nil,
+ parent_title: nil,
+ type: nil,
+ group: nil,
+ created_at: [^,]+,
+ updated_at: [^,>]+>
+ PRETTY
+ assert_match(/\A#{expected}\z/, actual)
+ end
+
+ def test_pretty_print_uninitialized
+ topic = Topic.allocate
+ actual = ''
+ PP.pp(topic, StringIO.new(actual))
+ expected = "#<Topic:XXXXXX not initialized>\n"
+ assert actual.start_with?(expected.split('XXXXXX').first)
+ assert actual.end_with?(expected.split('XXXXXX').last)
+ end
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index f885a8cbc0..92144bc802 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -206,6 +206,11 @@ if current_adapter?(:PostgreSQLAdapter)
assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parse after updating default using '::text' since postgreSQL will add parens to the default in db"
end
+ def test_default_containing_quote_and_colons
+ @connection.execute "ALTER TABLE defaults ALTER COLUMN string_col SET DEFAULT 'foo''::bar'"
+ assert_equal "foo'::bar", Default.new.string_col
+ end
+
teardown do
@connection.schema_search_path = @old_search_path
Default.reset_column_information
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index df4183c065..987c55ebc2 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -616,6 +616,34 @@ class DirtyTest < ActiveRecord::TestCase
end
end
+ test "defaults with type that implements `type_cast_for_write`" do
+ type = Class.new(ActiveRecord::Type::Value) do
+ def type_cast(value)
+ value.to_i
+ end
+
+ def type_cast_for_write(value)
+ value.to_s
+ end
+
+ alias type_cast_for_database type_cast_for_write
+ end
+
+ model_class = Class.new(ActiveRecord::Base) do
+ self.table_name = 'numeric_data'
+ property :foo, type.new, default: 1
+ end
+
+ model = model_class.new
+ assert_not model.foo_changed?
+
+ model = model_class.new(foo: 1)
+ assert_not model.foo_changed?
+
+ model = model_class.new(foo: '1')
+ assert_not model.foo_changed?
+ end
+
private
def with_partial_writes(klass, on = true)
old = klass.partial_writes?
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index e6603f28be..b3c02d29cb 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -80,6 +80,25 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal :integer, @first.column_for_attribute("id").type
end
+ def test_non_existent_columns_return_null_object
+ column = @first.column_for_attribute("attribute_that_doesnt_exist")
+ assert_equal "attribute_that_doesnt_exist", column.name
+ assert_equal nil, column.sql_type
+ assert_equal nil, column.type
+ assert_not column.number?
+ assert_not column.text?
+ assert_not column.binary?
+ end
+
+ def test_non_existent_columns_are_identity_types
+ column = @first.column_for_attribute("attribute_that_doesnt_exist")
+ object = Object.new
+
+ assert_equal object, column.type_cast(object)
+ assert_equal object, column.type_cast_for_write(object)
+ assert_equal object, column.type_cast_for_database(object)
+ end
+
def test_reflection_klass_for_nested_class_name
reflection = MacroReflection.new(:company, nil, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base)
assert_nothing_raised do
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index 1a690c01a6..c34e7d5a30 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -416,8 +416,9 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
def test_should_support_aliased_attributes
xml = Author.select("name as firstname").to_xml
- array = Hash.from_xml(xml)['authors']
- assert_equal array.size, array.select { |author| author.has_key? 'firstname' }.size
+ Author.all.each do |author|
+ assert xml.include?(%(<firstname>#{author.name}</firstname>)), xml
+ end
end
def test_array_to_xml_including_has_many_association
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
index cf24502d3a..2e3a9a3681 100644
--- a/activerecord/test/models/owner.rb
+++ b/activerecord/test/models/owner.rb
@@ -3,6 +3,18 @@ class Owner < ActiveRecord::Base
has_many :pets, -> { order 'pets.name desc' }
has_many :toys, :through => :pets
+ belongs_to :last_pet, class_name: 'Pet'
+ scope :including_last_pet, -> {
+ select(%q[
+ owners.*, (
+ select p.pet_id from pets p
+ where p.owner_id = owners.owner_id
+ order by p.name desc
+ limit 1
+ ) as last_pet_id
+ ]).includes(:last_pet)
+ }
+
after_commit :execute_blocks
def blocks