Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

ActiveRecord#serialize method and Ruby 1.9.x / Rails 3.1.0.rc1 #1585

Closed
otobrglez opened this Issue · 20 comments

9 participants

@otobrglez

Hi!

I found this problem in Rails 3.1.0.rc1 with Ruby 1.9.2p0.

I have simple model "Article" and and it has many "Sources". I want to store sources into database serialized as simple Hash.

# article.rb
class Article < ActiveRecord::Base
  serialize :sources
  # ... #
end
# source.rb
class Source
  include ActiveModel::Validations
  attr_accessor :title, :url
  validates_presence_of :title, :url
end

Now i store like this...

a = Article.new
a.sources = Array.new

s = Source.new
s.title = "github"
s.url = "http://github.com"

a.sources << s
a.save

If i later retrive this article i get something like...

ruby-1.9.2-head :054 > s2 = Article.sources.first
=> #<Syck::Object:0x000001044d2218 @class="Source", @ivars={"errors"=>#<ActiveModel::Errors:0x000001044d1b10 @base=#<Syck::Object:0x000001044d2218 ...>, @messages={}>, "title"=>"github", "url"=>"http://github.com", "validation_context"=>nil}>

And I cant basically do nothing about "sources". Becouse object is "serialized" with Syck::Object...
Later i found out if I use...

require_dependency "source" 

... inside Article model it serializes just the way it should.

I don't know if this is really a bug. But it sure gave me a headache.

I found some notes here:
http://stackoverflow.com/questions/4705867/rails-doesnt-load-classes-on-deserializing-yaml-marshal-objects

If this is a bug - hope u can fix it. :)

@brendon9x

This bug has always existed (we have it in 2.3/1.8.7). But it definitely needs to be addressed on the "least surprise" principle.

@jake3030 jake3030 referenced this issue from a commit in jake3030/rails
@mislav mislav Ensure correct content type is declared after cache hits on actions w…
…ith string cache keys [#1585 state:resolved]

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
fa45540
@isaacsanders

Is this still an issue @otobrglez?

@steveklabnik
Collaborator

More specifically, is it an issue with rails 3.2 or above?

@carols10cents

Fun, I just ran into this today! I believe I've confirmed that it is still an issue in rails 3.2.2.

I followed the procedure laid out in the stack overflow question, here are the files I used/generated:

https://gist.github.com/2573092

Running the serializing_test will produce that yaml file, and trying to load it in development raises ArgumentError: undefined class/module MyModel and loading it in test works as expected.

@frodsan

@otobrglez do you put source.rb in app or lib folder?

@carols10cents

@frodsan from @otobrglez's stack overflow question it looks lite it's in app.

why do you ask?

@frodsan

Hi @carols10cents, i just want to reproduce the issue. Thanks :)

@frodsan

Works fine with master (Rails 4.0.0.beta):

