jbuilder 0.4.3 → 0.5.0

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/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gemspec
3
+ gemspec
4
+
5
+ gem "actionpack"
@@ -1,20 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jbuilder (0.3)
4
+ jbuilder (0.4.1)
5
5
  activesupport (>= 3.0.0)
6
6
  blankslate (>= 2.1.2.4)
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
- activesupport (3.1.3)
11
+ actionpack (3.2.8)
12
+ activemodel (= 3.2.8)
13
+ activesupport (= 3.2.8)
14
+ builder (~> 3.0.0)
15
+ erubis (~> 2.7.0)
16
+ journey (~> 1.0.4)
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.8)
22
+ activesupport (= 3.2.8)
23
+ builder (~> 3.0.0)
24
+ activesupport (3.2.8)
25
+ i18n (~> 0.6)
12
26
  multi_json (~> 1.0)
13
27
  blankslate (2.1.2.4)
14
- multi_json (1.0.4)
28
+ builder (3.0.0)
29
+ erubis (2.7.0)
30
+ hike (1.2.1)
31
+ i18n (0.6.1)
32
+ journey (1.0.4)
33
+ multi_json (1.3.6)
34
+ rack (1.4.1)
35
+ rack-cache (1.2)
36
+ rack (>= 0.4)
37
+ rack-test (0.6.1)
38
+ rack (>= 1.0)
39
+ sprockets (2.1.3)
40
+ hike (~> 1.2)
41
+ rack (~> 1.0)
42
+ tilt (~> 1.1, != 1.3.0)
43
+ tilt (1.3.3)
15
44
 
16
45
  PLATFORMS
17
46
  ruby
18
47
 
19
48
  DEPENDENCIES
49
+ actionpack
20
50
  jbuilder!
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'jbuilder'
3
- s.version = '0.4.3'
3
+ s.version = '0.5.0'
4
4
  s.author = 'David Heinemeier Hansson'
5
5
  s.email = 'david@37signals.com'
6
6
  s.summary = 'Create JSON structures via a Builder-style DSL'
@@ -5,21 +5,50 @@ require 'active_support/core_ext/enumerable'
5
5
  require 'active_support/json'
6
6
  require 'multi_json'
7
7
  class Jbuilder < BlankSlate
8
+ class KeyFormatter
9
+ def initialize(*args)
10
+ @format = {}
11
+ @cache = {}
12
+
13
+ options = args.extract_options!
14
+ args.each do |name|
15
+ @format[name] = []
16
+ end
17
+ options.each do |name, paramaters|
18
+ @format[name] = paramaters
19
+ end
20
+ end
21
+
22
+ def initialize_copy(original)
23
+ @cache = {}
24
+ end
25
+
26
+ def format(key)
27
+ @cache[key] ||= @format.inject(key.to_s) do |result, args|
28
+ func, args = args
29
+ if func.is_a? Proc
30
+ func.call(result, *args)
31
+ else
32
+ result.send(func, *args)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
8
38
  # Yields a builder and automatically turns the result into a JSON string
9
39
  def self.encode
10
40
  new._tap { |jbuilder| yield jbuilder }.target!
11
41
  end
12
42
 
13
- @@key_format = {}
43
+ @@key_formatter = KeyFormatter.new
14
44
 
15
45
  define_method(:__class__, find_hidden_method(:class))
16
46
  define_method(:_tap, find_hidden_method(:tap))
17
- define_method(:_is_a?, find_hidden_method(:is_a?))
18
47
  reveal(:respond_to?)
19
48
 
20
- def initialize(key_format = @@key_format.clone)
49
+ def initialize(key_formatter = @@key_formatter.clone)
21
50
  @attributes = ActiveSupport::OrderedHash.new
22
- @key_format = key_format
51
+ @key_formatter = key_formatter
23
52
  end
24
53
 
25
54
  # Dynamically set a key value pair.
@@ -42,7 +71,7 @@ class Jbuilder < BlankSlate
42
71
  if block_given?
43
72
  _yield_nesting(key) { |jbuilder| yield jbuilder }
