ultra-pure-box 0.0.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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/jbuilder-2.15.1/Appraisals +26 -0
  3. data/jbuilder-2.15.1/CONTRIBUTING.md +100 -0
  4. data/jbuilder-2.15.1/Gemfile +9 -0
  5. data/jbuilder-2.15.1/MIT-LICENSE +20 -0
  6. data/jbuilder-2.15.1/README.md +394 -0
  7. data/jbuilder-2.15.1/Rakefile +21 -0
  8. data/jbuilder-2.15.1/bin/release +14 -0
  9. data/jbuilder-2.15.1/bin/test +6 -0
  10. data/jbuilder-2.15.1/gemfiles/rails_7_0.gemfile +11 -0
  11. data/jbuilder-2.15.1/gemfiles/rails_7_1.gemfile +10 -0
  12. data/jbuilder-2.15.1/gemfiles/rails_7_2.gemfile +10 -0
  13. data/jbuilder-2.15.1/gemfiles/rails_8_0.gemfile +10 -0
  14. data/jbuilder-2.15.1/gemfiles/rails_8_1.gemfile +10 -0
  15. data/jbuilder-2.15.1/gemfiles/rails_head.gemfile +10 -0
  16. data/jbuilder-2.15.1/jbuilder.gemspec +35 -0
  17. data/jbuilder-2.15.1/lib/generators/rails/jbuilder_generator.rb +65 -0
  18. data/jbuilder-2.15.1/lib/generators/rails/scaffold_controller_generator.rb +24 -0
  19. data/jbuilder-2.15.1/lib/generators/rails/templates/api_controller.rb +69 -0
  20. data/jbuilder-2.15.1/lib/generators/rails/templates/controller.rb +86 -0
  21. data/jbuilder-2.15.1/lib/generators/rails/templates/index.json.jbuilder +1 -0
  22. data/jbuilder-2.15.1/lib/generators/rails/templates/partial.json.jbuilder +16 -0
  23. data/jbuilder-2.15.1/lib/generators/rails/templates/show.json.jbuilder +1 -0
  24. data/jbuilder-2.15.1/lib/jbuilder/blank.rb +13 -0
  25. data/jbuilder-2.15.1/lib/jbuilder/collection_renderer.rb +58 -0
  26. data/jbuilder-2.15.1/lib/jbuilder/errors.rb +26 -0
  27. data/jbuilder-2.15.1/lib/jbuilder/jbuilder.rb +3 -0
  28. data/jbuilder-2.15.1/lib/jbuilder/jbuilder_dependency_tracker.rb +75 -0
  29. data/jbuilder-2.15.1/lib/jbuilder/jbuilder_template.rb +264 -0
  30. data/jbuilder-2.15.1/lib/jbuilder/key_formatter.rb +32 -0
  31. data/jbuilder-2.15.1/lib/jbuilder/railtie.rb +34 -0
  32. data/jbuilder-2.15.1/lib/jbuilder/version.rb +5 -0
  33. data/jbuilder-2.15.1/lib/jbuilder.rb +384 -0
  34. data/jbuilder-2.15.1/test/jbuilder_dependency_tracker_test.rb +71 -0
  35. data/jbuilder-2.15.1/test/jbuilder_generator_test.rb +68 -0
  36. data/jbuilder-2.15.1/test/jbuilder_template_test.rb +469 -0
  37. data/jbuilder-2.15.1/test/jbuilder_test.rb +954 -0
  38. data/jbuilder-2.15.1/test/scaffold_api_controller_generator_test.rb +83 -0
  39. data/jbuilder-2.15.1/test/scaffold_controller_generator_test.rb +116 -0
  40. data/jbuilder-2.15.1/test/test_helper.rb +47 -0
  41. data/ultra-pure-box.gemspec +12 -0
  42. metadata +81 -0
