我期望返回以下格式的数据:
{
    code: 1000,
    msg: "OK",
    data: {
        // normal data goes here if exists
    }
}
present 具体 Entity 时挺好用的,但涉及到内嵌结构的时候就不灵了
// ok
present :items, Book.all, with: Entities::Book
// does not work
resources :books do
    get do
        present :code, 1000
        present :msg, "OK"
        present :data do
            present :items, Book.all, with: Entities::Book
        end
    end
end
本来手动组织这样的结构是微不足道的,引入框架后反而这个过程变得异常复杂和不灵活!~ 请问有什么替代方法吗?
貌似这段代码也不行。
# does not work
present :data,  present(:items, Book.all, with: Entities::Book)
建议写进 entity 里。本来 json 的结构 grape 都是交给 grape-entity 负责的。换用 jbuilder 结果是同一回事。
直接定义一个 format 格式的函数 helper 也可以。这样也统一了返回格式。
def format(data, msg="ok", code=200)
  {data: data, msg: msg, code: code}
end
          楼主你用的什么 Entity,grape 的 Entity 非常灵活方便啊 内嵌复用什么的妥妥的
module Entities
  class ProductService < Grape::Entity
    expose :name
    expose :description
    expose :resource_category, using: ::Entities::ResourceCategory
  end
end
          我们现在这样做的,将 rescue_from formate 成约束的协议,然后定义正常的 formatter 和出错时的 formatter
rescue_from ActiveRecord::RecordNotFound  do |e|
    Rack::Response.new(
        {
            meta:{
                status: :not_found,
                msg: e.message
            }
        }.to_json, 404).finish
end
   ...
   ...
formatter :json, lambda {|object,env|               
    { :meta => { status: :ok, msg: "" }, :data => object }.to_json
}
error_formatter :json, lambda { |message, backtrace, options, env|  
    { :meta => { status: message[:status], msg: message[:msg] }}.to_json        
}
碰到需要容错的地方,用 grape 提供的 error!
error!({ msg: 'Email has been registered', status: :registered }, 200)
#返回值
{
    "meta": {
        "status": "registered", 
        "msg": "Email has been registered"
    }
}
然后正常的返回是这样
order = Order.first
present order, with: Entities::Order
# 返回值
{
    "meta": {
        "status": "ok", 
        "msg": ""
    }, 
    "data": {
        "id": 278, 
        "text": "order1"
    }
}
#ActiveRecord::RecordNotFound
{
    "meta": {
        "status": "not_found", 
        "msg": "Couldn't find City with 'id'=22"
    }
}
Entity(模板)仅仅定义需要对外暴露的 data,而不需要去关心 meta
@hammer 1. grape-jbuilder 看起来有点糟糕。2. grape 与 web 分离,做成两个项目,ActiveRecord 怎么复用?
@yukihiro_matz rails 太重了,似乎不是为 web service 设计的,我也不想把 web 代码和接口代码交织在一起,接口可能需要 token 认证等等,web 代码可能不用。另外两者的错误处理也不同。
@hardywu 嗯,不行。查看代码发现 present 并不是直接返回一个 Hash, 而是做了一些副作用的事,把结果赋值给 @body。https://github.com/intridea/grape/blob/a0447887b46902c392973aac6e7623f6fd63fd5d/lib/grape/dsl/inside_route.rb#L169.
#14 楼 @rbprocareer 加 Grape 在 Gemfile 正是为了复用 ActiveRecord 要不用可以直接上 sinatra、pardino 这些了
#18 楼 @rbprocareer , AR 可以自己独立使用。不管是在 sinatra 还是在别的什么 framework 里。 这里是我自写的一个 rails-like web framework, 里面就直接用的 AR. https://github.com/suffering/learnmvc/blob/master/srbmvc/lib/srbmvc.rb#L34 做这样的简单设置后,就可以像是在 rails 里一样使用 AR 了。
哪些东西REPEAT YOURSLEF了?或者说哪些东西在使用 AR 时导致代码一再重复了?
避免 grape 重复代码,可以参考 ruby-china 的源码:https://github.com/ruby-china/ruby-china/tree/master/app/grape
另外,rails 中 grape 的使用与 ORM 无关,资料查询与处理的方法都在 model 中写好,grape 中直接用就可以了。
@suffering 当将 web 代码和 api 代码分成两个项目,部署成两个服务后,AR 代码要么重写一遍,要么一个项目写好后,简单粗暴地 copy & paste 到另一个项目,要么做成一个 gem 啥的进行共享,使用 bundler 导入。。。视乎都不是好办法