aletapool aletapool - 2 months ago 14
Ruby Question

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}

phs phs
Answer

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.

Comments