44
73
  else
45
- @attributes[_format_key(key)] = value
74
+ _set_value(key, value)
46
75
  end
47
76
  end
48
77
 
@@ -75,12 +104,12 @@ class Jbuilder < BlankSlate
75
104
  # { "_first_name": "David" }
76
105
  #
77
106
  def key_format!(*args)
78
- __class__.extract_key_format(args, @key_format)
107
+ @key_formatter = KeyFormatter.new(*args)
79
108
  end
80
109
 
81
110
  # Same as the instance method key_format! except sets the default.
82
111
  def self.key_format(*args)
83
- extract_key_format(args, @@key_format)
112
+ @@key_formatter = KeyFormatter.new(*args)
84
113
  end
85
114
 
86
115
  # Turns the current element into an array and yields a builder to add a hash.
@@ -129,19 +158,22 @@ class Jbuilder < BlankSlate
129
158
  #
130
159
  # { "people": [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ] }
131
160
  def array!(collection)
132
- @attributes = [] and return if collection.empty?
133
-
134
- collection.each do |element|
135
- child! do |child|
136
- yield child, element
137
- end
161
+ @attributes = []
162
+ collection.each do |element| #[] and return if collection.empty?
163
+ @attributes << _new_instance._tap { |jbuilder| yield jbuilder, element }.attributes!
138
164
  end
139
165
  end
140
166
 
141
- # Extracts the mentioned attributes from the passed object and turns them into attributes of the JSON.
167
+ # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.
142
168
  #
143
169
  # Example:
144
170
  #
171
+ # @person = Struct.new(:name, :age).new("David", 32)
172
+ #
173
+ # or you can utilize a Hash
174
+ #
175
+ # @person = {:name => "David", :age => 32}
176
+ #
145
177
  # json.extract! @person, :name, :age
146
178
  #
147
179
  # { "name": David", "age": 32 }, { "name": Jamie", "age": 31 }
@@ -150,18 +182,19 @@ class Jbuilder < BlankSlate
150
182
  #
151
183
  # json.(@person, :name, :age)
152
184
  def extract!(object, *attributes)
153
- attributes.each do |attribute|
154
- __send__ attribute, object.send(attribute)
185
+ if object.is_a?(Hash)
186
+ attributes.each {|attribute| _set_value attribute, object.send(:fetch, attribute)}
187
+ else
188
+ attributes.each {|attribute| _set_value attribute, object.send(attribute)}
155
189
  end
156
190
  end
157
191
 
158
192
  if RUBY_VERSION > '1.9'
159
- def call(*args)
160
- case
161
- when args.one?
162
- array!(args.first) { |json, element| yield json, element }
163
- when args.many?
164
- extract!(*args)
193
+ def call(object = nil, *attributes)
194
+ if attributes.empty?
195
+ array!(object) { |json, element| yield json, element }
196
+ else
197
+ extract!(object, *attributes)
165
198
  end
166
199
  end
167
200
  end
@@ -177,67 +210,71 @@ class Jbuilder < BlankSlate
177
210
  end
178
211
 
179
212
 
180
- private
181
- def method_missing(method, *args)
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
-
189
- # json.comments @post.comments { |json, comment| ... }
190
- # { "comments": [ { ... }, { ... } ] }
191
- when args.one? && block_given?
192
- _yield_iteration(method, args.first) { |child, element| yield child, element }
193
-
194
- # json.age 32
195
- # { "age": 32 }
196
- when args.length == 1
197
- set! method, args.first
198
-
199
- # json.comments { |json| ... }
200
- # { "comments": ... }
201
- when args.empty? && block_given?
202
- _yield_nesting(method) { |jbuilder| yield jbuilder }
213
+ protected
214
+ def _set_value(key, value)
215
+ @attributes[@key_formatter.format(key)] = value
216
+ end
203
217
 
204
- # json.comments(@post.comments, :content, :created_at)
205
- # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
206
- when args.many? && args.first.respond_to?(:each)
207
- _inline_nesting method, args.first, args.from(1)
208
218
 
