jbuilder 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +44 -1
- data/jbuilder.gemspec +1 -1
- data/lib/jbuilder.rb +96 -7
- data/lib/jbuilder_template.rb +15 -13
- data/test/jbuilder_test.rb +90 -1
- metadata +7 -10
- data/jbuilder-0.3.1.gem +0 -0
- data/jbuilder-0.3.2.gem +0 -0
- data/jbuilder-0.3.gem +0 -0
data/README.md
CHANGED
@@ -66,6 +66,34 @@ end
|
|
66
66
|
# => [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ]
|
67
67
|
```
|
68
68
|
|
69
|
+
Jbuilder objects can be directly nested inside each other. Useful for composing objects.
|
70
|
+
|
71
|
+
``` ruby
|
72
|
+
class Person
|
73
|
+
# ... Class Definition ... #
|
74
|
+
def to_builder
|
75
|
+
person = Jbuilder.new
|
76
|
+
person.(self, :name, :age)
|
77
|
+
person
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Company
|
82
|
+
# ... Class Definition ... #
|
83
|
+
def to_builder
|
84
|
+
company = Jbuilder.new
|
85
|
+
company.name name
|
86
|
+
company.president president.to_builder
|
87
|
+
company
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
company = Company.new("Doodle Corp", Person.new("John Stobs", 58))
|
92
|
+
company.to_builder.target!
|
93
|
+
|
94
|
+
# => {"name":"Doodle Corp","president":{"name":"John Stobs","age":58}}
|
95
|
+
```
|
96
|
+
|
69
97
|
You can either use Jbuilder stand-alone or directly as an ActionView template language. When required in Rails, you can create views ala show.json.jbuilder (the json is already yielded):
|
70
98
|
|
71
99
|
``` ruby
|
@@ -90,10 +118,25 @@ end
|
|
90
118
|
json.partial! "api/comments/comments", comments: @message.comments
|
91
119
|
```
|
92
120
|
|
121
|
+
Keys can be auto formatted using `key_format!`, this can be used to convert keynames from the standard ruby_format to CamelCase:
|
122
|
+
|
123
|
+
``` ruby
|
124
|
+
json.key_format! :camelize => :lower
|
125
|
+
json.first_name "David"
|
126
|
+
|
127
|
+
# { "firstName": "David" }
|
128
|
+
```
|
129
|
+
|
130
|
+
You can set this globaly with the class method `key_format` (from inside your enviorment.rb for example):
|
131
|
+
|
132
|
+
``` ruby
|
133
|
+
Jbuilder.key_format :camelize => :lower
|
134
|
+
```
|
135
|
+
|
93
136
|
Libraries similar to this in some form or another include:
|
94
137
|
|
95
138
|
* RABL: https://github.com/nesquena/rabl
|
96
139
|
* JsonBuilder: https://github.com/nov/jsonbuilder
|
97
140
|
* JSON Builder: https://github.com/dewski/json_builder
|
98
141
|
* Jsonify: https://github.com/bsiggelkow/jsonify
|
99
|
-
* RepresentationView: https://github.com/mdub/representative_view
|
142
|
+
* RepresentationView: https://github.com/mdub/representative_view
|
data/jbuilder.gemspec
CHANGED
data/lib/jbuilder.rb
CHANGED
@@ -3,23 +3,84 @@ require 'active_support/ordered_hash'
|
|
3
3
|
require 'active_support/core_ext/array/access'
|
4
4
|
require 'active_support/core_ext/enumerable'
|
5
5
|
require 'active_support/json'
|
6
|
-
|
6
|
+
require 'multi_json'
|
7
7
|
class Jbuilder < BlankSlate
|
8
8
|
# Yields a builder and automatically turns the result into a JSON string
|
9
9
|
def self.encode
|
10
10
|
new._tap { |jbuilder| yield jbuilder }.target!
|
11
11
|
end
|
12
12
|
|
13
|
+
@@key_format = {}
|
14
|
+
|
13
15
|
define_method(:__class__, find_hidden_method(:class))
|
14
16
|
define_method(:_tap, find_hidden_method(:tap))
|
17
|
+
define_method(:_is_a?, find_hidden_method(:is_a?))
|
18
|
+
reveal(:respond_to?)
|
15
19
|
|
16
|
-
def initialize
|
20
|
+
def initialize(key_format = @@key_format.clone)
|
17
21
|
@attributes = ActiveSupport::OrderedHash.new
|
22
|
+
@key_format = key_format
|
18
23
|
end
|
19
24
|
|
20
25
|
# Dynamically set a key value pair.
|
21
|
-
|
22
|
-
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
#
|
29
|
+
# json.set!(:each, "stuff")
|
30
|
+
#
|
31
|
+
# { "each": "stuff" }
|
32
|
+
#
|
33
|
+
# You can also pass a block for nested attributes
|
34
|
+
#
|
35
|
+
# json.set!(:author) do |json|
|
36
|
+
# json.name "David"
|
37
|
+
# json.age 32
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# { "author": { "name": "David", "age": 32 } }
|
41
|
+
def set!(key, value = nil)
|
42
|
+
if block_given?
|
43
|
+
_yield_nesting(key) { |jbuilder| yield jbuilder }
|
44
|
+
else
|
45
|
+
@attributes[_format_key(key)] = value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Specifies formatting to be applied to the key. Passing in a name of a function
|
50
|
+
# will cause that function to be called on the key. So :upcase will upper case
|
51
|
+
# the key. You can also pass in lambdas for more complex transformations.
|
52
|
+
#
|
53
|
+
# Example:
|
54
|
+
#
|
55
|
+
# json.key_format! :upcase
|
56
|
+
# json.author do |json|
|
57
|
+
# json.name "David"
|
58
|
+
# json.age 32
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# { "AUTHOR": { "NAME": "David", "AGE": 32 } }
|
62
|
+
#
|
63
|
+
# You can pass parameters to the method using a hash pair.
|
64
|
+
#
|
65
|
+
# json.key_format! :camelize => :lower
|
66
|
+
# json.first_name "David"
|
67
|
+
#
|
68
|
+
# { "firstName": "David" }
|
69
|
+
#
|
70
|
+
# Lambdas can also be used.
|
71
|
+
#
|
72
|
+
# json.key_format! ->(key){ "_" + key }
|
73
|
+
# json.first_name "David"
|
74
|
+
#
|
75
|
+
# { "_first_name": "David" }
|
76
|
+
#
|
77
|
+
def key_format!(*args)
|
78
|
+
__class__.extract_key_format(args, @key_format)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Same as the instance method key_format! except sets the default.
|
82
|
+
def self.key_format(*args)
|
83
|
+
extract_key_format(args, @@key_format)
|
23
84
|
end
|
24
85
|
|
25
86
|
# Turns the current element into an array and yields a builder to add a hash.
|
@@ -112,13 +173,19 @@ class Jbuilder < BlankSlate
|
|
112
173
|
|
113
174
|
# Encodes the current builder as JSON.
|
114
175
|
def target!
|
115
|
-
|
176
|
+
MultiJson.encode @attributes
|
116
177
|
end
|
117
178
|
|
118
179
|
|
119
180
|
private
|
120
181
|
def method_missing(method, *args)
|
121
182
|
case
|
183
|
+
# json.age 32
|
184
|
+
# json.person another_jbuilder
|
185
|
+
# { "age": 32, "person": { ... }
|
186
|
+
when args.one? && args.first.respond_to?(:_is_a?) && args.first._is_a?(Jbuilder)
|
187
|
+
set! method, args.first.attributes!
|
188
|
+
|
122
189
|
# json.comments @post.comments { |json, comment| ... }
|
123
190
|
# { "comments": [ { ... }, { ... } ] }
|
124
191
|
when args.one? && block_given?
|
@@ -133,7 +200,7 @@ class Jbuilder < BlankSlate
|
|
133
200
|
# { "comments": ... }
|
134
201
|
when args.empty? && block_given?
|
135
202
|
_yield_nesting(method) { |jbuilder| yield jbuilder }
|
136
|
-
|
203
|
+
|
137
204
|
# json.comments(@post.comments, :content, :created_at)
|
138
205
|
# { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
|
139
206
|
when args.many? && args.first.is_a?(Enumerable)
|
@@ -148,7 +215,7 @@ class Jbuilder < BlankSlate
|
|
148
215
|
|
149
216
|
# Overwrite in subclasses if you need to add initialization values
|
150
217
|
def _new_instance
|
151
|
-
__class__.new
|
218
|
+
__class__.new(@key_format)
|
152
219
|
end
|
153
220
|
|
154
221
|
def _yield_nesting(container)
|
@@ -180,6 +247,28 @@ class Jbuilder < BlankSlate
|
|
180
247
|
def _inline_extract(container, record, attributes)
|
181
248
|
__send__(container) { |parent| parent.extract! record, *attributes }
|
182
249
|
end
|
250
|
+
|
251
|
+
# Format the key using the methods described in @key_format
|
252
|
+
def _format_key(key)
|
253
|
+
@key_format.inject(key.to_s) do |result, args|
|
254
|
+
func, args = args
|
255
|
+
if func.is_a? Proc
|
256
|
+
func.call(result, *args)
|
257
|
+
else
|
258
|
+
result.send(func, *args)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.extract_key_format(args, target)
|
264
|
+
options = args.extract_options!
|
265
|
+
args.each do |name|
|
266
|
+
target[name] = []
|
267
|
+
end
|
268
|
+
options.each do |name, paramaters|
|
269
|
+
target[name] = paramaters
|
270
|
+
end
|
271
|
+
end
|
183
272
|
end
|
184
273
|
|
185
274
|
require "jbuilder_template" if defined?(ActionView::Template)
|
data/lib/jbuilder_template.rb
CHANGED
@@ -7,11 +7,18 @@ class JbuilderTemplate < Jbuilder
|
|
7
7
|
@context = context
|
8
8
|
super()
|
9
9
|
end
|
10
|
-
|
11
|
-
def partial!(
|
12
|
-
|
10
|
+
|
11
|
+
def partial!(options, locals = {})
|
12
|
+
case options
|
13
|
+
when Hash
|
14
|
+
options[:locals] ||= {}
|
15
|
+
options[:locals].merge!(:json => self)
|
16
|
+
@context.render(options)
|
17
|
+
else
|
18
|
+
@context.render(options, locals.merge(:json => self))
|
19
|
+
end
|
13
20
|
end
|
14
|
-
|
21
|
+
|
15
22
|
private
|
16
23
|
def _new_instance
|
17
24
|
__class__.new(@context)
|
@@ -23,15 +30,10 @@ class JbuilderHandler
|
|
23
30
|
self.default_format = Mime::JSON
|
24
31
|
|
25
32
|
def self.call(template)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
JbuilderTemplate.encode(self) do |json|
|
31
|
-
#{template.source}
|
32
|
-
end
|
33
|
-
end
|
34
|
-
}
|
33
|
+
|
34
|
+
# this juggling is required to keep line numbers right in the error
|
35
|
+
%{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{template.source}
|
36
|
+
json.target! unless __already_defined}
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
data/test/jbuilder_test.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'active_support/test_case'
|
3
|
+
require 'active_support/inflector'
|
3
4
|
|
4
5
|
require 'jbuilder'
|
5
6
|
|
@@ -207,7 +208,19 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
207
208
|
|
208
209
|
assert_equal "david", JSON.parse(json)["comments"].first["authors"].first["name"]
|
209
210
|
end
|
210
|
-
|
211
|
+
|
212
|
+
test "nested jbuilder objects" do
|
213
|
+
to_nest = Jbuilder.new
|
214
|
+
to_nest.nested_value "Nested Test"
|
215
|
+
json = Jbuilder.encode do |json|
|
216
|
+
json.value "Test"
|
217
|
+
json.nested to_nest
|
218
|
+
end
|
219
|
+
parsed = JSON.parse(json)
|
220
|
+
assert_equal "Test", parsed['value']
|
221
|
+
assert_equal "Nested Test", parsed["nested"]["nested_value"]
|
222
|
+
end
|
223
|
+
|
211
224
|
test "top-level array" do
|
212
225
|
comments = [ Struct.new(:content, :id).new("hello", 1), Struct.new(:content, :id).new("world", 2) ]
|
213
226
|
|
@@ -242,4 +255,80 @@ class JbuilderTest < ActiveSupport::TestCase
|
|
242
255
|
|
243
256
|
assert_equal "stuff", JSON.parse(json)["each"]
|
244
257
|
end
|
258
|
+
|
259
|
+
test "dynamically set a key/nested child with block" do
|
260
|
+
json = Jbuilder.encode do |json|
|
261
|
+
json.set!(:author) do |json|
|
262
|
+
json.name "David"
|
263
|
+
json.age 32
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
JSON.parse(json).tap do |parsed|
|
268
|
+
assert_equal "David", parsed["author"]["name"]
|
269
|
+
assert_equal 32, parsed["author"]["age"]
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
test "key_format! with parameter" do
|
274
|
+
json = Jbuilder.new
|
275
|
+
json.key_format! :camelize => [:lower]
|
276
|
+
json.camel_style "for JS"
|
277
|
+
|
278
|
+
assert_equal ['camelStyle'], json.attributes!.keys
|
279
|
+
end
|
280
|
+
|
281
|
+
test "key_format! with parameter not as an array" do
|
282
|
+
json = Jbuilder.new
|
283
|
+
json.key_format! :camelize => :lower
|
284
|
+
json.camel_style "for JS"
|
285
|
+
|
286
|
+
assert_equal ['camelStyle'], json.attributes!.keys
|
287
|
+
end
|
288
|
+
|
289
|
+
test "key_format! propagates to child elements" do
|
290
|
+
json = Jbuilder.new
|
291
|
+
json.key_format! :upcase
|
292
|
+
json.level1 "one"
|
293
|
+
json.level2 do |json|
|
294
|
+
json.value "two"
|
295
|
+
end
|
296
|
+
|
297
|
+
result = json.attributes!
|
298
|
+
assert_equal "one", result["LEVEL1"]
|
299
|
+
assert_equal "two", result["LEVEL2"]["VALUE"]
|
300
|
+
end
|
301
|
+
|
302
|
+
test "key_format! with no parameter" do
|
303
|
+
json = Jbuilder.new
|
304
|
+
json.key_format! :upcase
|
305
|
+
json.lower "Value"
|
306
|
+
|
307
|
+
assert_equal ['LOWER'], json.attributes!.keys
|
308
|
+
end
|
309
|
+
|
310
|
+
test "key_format! with multiple steps" do
|
311
|
+
json = Jbuilder.new
|
312
|
+
json.key_format! :upcase, :pluralize
|
313
|
+
json.pill ""
|
314
|
+
|
315
|
+
assert_equal ["PILLs"], json.attributes!.keys
|
316
|
+
end
|
317
|
+
|
318
|
+
test "key_format! with lambda/proc" do
|
319
|
+
json = Jbuilder.new
|
320
|
+
json.key_format! ->(key){ key + " and friends" }
|
321
|
+
json.oats ""
|
322
|
+
|
323
|
+
assert_equal ["oats and friends"], json.attributes!.keys
|
324
|
+
end
|
325
|
+
|
326
|
+
test "default key_format!" do
|
327
|
+
Jbuilder.key_format :camelize => :lower
|
328
|
+
json = Jbuilder.new
|
329
|
+
json.camel_style "for JS"
|
330
|
+
|
331
|
+
assert_equal ['camelStyle'], json.attributes!.keys
|
332
|
+
Jbuilder.class_variable_set("@@key_format", {})
|
333
|
+
end
|
245
334
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jbuilder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152793760 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.0.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152793760
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: blankslate
|
27
|
-
requirement: &
|
27
|
+
requirement: &2152793020 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: 2.1.2.4
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2152793020
|
36
36
|
description:
|
37
37
|
email: david@37signals.com
|
38
38
|
executables: []
|
@@ -41,9 +41,6 @@ extra_rdoc_files: []
|
|
41
41
|
files:
|
42
42
|
- ./Gemfile
|
43
43
|
- ./Gemfile.lock
|
44
|
-
- ./jbuilder-0.3.1.gem
|
45
|
-
- ./jbuilder-0.3.2.gem
|
46
|
-
- ./jbuilder-0.3.gem
|
47
44
|
- ./jbuilder.gemspec
|
48
45
|
- ./lib/jbuilder.rb
|
49
46
|
- ./lib/jbuilder_template.rb
|
@@ -70,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
67
|
version: '0'
|
71
68
|
requirements: []
|
72
69
|
rubyforge_project:
|
73
|
-
rubygems_version: 1.8.
|
70
|
+
rubygems_version: 1.8.7
|
74
71
|
signing_key:
|
75
72
|
specification_version: 3
|
76
73
|
summary: Create JSON structures via a Builder-style DSL
|
data/jbuilder-0.3.1.gem
DELETED
Binary file
|
data/jbuilder-0.3.2.gem
DELETED
Binary file
|
data/jbuilder-0.3.gem
DELETED
Binary file
|