RABL(Ruby API Builder Language)是一个用于生成 JSON、XML、MessagePack、PList 和 BSON 的 Rails 和 Padrino Ruby 模板系统。在使用 ActiveRecord 的 'to_json' 方法时,我发现自己渴望一个更具表现力和更强大的 API 生成方案。当 JSON 表示复杂或与数据库中定义的确切模式不匹配时,这一点尤为突出。
特别是,我希望能够轻松实现:
任何尝试过使用 ActiveRecord 的 'to_json' 方法生成 JSON 响应的人都体会过这种受限方法的痛苦。RABL 是一个通用的模板系统,通过以全新的方式处理 API 响应生成,旨在解决这些问题。
RABL 的核心是坚持 MVC 原则,将 API 数据表示推迟到应用程序的视图层。关于对 RABL 常见误解的详细说明,请查看我们的指南: 理解 RABL,这有助于澄清关于此项目的任何混淆。
v0.8.0(2013年2月14日发布)移除了 multi_json 依赖, 转而依赖 Oj(或 JSON)作为 JSON 解析器。简化了代码,移除了一个依赖, 但你可能需要移除对 MultiJson 的任何引用。
v0.6.14(2012年6月28日发布)要求使用 render_views 与 RSpec 一起测试模板。否则,控制器只会 像处理 ERB 模板一样直接传递 render 命令。
以 gem 方式安装 RABL:``` gem install rabl
或者添加到你的Gemfile中:```ruby
# Gemfile
gem 'rabl'
# Also add either `oj` or `yajl-ruby` as the JSON parser
gem 'oj'
然后运行 bundle install 来安装依赖。
如果你使用的是 Rails 2.3.8(及更高版本)、Rails 3.X 或 Padrino,RABL 无需额外配置即可正常工作。
重要提示: 在 Padrino 中,请确保 rabl gem 在 Gemfile 中列于 padrino gem 之后,否则 Rabl 将不会正确注册为模板引擎。
对于 Sinatra 或任何其他基于 tilt 的框架,只需注册:```ruby Rabl.register!
RABL 将被初始化并准备好使用。关于在 Sinatra 中的使用,请查阅
[Sinatra 用法](https://github.com/nesquena/rabl/wiki/Setup-for-Sinatra) 指南。
## 概述 ##
你可以使用 RABL 从任何 Ruby 对象生成基于 JSON 和 XML 的 API。
使用 RABL,数据通常主要来自模型(ORM 无关),并且 API 输出的表示形式在视图模板中使用简单的 Ruby DSL 描述。这使你可以将数据与你希望输出的 JSON 或 XML 分离。
一旦你安装了 RABL(如上所述),你可以构建一个 RABL 视图模板,然后非常容易地从你的 Sinatra、Padrino 或 Rails 应用程序的控制器(或路由)中渲染该模板。以 [Padrino](http://padrinorb.com) 为例,假设你有一个包含博客文章的 `Post` 模型,你可以通过创建一个路由来渲染 API 表示(JSON 和 XML):```ruby
# app/app.rb
get "/posts", :provides => [:json, :xml] do
@user = current_user
@posts = Post.order("id DESC")
render "posts/index"
end
然后我们可以创建以下RABL模板来表达@posts的API输出:```ruby
collection @posts attributes :id, :title, :subject child(:user) { attributes :full_name } node(:read) { |post| post.read_by?(@user) }
访问 `http://localhost:3000/posts.json` 时将会输出以下 JSON 或 XML```js
[{ "post" :
{
"id" : 5, title: "...", subject: "...",
"user" : { full_name : "..." },
"read" : true
}
}]
这是一个基本概述,但还有更多内容可以查看,比如局部模板、继承、自定义节点等。请阅读下面关于 RABL 的完整细节。
RABL 的设计目标是几乎不需要配置即可运行。在大多数情况下确实如此,但根据你的需求,你可能需要在应用程序中设置以下全局配置(这个代码块完全是可选的):```ruby
require 'rabl' Rabl.configure do |config|
end
每个选项指定了与 RABL 输出相关的行为。如果禁用了 `include_json_root`,则会移除输出中每个根对象的根节点;而 `enable_json_callbacks` 如果启用,当传入请求包含 'callback' 参数时,支持 'jsonp' 风格的回调输出。
如果 `include_child_root` 设置为 false,则响应中的子对象默认不会包含根节点。这允许您进一步微调所需的响应结构。
如果设置了 `cache_engine`,则应将其赋给一个具有 `fetch` 方法的类。有关示例,请参阅[默认引擎](https://github.com/nesquena/rabl/blob/master/lib/rabl/cache_engine.rb)。
如果 `perform_caching` 设置为 `true`,则会执行缓存。如果使用 Rails,可以忽略此选项,它与 Rails 的 `config.action_controller.perform_caching` 相同。
如果 `cache_sources` 设置为 `true`,则模板查找将被缓存以提高性能。可以通过在应用程序中运行 `Rabl.reset_source_cache!` 手动重置缓存。
如果 `cache_all_output` 设置为 `true`,则每个模板(包括作为集合一部分使用的每个单独模板)都将被单独缓存。此外,child、glue 和 partial 中的任何内容也会被单独缓存。如果只想缓存单个模板,请参阅下面的“Caching”部分。
如果 `escape_all_output` 设置为 `true` 并且 ActiveSupport 可用,则属性输出将使用 [ERB::Util.html_escape](http://corelib.rubyonrails.org/classes/ERB/Util.html) 进行转义。自定义节点不会被转义,请使用 `ERB::Util.h(value)`。
如果 `view_paths` 设置为一个路径,则该视图路径将在应用程序中的每个 rabl 模板中被检查。尤其是在将 Rabl 包含在引擎中并在另一个 Rails 应用中使用视图路径时,添加到该路径。
如果 `raise_on_missing_attribute` 设置为 `true`,则每当 Rabl 尝试渲染不存在的属性时,将引发 RuntimeError。否则,该属性将被简单地忽略。在开发过程中将其设置为 true 可能有助于提高代码的健壮性,但不建议在生产代码中使用 `true`。
如果 `replace_nil_values_with_empty_strings` 设置为 `true`,则所有原本在响应中显示为 `null` 的 `nil` 值将被转换为空字符串。
如果您希望使用 [oj](https://github.com/ohler55/oj) 作为主要的 JSON 编码引擎,只需将其添加到您的 Gemfile 中:```ruby
# Gemfile
gem 'oj'
而 RABL 将自动使用该引擎来编码您的 JSON 响应。
设置您自己的自定义 json_engine,它定义一个 dump 或 encode 方法,用于从 Ruby 数据转换为 JSON 格式:```ruby
config.json_engine = ActiveSupport::JSON
### 格式配置 ###
RABL 支持 MessagePack、BSON 和 Plist 的配置。更多详情请查看
[格式配置](https://github.com/nesquena/rabl/wiki/Configuring-Formats)页面。
## 用法 ##
### 对象分配 ###
声明模板中使用的数据对象:```ruby
# app/views/users/show.json.rabl
object @user
或为对象指定别名:```ruby object @user => :person
或传递一个对象集合:```ruby
collection @users
# => [ { "user" : { ... } } ]
或为集合指定一个根节点标签:```ruby collection @users => :people
或者甚至可以为一个集合同时指定子标签和根标签:```ruby
collection @users, :root => "people", :object_root => "user"
# => { "people" : [ { "user" : { ... } } ] }
并且这将用作渲染的默认数据,或者显式禁用对象根:```ruby collection @users, :root => "people", :object_root => false
仅存在特殊情况,其中响应的根级不直接映射到任何对象:```ruby
object false
node(:some_count) { |m| @user.posts.count }
child(@user) { attribute :name }
在这些情况下,可以将对象赋值为'false',并且可以自由构建节点。
模板器的基本用法是为响应定义一些简单的属性:```ruby
attributes :id, :foo, :bar
或使用别名属性:```ruby
# Take the value of model attribute `foo` and name the node `bar`
attribute :foo => :bar
# => { bar : 5 }
甚至多个别名字段:```ruby attributes :bar => :baz, :dog => :animal
或者仅在条件为真时显示属性:```ruby
# m is the object being rendered, also supports :unless
attributes :foo, :bar, :if => lambda { |m| m.condition? }
命名属性和别名属性不能在同一行组合。目前这不起作用:```ruby attributes :foo, :bar => :baz # throws exception
### Child Nodes ###
通常,响应需要包含与父模型关联的数据中的嵌套信息:```ruby
child :address do
attributes :street, :city, :zip, :state
end
您也可以对子节点禁用对象根:```ruby child :posts, :object_root => false do attributes :id, :title end
您也可以从任意数据源添加子节点:```ruby
child @posts => :foobar do
attributes :id, :title
end
或者使用带有别名的模型关联:```ruby
child :posts => :foobar do attributes :id, :title end
您也可以传入当前对象:```ruby
object @user
child :posts do |user|
attribute :title unless user.suspended?
end
你也可以将子属性附加回根节点:```ruby
glue @post do attributes :id => :post_id, :name => :post_name end
使用 glue 为父对象添加额外属性。
你也可以传入当前对象:```ruby
object @user
glue(@post) {|user| attribute :title if user.active? }