Loading development environment (Rails 4.0.0.beta)
>> a = Article.new
=> #<Article id: nil, sources: nil, created_at: nil, updated_at: nil>
>> a.sources = Array.new
=> []
>> s = Source.new
=> #<Source:0x007fabadc72148>
>> s.title = "github"
=> "github"
>> s.url = "http://github.com"
=> "http://github.com"
>> a.sources << s
=> [#<Source:0x007fabadc72148 @title="github", @url="http://github.com">]
>> a.save
   (0.1ms)  begin transaction
  SQL (46.3ms)  INSERT INTO "articles" ("created_at", "sources", "updated_at") VALUES (?, ?, ?)  [["created_at", Thu, 05 Jul 2012 03:52:56 UTC +00:00], ["sources", "---\n- !ruby/object:Source\n  title: github\n  url: http://github.com\n"], ["updated_at", Thu, 05 Jul 2012 03:52:56 UTC +00:00]]
   (256.3ms)  commit transaction
=> true

>> s2 = a.sources.first
=> #<Source:0x007fabaaa9ffa8 @title="github", @url="http://github.com">
>> s2.class
=> Source
@frodsan

Also, works fine with 3-2-stable (Rails 3.2.6):

Loading development environment (Rails 3.2.6)
>> a = Article.new
=> #<Article id: nil, sources: nil, created_at: nil, updated_at: nil>
>> a.sources = Array.new
=> []
>> s = Source.new
=> #<Source:0x007fe0c63bced8>
>> s.title = "github"
=> "github"
>> s.url = "http://github.com"
=> "http://github.com"
>> a.sources << s
=> [#<Source:0x007fe0c63bced8 @title="github", @url="http://github.com">]
>> a.save
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "articles" ("created_at", "sources", "updated_at") VALUES (?, ?, ?)  [["created_at", Thu, 05 Jul 2012 03:59:35 UTC +00:00], ["sources", "---\n- !ruby/object:Source\n  title: github\n  url: http://github.com\n"], ["updated_at", Thu, 05 Jul 2012 03:59:35 UTC +00:00]]
   (49.8ms)  commit transaction
=> true

>> s2 = a.sources.first
=> #<Source:0x007fe0c608bde0 @title="github", @url="http://github.com">
>> a.sources
=> [#<Source:0x007fe0c608bde0 @title="github", @url="http://github.com">]
>> s2.class
=> Source
@rafaelfranca
Owner

@frodsan thanks for confirming.

@carols10cents

@frodsan I was under the impression that this issue had to do with serialization/deserialization, and I don't see where you're doing that in your reproduction, unless I'm misunderstanding something.

I'm still able to reproduce the issue as outlined in my comment in rails 3.2.6.

Please reopen this issue, @rafaelfranca!

@frodsan

@carols10cents i'm reproducing the case that @otobrglez posted in the description. I'm not sure, but i think you should open a new one with your case.

@steveklabnik steveklabnik reopened this
@steveklabnik
Collaborator

I don't think it's appropriate to close an issue when we do have someone that's seeing the same thing, especially when that person provides a reproduction.

Can we figure out if it's actually a different issue or not before hastily closing?

@frodsan

Hey @steveklabnik, if you look at the issue's description, it says "ActiveRecord#serialize" and that's what i tested. I just recently saw the gist provided by @carols10cents and i think is a different issue.

@mhuggins

@frodsan - your example doesn't mimic the problem @otobrglez pointed out. It doesn't appear that your code deserializes anywhere. Specifically, you make this call:

>> s2 = a.sources.first

instead of making this call:

>> s2 = Article.sources.first

The former returns the sources value that is already in memory. The latter retrieves the serialized value.

@rafaelfranca
Owner

@carols10cents sorry I didn't read the entire thread.

@steveklabnik thanks to reopen :heart:

@carols10cents

Here is my reproduction of the issue in Rails 3.2.6:

This is the issue from the stackoverflow question that @otobrglez posted. The tmp/object.yml file contains the output of a serialization:

--- !ruby/object:MyModel
id: my model

There is a model in app/models/my_model.rb that contains:

class MyModel
  attr_accessor :id
end

If you start the console in development, this is the reproduction of the issue (that I was able to do in Rails 3.2.6):

$ rails c
Loading development environment (Rails 3.2.6)
1.9.2p290 :001 > object = YAML.load_file "#{Rails.root}/tmp/object.yml"
ArgumentError: undefined class/module MyModel

And if you start the console in test, you can see the behavior that I would expect in development if this issue were fixed:

$ RAILS_ENV=test rails c
Loading test environment (Rails 3.2.6)
1.9.2p290 :001 > object = YAML.load_file "#{Rails.root}/tmp/object.yml"
 => #<MyModel:0x00000102c61af8 @id="my model"> 
@alisdair alisdair referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@alisdair alisdair referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
@alisdair alisdair referenced this issue from a commit in alisdair/rails
@alisdair alisdair Autoload missing classes for serialized columns.
Fixes #1585.
8380556
@steveklabnik
Collaborator

So.

@calebthompson and I were looking into this just now, and I happened to find this test:

    def test_load_doesnt_handle_undefined_class_or_module
         coder = YAMLColumn.new
         missing_class_yaml = '--- !ruby/object:DoesNotExistAndShouldntEver {}\n'
         assert_raises(ArgumentError) do
           coder.load(missing_class_yaml)
         end
    end

So, the current behavior is actually expected. If you come across this, best not to rely on the serialized class not being autoloaded. Make sure to use require_dependency and it'll all be good.

@calebthompson

Basically, if your namespace doesn't already know about the class then YAML can't and shouldn't shouldn't be able to deserialize and object of that type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.