diff options
author | Sean Griffin <sean@thoughtbot.com> | 2015-03-10 11:35:09 -0600 |
---|---|---|
committer | Sean Griffin <sean@thoughtbot.com> | 2015-03-10 11:56:45 -0600 |
commit | afc124c3b417b5327ed4d85dc34393f3d19bfbcf (patch) | |
tree | 5aca59a32ca87ef927ffd687c261cf5a110ebda7 /activerecord | |
parent | 08469012e4db36d4d8dea507691070cd39526268 (diff) | |
download | rails-afc124c3b417b5327ed4d85dc34393f3d19bfbcf.tar.gz rails-afc124c3b417b5327ed4d85dc34393f3d19bfbcf.tar.bz2 rails-afc124c3b417b5327ed4d85dc34393f3d19bfbcf.zip |
Attempt to provide backwards compatible YAML deserialization
I should have done this in the first place. We are now serializing an
explicit version so we can make more careful changes in the future. This
will load Active Record objects which were serialized in Rails 4.1.
There will be bugs, as YAML serialization was at least partially broken
back then. There will also be edge cases that we might not be able to
handle, especially if the type of a column has changed.
In addition, we're passing this as `from_database`, since that is
required for serialized columns at minimum. All other types were
serializing the cast value. At a glance, there should be no types for
which this is a problem.
Finally, dirty checking information will be lost on records serialized
in 4.1, so no columns will be marked as changed.
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/lib/active_record.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/core.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/legacy_yaml_adapter.rb | 30 | ||||
-rw-r--r-- | activerecord/test/cases/yaml_serialization_test.rb | 43 |
4 files changed, 76 insertions, 0 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index ef14e065ae..58a4694880 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -43,6 +43,7 @@ module ActiveRecord autoload :Explain autoload :Inheritance autoload :Integration + autoload :LegacyYamlAdapter autoload :Migration autoload :Migrator, 'active_record/migration' autoload :ModelSchema diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 97ce4642aa..640642e90b 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -300,6 +300,7 @@ module ActiveRecord # post.init_with('attributes' => { 'title' => 'hello world' }) # post.title # => 'hello world' def init_with(coder) + coder = LegacyYamlAdapter.convert(self.class, coder) @attributes = coder['attributes'] init_internals @@ -370,6 +371,7 @@ module ActiveRecord coder['raw_attributes'] = attributes_before_type_cast coder['attributes'] = @attributes coder['new_record'] = new_record? + coder['active_record_yaml_version'] = 1 end # Returns true if +comparison_object+ is the same exact object, or +comparison_object+ diff --git a/activerecord/lib/active_record/legacy_yaml_adapter.rb b/activerecord/lib/active_record/legacy_yaml_adapter.rb new file mode 100644 index 0000000000..0d018d4352 --- /dev/null +++ b/activerecord/lib/active_record/legacy_yaml_adapter.rb @@ -0,0 +1,30 @@ +module ActiveRecord + module LegacyYamlAdapter + def self.convert(klass, coder) + return coder unless coder.is_a?(Psych::Coder) + + case coder["active_record_yaml_version"] + when 1 then coder + else + if coder["attributes"].is_a?(AttributeSet) + coder + else + Rails41.convert(klass, coder) + end + end + end + + module Rails41 + def self.convert(klass, coder) + attributes = klass.attributes_builder + .build_from_database(coder["attributes"]) + new_record = coder["attributes"][klass.primary_key].blank? + + { + "attributes" => attributes, + "new_record" => new_record, + } + end + end + end +end diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb index bce59b4fcd..65cd54e380 100644 --- a/activerecord/test/cases/yaml_serialization_test.rb +++ b/activerecord/test/cases/yaml_serialization_test.rb @@ -83,4 +83,47 @@ class YamlSerializationTest < ActiveRecord::TestCase assert_equal 5, author.posts_count assert_equal 5, dumped.posts_count end + + def test_a_yaml_version_is_provided_for_future_backwards_compat + coder = {} + Topic.first.encode_with(coder) + + assert coder['active_record_yaml_version'] + end + + def test_deserializing_rails_41_yaml + yaml = <<-YAML.strip_heredoc + --- !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_equal nil, topic.id + assert_equal "The First Topic", topic.title + assert_equal({ omg: :lol }, topic.content) + end + + def test_deserializing_rails_42_yaml + end end |