209
- # json.author @post.creator, :name, :email_address
210
- # { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
211
- when args.many?
212
- _inline_extract method, args.first, args.from(1)
219
+ private
220
+ def method_missing(method, value = nil, *args)
221
+ if block_given?
222
+ if value
223
+ # json.comments @post.comments { |json, comment| ... }
224
+ # { "comments": [ { ... }, { ... } ] }
225
+ _yield_iteration(method, value) { |child, element| yield child, element }
226
+ else
227
+ # json.comments { |json| ... }
228
+ # { "comments": ... }
229
+ _yield_nesting(method) { |jbuilder| yield jbuilder }
230
+ end
231
+ else
232
+ if args.empty?
233
+ if Jbuilder === value
234
+ # json.age 32
235
+ # json.person another_jbuilder
236
+ # { "age": 32, "person": { ... }
237
+ _set_value method, value.attributes!
238
+ else
239
+ # json.age 32
240
+ # { "age": 32 }
241
+ _set_value method, value
242
+ end
243
+ else
244
+ if value.respond_to?(:each)
245
+ # json.comments(@post.comments, :content, :created_at)
246
+ # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
247
+ _inline_nesting method, value, args
248
+ else
249
+ # json.author @post.creator, :name, :email_address
250
+ # { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
251
+ _inline_extract method, value, args
252
+ end
253
+ end
213
254
  end
214
255
  end
215
256
 
216
257
  # Overwrite in subclasses if you need to add initialization values
217
258
  def _new_instance
218
- __class__.new(@key_format)
259
+ __class__.new(@key_formatter)
219
260
  end
220
261
 
221
262
  def _yield_nesting(container)
222
- set! container, _new_instance._tap { |jbuilder| yield jbuilder }.attributes!
263
+ _set_value container, _new_instance._tap { |jbuilder| yield jbuilder }.attributes!
223
264
  end
224
265
 
225
266
  def _inline_nesting(container, collection, attributes)
226
- __send__(container) do |parent|
227
- parent.array!(collection) and return if collection.empty?
228
-
229
- collection.each do |element|
230
- parent.child! do |child|
231
- attributes.each do |attribute|
232
- child.__send__ attribute, element.send(attribute)
233
- end
267
+ _yield_nesting(container) do |parent|
268
+ parent.array!(collection) do |child, element|
269
+ attributes.each do |attribute|
270
+ child._set_value attribute, element.send(attribute)
234
271
  end
235
272
  end
236
273
  end
237
274
  end
238
275
 
239
276
  def _yield_iteration(container, collection)
240
- __send__(container) do |parent|
277
+ _yield_nesting(container) do |parent|
241
278
  parent.array!(collection) do |child, element|
242
279
  yield child, element
243
280
  end
@@ -245,29 +282,7 @@ class Jbuilder < BlankSlate
245
282
  end
246
283
 
247
284
  def _inline_extract(container, record, attributes)
248
- __send__(container) { |parent| parent.extract! record, *attributes }
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
285
+ _yield_nesting(container) { |parent| parent.extract! record, *attributes }
271
286
  end
272
287
  end
273
288
 
@@ -3,9 +3,9 @@ class JbuilderTemplate < Jbuilder
3
3
  new(context)._tap { |jbuilder| yield jbuilder }.target!
4
4
  end
5
5
 
6
- def initialize(context)
6
+ def initialize(context, *args)
7
7
  @context = context
8
- super()
8
+ super(*args)
9
9
  end
10
10
 
11
11
  def partial!(options, locals = {})
@@ -21,7 +21,7 @@ class JbuilderTemplate < Jbuilder
21
21
 
22
22
  private
23
23
  def _new_instance
24
- __class__.new(@context)
24
+ __class__.new(@context, @key_formatter)
25
25
  end
26
26
  end
27
27
 
