rabl-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +69 -0
- data/MIT-LICENSE +20 -0
- data/README.md +248 -0
- data/Rakefile +35 -0
- data/lib/rabl-rails.rb +41 -0
- data/lib/rabl-rails/compiler.rb +146 -0
- data/lib/rabl-rails/handler.rb +15 -0
- data/lib/rabl-rails/library.rb +37 -0
- data/lib/rabl-rails/railtie.rb +9 -0
- data/lib/rabl-rails/renderer.rb +2 -0
- data/lib/rabl-rails/renderers/base.rb +116 -0
- data/lib/rabl-rails/renderers/json.rb +10 -0
- data/lib/rabl-rails/template.rb +11 -0
- data/lib/rabl-rails/version.rb +3 -0
- data/lib/tasks/rabl-rails.rake +4 -0
- data/rabl-rails.gemspec +23 -0
- data/test/cache_templates_test.rb +34 -0
- data/test/compiler_test.rb +163 -0
- data/test/deep_nesting_test.rb +58 -0
- data/test/non_restful_response_test.rb +35 -0
- data/test/renderers/json_renderer_test.rb +131 -0
- data/test/test_helper.rb +45 -0
- metadata +139 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rabl-rails (0.1.0)
|
5
|
+
activesupport (~> 3.0)
|
6
|
+
railties (~> 3.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionpack (3.2.6)
|
12
|
+
activemodel (= 3.2.6)
|
13
|
+
activesupport (= 3.2.6)
|
14
|
+
builder (~> 3.0.0)
|
15
|
+
erubis (~> 2.7.0)
|
16
|
+
journey (~> 1.0.1)
|
17
|
+
rack (~> 1.4.0)
|
18
|
+
rack-cache (~> 1.2)
|
19
|
+
rack-test (~> 0.6.1)
|
20
|
+
sprockets (~> 2.1.3)
|
21
|
+
activemodel (3.2.6)
|
22
|
+
activesupport (= 3.2.6)
|
23
|
+
builder (~> 3.0.0)
|
24
|
+
activesupport (3.2.6)
|
25
|
+
i18n (~> 0.6)
|
26
|
+
multi_json (~> 1.0)
|
27
|
+
builder (3.0.0)
|
28
|
+
erubis (2.7.0)
|
29
|
+
hike (1.2.1)
|
30
|
+
i18n (0.6.0)
|
31
|
+
journey (1.0.4)
|
32
|
+
json (1.7.3)
|
33
|
+
multi_json (1.3.6)
|
34
|
+
rack (1.4.1)
|
35
|
+
rack-cache (1.2)
|
36
|
+
rack (>= 0.4)
|
37
|
+
rack-ssl (1.3.2)
|
38
|
+
rack
|
39
|
+
rack-test (0.6.1)
|
40
|
+
rack (>= 1.0)
|
41
|
+
railties (3.2.6)
|
42
|
+
actionpack (= 3.2.6)
|
43
|
+
activesupport (= 3.2.6)
|
44
|
+
rack-ssl (~> 1.3.2)
|
45
|
+
rake (>= 0.8.7)
|
46
|
+
rdoc (~> 3.4)
|
47
|
+
thor (>= 0.14.6, < 2.0)
|
48
|
+
rake (0.9.2.2)
|
49
|
+
rdoc (3.12)
|
50
|
+
json (~> 1.4)
|
51
|
+
rspec-mocks (2.11.1)
|
52
|
+
sprockets (2.1.3)
|
53
|
+
hike (~> 1.2)
|
54
|
+
rack (~> 1.0)
|
55
|
+
tilt (~> 1.1, != 1.3.0)
|
56
|
+
sqlite3 (1.3.6)
|
57
|
+
thor (0.15.4)
|
58
|
+
tilt (1.3.3)
|
59
|
+
yajl-ruby (1.1.0)
|
60
|
+
|
61
|
+
PLATFORMS
|
62
|
+
ruby
|
63
|
+
|
64
|
+
DEPENDENCIES
|
65
|
+
actionpack (~> 3.0)
|
66
|
+
rabl-rails!
|
67
|
+
rspec-mocks
|
68
|
+
sqlite3
|
69
|
+
yajl-ruby
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 Christopher Cocchi-Perrier
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
# RABL for Rails #
|
2
|
+
|
3
|
+
RABL (Ruby API Builder Language) is a ruby templating system for rendering resources in different format (JSON, XML, BSON, ...). You can find documentation [here](http://github.com/nesquena/rabl).
|
4
|
+
|
5
|
+
RABL-rails only target Rails 3+ application because Rails 2 applications are becoming less and less present and will be obsolete with Rails 4. So let's look to the future !
|
6
|
+
|
7
|
+
So now you ask why used `rabl-rails` if `rabl` already exists and supports Rails. Rabl-rails is **faster** and uses **less memory** than standard rabl gem while letting you access same features. Of course, there are some slight changes to do on your templates to get this gem to work but it should't take you more than 5 minutes.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Install as a gem :
|
12
|
+
|
13
|
+
```
|
14
|
+
gem install rabl-rails
|
15
|
+
```
|
16
|
+
|
17
|
+
or add directly to your `Gemfile`
|
18
|
+
|
19
|
+
```
|
20
|
+
gem 'rabl-rails'
|
21
|
+
```
|
22
|
+
|
23
|
+
And that's it !
|
24
|
+
|
25
|
+
## Overview
|
26
|
+
|
27
|
+
Once you have installed RABL, you can directly used RABL templates to render your resources without changing anything to you controller. As example,
|
28
|
+
assuming you have a `Post` model filled with blog posts, and a `PostController` that look like this :
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class PostController < ApplicationController
|
32
|
+
respond_to :html, :json, :xml
|
33
|
+
|
34
|
+
def index
|
35
|
+
@posts = Post.order('created_at DESC')
|
36
|
+
respond_with(@posts)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
You can create the following RABL-rails template to express the API output of `@posts`
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
# app/views/post/index.rabl
|
45
|
+
collection :@posts
|
46
|
+
attributes :id, :title, :subject
|
47
|
+
child(:user) { attributes :full_name }
|
48
|
+
node(:read) { |post| post.read_by?(@user) }
|
49
|
+
```
|
50
|
+
|
51
|
+
This would output the following JSON when visiting `http://localhost:3000/posts.json`
|
52
|
+
|
53
|
+
```js
|
54
|
+
[{
|
55
|
+
"id" : 5, title: "...", subject: "...",
|
56
|
+
"user" : { full_name : "..." },
|
57
|
+
"read" : true
|
58
|
+
}]
|
59
|
+
```
|
60
|
+
|
61
|
+
That's a basic overview but there is a lot more to see such as partials, inheritance or fragment caching.
|
62
|
+
|
63
|
+
## How it works
|
64
|
+
|
65
|
+
As opposed to standard RABL gem, this gem separate compiling (a.k.a transforming a RABL-rails template into a Ruby hash) and the actual rendering of the object or collection. This allow to only compile the template once and only Ruby hashes.
|
66
|
+
|
67
|
+
The fact of compiling the template outside of any rendering context prevent us to use any instances variables (with the exception of node) in the template because they are rendering objects. So instead, you'll have to use symbols of these variables.For example, to render the collection `@posts` inside your `PostController`, you need to use `:@posts` inside of the template.
|
68
|
+
|
69
|
+
The only places where you can actually used instance variables are into Proc (or lambda) or into custom node (because they are treated as Proc).
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
# We reference the @posts varibles that will be used at rendering time
|
73
|
+
collection :@posts
|
74
|
+
|
75
|
+
# Here you can use directly the instance variable because it
|
76
|
+
# will be evaluated when rendering the object
|
77
|
+
node(:read) { |post| post.read_by?(@user) }
|
78
|
+
```
|
79
|
+
|
80
|
+
The same rule applies for view helpers such as `current_user`
|
81
|
+
|
82
|
+
After the template is compiled into a hash, Rabl-rails will use a renderer to do the actual output. Actually, only JSON and XML formats are supported.
|
83
|
+
|
84
|
+
## Usage
|
85
|
+
|
86
|
+
### Data declaration
|
87
|
+
|
88
|
+
To declare data to use in the template, you can use either `object` or `collection` with the symbol name or your data.
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# app/views/users/show.json.rabl
|
92
|
+
object :@user
|
93
|
+
|
94
|
+
# app/views/users/index.json.rabl
|
95
|
+
collection :@users
|
96
|
+
```
|
97
|
+
|
98
|
+
You can specify root label for the collection using hash or `:root` option
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
collection :@posts, root: :articles
|
102
|
+
#is equivalent to
|
103
|
+
collection :@posts => :articles
|
104
|
+
|
105
|
+
# => { "articles" : [{...}, {...}] }
|
106
|
+
```
|
107
|
+
|
108
|
+
There are rares cases when the template doesn't map directly to any object. In these cases, you can set data to false or skip data declaration altogether.
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
object false
|
112
|
+
node(:some_count) { |_| @user.posts.count }
|
113
|
+
child(:@user) { attribute :name }
|
114
|
+
```
|
115
|
+
|
116
|
+
### Attributes / Methods
|
117
|
+
|
118
|
+
Basic usage is to declared attributes to include in the response. These can be database attributes or any instance method.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
attributes :id, :title, :to_s
|
122
|
+
```
|
123
|
+
|
124
|
+
You can aliases these attributes in your response
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
attributes title: :foo, to_s: :bar
|
128
|
+
# => { "foo" : <title value>, "bar" : <to_s value> }
|
129
|
+
```
|
130
|
+
|
131
|
+
### Child nodes
|
132
|
+
|
133
|
+
You can include nested information from data associated with the parent model. You can also alias these associations.
|
134
|
+
For example if you have a `Post` model that belongs to a `User`
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
object :@post
|
138
|
+
child(user: :author) do
|
139
|
+
attributes :name
|
140
|
+
end
|
141
|
+
# => { "post" : { "author" : { "name" : "John D." } } }
|
142
|
+
```
|
143
|
+
|
144
|
+
You can also use arbitrary data source with child nodes
|
145
|
+
```ruby
|
146
|
+
child(:@users) do
|
147
|
+
attributes :id, :name
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
### Custom nodes
|
152
|
+
|
153
|
+
You can create custom node in your response, based on the result of the given block
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
object :@user
|
157
|
+
node(:full_name) { |u| u.first_name + " " + u.last_name }
|
158
|
+
# => { "user" : { "full_name" : "John Doe" } }
|
159
|
+
```
|
160
|
+
|
161
|
+
You can add the node only if a condition is true
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
node(:email, if: -> { |u| u.valid_email? }) do |u|
|
165
|
+
u.email
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
Nodes are evaluated at the rendering time, so you can use any instance variables or view helpers inside them
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
node(:url) { |post| post_url(post) }
|
173
|
+
```
|
174
|
+
|
175
|
+
Custom nodes are really usefull to create flexible representations of your resources.
|
176
|
+
|
177
|
+
### Extends & Partials
|
178
|
+
|
179
|
+
Often objects have a basic representation that is shared accross different views and enriched according to it. To avoid code redundancy you can extend your template from any other RABL template.
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
# app/views/users/base.json.rabl
|
183
|
+
attributes :id, :name
|
184
|
+
|
185
|
+
# app/views/users/private.json.rabl
|
186
|
+
extends 'users/base'
|
187
|
+
attributes :super_secret_attribute
|
188
|
+
```
|
189
|
+
|
190
|
+
You can also extends template in child nodes using `partial` option (this is the same as using `extends` in the child block)
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
collection @posts
|
194
|
+
attribute :title
|
195
|
+
child(:user, partial: 'users/base')
|
196
|
+
```
|
197
|
+
|
198
|
+
Partials can also be used inside custom nodes. When using partial this way, you MUST declare the object associated to the partial
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
node(:location) do |user|
|
202
|
+
{ city: user.city, address: partial('users/address', object: m.address) }
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
### Nesting
|
207
|
+
|
208
|
+
Rabl allow you to define easily your templates, even with hierarchy of 2 or 3 levels. Let's suppose your have a `thread` model that has many `posts` and that each post has many `comments`. We can display a full thread in a few lines
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
object :@thread
|
212
|
+
attribute :caption
|
213
|
+
child :posts do
|
214
|
+
attribute :title
|
215
|
+
child :comments do
|
216
|
+
extends 'comments/base'
|
217
|
+
end
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
## Performance
|
222
|
+
|
223
|
+
Benchmarks have been made using this [application](http://github.com/ccocchi/rabl-benchmark), with rabl 0.6.14 and rabl-rails 0.1.0
|
224
|
+
|
225
|
+
Overall, Rabl-rails is **20% faster and use 10% less memory**.
|
226
|
+
|
227
|
+
You can see full tests on test application repository.
|
228
|
+
|
229
|
+
## Caching
|
230
|
+
|
231
|
+
Caching is not a part of Rabl-rails. It is already in Rails itself, because caching all view output is the same as action caching (Rails caching is even better because it will also not run your queries).
|
232
|
+
|
233
|
+
And caching each object in a collection can be really not effective with big collections or simple objects. This is also a nightmare with cache expiration.
|
234
|
+
|
235
|
+
## Authors and contributors
|
236
|
+
|
237
|
+
* [Christopher Cocchi-Perrier](http://github.com/ccocchi) - Creator of the project
|
238
|
+
|
239
|
+
Want to add another format to Rabl-rails ? Checkout [JSON renderer](http://github.com/ccocchi/rabl-rails/blob/master/lib/rabl-rails/renderers/json.rb) for reference
|
240
|
+
Want to make another change ? Just fork and contribute, any help is very much appreciated
|
241
|
+
|
242
|
+
## Original idea
|
243
|
+
|
244
|
+
* [RABL](http://github.com/nesquena/rabl) Standart RABL gem. I used it a lot before deciding I wanted faster views
|
245
|
+
|
246
|
+
## Copyright
|
247
|
+
|
248
|
+
Copyright © 2011-2012 Christopher Cocchi-Perrier. See [MIT-LICENSE](http://github.com/ccocchi/rabl-rails/blob/master/MIT-LICENSE) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
# begin
|
3
|
+
# require 'bundler/setup'
|
4
|
+
# rescue LoadError
|
5
|
+
# puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
# end
|
7
|
+
# begin
|
8
|
+
# require 'rdoc/task'
|
9
|
+
# rescue LoadError
|
10
|
+
# require 'rdoc/rdoc'
|
11
|
+
# require 'rake/rdoctask'
|
12
|
+
# RDoc::Task = Rake::RDocTask
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
# rdoc.rdoc_dir = 'rdoc'
|
17
|
+
# rdoc.title = 'RablRails'
|
18
|
+
# rdoc.options << '--line-numbers'
|
19
|
+
# rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
# rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
# end
|
22
|
+
|
23
|
+
require 'bundler'
|
24
|
+
Bundler::GemHelper.install_tasks
|
25
|
+
|
26
|
+
require 'rake/testtask'
|
27
|
+
Rake::TestTask.new(:test) do |t|
|
28
|
+
t.libs << 'lib'
|
29
|
+
t.libs << 'test'
|
30
|
+
t.pattern = 'test/**/*_test.rb'
|
31
|
+
t.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
task :default => :test
|
data/lib/rabl-rails.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
5
|
+
|
6
|
+
require 'rabl-rails/version'
|
7
|
+
require 'rabl-rails/template'
|
8
|
+
require 'rabl-rails/compiler'
|
9
|
+
|
10
|
+
require 'rabl-rails/renderer'
|
11
|
+
|
12
|
+
require 'rabl-rails/library'
|
13
|
+
require 'rabl-rails/handler'
|
14
|
+
require 'rabl-rails/railtie'
|
15
|
+
|
16
|
+
require 'multi_json'
|
17
|
+
|
18
|
+
module RablRails
|
19
|
+
mattr_accessor :cache_templates
|
20
|
+
@@cache_templates = true
|
21
|
+
|
22
|
+
mattr_accessor :include_json_root
|
23
|
+
@@include_json_root = true
|
24
|
+
|
25
|
+
mattr_accessor :json_engine
|
26
|
+
@@json_engine = :yajl
|
27
|
+
|
28
|
+
def self.configure
|
29
|
+
yield self
|
30
|
+
post_configure
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.cache_templates?
|
34
|
+
ActionController::Base.perform_caching && @@cache_templates
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def self.post_configure
|
39
|
+
MultiJson.engine = self.json_engine
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module RablRails
|
2
|
+
#
|
3
|
+
# Class that will compile RABL source code into a hash
|
4
|
+
# representing data structure
|
5
|
+
#
|
6
|
+
class Compiler
|
7
|
+
def initialize
|
8
|
+
@glue_count = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# Compile from source code and return the CompiledTemplate
|
13
|
+
# created.
|
14
|
+
#
|
15
|
+
def compile_source(source)
|
16
|
+
@template = CompiledTemplate.new
|
17
|
+
instance_eval(source)
|
18
|
+
@template
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Sets the object to be used as the data for the template
|
23
|
+
# Example:
|
24
|
+
# object :@user
|
25
|
+
# object :@user, :root => :author
|
26
|
+
#
|
27
|
+
def object(data, options = {})
|
28
|
+
@template.data, @template.root_name = extract_data_and_name(data)
|
29
|
+
@template.root_name = options[:root] if options.has_key? :root
|
30
|
+
end
|
31
|
+
alias_method :collection, :object
|
32
|
+
|
33
|
+
#
|
34
|
+
# Includes the attribute or method in the output
|
35
|
+
# Example:
|
36
|
+
# attributes :id, :name
|
37
|
+
# attribute :email => :super_secret
|
38
|
+
#
|
39
|
+
def attribute(*args)
|
40
|
+
if args.first.is_a?(Hash)
|
41
|
+
args.first.each_pair { |k, v| @template[v] = k }
|
42
|
+
else
|
43
|
+
options = args.extract_options!
|
44
|
+
args.each { |name|
|
45
|
+
key = options[:as] || name
|
46
|
+
@template[key] = name
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
alias_method :attributes, :attribute
|
51
|
+
|
52
|
+
#
|
53
|
+
# Creates a child node to be included in the output.
|
54
|
+
# name_or data can be an object or collection or a method to call on the data. It
|
55
|
+
# accepts :root and :partial options.
|
56
|
+
# Notes that partial and blocks are not compatible
|
57
|
+
# Example:
|
58
|
+
# child(:@posts, :root => :posts) { attribute :id }
|
59
|
+
# child(:posts, :partial => 'posts/base')
|
60
|
+
#
|
61
|
+
def child(name_or_data, options = {})
|
62
|
+
data, name = extract_data_and_name(name_or_data)
|
63
|
+
name = options[:root] if options.has_key? :root
|
64
|
+
if options[:partial]
|
65
|
+
template = Library.instance.compile_template_from_path(options[:partial])
|
66
|
+
@template[name] = template.merge!(:_data => data)
|
67
|
+
elsif block_given?
|
68
|
+
@template[name] = sub_compile(data) { yield }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Glues data from a child node to the output
|
74
|
+
# Example:
|
75
|
+
# glue(:@user) { attribute :name }
|
76
|
+
#
|
77
|
+
def glue(data)
|
78
|
+
return unless block_given?
|
79
|
+
name = :"_glue#{@glue_count}"
|
80
|
+
@glue_count += 1
|
81
|
+
@template[name] = sub_compile(data) { yield }
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Creates an arbitrary node in the json output.
|
86
|
+
# It accepts :if option to create conditionnal nodes. The current data will
|
87
|
+
# be passed to the block so it is advised to use it instead of ivars.
|
88
|
+
# Example:
|
89
|
+
# node(:name) { |user| user.first_name + user.last_name }
|
90
|
+
# node(:role, if: ->(u) { !u.admin? }) { |u| u.role }
|
91
|
+
#
|
92
|
+
def node(name, options = {}, &block)
|
93
|
+
condition = options[:if]
|
94
|
+
|
95
|
+
if condition
|
96
|
+
if condition.is_a?(Proc)
|
97
|
+
@template[name] = [condition, block]
|
98
|
+
else
|
99
|
+
@template[name] = block if condition
|
100
|
+
end
|
101
|
+
else
|
102
|
+
@template[name] = block
|
103
|
+
end
|
104
|
+
end
|
105
|
+
alias_method :code, :node
|
106
|
+
|
107
|
+
#
|
108
|
+
# Extends an existing rabl template
|
109
|
+
# Example:
|
110
|
+
# extends 'users/base'
|
111
|
+
#
|
112
|
+
def extends(path)
|
113
|
+
t = Library.instance.compile_template_from_path(path)
|
114
|
+
@template.merge!(t.source)
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
|
119
|
+
#
|
120
|
+
# Extract data root_name and root name
|
121
|
+
# Example:
|
122
|
+
# :@users -> [:@users, nil]
|
123
|
+
# :@users => :authors -> [:@users, :authors]
|
124
|
+
#
|
125
|
+
def extract_data_and_name(name_or_data)
|
126
|
+
case name_or_data
|
127
|
+
when Symbol
|
128
|
+
str = name_or_data.to_s
|
129
|
+
str.start_with?('@') ? [name_or_data, str[1..-1]] : [name_or_data, name_or_data]
|
130
|
+
when Hash
|
131
|
+
name_or_data.first
|
132
|
+
else
|
133
|
+
name_or_data
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def sub_compile(data)
|
138
|
+
return {} unless block_given?
|
139
|
+
old_template, @template = @template, {}
|
140
|
+
yield
|
141
|
+
@template.merge!(:_data => data)
|
142
|
+
ensure
|
143
|
+
@template = old_template
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|