jbuilder 2.6.4 → 2.11.1

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.
@@ -73,8 +73,8 @@ class JbuilderTemplate < Jbuilder
73
73
  # json.cache_if! !admin?, @person, expires_in: 10.minutes do
74
74
  # json.extract! @person, :name, :age
75
75
  # end
76
- def cache_if!(condition, *args)
77
- condition ? cache!(*args, &::Proc.new) : yield
76
+ def cache_if!(condition, *args, &block)
77
+ condition ? cache!(*args, &block) : yield
78
78
  end
79
79
 
80
80
  def target!
@@ -104,7 +104,7 @@ class JbuilderTemplate < Jbuilder
104
104
  private
105
105
 
106
106
  def _render_partial_with_options(options)
107
- options.reverse_merge! locals: {}
107
+ options.reverse_merge! locals: options.except(:partial, :as, :collection)
108
108
  options.reverse_merge! ::JbuilderTemplate.template_lookup_options
109
109
  as = options[:as]
110
110
 
@@ -151,8 +151,8 @@ class JbuilderTemplate < Jbuilder
151
151
  name_options = options.slice(:skip_digest, :virtual_path)
152
152
  key = _fragment_name_with_digest(key, name_options)
153
153
 
154
- if @context.respond_to?(:fragment_cache_key)
155
- key = @context.fragment_cache_key(key)
154
+ if @context.respond_to?(:combined_fragment_cache_key)
155
+ key = @context.combined_fragment_cache_key(key)
156
156
  else
157
157
  key = url_for(key).split('://', 2).last if ::Hash === key
158
158
  end
@@ -164,7 +164,7 @@ class JbuilderTemplate < Jbuilder
164
164
  if @context.respond_to?(:cache_fragment_name)
165
165
  # Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
166
166
  # should be used instead.
167
- @context.cache_fragment_name(key, options)
167
+ @context.cache_fragment_name(key, **options)
168
168
  elsif @context.respond_to?(:fragment_name_with_digest)
169
169
  # Backwards compatibility for period of time when fragment_name_with_digest was made public.
170
170
  @context.fragment_name_with_digest(key)
@@ -188,7 +188,7 @@ class JbuilderTemplate < Jbuilder
188
188
  _scope{ _render_partial_with_options options.merge(collection: object) }
189
189
  else
190
190
  locals = ::Hash[options[:as], object]
191
- _scope{ _render_partial options.merge(locals: locals) }
191
+ _scope{ _render_partial_with_options options.merge(locals: locals) }
192
192
  end
193
193
 
194
194
  set! name, value
@@ -222,11 +222,12 @@ end
222
222
 
223
223
  class JbuilderHandler
224
224
  cattr_accessor :default_format
225
- self.default_format = Mime[:json]
225
+ self.default_format = :json
226
226
 
227
- def self.call(template)
227
+ def self.call(template, source = nil)
228
+ source ||= template.source
228
229
  # this juggling is required to keep line numbers right in the error