@@ -0,0 +1,39 @@
1
+ require 'test/unit'
2
+ require 'active_support/test_case'
3
+ require 'active_support/inflector'
4
+ require 'action_dispatch'
5
+ require 'action_view'
6
+
7
+ require 'jbuilder'
8
+ require 'jbuilder_template'
9
+
10
+ class JbuilderTemplateTest < ActiveSupport::TestCase
11
+ test "rendering" do
12
+ json = JbuilderTemplate.encode(binding) do |json|
13
+ json.content "hello"
14
+ end
15
+
16
+ assert_equal "hello", JSON.parse(json)["content"]
17
+ end
18
+
19
+ test "key_format! with parameter" do
20
+ json = JbuilderTemplate.new(binding)
21
+ json.key_format! :camelize => [:lower]
22
+ json.camel_style "for JS"
23
+
24
+ assert_equal ['camelStyle'], json.attributes!.keys
25
+ end
26
+
27
+ test "key_format! propagates to child elements" do
28
+ json = JbuilderTemplate.new(binding)
29
+ json.key_format! :upcase
30
+ json.level1 "one"
31
+ json.level2 do |json|
32
+ json.value "two"
33
+ end
34
+
35
+ result = json.attributes!
36
+ assert_equal "one", result["LEVEL1"]
37
+ assert_equal "two", result["LEVEL2"]["VALUE"]
38
+ end
39
+ end
@@ -67,7 +67,20 @@ class JbuilderTest < ActiveSupport::TestCase
67
67
  assert_equal 32, parsed["age"]
68
68
  end
69
69
  end
70
-
70
+
71
+ test "extracting from hash" do
72
+ person = {:name => "Jim", :age => 34}
73
+
74
+ json = Jbuilder.encode do |json|
75
+ json.extract! person, :name, :age
76
+ end
77
+
78
+ JSON.parse(json).tap do |parsed|
79
+ assert_equal "Jim", parsed["name"]
80
+ assert_equal 34, parsed["age"]
81
+ end
82
+ end
83
+
71
84
  test "nesting single child with block" do
72
85
  json = Jbuilder.encode do |json|
73
86
  json.author do |json|
@@ -356,6 +369,13 @@ class JbuilderTest < ActiveSupport::TestCase
356
369
  json.camel_style "for JS"
357
370
 
358
371
  assert_equal ['camelStyle'], json.attributes!.keys
359
- Jbuilder.class_variable_set("@@key_format", {})
372
+ Jbuilder.class_variable_set("@@key_formatter", Jbuilder::KeyFormatter.new)
373
+ end
374
+
375
+ test "don't use default key formatter directly" do
376
+ json = Jbuilder.new
377
+ json.key "value"
378
+
379
+ assert_equal [], Jbuilder.class_variable_get("@@key_formatter").instance_variable_get("@cache").keys
360
380
  end
361
381
  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.3
4
+ version: 0.5.0
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-08-08 00:00:00.000000000 Z
12
+ date: 2012-09-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &2160638200 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 3.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2160638200
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: blankslate
27
- requirement: &2160637580 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,7 +37,12 @@ dependencies:
32
37
  version: 2.1.2.4
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *2160637580
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.1.2.4
36
46
  description:
37
47
  email: david@37signals.com
38
48
  executables: []
@@ -41,12 +51,12 @@ extra_rdoc_files: []
41
51
  files:
42
52
  - ./Gemfile
43
53
  - ./Gemfile.lock
44
- - ./jbuilder-0.4.2.gem
45
54
  - ./jbuilder.gemspec
46
55
  - ./lib/jbuilder.rb
47
56
  - ./lib/jbuilder_template.rb
48
57
  - ./MIT-LICENSE
49
58
  - ./README.md
59
+ - ./test/jbuilder_template_test.rb
50
60
  - ./test/jbuilder_test.rb
51
61
  homepage:
52
62
  licenses: []
@@ -68,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
78
  version: '0'
69
79
  requirements: []
70
80
  rubyforge_project:
71
- rubygems_version: 1.8.7
81
+ rubygems_version: 1.8.23
72
82
  signing_key:
73
83
  specification_version: 3
74
84
  summary: Create JSON structures via a Builder-style DSL
Binary file