rabl 0.5.5.e → 0.5.5.f
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -1
- data/README.md +11 -2
- data/lib/rabl/builder.rb +10 -9
- data/lib/rabl/engine.rb +24 -21
- data/lib/rabl/helpers.rb +22 -1
- data/lib/rabl/partials.rb +11 -3
- data/lib/rabl/version.rb +1 -1
- data/test/builder_test.rb +2 -2
- data/test/engine_test.rb +20 -3
- metadata +1 -1
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
## 0.5.5.a-
|
3
|
+
## 0.5.5.a-f
|
4
4
|
|
5
5
|
* Change engine to only instantiate one builder when rendering a collection
|
6
6
|
* Alias to\_msgpack to to\_mpac
|
7
7
|
* Cache template sources for faster partial lookups (thanks cj)
|
8
8
|
* Adds BSON format support (thanks Antiarchitect)
|
9
|
+
* Use template lookup mechanism to find templates in Rails 3 (thanks blakewatters)
|
10
|
+
* Adds a 'object_root' option to collection (thanks blakewatters)
|
11
|
+
* Adds a 'root_name' option to collection
|
9
12
|
|
10
13
|
## 0.5.4
|
11
14
|
|
data/README.md
CHANGED
@@ -165,7 +165,9 @@ To enable, include the bson gem in your project's Gemfile. Then use Rabl as norm
|
|
165
165
|
# Gemfile
|
166
166
|
gem 'bson', '~> 1.5.2'
|
167
167
|
```
|
168
|
-
|
168
|
+
|
169
|
+
To use it with Rails, also register the bson mime type format:
|
170
|
+
|
169
171
|
```ruby
|
170
172
|
# config/initializers/mime_types.rb
|
171
173
|
Mime::Type.register "application/bson", :bson
|
@@ -214,13 +216,20 @@ collection @users
|
|
214
216
|
# => [ { "user" : { ... } } ]
|
215
217
|
```
|
216
218
|
|
217
|
-
or
|
219
|
+
or specify a root node label for the collection:
|
218
220
|
|
219
221
|
```ruby
|
220
222
|
collection @users => :people
|
221
223
|
# => { "people" : [ { "person" : { ... } } ] }
|
222
224
|
```
|
223
225
|
|
226
|
+
or even specify both the child and root labels for a collection:
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
collection @users, :root_name => "people", :object_root => "user"
|
230
|
+
# => { "people" : [ { "user" : { ... } } ] }
|
231
|
+
```
|
232
|
+
|
224
233
|
and this will be used as the default data for the rendering.
|
225
234
|
|
226
235
|
There can also be odd cases where the root-level of the response doesn't map directly to any object:
|
data/lib/rabl/builder.rb
CHANGED
@@ -3,25 +3,26 @@ module Rabl
|
|
3
3
|
include Rabl::Partials
|
4
4
|
|
5
5
|
# Constructs a new rabl hash based on given object and options
|
6
|
-
# options = { :format => "json", :
|
7
|
-
# :
|
6
|
+
# options = { :format => "json", :root => true, :child_root => true,
|
7
|
+
# :attributes, :node, :child, :glue, :extends }
|
8
|
+
#
|
8
9
|
def initialize(options={}, &block)
|
9
10
|
@options = options
|
10
11
|
@_scope = options[:scope]
|
11
12
|
end
|
12
13
|
|
13
14
|
# Given an object and options, returns the hash representation
|
14
|
-
# build(@user, :format => "json", :attributes => { ... })
|
15
|
-
def build(
|
16
|
-
@
|
17
|
-
@_object = data_object(data)
|
15
|
+
# build(@user, :format => "json", :attributes => { ... }, :root_name => "user")
|
16
|
+
def build(object, options={})
|
17
|
+
@_object = object
|
18
18
|
compile_hash(options)
|
19
19
|
end
|
20
20
|
|
21
21
|
protected
|
22
22
|
|
23
23
|
# Returns a hash representation of the data object
|
24
|
-
# compile_hash(:
|
24
|
+
# compile_hash(:root_name => false)
|
25
|
+
# compile_hash(:root_name => "user")
|
25
26
|
def compile_hash(options={})
|
26
27
|
@_result = {}
|
27
28
|
# Extends
|
@@ -46,8 +47,8 @@ module Rabl
|
|
46
47
|
end if @options.has_key?(:glue)
|
47
48
|
|
48
49
|
# Wrap result in root
|
49
|
-
if
|
50
|
-
@_root_name
|
50
|
+
if options[:root_name].present?
|
51
|
+
@_root_name = options[:root_name]
|
51
52
|
else # no root
|
52
53
|
@_root_name = nil
|
53
54
|
end
|
data/lib/rabl/engine.rb
CHANGED
@@ -31,17 +31,15 @@ module Rabl
|
|
31
31
|
# to_hash(:root => true, :child_root => true)
|
32
32
|
def to_hash(options={})
|
33
33
|
options = @_options.merge(options)
|
34
|
-
data = data_object(@_data)
|
34
|
+
data, root_name = data_object(@_data), data_name(@_data)
|
35
35
|
builder = Rabl::Builder.new(options)
|
36
36
|
if is_object?(data) || !data # object @user
|
37
|
-
|
37
|
+
options[:root_name] = root_name if options[:root]
|
38
|
+
builder.build(data, options)
|
38
39
|
elsif is_collection?(data) # collection @users
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
else # skip root name
|
43
|
-
data.map { |object| builder.build(object, options) }
|
44
|
-
end
|
40
|
+
options[:root_name] = object_root_name if object_root_name
|
41
|
+
options[:root_name] ||= root_name.to_s.singularize if options[:root]
|
42
|
+
data.map { |object| builder.build(object, options) }
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
@@ -50,7 +48,7 @@ module Rabl
|
|
50
48
|
def to_json(options={})
|
51
49
|
include_root = Rabl.configuration.include_json_root
|
52
50
|
options = options.reverse_merge(:root => include_root, :child_root => include_root)
|
53
|
-
result =
|
51
|
+
result = collection_root_name ? { collection_root_name => to_hash(options) } : to_hash(options)
|
54
52
|
format_json(result)
|
55
53
|
end
|
56
54
|
|
@@ -59,7 +57,7 @@ module Rabl
|
|
59
57
|
def to_msgpack(options={})
|
60
58
|
include_root = Rabl.configuration.include_msgpack_root
|
61
59
|
options = options.reverse_merge(:root => include_root, :child_root => include_root)
|
62
|
-
result =
|
60
|
+
result = collection_root_name ? { collection_root_name => to_hash(options) } : to_hash(options)
|
63
61
|
Rabl.configuration.msgpack_engine.pack result
|
64
62
|
end
|
65
63
|
alias_method :to_mpac, :to_msgpack
|
@@ -78,8 +76,8 @@ module Rabl
|
|
78
76
|
def to_bson(options={})
|
79
77
|
include_root = Rabl.configuration.include_bson_root
|
80
78
|
options = options.reverse_merge(:root => include_root, :child_root => include_root)
|
81
|
-
result = if
|
82
|
-
{
|
79
|
+
result = if collection_root_name
|
80
|
+
{ collection_root_name => to_hash(options) }
|
83
81
|
elsif is_collection?(@_data) && @_data.is_a?(Array)
|
84
82
|
{ data_name(@_data) => to_hash(options) }
|
85
83
|
else
|
@@ -99,8 +97,12 @@ module Rabl
|
|
99
97
|
# Sets the object as a collection casted to a simple array
|
100
98
|
# collection @users
|
101
99
|
# collection @users => :people
|
102
|
-
|
103
|
-
|
100
|
+
# collection @users, :root => :person
|
101
|
+
# collection @users, :object_root => :person
|
102
|
+
def collection(data, options={})
|
103
|
+
@_collection_name = options[:root] if options[:root]
|
104
|
+
@_collection_name ||= data.values.first if data.respond_to?(:each_pair)
|
105
|
+
@_object_root_name = options[:object_root] if options[:object_root]
|
104
106
|
self.object(data_object(data).to_a) if data
|
105
107
|
end
|
106
108
|
|
@@ -155,8 +157,8 @@ module Rabl
|
|
155
157
|
# Returns a guess at the default object for this template
|
156
158
|
# default_object => @user
|
157
159
|
def default_object
|
158
|
-
if
|
159
|
-
full_name =
|
160
|
+
if context_scope.respond_to?(:controller)
|
161
|
+
full_name = context_scope.controller.controller_name
|
160
162
|
instance_variable_get("@#{ full_name.split("::").last }")
|
161
163
|
end
|
162
164
|
end
|
@@ -164,8 +166,8 @@ module Rabl
|
|
164
166
|
# Returns a guess at the format in this scope
|
165
167
|
# request_format => "xml"
|
166
168
|
def request_format
|
167
|
-
format = self.request_params.has_key?(:format) ?
|
168
|
-
if request =
|
169
|
+
format = self.request_params.has_key?(:format) ? context_scope.params[:format] : nil
|
170
|
+
if request = context_scope.respond_to?(:request) && context_scope.request
|
169
171
|
format ||= request.format.to_sym.to_s if request.respond_to?(:format)
|
170
172
|
end
|
171
173
|
format && self.respond_to?("to_#{format}") ? format : "json"
|
@@ -174,7 +176,7 @@ module Rabl
|
|
174
176
|
# Returns the request parameters if available in the scope
|
175
177
|
# request_params => { :foo => "bar" }
|
176
178
|
def request_params
|
177
|
-
|
179
|
+
context_scope.respond_to?(:params) ? context_scope.params : {}
|
178
180
|
end
|
179
181
|
|
180
182
|
# Returns data as json embraced with callback when detected
|
@@ -188,12 +190,12 @@ module Rabl
|
|
188
190
|
|
189
191
|
# Augments respond to supporting scope methods
|
190
192
|
def respond_to?(name, include_private=false)
|
191
|
-
|
193
|
+
context_scope.respond_to?(name, include_private) ? true : super
|
192
194
|
end
|
193
195
|
|
194
196
|
# Supports calling helpers defined for the template scope using method_missing hook
|
195
197
|
def method_missing(name, *args, &block)
|
196
|
-
|
198
|
+
context_scope.respond_to?(name) ? context_scope.send(name, *args, &block) : super
|
197
199
|
end
|
198
200
|
|
199
201
|
def copy_instance_variables_from(object, exclude = []) #:nodoc:
|
@@ -210,6 +212,7 @@ module Rabl
|
|
210
212
|
@_options[:child] = []
|
211
213
|
@_options[:glue] = []
|
212
214
|
@_options[:extends] = []
|
215
|
+
@_options[:root_name] = nil
|
213
216
|
end
|
214
217
|
end
|
215
218
|
end
|
data/lib/rabl/helpers.rb
CHANGED
@@ -23,7 +23,8 @@ module Rabl
|
|
23
23
|
if data.respond_to?(:first)
|
24
24
|
data_name(data.first).to_s.pluralize if data.first.present?
|
25
25
|
else # actual data object
|
26
|
-
object_name =
|
26
|
+
object_name = object_root_name if object_root_name
|
27
|
+
object_name ||= collection_root_name.to_s.singularize if collection_root_name
|
27
28
|
object_name ||= data.class.respond_to?(:model_name) ? data.class.model_name.element : data.class.to_s.downcase
|
28
29
|
object_name
|
29
30
|
end
|
@@ -42,5 +43,25 @@ module Rabl
|
|
42
43
|
obj && data_object(obj).respond_to?(:each)
|
43
44
|
end
|
44
45
|
|
46
|
+
# Returns the scope wrapping this engine, used for retrieving data, invoking methods, etc
|
47
|
+
# In Rails, this is the controller and in Padrino this is the request context
|
48
|
+
def context_scope
|
49
|
+
defined?(@_scope) ? @_scope : nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the root (if any) name for an object within a collection
|
53
|
+
# Sets the name of the object i.e "person"
|
54
|
+
# => { "users" : [{ "person" : {} }] }
|
55
|
+
def object_root_name
|
56
|
+
defined?(@_object_root_name) ? @_object_root_name : nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the root for the collection
|
60
|
+
# Sets the name of the collection i.e "people"
|
61
|
+
# => { "people" : [] }
|
62
|
+
def collection_root_name
|
63
|
+
defined?(@_collection_name) ? @_collection_name : nil
|
64
|
+
end
|
65
|
+
|
45
66
|
end
|
46
67
|
end
|
data/lib/rabl/partials.rb
CHANGED
@@ -7,6 +7,7 @@ module Rabl
|
|
7
7
|
# options must have :object
|
8
8
|
# options can have :view_path, :child_root, :root
|
9
9
|
def partial(file, options={}, &block)
|
10
|
+
raise ArgumentError, "Must provide an :object option to render a partial" unless options[:object]
|
10
11
|
object, view_path = options.delete(:object), options.delete(:view_path)
|
11
12
|
source, location = self.fetch_source(file, :view_path => view_path)
|
12
13
|
engine_options = options.merge(:source => source, :source_location => location)
|
@@ -35,9 +36,16 @@ module Rabl
|
|
35
36
|
# Padrino chops the extension, stitch it back on
|
36
37
|
file_path = File.join(@_scope.settings.views, (file_path.to_s + ".rabl"))
|
37
38
|
elsif defined? Rails
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
if defined?(@_scope) && @_scope.respond_to?(:find_template)
|
40
|
+
# use Rails's own template resolution mechanism (partials and no partial)
|
41
|
+
lookup_proc = lambda { |partial| @_scope.find_template(file, [], partial) }
|
42
|
+
template = lookup_proc.call(false) rescue lookup_proc.call(true)
|
43
|
+
file_path = File.join(Rails.root.to_s, template.inspect) if template
|
44
|
+
else # fallback to manual
|
45
|
+
root_path = Rails.root
|
46
|
+
view_path = options[:view_path] || File.join(root_path, "app/views/")
|
47
|
+
file_path = Dir[File.join(view_path, file + ".{*.,}rabl")].first
|
48
|
+
end
|
41
49
|
elsif defined? Sinatra
|
42
50
|
view_path = options[:view_path] || @_scope.settings.views
|
43
51
|
file_path = Dir[File.join(view_path, file + ".{*.,}rabl")].first
|
data/lib/rabl/version.rb
CHANGED
data/test/builder_test.rb
CHANGED
@@ -28,7 +28,7 @@ context "Rabl::Builder" do
|
|
28
28
|
|
29
29
|
setup { builder({ :attributes => { :name => :name } }) }
|
30
30
|
asserts "that the object is set properly" do
|
31
|
-
topic.build(User.new, :
|
31
|
+
topic.build(User.new, :root_name => "user")
|
32
32
|
end.equivalent_to({ "user" => { :name => "rabl" } })
|
33
33
|
|
34
34
|
end
|
@@ -37,7 +37,7 @@ context "Rabl::Builder" do
|
|
37
37
|
|
38
38
|
setup { builder({ :attributes => { :name => :name } }) }
|
39
39
|
asserts "that the object is set properly" do
|
40
|
-
topic.build(
|
40
|
+
topic.build(User.new, :root_name => "person")
|
41
41
|
end.equivalent_to({ "person" => { :name => "rabl" } })
|
42
42
|
|
43
43
|
end
|
data/test/engine_test.rb
CHANGED
@@ -263,15 +263,32 @@ context "Rabl::Engine" do
|
|
263
263
|
template.render(scope)
|
264
264
|
end.equals "[{},{}]"
|
265
265
|
|
266
|
-
asserts "that it sets root node for objects" do
|
266
|
+
asserts "that it sets root node for objects using hash" do
|
267
267
|
template = rabl %{
|
268
|
-
collection @users => :
|
268
|
+
collection @users => :people
|
269
269
|
}
|
270
270
|
scope = Object.new
|
271
271
|
scope.instance_variable_set :@users, [User.new, User.new]
|
272
272
|
template.render(scope)
|
273
|
-
end.equals "{\"
|
273
|
+
end.equals "{\"people\":[{},{}]}"
|
274
274
|
|
275
|
+
asserts "that it sets root node for objects using root option" do
|
276
|
+
template = rabl %{
|
277
|
+
collection @users, :root => :people
|
278
|
+
}
|
279
|
+
scope = Object.new
|
280
|
+
scope.instance_variable_set :@users, [User.new, User.new]
|
281
|
+
template.render(scope)
|
282
|
+
end.equals "{\"people\":[{},{}]}"
|
283
|
+
|
284
|
+
asserts "that it sets root node for objects using object_root option" do
|
285
|
+
template = rabl %{
|
286
|
+
collection @users, :root => :humans, :object_root => :person
|
287
|
+
}
|
288
|
+
scope = Object.new
|
289
|
+
scope.instance_variable_set :@users, [User.new, User.new]
|
290
|
+
template.render(scope)
|
291
|
+
end.equals %Q^{"humans":[{"person":{}},{"person":{}}]}^
|
275
292
|
end
|
276
293
|
|
277
294
|
context "#attribute" do
|