@@ -0,0 +1,384 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'jbuilder/jbuilder'
5
+ require 'jbuilder/blank'
6
+ require 'jbuilder/key_formatter'
7
+ require 'jbuilder/errors'
8
+ require 'json'
9
+ require 'active_support/core_ext/hash/deep_merge'
10
+ require 'active_support/core_ext/object/blank'
11
+
12
+ class Jbuilder
13
+ @@key_formatter = nil
14
+ @@ignore_nil = false
15
+ @@deep_format_keys = false
16
+
17
+ def initialize(
18
+ key_formatter: @@key_formatter,
19
+ ignore_nil: @@ignore_nil,
20
+ deep_format_keys: @@deep_format_keys,
21
+ &block
22
+ )
23
+ @attributes = {}
24
+ @key_formatter = key_formatter
25
+ @ignore_nil = ignore_nil
26
+ @deep_format_keys = deep_format_keys
27
+
28
+ yield self if block
29
+ end
30
+
31
+ # Yields a builder and automatically turns the result into a JSON string
32
+ def self.encode(...)
33
+ new(...).target!
34
+ end
35
+
36
+ BLANK = Blank.new.freeze
37
+ EMPTY_ARRAY = [].freeze
38
+ private_constant :BLANK, :EMPTY_ARRAY
39
+
40
+ def set!(key, value = BLANK, *args, &block)
41
+ _set(key, value, args, &block)
42
+ end
43
+
44
+ # Specifies formatting to be applied to the key. Passing in a name of a function
45
+ # will cause that function to be called on the key. So :upcase will upper case
46
+ # the key. You can also pass in lambdas for more complex transformations.
47
+ #
48
+ # Example:
49
+ #
50
+ # json.key_format! :upcase
51
+ # json.author do
52
+ # json.name "David"
53
+ # json.age 32
54
+ # end
55
+ #
56
+ # { "AUTHOR": { "NAME": "David", "AGE": 32 } }
57
+ #
58
+ # You can pass parameters to the method using a hash pair.
59
+ #
60
+ # json.key_format! camelize: :lower
61
+ # json.first_name "David"
62
+ #
63
+ # { "firstName": "David" }
64
+ #
65
+ # Lambdas can also be used.
66
+ #
67
+ # json.key_format! ->(key){ "_" + key }
68
+ # json.first_name "David"
69
+ #
70
+ # { "_first_name": "David" }
71
+ #
72
+ def key_format!(...)
73
+ @key_formatter = KeyFormatter.new(...)
74
+ end
75
+
76
+ # Same as the instance method key_format! except sets the default.
77
+ def self.key_format(...)
78
+ @@key_formatter = KeyFormatter.new(...)
79
+ end
80
+
81
+ # If you want to skip adding nil values to your JSON hash. This is useful
82
+ # for JSON clients that don't deal well with nil values, and would prefer
83
+ # not to receive keys which have null values.
84
+ #
85
+ # Example:
86
+ # json.ignore_nil! false
87
+ # json.id User.new.id
88
+ #
89
+ # { "id": null }
90
+ #
91
+ # json.ignore_nil!
92
+ # json.id User.new.id
93
+ #
94
+ # {}
95
+ #
96
+ def ignore_nil!(value = true)
97
+ @ignore_nil = value
98
+ end
99
+
100
+ # Same as instance method ignore_nil! except sets the default.
101
+ def self.ignore_nil(value = true)
102
+ @@ignore_nil = value
103
+ end
104
+
105
+ # Deeply apply key format to nested hashes and arrays passed to
106
+ # methods like set!, merge! or array!.
107
+ #
108
+ # Example:
109
+ #
110
+ # json.key_format! camelize: :lower
111
+ # json.settings({some_value: "abc"})
112
+ #
113
+ # { "settings": { "some_value": "abc" }}
114
+ #
115
+ # json.key_format! camelize: :lower
116
+ # json.deep_format_keys!
117
+ # json.settings({some_value: "abc"})
118
+ #
119
+ # { "settings": { "someValue": "abc" }}
120
+ #
121
+ def deep_format_keys!(value = true)
122
+ @deep_format_keys = value
123
+ end
124
+
125
+ # Same as instance method deep_format_keys! except sets the default.
126
+ def self.deep_format_keys(value = true)
127
+ @@deep_format_keys = value
128
+ end
129
+
130
+ # Turns the current element into an array and yields a builder to add a hash.
131
+ #
132
+ # Example:
133
+ #
134
+ # json.comments do
135
+ # json.child! { json.content "hello" }
136
+ # json.child! { json.content "world" }
137
+ # end
138
+ #
139
+ # { "comments": [ { "content": "hello" }, { "content": "world" } ]}
140
+ #
141
+ # More commonly, you'd use the combined iterator, though:
142
+ #
143
+ # json.comments(@post.comments) do |comment|
144
+ # json.content comment.formatted_content
145
+ # end
146
+ def child!
147
+ @attributes = [] unless ::Array === @attributes
148
+ @attributes << _scope{ yield self }
149
+ end
150
+
151
+ # Turns the current element into an array and iterates over the passed collection, adding each iteration as
152
+ # an element of the resulting array.
153
+ #
154
+ # Example:
155
+ #
156
+ # json.array!(@people) do |person|
157
+ # json.name person.name
158
+ # json.age calculate_age(person.birthday)
159
+ # end
160
+ #
161
+ # [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ]
162
+ #
163
+ # You can use the call syntax instead of an explicit extract! call:
164
+ #
165
+ # json.(@people) { |person| ... }
166
+ #
167
+ # It's generally only needed to use this method for top-level arrays. If you have named arrays, you can do:
168
+ #
169
+ # json.people(@people) do |person|
170
+ # json.name person.name
171
+ # json.age calculate_age(person.birthday)
172
+ # end
173
+ #
174
+ # { "people": [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ] }
175
+ #
176
+ # If you omit the block then you can set the top level array directly:
177
+ #
178
+ # json.array! [1, 2, 3]
179
+ #
180
+ # [1,2,3]
181
+ def array!(collection = EMPTY_ARRAY, *attributes, &block)
182
+ _array collection, attributes, &block
183
+ end
184
+
185
+ # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.
186
+ #
187
+ # Example:
188
+ #
189
+ # @person = Struct.new(:name, :age).new('David', 32)
190
+ #
191
+ # or you can utilize a Hash
192
+ #
193
+ # @person = { name: 'David', age: 32 }
194
+ #
195
+ # json.extract! @person, :name, :age
196
+ #
197
+ # { "name": David", "age": 32 }, { "name": Jamie", "age": 31 }
198
+ #
199
+ # You can also use the call syntax instead of an explicit extract! call:
200
+ #
201
+ # json.(@person, :name, :age)
202
+ def extract!(object, *attributes)
203
+ _extract object, attributes
204
+ end
205
+
206
+ def call(object, *attributes, &block)
207
+ if block
208
+ _array object, &block
209
+ else
210
+ _extract object, attributes
211
+ end
212
+ end
213
+
214
+ # Returns the nil JSON.
215
+ def nil!
216
+ @attributes = nil
217
+ end
218
+
219
+ alias_method :null!, :nil!
220
+
221
+ # Returns the attributes of the current builder.
222
+ def attributes!
223
+ @attributes
224
+ end
225
+
226
+ # Merges hash, array, or Jbuilder instance into current builder.
227
+ def merge!(object)
228
+ hash_or_array = ::Jbuilder === object ? object.attributes! : object
229
+ @attributes = _merge_values(@attributes, _format_keys(hash_or_array))
230
+ end
231
+
232
+ # Encodes the current builder as JSON.
233
+ def target!
234
+ @attributes.to_json
235
+ end
236
+
237
+ alias_method :method_missing, :set!
238
+ private :method_missing
239
+
240
+ private
241
+
242
+ def _set(key, value = BLANK, attributes = nil, &block)
243
+ result = if block
244
+ if _blank?(value)
245
+ # json.comments { ... }
246
+ # { "comments": ... }
247
+ _merge_block key, &block
248
+ else
249
+ # json.comments @post.comments { |comment| ... }
250
+ # { "comments": [ { ... }, { ... } ] }
251
+ _scope { _array value, &block }
252
+ end
253
+ elsif attributes.empty?
254
+ if ::Jbuilder === value
255
+ # json.age 32
256
+ # json.person another_jbuilder
257
+ # { "age": 32, "person": { ... }
258
+ _format_keys(value.attributes!)
259
+ else
260
+ # json.age 32
261
+ # { "age": 32 }
262
+ _format_keys(value)
263
+ end
264
+ elsif _is_collection?(value)
265
+ # json.comments @post.comments, :content, :created_at
266
+ # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
267
+ _scope { _array value, attributes }
268
+ else
269
+ # json.author @post.creator, :name, :email_address
270
+ # { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
271
+ _merge_block(key) { _extract value, attributes }
272
+ end
273
+
274
+ _set_value key, result
275
+ end
276
+
277
+ def _array(collection = EMPTY_ARRAY, attributes = nil, &block)
278
+ array = if collection.nil?
279
+ EMPTY_ARRAY
280
+ elsif block
281
+ _map_collection(collection, &block)
282
+ elsif attributes.present?
283
+ _map_collection(collection) { |element| _extract element, attributes }
284
+ else
285
+ _format_keys(collection.to_a)
286
+ end
287
+
288
+ @attributes = _merge_values(@attributes, array)
289
+ end
290
+
291
+ def _extract(object, attributes)
292
+ if ::Hash === object
293
+ _extract_hash_values(object, attributes)
294
+ else
295
+ _extract_method_values(object, attributes)
296
+ end
297
+ end
298
+
299
+ def _extract_hash_values(object, attributes)
300
+ attributes.each{ |key| _set_value key, _format_keys(object.fetch(key)) }
301
+ end
302
+
303
+ def _extract_method_values(object, attributes)
304
+ attributes.each{ |key| _set_value key, _format_keys(object.public_send(key)) }
305
+ end
306
+
307
+ def _merge_block(key)
308
+ current_value = _blank? ? BLANK : @attributes.fetch(_key(key), BLANK)
309
+ ::Kernel.raise NullError.build(key) if current_value.nil?
310
+ new_value = _scope{ yield self }
311
+ _merge_values(current_value, new_value)
312
+ end
313
+
314
+ def _merge_values(current_value, updates)
315
+ if _blank?(updates)
316
+ current_value
317
+ elsif _blank?(current_value) || updates.nil? || current_value.empty? && ::Array === updates
318
+ updates
319
+ elsif ::Array === current_value && ::Array === updates
320
+ current_value + updates
321
+ elsif ::Hash === current_value && ::Hash === updates
322
+ current_value.deep_merge(updates)
323
+ else
324
+ ::Kernel.raise MergeError.build(current_value, updates)
325
+ end
326
+ end
327
+
328
+ def _key(key)
329
+ if @key_formatter
330
+ @key_formatter.format(key)
331
+ elsif key.is_a?(::Symbol)
332
+ key.name
333
+ else
334
+ key.to_s
335
+ end
336
+ end
337
+
338
+ def _format_keys(hash_or_array)
339
+ return hash_or_array unless @deep_format_keys
340
+
341
+ if ::Array === hash_or_array
342
+ hash_or_array.map { |value| _format_keys(value) }
343
+ elsif ::Hash === hash_or_array
344
+ ::Hash[hash_or_array.collect { |k, v| [_key(k), _format_keys(v)] }]
345
+ else
346
+ hash_or_array
347
+ end
348
+ end
349
+
350
+ def _set_value(key, value)
351
+ ::Kernel.raise NullError.build(key) if @attributes.nil?
352
+ ::Kernel.raise ArrayError.build(key) if ::Array === @attributes
353
+ return if @ignore_nil && value.nil? or _blank?(value)
354
+ @attributes = {} if _blank?
355
+ @attributes[_key(key)] = value
356
+ end
357
+
358
+ def _map_collection(collection)
359
+ collection = collection.map do |element|
360
+ _scope{ yield element }
361
+ end
362
+ collection.delete(BLANK)
363
+ collection
364
+ end
365
+
366
+ def _scope
367
+ parent_attributes, parent_formatter, parent_deep_format_keys = @attributes, @key_formatter, @deep_format_keys
368
+ @attributes = BLANK
369
+ yield
370
+ @attributes
371
+ ensure
372
+ @attributes, @key_formatter, @deep_format_keys = parent_attributes, parent_formatter, parent_deep_format_keys
373
+ end
374
+
375
+ def _is_collection?(object)
376
+ object.respond_to?(:map) && object.respond_to?(:count) && !(::Struct === object)
377
+ end
378
+
379
+ def _blank?(value=@attributes)
380
+ BLANK == value
381
+ end
382
+ end
383
+
384
+ require 'jbuilder/railtie' if defined?(Rails)
@@ -0,0 +1,71 @@
1
+ require 'test_helper'
2
+ require 'jbuilder/jbuilder_dependency_tracker'
3
+
4
+ class FakeTemplate
5
+ attr_reader :source, :handler
6
+ def initialize(source, handler = :jbuilder)
7
+ @source, @handler = source, handler
8
+ end
9
+ end
10
+
11
+
12
+ class JbuilderDependencyTrackerTest < ActiveSupport::TestCase
13
+ def make_tracker(name, source)
14
+ template = FakeTemplate.new(source)
15
+ Jbuilder::DependencyTracker.new(name, template)
16
+ end
17
+
18
+ def track_dependencies(source)
19
+ make_tracker('jbuilder_template', source).dependencies
20
+ end
21
+
22
+ test 'detects dependency via direct partial! call' do
23
+ dependencies = track_dependencies <<-RUBY
24
+ json.partial! 'path/to/partial', foo: bar
25
+ json.partial! 'path/to/another/partial', :fizz => buzz
26
+ RUBY
27
+
28
+ assert_equal %w[path/to/partial path/to/another/partial], dependencies
29
+ end
30
+
31
+ test 'detects dependency via direct partial! call with parens' do
32
+ dependencies = track_dependencies <<-RUBY
33
+ json.partial!("path/to/partial")
34
+ RUBY
35
+
36
+ assert_equal %w[path/to/partial], dependencies
37
+ end
38
+
39
+ test 'detects partial with options (1.9 style)' do
40
+ dependencies = track_dependencies <<-RUBY
41
+ json.partial! hello: 'world', partial: 'path/to/partial', foo: :bar
42
+ RUBY
43
+
44
+ assert_equal %w[path/to/partial], dependencies
45
+ end
46
+
47
+ test 'detects partial with options (1.8 style)' do
48
+ dependencies = track_dependencies <<-RUBY
49
+ json.partial! :hello => 'world', :partial => 'path/to/partial', :foo => :bar
50
+ RUBY
51
+
52
+ assert_equal %w[path/to/partial], dependencies
53
+ end
54
+
55
+ test 'detects partial in indirect collection calls' do
56
+ dependencies = track_dependencies <<-RUBY
57
+ json.comments @post.comments, partial: 'comments/comment', as: :comment
58
+ RUBY
59
+
60
+ assert_equal %w[comments/comment], dependencies
61
+ end
62
+
63
+ test 'detects explicit dependency' do
64
+ dependencies = track_dependencies <<-RUBY
65
+ # Template Dependency: path/to/partial
66
+ json.foo 'bar'
67
+ RUBY
68
+
69
+ assert_equal %w[path/to/partial], dependencies
70
+ end
71
+ end
@@ -0,0 +1,68 @@
1
+ require 'test_helper'
2
+ require 'rails/generators/test_case'
3
+ require 'generators/rails/jbuilder_generator'
4
+
5
+ class JbuilderGeneratorTest < Rails::Generators::TestCase
6
+ tests Rails::Generators::JbuilderGenerator
7
+ arguments %w(Post title body:text password:digest)
8
+ destination File.expand_path('../tmp', __FILE__)
9
+ setup :prepare_destination
10
+
11
+ test 'views are generated' do
12
+ run_generator
13
+
14
+ %w(index show).each do |view|
15
+ assert_file "app/views/posts/#{view}.json.jbuilder"
16
+ end
17
+ assert_file "app/views/posts/_post.json.jbuilder"
18
+ end
19
+
20
+ test 'index content' do
21
+ run_generator
22
+
23
+ assert_file 'app/views/posts/index.json.jbuilder' do |content|
24
+ assert_match %r{json\.array! @posts, partial: "posts/post", as: :post}, content
25
+ end
26
+
27
+ assert_file 'app/views/posts/show.json.jbuilder' do |content|
28
+ assert_match %r{json\.partial! "posts/post", post: @post}, content
29
+ end
30
+
31
+ assert_file 'app/views/posts/_post.json.jbuilder' do |content|
32
+ assert_match %r{json\.extract! post, :id, :title, :body}, content
33
+ assert_match %r{:created_at, :updated_at}, content
34
+ assert_match %r{json\.url post_url\(post, format: :json\)}, content
35
+ end
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
46
+
47
+ test 'namespaced views are generated correctly for index' do
48
+ run_generator %w(Admin::Post --model-name=Post)
49
+
50
+ assert_file 'app/views/admin/posts/index.json.jbuilder' do |content|
51
+ assert_match %r{json\.array! @posts, partial: "admin/posts/post", as: :post}, content
52
+ end
53
+
54
+ assert_file 'app/views/admin/posts/show.json.jbuilder' do |content|
55
+ assert_match %r{json\.partial! "admin/posts/post", post: @post}, content
56
+ end
57
+ end
58
+
59
+ test 'handles virtual attributes' do
60
+ run_generator %w(Message content:rich_text video:attachment photos:attachments)
61
+
62
+ assert_file 'app/views/messages/_message.json.jbuilder' do |content|
63
+ assert_match %r{json\.content message\.content\.to_s}, content
64
+ assert_match %r{json\.video url_for\(message\.video\)}, content
65
+ 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
66
+ end
67
+ end
68
+ end