我缺少什么?我正在尝试使用Active资源的休息服务,我有以下内容:
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
以下为用户输出:
#<User:0x1011a2d20 @prefix_options={}, @attributes={"name"=>"Test", "email"=>"test.user@domain.com"}>
并出现此错误:
/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
如果我用户为我的休息服务卷曲,那就像是:
curl -v -X POST -H 'Content-Type: application/json' -d '{"name":"test curl", "email":"test@gmail.com"}' http://localhost:3000/users
回复:
{"email":"test@gmail.com","name":"test curl","admin":false,"uuid":"afb8c98b-562a-4603-bbe4-f8f0816cef0d","creation_limit":5}
答案 0 :(得分:13)
有一个名为Data
的内置类型,其目的是rather mysterious。你似乎碰到了它:
$ ruby -e 'Data.new'
-e:1:in `new': allocator undefined for Data (TypeError)
from -e:1
问题是,它是如何实现的?最后一个堆栈帧给我们here。因此,Data
看似find_or_create_resource_for
出现了$ irb
>> class C
>> end
=> nil
>> C.const_get('Data')
=> Data
。代码分支here看起来很可能:
:data
这导致我怀疑你有一个名为"data"
或$ 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
的属性或类似物,即使你没有提到上面的一个。你呢?特别是,似乎我们有一个带有子哈希的JSON响应,其键是“data”。
这是一个可以触发精心设计的输入错误的脚本,但不是来自您发布的响应:
$ ./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
产生..
/Library/Ruby/Gems/1.8/gems/activeresource-3.0.10/lib/active_resource/base.rb
如果我是你,我会打开load_attributes_from_response
,在第1320行找到load(self.class.format.decode(response.body))
并暂时更改
load(self.class.format.decode(response.body).tap { |decoded| puts "Decoded: #{decoded.inspect}" })
到
{{1}}
..并再次重现错误,看看你的json解码器真正发生了什么。
答案 1 :(得分:1)
我刚刚在最新版本的ActiveResource中遇到了同样的错误,我找到了一个不需要对lib进行猴子修补的解决方案:在与ActiveResource对象相同的命名空间中创建一个Data
类。 E.g:
class User < ActiveResource::Base
self.site = "http://localhost:3000/"
self.element_name = "users"
self.format = :json
class Data < ActiveResource::Base; end
end
从根本上说,问题与ActiveResource选择从API响应中实例化的对象的类的方式有关。它将为您的响应中的每个哈希创建一个 something 的实例。例如,它会为以下JSON创建User
,Data
和Pet
个对象:
{
"name": "Bob",
"email": "bob@example.com",
"data": {"favorite_color": "purple"},
"pets": [{"name": "Puffball", "type": "cat"}]
}
可以找到类查找机制here。基本上,它检查资源(User
)及其祖先的常量匹配它想要实例化的子资源的名称(即Data
)。这个例外是由于这个查找从Stdlib找到顶级Data
常量引起的;因此,您可以通过在资源的命名空间(User::Data
)中提供更具体的常量来避免它。如果根本找不到常量(see here),则使此类继承自ActiveResource::Base
复制您将获得的行为。
答案 2 :(得分:0)
感谢phs的分析 - 它让我指出了正确的方向。
我别无选择,只能入侵ActiveResource来解决这个问题,因为我无法控制的外部服务已经发布了一个API,其中响应的所有属性都隐藏在顶级:data属性中。
这是hack我最终放入config / initializers / active_resource.rb以使用活动资源3.2.8为我工作:
class ActiveResource::Base
def load(attributes, remove_root = false)
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
@prefix_options, attributes = split_options(attributes)
if attributes.keys.size == 1
remove_root = self.class.element_name == attributes.keys.first.to_s
end
# THIS IS THE PATCH
attributes = ActiveResource::Formats.remove_root(attributes) if remove_root
if data = attributes.delete(:data)
attributes.merge!(data)
end
# END PATCH
attributes.each do |key, value|
@attributes[key.to_s] =
case value
when Array
resource = nil
value.map do |attrs|
if attrs.is_a?(Hash)
resource ||= find_or_create_resource_for_collection(key)
resource.new(attrs)
else
attrs.duplicable? ? attrs.dup : attrs
end
end
when Hash
resource = find_or_create_resource_for(key)
resource.new(value)
else
value.duplicable? ? value.dup : value
end
end
self
end
class << self
def find_every(options)
begin
case from = options[:from]
when Symbol
instantiate_collection(get(from, options[:params]))
when String
path = "#{from}#{query_string(options[:params])}"
instantiate_collection(format.decode(connection.get(path, headers).body) || [])
else
prefix_options, query_options = split_options(options[:params])
path = collection_path(prefix_options, query_options)
# THIS IS THE PATCH
body = (format.decode(connection.get(path, headers).body) || [])
body = body['data'] if body['data']
instantiate_collection( body, prefix_options )
# END PATCH
end
rescue ActiveResource::ResourceNotFound
# Swallowing ResourceNotFound exceptions and return nil - as per
# ActiveRecord.
nil
end
end
end
end
答案 3 :(得分:0)
我使用猴子补丁方法解决了这个问题,在运行find_or_create_resource_for
(违规方法)之前将“数据”更改为“xdata”。这样,当find_or_create_resource_for
方法运行时,它不会搜索Data
类(会崩溃)。它会搜索Xdata
类,但希望不存在,并且将由该方法动态创建。这将是ActiveResource
只需在config/initializers
module ActiveResource
class Base
alias_method :_find_or_create_resource_for, :find_or_create_resource_for
def find_or_create_resource_for(name)
name = "xdata" if name.to_s.downcase == "data"
_find_or_create_resource_for(name)
end
end
end