jbuilder 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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