Rails带有自定义REST集合模式的ActiveResource?

时间:2016-11-08 16:12:04

标签: ruby-on-rails json api activeresource

我正在尝试在active_resource gem(ActiveResource)之上设置一些Rails模型

module Core
    class RestResource < ActiveResource::Base
        self.format = :json
    end
end

module Core
    module API
        class BaseModel < ::Core::RestResource
        self.site = ENV['API_URI']
        self.headers['Api-Token'] = ENV['API_TOKEN']
        end
    end
end

module Core
    module API
        class MyModel < ::Core::API::BaseModel
        end
    end
end

我正在尝试运行rails控制台,如下所示

2.3.1 :001 > MyModel = Core::API::MyModel
 => Core::API::MyModel 
2.3.1 :002 > MyModel.all
ArgumentError: expected an attributes Hash, got ["data", [{"first_name"=>"walter", "last_name"=>"white", "timestamp"=>1478515925}]]
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:1377:in `load'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:1124:in `initialize'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:1051:in `new'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:1051:in `instantiate_record'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:1047:in `block in instantiate_collection'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/collection.rb:68:in `block in collect!'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/collection.rb:8:in `each'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/collection.rb:8:in `each'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/collection.rb:68:in `collect!'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:1047:in `instantiate_collection'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:1016:in `find_every'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:923:in `find'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/bundler/gems/activeresource-f4bec7f8f389/lib/active_resource/base.rb:949:in `all'
    from (irb):2
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/gems/railties-5.0.0.1/lib/rails/commands/console.rb:65:in `start'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/gems/railties-5.0.0.1/lib/rails/commands/console_helper.rb:9:in `start'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:78:in `console'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/gems/railties-5.0.0.1/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
    from /Users/nakwa/.rvm/gems/ruby-2.3.1/gems/railties-5.0.0.1/lib/rails/commands.rb:18:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'
2.3.1 :003 > 

在我的rails控制台(用于启动API的rails实例)上,我可以看到

Started GET "/v1/my_model.json" for ::1 at 2016-11-08 16:42:11 +0100
Processing by V1::MyModelController#index as JSON
  MyModel Load (0.2ms)  SELECT "my_model".* FROM "my_model"
[active_model_serializers] Rendered V1::MyModelSerializer with MyModelSerializer::Adapter::Attributes (2.2ms)
Completed 200 OK in 13ms (Views: 2.8ms | ActiveRecord: 1.8ms)

我的理解是Rails activeresource期望JSON API返回类似于:

的内容
[
  {"first_name": "walter", "last_name": "white"}, 
  {"first_name": "chuck", "last_name": "norris"}
]

当我的收藏品看起来像这样:

{
  "data": [
    {"first_name": "walter", "last_name": "white"}, 
    {"first_name": "chuck", "last_name": "norris"}
  ],
  "collection": {
    "total_entries": 2
  }
}

我现在正在寻找一种很好的自定义ActiveResource Collection解析器的方法,可能就像backbone.js正在做的那样(http://backbonejs.org/#Collection-parse),但不知怎的,我找不到这个问题的明显答案。

有什么想法吗?谢谢!

1 个答案:

答案 0 :(得分:2)

默认情况下,ActiveResource对API返回的数据格式有很强的期望,但您可以为这样的场景提供自己的格式

任何实现一些预期方法的Ruby对象都可以用作格式。 ActiveResource本身将格式作为模块实现,因此我们将坚持这一惯例。

作为一个懒惰的人,我将重复使用标准JsonFormat实现,并仅覆盖方法decode

module CustomJsonFormat
  include ActiveResource::Formats::JsonFormat

  extend self

  def decode(json)
    ActiveSupport::JSON.decode(json)['data']
  end
end

现在我们告诉ActiveResource子类使用它:

class BaseModel < ActiveResource::Base
  self.format = CustomJsonFormat

  # ... set self.site, self.headers etc.
end