rubyactiveresource

Having 'allocator undefined for Data' when saving with ActiveResource


What I am missing? I am trying to use a rest service for with Active resource, I have the following:

class User < ActiveResource::Base
  self.site = "http://localhost:3000/"
  self.element_name = "users"
  self.format = :json
end

user = User.new(
        :name => "Test",
        :email => "test.user@domain.com")

p user 
if user.save
  puts "success: #{user.uuid}"
else
  puts "error: #{user.errors.full_messages.to_sentence}"
end

And the following output for the user:

#<User:0x1011a2d20 @prefix_options={}, @attributes={"name"=>"Test", "email"=>"test.user@domain.com"}>

and this error:

/Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1233:in `new': allocator undefined for Data (TypeError)
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1233:in `load'
from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1219:in `each'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1219:in `load'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1322:in `load_attributes_from_response'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1316:in `create_without_notifications'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1314:in `tap'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1314:in `create_without_notifications'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/observing.rb:11:in `create'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1117:in `save_without_validation'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/validations.rb:87:in `save_without_notifications'
    from /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/observing.rb:11:in `save'
    from import_rest.rb:22

If I user curl for my rest service it would be like:

curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"test curl", "email":"test@gmail.com"}' http://localhost:3000/users

with the response:

{"email":"test@gmail.com","name":"test curl","admin":false,"uuid":"afb8c98b-562a-4603-bbe4-f8f0816cef0d","creation_limit":5}

Solution

  • There is a built-in type named Data, whose purpose is rather mysterious. You appear to be bumping into it:

    $ ruby -e 'Data.new'
    -e:1:in `new': allocator undefined for Data (TypeError)
      from -e:1
    

    The question is, how did it get there? The last stack frame puts us here. So, it appears Data wandered out of a call to find_or_create_resource_for. The code branch here looks likely:

    $ irb
    >> class C
    >>   end
    => nil
    >> C.const_get('Data')
    => Data
    

    This leads me to suspect you have an attribute or similar floating around named :data or "data", even though you don't mention one above. Do you? Particularly, it seems we have a JSON response with a sub-hash whose key is "data".

    Here's a script that can trigger the error for crafted input, but not from the response you posted:

    $ cat ./activeresource-oddity.rb
    #!/usr/bin/env ruby
    
    require 'rubygems'
    gem 'activeresource', '3.0.10'
    require 'active_resource'
    
    class User < ActiveResource::Base
      self.site = "http://localhost:3000/"
      self.element_name = "users"
      self.format = :json
    end
    
    USER = User.new :name => "Test", :email => "test.user@domain.com"
    
    def simulate_load_attributes_from_response(response_body)
      puts "Loading #{response_body}.."
      USER.load User.format.decode(response_body)
    end
    
    OK = '{"email":"test@gmail.com","name":"test curl","admin":false,"uuid":"afb8c98b-562a-4603-bbe4-f8f0816cef0d","creation_limit":5}'
    BORKED = '{"data":{"email":"test@gmail.com","name":"test curl","admin":false,"uuid":"afb8c98b-562a-4603-bbe4-f8f0816cef0d","creation_limit":5}}'
    
    simulate_load_attributes_from_response OK
    simulate_load_attributes_from_response BORKED
    

    produces..

    $ ./activeresource-oddity.rb 
    Loading {"email":"test@gmail.com","name":"test curl","admin":false,"uuid":"afb8c98b-562a-4603-bbe4-f8f0816cef0d","creation_limit":5}..
    Loading {"data":{"email":"test@gmail.com","name":"test curl","admin":false,"uuid":"afb8c98b-562a-4603-bbe4-f8f0816cef0d","creation_limit":5}}..
    /opt/local/lib/ruby/gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1233:in `new': allocator undefined for Data (TypeError)
        from /opt/local/lib/ruby/gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1233:in `load'
        from /opt/local/lib/ruby/gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1219:in `each'
        from /opt/local/lib/ruby/gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb:1219:in `load'
        from ./activeresource-oddity.rb:17:in `simulate_load_attributes_from_response'
        from ./activeresource-oddity.rb:24
    

    If I were you, I would open /Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb, find load_attributes_from_response on line 1320 and temporarily change

    load(self.class.format.decode(response.body))
    

    to

    load(self.class.format.decode(response.body).tap { |decoded| puts "Decoded: #{decoded.inspect}" })
    

    ..and reproduce the error again to see what is really coming out of your json decoder.