rabl 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +10 -1
- data/README.md +55 -25
- data/lib/rabl/builder.rb +11 -11
- data/lib/rabl/engine.rb +8 -8
- data/lib/rabl/template.rb +2 -2
- data/lib/rabl/version.rb +1 -1
- metadata +2 -2
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
## 0.5.
|
3
|
+
## 0.5.5 (Unreleased)
|
4
|
+
|
5
|
+
## 0.5.4
|
6
|
+
|
7
|
+
* Ensure ActionView is defined before registering Rails template handler (thanks cj)
|
8
|
+
|
9
|
+
## 0.5.2-0.5.3
|
10
|
+
|
11
|
+
* Add better support for conditionals for child (thanks gregory)
|
12
|
+
* Fix issue introduced with 'node' and properly clear options (thanks joshbuddy)
|
4
13
|
|
5
14
|
## 0.5.1
|
6
15
|
|
data/README.md
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
# RABL #
|
2
2
|
|
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
|
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 solution for generating APIs.
|
4
|
+
This is especially frustrating when the JSON representation is complex or doesn't match the exact schema defined in the database.
|
4
5
|
|
5
|
-
I wanted a simple
|
6
|
+
I wanted a simple and flexible system for generating my APIs. In particular, I wanted to easily:
|
6
7
|
|
7
8
|
* Create arbitrary nodes named based on combining data in an object
|
8
9
|
* Pass arguments to methods and store the result as a child node
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
* Include nodes only if a certain condition
|
10
|
+
* Render partial templates and inherit to reduce code duplication
|
11
|
+
* Rename or alias attributes to change the name from the model
|
12
|
+
* Append attributes from a child into a parent node
|
13
|
+
* Include nodes only if a certain condition has been met
|
13
14
|
|
14
|
-
Anyone who has tried the 'to_json' method used in ActiveRecord for generating a
|
15
|
+
Anyone who has tried the 'to_json' method used in ActiveRecord for generating a JSON response has felt the pain of this restrictive approach.
|
16
|
+
RABL is a general templating system created to solve these problems in an entirely new way.
|
15
17
|
|
16
18
|
## Installation ##
|
17
19
|
|
@@ -32,7 +34,7 @@ gem 'yajl-ruby'
|
|
32
34
|
|
33
35
|
and run `bundle install` to install the dependency.
|
34
36
|
|
35
|
-
If you are using **Rails 2.X, Rails 3
|
37
|
+
If you are using **Rails 2.X, Rails 3 or Padrino**, RABL works without configuration.
|
36
38
|
|
37
39
|
With Sinatra, or any other tilt-based framework, simply register:
|
38
40
|
|
@@ -41,12 +43,19 @@ With Sinatra, or any other tilt-based framework, simply register:
|
|
41
43
|
and RABL will be initialized and ready for use. For usage with Sinatra, check out
|
42
44
|
the [Sinatra Usage](https://github.com/nesquena/rabl/wiki/Setup-for-Sinatra) guide.
|
43
45
|
|
46
|
+
**Note:** Users have reported a few rendering issues with Rails 3.1 and Rails 3.2.
|
47
|
+
The [template handler](https://github.com/nesquena/rabl/blob/master/lib/rabl/template.rb) probably needs
|
48
|
+
a patch to properly support Rails 3.2. Hopefully I can get to it soon but patches are welcome.
|
49
|
+
|
44
50
|
## Overview ##
|
45
51
|
|
46
|
-
|
47
|
-
primarily from
|
52
|
+
You can use RABL to generate JSON and XML based APIs from any ruby object.
|
53
|
+
With RABL, the data typically is derived primarily from models (ORM-agnostic) and the representation of the API output is described within
|
54
|
+
a view template using a simple ruby DSL. This allows you to keep your data separated from the JSON or XML you wish to output.
|
48
55
|
|
49
|
-
Once you have installed RABL (explained above), you can construct a RABL view template and then render the template
|
56
|
+
Once you have installed RABL (explained above), you can construct a RABL view template and then render the template
|
57
|
+
from your Sinatra, Padrino or Rails applications from the controller (or route) very easily. Using [Padrino](http://padrinorb.com) as an
|
58
|
+
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:
|
50
59
|
|
51
60
|
```ruby
|
52
61
|
# app/app.rb
|
@@ -79,7 +88,7 @@ Which would output the following JSON or XML when visiting `http://localhost:300
|
|
79
88
|
}]
|
80
89
|
```
|
81
90
|
|
82
|
-
That's
|
91
|
+
That's a basic overview but there is a lot more to see such as partials, inheritance, custom nodes, etc. Read the full details of RABL below.
|
83
92
|
|
84
93
|
## Configuration ##
|
85
94
|
|
@@ -174,11 +183,11 @@ There can also be odd cases where the root-level of the response doesn't map dir
|
|
174
183
|
|
175
184
|
```ruby
|
176
185
|
object false
|
177
|
-
|
186
|
+
node(:some_count) { |m| @user.posts.count }
|
178
187
|
child(@user) { attribute :name }
|
179
188
|
```
|
180
189
|
|
181
|
-
In those cases, object can be assigned to 'false' and
|
190
|
+
In those cases, object can be assigned to 'false' and nodes can be constructed free-form.
|
182
191
|
|
183
192
|
### Attributes ###
|
184
193
|
|
@@ -247,11 +256,11 @@ Use glue to add additional attributes to the parent object.
|
|
247
256
|
|
248
257
|
### Custom Nodes ###
|
249
258
|
|
250
|
-
This will generate a json response based on the result of the
|
259
|
+
This will generate a json response based on the result of the `node` block:
|
251
260
|
|
252
261
|
```ruby
|
253
262
|
# app/views/users/show.json.rabl
|
254
|
-
|
263
|
+
node :full_name do |u|
|
255
264
|
u.first_name + " " + u.last_name
|
256
265
|
end
|
257
266
|
```
|
@@ -260,28 +269,28 @@ or a custom node that exists only if a condition is true:
|
|
260
269
|
|
261
270
|
```ruby
|
262
271
|
# m is the object being rendered, also supports :unless
|
263
|
-
|
272
|
+
node(:foo, :if => lambda { |m| m.has_foo? }) do |m|
|
264
273
|
m.foo
|
265
274
|
end
|
266
275
|
```
|
267
276
|
|
268
|
-
or don't pass a name and have the
|
277
|
+
or don't pass a name and have the node block merged into the response:
|
269
278
|
|
270
279
|
```ruby
|
271
|
-
|
280
|
+
node do |u|
|
272
281
|
{ :full_name => u.first_name + " " + u.last_name }
|
273
282
|
# => { full_name : "Bob Johnson" }
|
274
283
|
end
|
275
284
|
```
|
276
285
|
|
277
|
-
You can use custom
|
286
|
+
You can use custom nodes like these to create flexible representations of a value utilizing all the data from the model.
|
278
287
|
|
279
288
|
### Partials ###
|
280
289
|
|
281
290
|
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:
|
282
291
|
|
283
292
|
```ruby
|
284
|
-
|
293
|
+
node :location do
|
285
294
|
{ :city => @city, :address => partial("users/address", :object => @address) }
|
286
295
|
end
|
287
296
|
```
|
@@ -289,7 +298,7 @@ end
|
|
289
298
|
or event access an object associated with the parent model:
|
290
299
|
|
291
300
|
```ruby
|
292
|
-
|
301
|
+
node :location do |m|
|
293
302
|
{ :city => m.city, :address => partial("users/address", :object => m.address) }
|
294
303
|
end
|
295
304
|
```
|
@@ -307,7 +316,7 @@ RABL has the ability to extend other "base" rabl templates and additional attrib
|
|
307
316
|
# app/views/users/advanced.json.rabl
|
308
317
|
extends "users/base" # another RABL template in "app/views/users/base.json.rabl"
|
309
318
|
|
310
|
-
|
319
|
+
node :can_drink do |m|
|
311
320
|
m.age > 21
|
312
321
|
end
|
313
322
|
```
|
@@ -334,7 +343,7 @@ object @post
|
|
334
343
|
# Access instance variables
|
335
344
|
child(@user => :user) { ... }
|
336
345
|
# or Rails helpers
|
337
|
-
|
346
|
+
node(:formatted_body) { |post| simple_format(post.body) }
|
338
347
|
```
|
339
348
|
|
340
349
|
There should be no problem fetching the appropriate data to construct a response.
|
@@ -362,10 +371,28 @@ Note that RABL can be nested arbitrarily deep within child nodes to allow for th
|
|
362
371
|
|
363
372
|
### Advanced Usage ###
|
364
373
|
|
374
|
+
Links to resources for advanced usage:
|
375
|
+
|
365
376
|
* Rendering JSON for a tree structure using RABL: https://github.com/nesquena/rabl/issues/70
|
366
377
|
* Layouts (erb, haml and rabl) in RABL: https://github.com/nesquena/rabl/wiki/Using-Layouts
|
367
378
|
* Backbone or [Ember.js](http://www.emberjs.com) Integration: https://github.com/nesquena/rabl/wiki/Backbone-Integration
|
368
379
|
|
380
|
+
Please add your own usages and let me know so we can add them here!
|
381
|
+
|
382
|
+
### Tutorials ###
|
383
|
+
|
384
|
+
Tutorials can always be helpful when first getting started:
|
385
|
+
|
386
|
+
* [Railscasts #322](http://railscasts.com/episodes/322-rabl)
|
387
|
+
* http://blog.joshsoftware.com/2011/12/23/designing-rails-api-using-rabl-and-devise/
|
388
|
+
* http://engineering.gomiso.com/2011/06/27/building-a-platform-api-on-rails/
|
389
|
+
* http://blog.lawrencenorton.com/better-json-requests-with-rabl
|
390
|
+
* http://www.rodrigoalvesvieira.com/developing-json-api-rails-rabl/
|
391
|
+
* http://tech.favoritemedium.com/2011/06/using-rabl-in-rails-json-web-api.html
|
392
|
+
* http://seesparkbox.com/foundry/better_rails_apis_with_rabl
|
393
|
+
|
394
|
+
Let me know if there's any other useful resources.
|
395
|
+
|
369
396
|
### Troubleshooting ###
|
370
397
|
|
371
398
|
* Redundant calls for a collection: https://github.com/nesquena/rabl/issues/142#issuecomment-2969107
|
@@ -399,6 +426,9 @@ Thanks to [Miso](http://gomiso.com) for allowing me to create this for our appli
|
|
399
426
|
* [Luke van der Hoeven](https://github.com/plukevdh) - Support non-ORM objects in templates
|
400
427
|
|
401
428
|
and many more contributors listed in the [CHANGELOG](https://github.com/nesquena/rabl/blob/master/CHANGELOG.md).
|
429
|
+
|
430
|
+
Want to contribute support for another format? Check out patch for [msgpack support](https://github.com/nesquena/rabl/pull/69).
|
431
|
+
|
402
432
|
Please fork and contribute, any help in making this project better is appreciated!
|
403
433
|
|
404
434
|
## Inspirations ##
|
@@ -417,4 +447,4 @@ See the [examples](https://github.com/nesquena/rabl/tree/master/examples) direct
|
|
417
447
|
|
418
448
|
## Copyright
|
419
449
|
|
420
|
-
Copyright © 2011 Nathan Esquenazi. See [MIT-LICENSE](https://github.com/nesquena/rabl/blob/master/MIT-LICENSE) for details.
|
450
|
+
Copyright © 2011-2012 Nathan Esquenazi. See [MIT-LICENSE](https://github.com/nesquena/rabl/blob/master/MIT-LICENSE) for details.
|
data/lib/rabl/builder.rb
CHANGED
@@ -4,7 +4,7 @@ module Rabl
|
|
4
4
|
|
5
5
|
# Constructs a new ejs hash based on given object and options
|
6
6
|
# options = { :format => "json", :attributes, :root => true,
|
7
|
-
# :child_root => true, :
|
7
|
+
# :child_root => true, :node, :child, :glue, :extends }
|
8
8
|
def initialize(data, options={}, &block)
|
9
9
|
@options = options
|
10
10
|
@_scope = options[:scope]
|
@@ -24,10 +24,10 @@ module Rabl
|
|
24
24
|
@options[:attributes].each_pair do |attribute, name|
|
25
25
|
attribute(attribute, :as => name)
|
26
26
|
end if @options.has_key?(:attributes)
|
27
|
-
#
|
28
|
-
@options[:
|
29
|
-
|
30
|
-
end if @options.has_key?(:
|
27
|
+
# Node
|
28
|
+
@options[:node].each do |settings|
|
29
|
+
node(settings[:name], settings[:options], &settings[:block])
|
30
|
+
end if @options.has_key?(:node)
|
31
31
|
# Children
|
32
32
|
@options[:child].each do |settings|
|
33
33
|
child(settings[:data], settings[:options], &settings[:block])
|
@@ -56,11 +56,11 @@ module Rabl
|
|
56
56
|
end
|
57
57
|
alias_method :attributes, :attribute
|
58
58
|
|
59
|
-
# Creates an arbitrary
|
59
|
+
# Creates an arbitrary node that is included in the json output
|
60
60
|
# node(:foo) { "bar" }
|
61
|
-
#
|
62
|
-
#
|
63
|
-
def
|
61
|
+
# node(:foo) { "bar" }
|
62
|
+
# node(:foo, :if => lambda { |m| m.foo.present? }) { "bar" }
|
63
|
+
def node(name, options={}, &block)
|
64
64
|
return unless resolve_condition(options)
|
65
65
|
result = block.call(@_object)
|
66
66
|
if name.present?
|
@@ -69,7 +69,7 @@ module Rabl
|
|
69
69
|
@_result.merge!(result) if result
|
70
70
|
end
|
71
71
|
end
|
72
|
-
alias_method :
|
72
|
+
alias_method :code, :node
|
73
73
|
|
74
74
|
# Creates a child node that is included in json output
|
75
75
|
# child(@user) { attribute :full_name }
|
@@ -81,7 +81,7 @@ module Rabl
|
|
81
81
|
include_root = is_collection?(object) && @options[:child_root] # child @users
|
82
82
|
engine_options = @options.slice(:child_root).merge(:root => include_root)
|
83
83
|
object = { object => name } if data.respond_to?(:each_pair) && object # child :users => :people
|
84
|
-
@_result[name] = self.object_to_hash(object, engine_options, &block)
|
84
|
+
@_result[name] = self.object_to_hash(object, engine_options, &block)
|
85
85
|
end
|
86
86
|
|
87
87
|
# Glues data from a child node to the json_output
|
data/lib/rabl/engine.rb
CHANGED
@@ -102,14 +102,14 @@ module Rabl
|
|
102
102
|
end
|
103
103
|
alias_method :attributes, :attribute
|
104
104
|
|
105
|
-
# Creates an arbitrary
|
106
|
-
#
|
107
|
-
#
|
108
|
-
def
|
109
|
-
@_options[:
|
110
|
-
@_options[:
|
105
|
+
# Creates an arbitrary node that is included in the json output.
|
106
|
+
# node(:foo) { "bar" }
|
107
|
+
# node(:foo, :if => lambda { ... }) { "bar" }
|
108
|
+
def node(name = nil, options={}, &block)
|
109
|
+
@_options[:node] ||= []
|
110
|
+
@_options[:node] << { :name => name, :options => options, :block => block }
|
111
111
|
end
|
112
|
-
alias_method :
|
112
|
+
alias_method :code, :node
|
113
113
|
|
114
114
|
# Creates a child node that is included in json output
|
115
115
|
# child(@user) { attribute :full_name }
|
@@ -194,7 +194,7 @@ module Rabl
|
|
194
194
|
def clear_compile_state
|
195
195
|
@_options.delete(:extends)
|
196
196
|
@_options.delete(:attributes)
|
197
|
-
@_options.delete(:
|
197
|
+
@_options.delete(:node)
|
198
198
|
@_options.delete(:child)
|
199
199
|
@_options.delete(:glue)
|
200
200
|
end
|
data/lib/rabl/template.rb
CHANGED
@@ -20,7 +20,7 @@ if defined?(Tilt)
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# Rails 2.X Template
|
23
|
-
if defined?(Rails) && Rails.version =~ /^2/
|
23
|
+
if defined?(ActionView) && defined?(Rails) && Rails.version =~ /^2/
|
24
24
|
require 'action_view/base'
|
25
25
|
require 'action_view/template'
|
26
26
|
|
@@ -41,7 +41,7 @@ if defined?(Rails) && Rails.version =~ /^2/
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Rails 3.X Template
|
44
|
-
if defined?(Rails) && Rails.version =~ /^3/
|
44
|
+
if defined?(ActionView) && defined?(Rails) && Rails.version =~ /^3/
|
45
45
|
module ActionView
|
46
46
|
module Template::Handlers
|
47
47
|
class Rabl
|
data/lib/rabl/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rabl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.5.
|
5
|
+
version: 0.5.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Nathan Esquenazi
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2012-02-10 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: multi_json
|