229
- %{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{template.source}
230
+ %{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{source}
230
231
  json.target! unless (__already_defined && __already_defined != "method")}
231
232
  end
232
233
  end
data/lib/jbuilder.rb CHANGED
@@ -2,8 +2,9 @@ require 'jbuilder/jbuilder'
2
2
  require 'jbuilder/blank'
3
3
  require 'jbuilder/key_formatter'
4
4
  require 'jbuilder/errors'
5
- require 'multi_json'
5
+ require 'json'
6
6
  require 'ostruct'
7
+ require 'active_support/core_ext/hash/deep_merge'
7
8
 
8
9
  class Jbuilder
9
10
  @@key_formatter = nil
@@ -26,12 +27,12 @@ class Jbuilder
26
27
  BLANK = Blank.new
27
28
  NON_ENUMERABLES = [ ::Struct, ::OpenStruct ].to_set
28
29
 
29
- def set!(key, value = BLANK, *args)
30
+ def set!(key, value = BLANK, *args, &block)
30
31
  result = if ::Kernel.block_given?
31
32
  if !_blank?(value)
32
33
  # json.comments @post.comments { |comment| ... }
33
34
  # { "comments": [ { ... }, { ... } ] }
34
- _scope{ array! value, &::Proc.new }
35
+ _scope{ array! value, &block }
35
36
  else
36
37
  # json.comments { ... }
37
38
  # { "comments": ... }
@@ -42,11 +43,11 @@ class Jbuilder
42
43
  # json.age 32
43
44
  # json.person another_jbuilder
44
45
  # { "age": 32, "person": { ... }
45
- value.attributes!
46
+ _format_keys(value.attributes!)
46
47
  else
47
48
  # json.age 32
48
49
  # { "age": 32 }
49
- value
50
+ _format_keys(value)
50
51
  end
51
52
  elsif _is_collection?(value)
52
53
  # json.comments @post.comments, :content, :created_at
@@ -61,9 +62,9 @@ class Jbuilder
61
62
  _set_value key, result
62
63
  end
63
64
 
64
- def method_missing(*args)
65
+ def method_missing(*args, &block)
65
66
  if ::Kernel.block_given?
66
- set!(*args, &::Proc.new)
67
+ set!(*args, &block)
67
68
  else
68
69
  set!(*args)
69
70
  end
@@ -163,7 +164,7 @@ class Jbuilder
163
164
  #
164
165
  # [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ]
165
166
  #
166
- # If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call:
167
+ # You can use the call syntax instead of an explicit extract! call:
167
168
  #
168
169
  # json.(@people) { |person| ... }
169
170
  #
@@ -181,11 +182,11 @@ class Jbuilder
181
182
  # json.array! [1, 2, 3]
182
183
  #
183
184
  # [1,2,3]
184
- def array!(collection = [], *attributes)
185
+ def array!(collection = [], *attributes, &block)
185
186
  array = if collection.nil?
186
187
  []
187
188
  elsif ::Kernel.block_given?
188
- _map_collection(collection, &::Proc.new)
189
+ _map_collection(collection, &block)
189
190
  elsif attributes.any?
190
191
  _map_collection(collection) { |element| extract! element, *attributes }
191
192
  else
@@ -220,9 +221,9 @@ class Jbuilder
220
221
  end
221
222
  end
222
223
 
223
- def call(object, *attributes)
224
+ def call(object, *attributes, &block)
224
225
  if ::Kernel.block_given?
225
- array! object, &::Proc.new
226
+ array! object, &block
226
227
  else
227
228
  extract! object, *attributes
228
229
  end
@@ -240,14 +241,15 @@ class Jbuilder
240
241
  @attributes
241
242
  end
242
243
 
243
- # Merges hash or array into current builder.
244
- def merge!(hash_or_array)
244
+ # Merges hash, array, or Jbuilder instance into current builder.
245
+ def merge!(object)
246
+ hash_or_array = ::Jbuilder === object ? object.attributes! : object
245
247
  @attributes = _merge_values(@attributes, hash_or_array)
246
248
  end
247
249
 
248
250
  # Encodes the current builder as JSON.
249
251
  def target!
250
- ::MultiJson.dump(@attributes)
252
+ @attributes.to_json
251
253
  end
252
254
 
253
255
  private
@@ -271,11 +273,11 @@ class Jbuilder
271
273
  if _blank?(updates)
272
274
  current_value
273
275
  elsif _blank?(current_value) || updates.nil? || current_value.empty? && ::Array === updates
274
- updates
276
+ _format_keys(updates)
275
277
  elsif ::Array === current_value && ::Array === updates
276
- current_value + updates
278
+ current_value + _format_keys(updates)
277
279
  elsif ::Hash === current_value && ::Hash === updates
278
- current_value.merge(updates)
280
+ current_value.deep_merge(_format_keys(updates))
279
281
  else
280
282
  raise MergeError.build(current_value, updates)
281
283
  end
@@ -285,6 +287,16 @@ class Jbuilder
285
287
  @key_formatter ? @key_formatter.format(key) : key.to_s
286
288
  end
287
289
 
290
+ def _format_keys(hash_or_array)
291
+ if ::Array === hash_or_array
292
+ hash_or_array.map { |value| _format_keys(value) }
293
+ elsif ::Hash === hash_or_array
294
+ ::Hash[hash_or_array.collect { |k, v| [_key(k), _format_keys(v)] }]
295
+ else
296
+ hash_or_array
297
+ end
298
+ end
299
+
288
300
  def _set_value(key, value)
289
301
  raise NullError.build(key) if @attributes.nil?
290
302
  raise ArrayError.build(key) if ::Array === @attributes
@@ -53,7 +53,7 @@ class JbuilderDependencyTrackerTest < ActiveSupport::TestCase
53
53
  assert_equal %w[path/to/partial], dependencies
54
54
  end
55
55
 
56
- test 'detects partial in indirect collecton calls' do
56
+ test 'detects partial in indirect collection calls' do
57
57
  dependencies = track_dependencies <<-RUBY
58
58
  json.comments @post.comments, partial: 'comments/comment', as: :comment
59
59
  RUBY
@@ -21,18 +21,38 @@ class JbuilderGeneratorTest < Rails::Generators::TestCase
21
21
  run_generator
22
22
 
23
23
  assert_file 'app/views/posts/index.json.jbuilder' do |content|
24
- assert_match %r{json.array! @posts, partial: 'posts/post', as: :post}, content
24
+ assert_match %r{json\.array! @posts, partial: "posts/post", as: :post}, content
25
25
  end
26
26
 
27
27
  assert_file 'app/views/posts/show.json.jbuilder' do |content|
28
- assert_match %r{json.partial! \"posts/post\", post: @post}, content
28
+ assert_match %r{json\.partial! "posts/post", post: @post}, content
29
29
  end
30
-
31
- assert_file 'app/views/posts/_post.json.jbuilder' do |content|
30
+
31
+ assert_file 'app/views/posts/_post.json.jbuilder' do |content|
32
32
  assert_match %r{json\.extract! post, :id, :title, :body}, content
33
+ assert_match %r{:created_at, :updated_at}, content
33
34
  assert_match %r{json\.url post_url\(post, format: :json\)}, content
34
35
  end
35
-
36
+ end
37
+
38
+ test 'timestamps are not generated in partial with --no-timestamps' do
39
+ run_generator %w(Post title body:text --no-timestamps)
40
+
41
+ assert_file 'app/views/posts/_post.json.jbuilder' do |content|
42
+ assert_match %r{json\.extract! post, :id, :title, :body$}, content
43
+ assert_no_match %r{:created_at, :updated_at}, content
44
+ end
45
+ end
36
46
 
47
+ if Rails::VERSION::MAJOR >= 6
48
+ test 'handles virtual attributes' do
49
+ run_generator %w(Message content:rich_text video:attachment photos:attachments)
50
+
51
+ assert_file 'app/views/messages/_message.json.jbuilder' do |content|
52
+ assert_match %r{json\.content message\.content\.to_s}, content
53
+ assert_match %r{json\.video url_for\(message\.video\)}, content
54
+ assert_match %r{json\.photos do\n json\.array!\(message\.photos\) do \|photo\|\n json\.id photo\.id\n json\.url url_for\(photo\)\n end\nend}, content
55
+ end
56
+ end
37
57
  end
38
58
  end