Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 349 lines (247 sloc) 10.873 kb
eec7acb @nesquena Initial commit to rabl
authored
1 # RABL #
2
e958e9d @nesquena Updates to README
authored
3 RABL (Ruby API Builder Language) is a Rails and [Padrino](http://padrinorb.com) ruby templating system for generating JSON and XML. When using the ActiveRecord 'to_json' method, I tend to quickly find myself wanting a more expressive and powerful system for generating APIs. This is especially frustrating when the json representation is complex or doesn't match the exact schema defined in the database itself.
eec7acb @nesquena Initial commit to rabl
authored
4
e958e9d @nesquena Updates to README
authored
5 I wanted a simple, and flexible system for generating APIs. In particular, I wanted to easily:
eec7acb @nesquena Initial commit to rabl
authored
6
a98331f @nesquena Edited README.md via GitHub
authored
7 * Create arbitrary nodes named based on combining data in an object
8 * Pass arguments to methods and store the result as a child node
9 * Partial templates and inheritance to reduce code duplication
10 * Easily renaming attributes from their name in the model
11 * Simple way to append attributes from a child into the parent
e958e9d @nesquena Updates to README
authored
12 * Include nodes only if a certain condition is met
eec7acb @nesquena Initial commit to rabl
authored
13
e958e9d @nesquena Updates to README
authored
14 Anyone who has tried the 'to_json' method used in ActiveRecord for generating a json response has felt the pain of this restrictive approach. RABL is a general templating system created to solve all of those problems.
eec7acb @nesquena Initial commit to rabl
authored
15
16 ## Installation ##
17
e958e9d @nesquena Updates to README
authored
18 Install RABL as a gem:
273c086 @nesquena Adds more notes about installation
authored
19
902ec60 @nesquena More README formatting
authored
20 ```
21 gem install rabl
22 ```
eec7acb @nesquena Initial commit to rabl
authored
23
273c086 @nesquena Adds more notes about installation
authored
24 or add to your Gemfile:
25
902ec60 @nesquena More README formatting
authored
26 ```ruby
27 # Gemfile
28 gem 'rabl'
29 ```
273c086 @nesquena Adds more notes about installation
authored
30
31 and run `bundle install` to install the dependency.
32
d1959f2 @nesquena README for Rails 3 support
authored
33 If you are using **Rails 2.X, Rails 3 or Padrino**, RABL works without configuration.
34
35 With Sinatra, or any other tilt-based framework, simply register:
d8c8a85 @nesquena Add note about Padrino/Rails/Tilt
authored
36
37 Rabl.register!
38
a98331f @nesquena Edited README.md via GitHub
authored
39 and RABL will be initialized and ready for use.
d8c8a85 @nesquena Add note about Padrino/Rails/Tilt
authored
40
478a6ab @nesquena Updates README with overview
authored
41 ## Overview ##
42
43 The quick idea here is that you can use RABL to generate JSON and XML API based on any arbitrary data source. With RABL, the data is expected to come
4480f83 @nesquena Small tweaks to README again
authored
44 primarily from a model (ORM-agnostic) and the representation of the API output is described in the view with a simple ruby DSL. This allows you to keep your data separate from the JSON or XML you wish to output.
478a6ab @nesquena Updates README with overview
authored
45
7b65716 @nesquena Minor README fixes
authored
46 Once you have installed RABL (explained above), you can construct a RABL view template and then render the template from your Sinatra, Padrino or Rails applications from the controller (or route) very easily. Using [Padrino](http://padrinorb.com) as an example, assuming you have a `Post` model filled with blog posts, you can render an API representation (both JSON and XML) by creating a route:
478a6ab @nesquena Updates README with overview
authored
47
48 ```ruby
49 # app/app.rb
50 get "/posts", :provides => [:json, :xml] do
51 @user = current_user
52 @posts = Post.order("id DESC")
53 render "posts/index"
54 end
55 ```
56
4480f83 @nesquena Small tweaks to README again
authored
57 Then we can create the following RABL template to express the API output of `@posts`:
478a6ab @nesquena Updates README with overview
authored
58
59 ```ruby
60 # app/views/posts/index.rabl
61 collection @posts
62 attributes :id, :title, :subject
63 child(:user) { attributes :full_name }
64 node(:read) { |post| post.read_by?(@user) }
65 ```
66
67 Which would output the following JSON or XML when visiting `http://localhost:3000/posts.json`
68
69 ```json
70 [{ post :
71 {
72 id : 5, title: "...", subject: "...",
73 user : { full_name : "..." },
74 read : true
75 }
76 }]
77 ```
78
4480f83 @nesquena Small tweaks to README again
authored
79 That's the basic overview but there is a lot more (partials, inheritance, custom nodes, etc). Read the full details below of RABL below.
478a6ab @nesquena Updates README with overview
authored
80
799084f @nesquena Adds a note about RABL configuration
authored
81 ## Configuration ##
82
fa7be4a @nesquena Minor updates to README
authored
83 RABL is intended to require little to no configuration to get working. This is the case in most scenarios, but depending on your needs you may want to set the following global configurations in your application (this block is completely optional):
799084f @nesquena Adds a note about RABL configuration
authored
84
902ec60 @nesquena More README formatting
authored
85 ```ruby
86 # config/initializers/rabl_init.rb
87 Rabl.configure do |config|
88 # Commented as these are the defaults
89 # config.include_json_root = true
90 # config.include_xml_root = false
91 # config.enable_json_callbacks = false
bae7071 @nesquena Allow passing of system wide xml options [Thanks mpagalan]
authored
92 # config.xml_options = { :dasherize => true, :skip_types => false }
902ec60 @nesquena More README formatting
authored
93 end
94 ```
799084f @nesquena Adds a note about RABL configuration
authored
95
fa7be4a @nesquena Minor updates to README
authored
96 Each option specifies behavior related to RABL's output. If `include_json_root` is disabled that removes the root node for each child in the output, and `enable_json_callbacks` enables support for 'jsonp' style callback output if the incoming request has a 'callback' parameter.
799084f @nesquena Adds a note about RABL configuration
authored
97
eec7acb @nesquena Initial commit to rabl
authored
98 ## Usage ##
99
f9e74fd @nesquena Adds note about object assignment
authored
100 ### Object Assignment ###
101
a98331f @nesquena Edited README.md via GitHub
authored
102 To declare the data object for use in the template:
f9e74fd @nesquena Adds note about object assignment
authored
103
902ec60 @nesquena More README formatting
authored
104 ```ruby
105 # app/views/users/show.json.rabl
106 object @user
107 ```
f9e74fd @nesquena Adds note about object assignment
authored
108
08f7666 @nesquena Update README and TODO with object aliases
authored
109 or specify an alias for the object:
110
902ec60 @nesquena More README formatting
authored
111 ```ruby
112 object @user => :person
113 # => { "person" : { ... } }
114 ```
08f7666 @nesquena Update README and TODO with object aliases
authored
115
ea780c4 @nesquena Update the readme with the new collection syntax
authored
116 or pass a collection of objects:
f9e74fd @nesquena Adds note about object assignment
authored
117
902ec60 @nesquena More README formatting
authored
118 ```ruby
119 collection @users
120 # => [ { "user" : { ... } } ]
121 ```
f9e74fd @nesquena Adds note about object assignment
authored
122
ea780c4 @nesquena Update the readme with the new collection syntax
authored
123 or even specify a root node label for the collection:
124
902ec60 @nesquena More README formatting
authored
125 ```ruby
126 collection @users => :people
127 # => { "people" : [ { "person" : { ... } } ] }
128 ```
ea780c4 @nesquena Update the readme with the new collection syntax
authored
129
130 and this will be used as the default data for the rendering.
f9e74fd @nesquena Adds note about object assignment
authored
131
6c5b3c8 @nesquena Updates README to include more examples
authored
132 There can also be odd cases where the root-level of the response doesn't map directly to any object:
133
902ec60 @nesquena More README formatting
authored
134 ```ruby
135 object false
136 code(:some_count) { |m| @user.posts.count }
137 child(@user) { attribute :name }
138 ```
6c5b3c8 @nesquena Updates README to include more examples
authored
139
140 In those cases, object can be assigned to 'false' and child nodes can be constructed independently.
141
2b1b46f @nesquena Improve readme docs
authored
142 ### Attributes ###
143
a98331f @nesquena Edited README.md via GitHub
authored
144 Basic usage of the templater to define a few simple attributes for the response:
eec7acb @nesquena Initial commit to rabl
authored
145
902ec60 @nesquena More README formatting
authored
146 ```ruby
147 # app/views/users/show.json.rabl
148 attributes :id, :foo, :bar
149 ```
eec7acb @nesquena Initial commit to rabl
authored
150
a98331f @nesquena Edited README.md via GitHub
authored
151 or use with aliased attributes:
3951fb7 @nesquena Updates and prepare for initial release
authored
152
902ec60 @nesquena More README formatting
authored
153 ```ruby
154 # Take the value of model attribute `foo` and name the node `bar`
155 attribute :foo => :bar
156 # => { bar : 5 }
157 ```
0e36cb2 @nesquena Cleans up attribute doc
authored
158
a98331f @nesquena Edited README.md via GitHub
authored
159 or even multiple aliased attributes:
0e36cb2 @nesquena Cleans up attribute doc
authored
160
902ec60 @nesquena More README formatting
authored
161 ```ruby
162 attributes :bar => :baz, :dog => :animal
163 # => # { baz : <bar value>, animal : <dog value> }
164 ```
3951fb7 @nesquena Updates and prepare for initial release
authored
165
2b1b46f @nesquena Improve readme docs
authored
166 ### Child Nodes ###
167
6c5b3c8 @nesquena Updates README to include more examples
authored
168 Often a response requires including nested information from data associated with the parent model:
169
902ec60 @nesquena More README formatting
authored
170 ```ruby
171 child :address do
172 attributes :street, :city, :zip, :state
173 end
174 ```
6c5b3c8 @nesquena Updates README to include more examples
authored
175
176 You can also add child nodes from an arbitrary data source:
3951fb7 @nesquena Updates and prepare for initial release
authored
177
902ec60 @nesquena More README formatting
authored
178 ```ruby
179 child @posts => :foobar do
180 attributes :id, :title
181 end
182 ```
3951fb7 @nesquena Updates and prepare for initial release
authored
183
6c5b3c8 @nesquena Updates README to include more examples
authored
184 or use model associations with an alias:
3951fb7 @nesquena Updates and prepare for initial release
authored
185
902ec60 @nesquena More README formatting
authored
186 ```ruby
187 # Renders all the 'posts' association
188 # from the model into a node called 'foobar'
189 child :posts => :foobar do
190 attributes :id, :title
191 end
192 ```
3951fb7 @nesquena Updates and prepare for initial release
authored
193
a98331f @nesquena Edited README.md via GitHub
authored
194 ### Gluing Attributes ###
2b1b46f @nesquena Improve readme docs
authored
195
a98331f @nesquena Edited README.md via GitHub
authored
196 You can also append child attributes back to the root node:
2b1b46f @nesquena Improve readme docs
authored
197
902ec60 @nesquena More README formatting
authored
198 ```ruby
199 # Appends post_id and post_name to parent json object
200 glue @post do
201 attributes :id => :post_id, :name => :post_name
202 end
203 ```
2b1b46f @nesquena Improve readme docs
authored
204
205 Use glue to add additional attributes to the parent object.
206
207 ### Custom Nodes ###
208
a98331f @nesquena Edited README.md via GitHub
authored
209 This will generate a json response based on the result of the code block:
2b1b46f @nesquena Improve readme docs
authored
210
902ec60 @nesquena More README formatting
authored
211 ```ruby
212 # app/views/users/show.json.rabl
213 code :full_name do |u|
214 u.first_name + " " + u.last_name
215 end
216 ```
2b1b46f @nesquena Improve readme docs
authored
217
627e51a @nesquena Adds support for 'if' conditional in code
authored
218 or a custom node that exists only if a condition is true:
219
902ec60 @nesquena More README formatting
authored
220 ```ruby
221 # m is the object being rendered, also supports :unless
222 code(:foo, :if => lambda { |m| m.has_foo? }) do |m|
223 m.foo
224 end
225 ```
627e51a @nesquena Adds support for 'if' conditional in code
authored
226
a98331f @nesquena Edited README.md via GitHub
authored
227 You can use custom "code" nodes to create flexible representations of a value utilizing all the data from the model.
2b1b46f @nesquena Improve readme docs
authored
228
da37d89 @nesquena Adds sub sections for features
authored
229 ### Partials ###
2b1b46f @nesquena Improve readme docs
authored
230
8dc65a4 @nesquena Minor cleanup to partial README
authored
231 Often you need to access other data objects in order to construct custom nodes in more complex associations. You can get access to the rabl representation of another data object by rendering a RABL partial:
3951fb7 @nesquena Updates and prepare for initial release
authored
232
902ec60 @nesquena More README formatting
authored
233 ```ruby
234 code :location do
8dc65a4 @nesquena Minor cleanup to partial README
authored
235 { :city => @city, :address => partial("users/address", :object => @address) }
902ec60 @nesquena More README formatting
authored
236 end
237 ```
3951fb7 @nesquena Updates and prepare for initial release
authored
238
8dc65a4 @nesquena Minor cleanup to partial README
authored
239 or event access an object associated with the parent model:
3951fb7 @nesquena Updates and prepare for initial release
authored
240
902ec60 @nesquena More README formatting
authored
241 ```ruby
242 code :location do |m|
8dc65a4 @nesquena Minor cleanup to partial README
authored
243 { :city => m.city, :address => partial("users/address", :object => m.address) }
902ec60 @nesquena More README formatting
authored
244 end
245 ```
3951fb7 @nesquena Updates and prepare for initial release
authored
246
8dc65a4 @nesquena Minor cleanup to partial README
authored
247 You can use this method to construct arbitrarily complex nodes for your APIs. Note that you need to have RABL templates defined
248 for each of the objects you wish to construct representations for in this manner.
2b1b46f @nesquena Improve readme docs
authored
249
da37d89 @nesquena Adds sub sections for features
authored
250 ### Inheritance ###
2b1b46f @nesquena Improve readme docs
authored
251
a98331f @nesquena Edited README.md via GitHub
authored
252 Another common issue of many template builders is unnecessary code redundancy. Typically many representations of an object across multiple endpoints share common attributes or nodes. The nodes for a 'post' object are probably the same or similar in most references throughout the various endpoints.
2b1b46f @nesquena Improve readme docs
authored
253
254 RABL has the ability to extend other "base" rabl templates and additional attributes:
3951fb7 @nesquena Updates and prepare for initial release
authored
255
902ec60 @nesquena More README formatting
authored
256 ```ruby
257 # app/views/users/advanced.json.rabl
258 extends "users/base" # another RABL template in "app/views/users/base.json.rabl"
3951fb7 @nesquena Updates and prepare for initial release
authored
259
902ec60 @nesquena More README formatting
authored
260 code :can_drink do |m|
261 m.age > 21
262 end
263 ```
3951fb7 @nesquena Updates and prepare for initial release
authored
264
a98331f @nesquena Edited README.md via GitHub
authored
265 You can also extend other rabl templates while constructing child nodes to reduce duplication:
61d9f8d @nesquena Cleanup readme order
authored
266
902ec60 @nesquena More README formatting
authored
267 ```ruby
268 # app/views/users/show.json.rabl
269 child @address do
270 extends "address/item"
271 end
272 ```
61d9f8d @nesquena Cleanup readme order
authored
273
2b1b46f @nesquena Improve readme docs
authored
274 Using partials and inheritance can significantly reduce code duplication in your templates.
61d9f8d @nesquena Cleanup readme order
authored
275
d86f423 @nesquena Adds 'deep nesting' example to the README
authored
276 ### Deep Nesting ###
277
278 In APIs, you can often need to construct 2nd or 3rd level nodes. Let's suppose we have a 'quiz' model that has many 'questions'
279 and then each question has many 'answers'. We can display this hierarchy in RABL quite easily:
280
afd747a @nesquena Update README formatting on certain blocks
authored
281 ```ruby
282 # app/views/quizzes/show.json.rabl
283 object @quiz
284 attribute :title
285 child :questions do
286 attribute :caption
287 child :answers do
288 # Use inheritance to reduce duplication
289 extends "answers/item"
290 end
291 end
292 ```
d86f423 @nesquena Adds 'deep nesting' example to the README
authored
293
294 This will display the quiz object with nested questions and answers as you would expect with a quiz node, and embedded questions and answers.
295 Note that RABL can be nested arbitrarily deep within child nodes to allow for these representations to be defined.
296
63b9c6f @nesquena Adds scope section to README
authored
297 ## Template Scope ##
298
299 In RABL, you have access to everything you need to build an API response. Each RABL template has full access to the controllers
300 instance variables as well as all view helpers and routing urls.
301
afd747a @nesquena Update README formatting on certain blocks
authored
302 ```ruby
303 # app/some/template.rabl
304 object @post
305 # Access instance variables
306 child(@user => :user) { ... }
307 # or Rails helpers
308 code(:formatted_body) { |post| simple_format(post.body) }
309 ```
63b9c6f @nesquena Adds scope section to README
authored
310
311 There should be no problem fetching the appropriate data to construct a response.
312
3951fb7 @nesquena Updates and prepare for initial release
authored
313 ## Issues ##
eec7acb @nesquena Initial commit to rabl
authored
314
2dd0fdd @nesquena Updates issues in the README
authored
315 Check out the [Issues](https://github.com/nesquena/rabl/issues) tab for a full list:
316
317 * Better Tilt template support (precompiling templates)
318 * Benchmarks and performance optimizations
9e691d3 @nesquena Edited README.md via GitHub
authored
319
320 ## Authors and Contributors ##
321
f3454c1 @nesquena Added Tim and Miso to README
authored
322 Thanks to [Miso](http://gomiso.com) for allowing me to create this for our applications and release this project!
323
9e691d3 @nesquena Edited README.md via GitHub
authored
324 * [Nathan Esquenazi](https://github.com/nesquena) - Creator of the project
325 * [Arthur Chiu](https://github.com/achiu) - Core Maintainer, Riot Testing Guru
f3454c1 @nesquena Added Tim and Miso to README
authored
326 * [Tim Lee](https://github.com/timothy1ee) - RABL is an awesome name and was chosen by the Miso CTO.
a890ce1 @nesquena Added Rick Thomas to readme
authored
327 * [Rick Thomas](https://github.com/rickthomasjr) - Added options passing for extends and Sinatra testing
bae7071 @nesquena Allow passing of system wide xml options [Thanks mpagalan]
authored
328 * [Marjun](https://github.com/mpagalan) - Added xml option configurations
1e8b95d @nesquena Adds kimptoc to README
authored
329 * [Chris Kimpton](https://github.com/kimptoc) - Helping with documentation and wiki
a890ce1 @nesquena Added Rick Thomas to readme
authored
330
2dd0fdd @nesquena Updates issues in the README
authored
331 More to come hopefully! Please fork and contribute, any help is appreciated!
332
9e691d3 @nesquena Edited README.md via GitHub
authored
333 ## Inspirations ##
334
335 There are a few excellent libraries that helped inspire RABL and they are listed below:
336
337 * [Tequila](https://github.com/inem/tequila)
338 * [JSON Builder](https://github.com/dewski/json_builder)
339 * [Argonaut](https://github.com/jbr/argonaut)
340
2f7d908 @nesquena Adds examples and copyright to readme
authored
341 Thanks again for all of these great projects.
342
343 ## Examples
344
345 See the [examples](https://github.com/nesquena/rabl/tree/master/examples) directory.
346
347 ## Copyright
348
349 Copyright © 2011 Nathan Esquenazi. See [MIT-LICENSE](https://github.com/nesquena/rabl/blob/master/MIT-LICENSE) for details.
Something went wrong with that request. Please try again.