Add YAML compatibility for objects from Rails 4.2

As of Ruby 2.2, Psych can handle any object which is marshallable. This
was not true on previous versions of Ruby, so our delegator types had to
provide their own implementation of `init_with` and `encode_with`.
Unfortunately, this doesn't match up with what Psych will do today.

Since by the time we hit this layer, the objects will have already been
created, I think it makes the most sense to just grab the current type
from the class.
This commit is contained in:
Sean Griffin 2015-03-10 11:54:17 -06:00
parent afc124c3b4
commit 4e7217027f
4 changed files with 239 additions and 27 deletions

View File

@ -7,13 +7,29 @@ module ActiveRecord
when 1 then coder when 1 then coder
else else
if coder["attributes"].is_a?(AttributeSet) if coder["attributes"].is_a?(AttributeSet)
coder Rails420.convert(klass, coder)
else else
Rails41.convert(klass, coder) Rails41.convert(klass, coder)
end end
end end
end end
module Rails420
def self.convert(klass, coder)
attribute_set = coder["attributes"]
klass.attribute_names.each do |attr_name|
attribute = attribute_set[attr_name]
if attribute.type.is_a?(Delegator)
type_from_klass = klass.type_for_attribute(attr_name)
attribute_set[attr_name] = attribute.with_type(type_from_klass)
end
end
coder
end
end
module Rails41 module Rails41
def self.convert(klass, coder) def self.convert(klass, coder)
attributes = klass.attributes_builder attributes = klass.attributes_builder

View File

@ -92,31 +92,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
end end
def test_deserializing_rails_41_yaml def test_deserializing_rails_41_yaml
yaml = <<-YAML.strip_heredoc topic = YAML.load(yaml_fixture("rails_4_1"))
--- !ruby/object:Topic
attributes:
id:
title: The First Topic
author_name: David
author_email_address: david@loudthinking.com
written_on: 2003-07-16 14:28:11.223300000 Z
bonus_time: 2000-01-01 14:28:00.000000000 Z
last_read: 2004-04-15
content: |
---
:omg: :lol
important:
approved: false
replies_count: 1
unique_replies_count: 0
parent_id:
parent_title:
type:
group:
created_at: 2015-03-10 17:05:42.000000000 Z
updated_at: 2015-03-10 17:05:42.000000000 Z
YAML
topic = YAML.load(yaml)
assert topic.new_record? assert topic.new_record?
assert_equal nil, topic.id assert_equal nil, topic.id
@ -124,6 +100,22 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_equal({ omg: :lol }, topic.content) assert_equal({ omg: :lol }, topic.content)
end end
def test_deserializing_rails_42_yaml def test_deserializing_rails_4_2_0_yaml
topic = YAML.load(yaml_fixture("rails_4_2_0"))
assert_not topic.new_record?
assert_equal 1, topic.id
assert_equal "The First Topic", topic.title
assert_equal("Have a nice day", topic.content)
end
private
def yaml_fixture(file_name)
path = File.expand_path(
"../../support/yaml_compatibility_fixtures/#{file_name}.yml",
__FILE__
)
File.read(path)
end end
end end

View File

@ -0,0 +1,22 @@
--- !ruby/object:Topic
attributes:
id:
title: The First Topic
author_name: David
author_email_address: david@loudthinking.com
written_on: 2003-07-16 14:28:11.223300000 Z
bonus_time: 2000-01-01 14:28:00.000000000 Z
last_read: 2004-04-15
content: |
---
:omg: :lol
important:
approved: false
replies_count: 1
unique_replies_count: 0
parent_id:
parent_title:
type:
group:
created_at: 2015-03-10 17:05:42.000000000 Z
updated_at: 2015-03-10 17:05:42.000000000 Z

View File

@ -0,0 +1,182 @@
--- !ruby/object:Topic
raw_attributes:
id: 1
title: The First Topic
author_name: David
author_email_address: david@loudthinking.com
written_on: '2003-07-16 14:28:11.223300'
bonus_time: '2005-01-30 14:28:00.000000'
last_read: '2004-04-15'
content: |
--- Have a nice day
...
important:
approved: f
replies_count: 1
unique_replies_count: 0
parent_id:
parent_title:
type:
group:
created_at: '2015-03-10 17:44:41'
updated_at: '2015-03-10 17:44:41'
attributes: !ruby/object:ActiveRecord::AttributeSet
attributes: !ruby/object:ActiveRecord::LazyAttributeHash
types:
id: &5 !ruby/object:ActiveRecord::Type::Integer
precision:
scale:
limit:
range: !ruby/range
begin: -2147483648
end: 2147483648
excl: true
title: &6 !ruby/object:ActiveRecord::Type::String
precision:
scale:
limit: 250
author_name: &1 !ruby/object:ActiveRecord::Type::String
precision:
scale:
limit:
author_email_address: *1
written_on: &4 !ruby/object:ActiveRecord::Type::DateTime
precision:
scale:
limit:
bonus_time: &7 !ruby/object:ActiveRecord::Type::Time
precision:
scale:
limit:
last_read: &8 !ruby/object:ActiveRecord::Type::Date
precision:
scale:
limit:
content: !ruby/object:ActiveRecord::Type::Serialized
coder: &9 !ruby/object:ActiveRecord::Coders::YAMLColumn
object_class: !ruby/class 'Object'
subtype: &2 !ruby/object:ActiveRecord::Type::Text
precision:
scale:
limit:
important: *2
approved: &10 !ruby/object:ActiveRecord::Type::Boolean
precision:
scale:
limit:
replies_count: &3 !ruby/object:ActiveRecord::Type::Integer
precision:
scale:
limit:
range: !ruby/range
begin: -2147483648
end: 2147483648
excl: true
unique_replies_count: *3
parent_id: *3
parent_title: *1
type: *1
group: *1
created_at: *4
updated_at: *4
values:
id: 1
title: The First Topic
author_name: David
author_email_address: david@loudthinking.com
written_on: '2003-07-16 14:28:11.223300'
bonus_time: '2005-01-30 14:28:00.000000'
last_read: '2004-04-15'
content: |
--- Have a nice day
...
important:
approved: f
replies_count: 1
unique_replies_count: 0
parent_id:
parent_title:
type:
group:
created_at: '2015-03-10 17:44:41'
updated_at: '2015-03-10 17:44:41'
additional_types: {}
materialized: true
delegate_hash:
id: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: id
value_before_type_cast: 1
type: *5
title: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: title
value_before_type_cast: The First Topic
type: *6
author_name: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: author_name
value_before_type_cast: David
type: *1
author_email_address: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: author_email_address
value_before_type_cast: david@loudthinking.com
type: *1
written_on: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: written_on
value_before_type_cast: '2003-07-16 14:28:11.223300'
type: *4
bonus_time: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: bonus_time
value_before_type_cast: '2005-01-30 14:28:00.000000'
type: *7
last_read: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: last_read
value_before_type_cast: '2004-04-15'
type: *8
content: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: content
value_before_type_cast: |
--- Have a nice day
...
type: !ruby/object:ActiveRecord::Type::Serialized
coder: *9
subtype: *2
important: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: important
value_before_type_cast:
type: *2
approved: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: approved
value_before_type_cast: f
type: *10
replies_count: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: replies_count
value_before_type_cast: 1
type: *3
unique_replies_count: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: unique_replies_count
value_before_type_cast: 0
type: *3
parent_id: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: parent_id
value_before_type_cast:
type: *3
parent_title: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: parent_title
value_before_type_cast:
type: *1
type: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: type
value_before_type_cast:
type: *1
group: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: group
value_before_type_cast:
type: *1
created_at: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: created_at
value_before_type_cast: '2015-03-10 17:44:41'
type: *4
updated_at: !ruby/object:ActiveRecord::Attribute::FromDatabase
name: updated_at
value_before_type_cast: '2015-03-10 17:44:41'
type: *4
new_record: false