jwtbuilder 0.0.1 → 2.2.12.jwt

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/lib/jbuilder.rb ADDED
@@ -0,0 +1,313 @@
1
+ require 'jbuilder/jbuilder'
2
+ require 'jbuilder/blank'
3
+ require 'jbuilder/key_formatter'
4
+ require 'jbuilder/errors'
5
+ require 'multi_json'
6
+
7
+ class Jbuilder
8
+ @@key_formatter = KeyFormatter.new
9
+ @@ignore_nil = false
10
+
11
+ def initialize(options = {})
12
+ @attributes = {}
13
+
14
+ @key_formatter = options.fetch(:key_formatter){ @@key_formatter.clone }
15
+ @ignore_nil = options.fetch(:ignore_nil, @@ignore_nil)
16
+
17
+ yield self if ::Kernel.block_given?
18
+ end
19
+
20
+ # Yields a builder and automatically turns the result into a JSON string
21
+ def self.encode(*args)
22
+ new(*args, &::Proc.new).target!
23
+ end
24
+
25
+ BLANK = Blank.new
26
+
27
+ def set!(key, value = BLANK, *args)
28
+ result = if ::Kernel.block_given?
29
+ if !_blank?(value)
30
+ # json.comments @post.comments { |comment| ... }
31
+ # { "comments": [ { ... }, { ... } ] }
32
+ _scope{ array! value, &::Proc.new }
33
+ else
34
+ # json.comments { ... }
35
+ # { "comments": ... }
36
+ _merge_block(key){ yield self }
37
+ end
38
+ elsif args.empty?
39
+ if ::Jbuilder === value
40
+ # json.age 32
41
+ # json.person another_jbuilder
42
+ # { "age": 32, "person": { ... }
43
+ value.attributes!
44
+ else
45
+ # json.age 32
46
+ # { "age": 32 }
47
+ value
48
+ end
49
+ elsif _mapable_arguments?(value, *args)
50
+ # json.comments @post.comments, :content, :created_at
51
+ # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
52
+ _scope{ array! value, *args }
53
+ else
54
+ # json.author @post.creator, :name, :email_address
55
+ # { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
56
+ _merge_block(key){ extract! value, *args }
57
+ end
58
+
59
+ _set_value key, result
60
+ end
61
+
62
+ alias_method :method_missing, :set!
63
+ private :method_missing
64
+
65
+ # Specifies formatting to be applied to the key. Passing in a name of a function
66
+ # will cause that function to be called on the key. So :upcase will upper case
67
+ # the key. You can also pass in lambdas for more complex transformations.
68
+ #
69
+ # Example:
70
+ #
71
+ # json.key_format! :upcase
72
+ # json.author do
73
+ # json.name "David"
74
+ # json.age 32
75
+ # end
76
+ #
77
+ # { "AUTHOR": { "NAME": "David", "AGE": 32 } }
78
+ #
79
+ # You can pass parameters to the method using a hash pair.
80
+ #
81
+ # json.key_format! camelize: :lower
82
+ # json.first_name "David"
83
+ #
84
+ # { "firstName": "David" }
85
+ #
86
+ # Lambdas can also be used.
87
+ #
88
+ # json.key_format! ->(key){ "_" + key }
89
+ # json.first_name "David"
90
+ #
91
+ # { "_first_name": "David" }
92
+ #
93
+ def key_format!(*args)
94
+ @key_formatter = KeyFormatter.new(*args)
95
+ end
96
+
97
+ # Same as the instance method key_format! except sets the default.
98
+ def self.key_format(*args)
99
+ @@key_formatter = KeyFormatter.new(*args)
100
+ end
101
+
102
+ # If you want to skip adding nil values to your JSON hash. This is useful
103
+ # for JSON clients that don't deal well with nil values, and would prefer
104
+ # not to receive keys which have null values.
105
+ #
106
+ # Example:
107
+ # json.ignore_nil! false
108
+ # json.id User.new.id
109
+ #
110
+ # { "id": null }
111
+ #
112
+ # json.ignore_nil!
113
+ # json.id User.new.id
114
+ #
115
+ # {}
116
+ #
117
+ def ignore_nil!(value = true)
118
+ @ignore_nil = value
119
+ end
120
+
121
+ # Same as instance method ignore_nil! except sets the default.
122
+ def self.ignore_nil(value = true)
123
+ @@ignore_nil = value
124
+ end
125
+
126
+ # Turns the current element into an array and yields a builder to add a hash.
127
+ #
128
+ # Example:
129
+ #
130
+ # json.comments do
131
+ # json.child! { json.content "hello" }
132
+ # json.child! { json.content "world" }
133
+ # end
134
+ #
135
+ # { "comments": [ { "content": "hello" }, { "content": "world" } ]}
136
+ #
137
+ # More commonly, you'd use the combined iterator, though:
138
+ #
139
+ # json.comments(@post.comments) do |comment|
140
+ # json.content comment.formatted_content
141
+ # end
142
+ def child!
143
+ @attributes = [] unless ::Array === @attributes
144
+ @attributes << _scope{ yield self }
145
+ end
146
+
147
+ # Turns the current element into an array and iterates over the passed collection, adding each iteration as
148
+ # an element of the resulting array.
149
+ #
150
+ # Example:
151
+ #
152
+ # json.array!(@people) do |person|
153
+ # json.name person.name
154
+ # json.age calculate_age(person.birthday)
155
+ # end
156
+ #
157
+ # [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ]
158
+ #
159
+ # If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call:
160
+ #
161
+ # json.(@people) { |person| ... }
162
+ #
163
+ # It's generally only needed to use this method for top-level arrays. If you have named arrays, you can do:
164
+ #
165
+ # json.people(@people) do |person|
166
+ # json.name person.name
167
+ # json.age calculate_age(person.birthday)
168
+ # end
169
+ #
170
+ # { "people": [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ] }
171
+ #
172
+ # If you omit the block then you can set the top level array directly:
173
+ #
174
+ # json.array! [1, 2, 3]
175
+ #
176
+ # [1,2,3]
177
+ def array!(collection = [], *attributes)
178
+ array = if collection.nil?
179
+ []
180
+ elsif ::Kernel.block_given?
181
+ _map_collection(collection, &::Proc.new)
182
+ elsif attributes.any?
183
+ _map_collection(collection) { |element| extract! element, *attributes }
184
+ else
185
+ collection.to_a
186
+ end
187
+
188
+ merge! array
189
+ end
190
+
191
+ # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.
192
+ #
193
+ # Example:
194
+ #
195
+ # @person = Struct.new(:name, :age).new('David', 32)
196
+ #
197
+ # or you can utilize a Hash
198
+ #
199
+ # @person = { name: 'David', age: 32 }
200
+ #
201
+ # json.extract! @person, :name, :age
202
+ #
203
+ # { "name": David", "age": 32 }, { "name": Jamie", "age": 31 }
204
+ #
205
+ # You can also use the call syntax instead of an explicit extract! call:
206
+ #
207
+ # json.(@person, :name, :age)
208
+ def extract!(object, *attributes)
209
+ if ::Hash === object
210
+ _extract_hash_values(object, attributes)
211
+ else
212
+ _extract_method_values(object, attributes)
213
+ end
214
+ end
215
+
216
+ def call(object, *attributes)
217
+ if ::Kernel.block_given?
218
+ array! object, &::Proc.new
219
+ else
220
+ extract! object, *attributes
221
+ end
222
+ end
223
+
224
+ # Returns the nil JSON.
225
+ def nil!
226
+ @attributes = nil
227
+ end
228
+
229
+ alias_method :null!, :nil!
230
+
231
+ # Returns the attributes of the current builder.
232
+ def attributes!
233
+ @attributes
234
+ end
235
+
236
+ # Merges hash or array into current builder.
237
+ def merge!(hash_or_array)
238
+ @attributes = _merge_values(@attributes, hash_or_array)
239
+ end
240
+
241
+ # Encodes the current builder as JSON.
242
+ def target!
243
+ ::MultiJson.dump(@attributes)
244
+ end
245
+
246
+ private
247
+
248
+ def _extract_hash_values(object, attributes)
249
+ attributes.each{ |key| _set_value key, object.fetch(key) }
250
+ end
251
+
252
+ def _extract_method_values(object, attributes)
253
+ attributes.each{ |key| _set_value key, object.public_send(key) }
254
+ end
255
+
256
+ def _merge_block(key)
257
+ current_value = _blank? ? BLANK : @attributes.fetch(_key(key), BLANK)
258
+ raise NullError.build(key) if current_value.nil?
259
+ new_value = _scope{ yield self }
260
+ _merge_values(current_value, new_value)
261
+ end
262
+
263
+ def _merge_values(current_value, updates)
264
+ if _blank?(updates)
265
+ current_value
266
+ elsif _blank?(current_value) || updates.nil?
267
+ updates
268
+ elsif ::Array === updates
269
+ ::Array === current_value ? current_value + updates : updates
270
+ elsif ::Hash === current_value
271
+ current_value.merge(updates)
272
+ else
273
+ raise "Can't merge #{updates.inspect} with #{current_value.inspect}"
274
+ end
275
+ end
276
+
277
+ def _key(key)
278
+ @key_formatter.format(key)
279
+ end
280
+
281
+ def _set_value(key, value)
282
+ raise NullError.build(key) if @attributes.nil?
283
+ raise ArrayError.build(key) if ::Array === @attributes
284
+ return if @ignore_nil && value.nil? or _blank?(value)
285
+ @attributes = {} if _blank?
286
+ @attributes[_key(key)] = value
287
+ end
288
+
289
+ def _map_collection(collection)
290
+ collection.map do |element|
291
+ _scope{ yield element }
292
+ end - [BLANK]
293
+ end
294
+
295
+ def _scope
296
+ parent_attributes, parent_formatter = @attributes, @key_formatter
297
+ @attributes = BLANK
298
+ yield
299
+ @attributes
300
+ ensure
301
+ @attributes, @key_formatter = parent_attributes, parent_formatter
302
+ end
303
+
304
+ def _mapable_arguments?(value, *args)
305
+ value.respond_to?(:map)
306
+ end
307
+
308
+ def _blank?(value=@attributes)
309
+ BLANK == value
310
+ end
311
+ end
312
+
313
+ require 'jbuilder/railtie' if defined?(Rails)
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+ require 'jbuilder/dependency_tracker'
3
+
4
+
5
+ class FakeTemplate
6
+ attr_reader :source, :handler
7
+ def initialize(source, handler = :jbuilder)
8
+ @source, @handler = source, handler
9
+ end
10
+ end
11
+
12
+
13
+ class JbuilderDependencyTrackerTest < ActiveSupport::TestCase
14
+ def make_tracker(name, source)
15
+ template = FakeTemplate.new(source)
16
+ Jbuilder::DependencyTracker.new(name, template)
17
+ end
18
+
19
+ def track_dependencies(source)
20
+ make_tracker('jbuilder_template', source).dependencies
21
+ end
22
+
23
+ test 'detects dependency via direct partial! call' do
24
+ dependencies = track_dependencies <<-RUBY
25
+ json.partial! 'path/to/partial', foo: bar
26
+ json.partial! 'path/to/another/partial', :fizz => buzz
27
+ RUBY
28
+
29
+ assert_equal %w[path/to/partial path/to/another/partial], dependencies
30
+ end
31
+
32
+ test 'detects dependency via direct partial! call with parens' do
33
+ dependencies = track_dependencies <<-RUBY
34
+ json.partial!("path/to/partial")
35
+ RUBY
36
+
37
+ assert_equal %w[path/to/partial], dependencies
38
+ end
39
+
40
+ test 'detects partial with options (1.9 style)' do
41
+ dependencies = track_dependencies <<-RUBY
42
+ json.partial! hello: 'world', partial: 'path/to/partial', foo: :bar
43
+ RUBY
44
+
45
+ assert_equal %w[path/to/partial], dependencies
46
+ end
47
+
48
+ test 'detects partial with options (1.8 style)' do
49
+ dependencies = track_dependencies <<-RUBY
50
+ json.partial! :hello => 'world', :partial => 'path/to/partial', :foo => :bar
51
+ RUBY
52
+
53
+ assert_equal %w[path/to/partial], dependencies
54
+ end
55
+
56
+ test 'detects partial in indirect collecton calls' do
57
+ dependencies = track_dependencies <<-RUBY
58
+ json.comments @post.comments, partial: 'comments/comment', as: :comment
59
+ RUBY
60
+
61
+ assert_equal %w[comments/comment], dependencies
62
+ end
63
+
64
+ test 'detects explicit depedency' do
65
+ dependencies = track_dependencies <<-RUBY
66
+ # Template Dependency: path/to/partial
67
+ json.foo 'bar'
68
+ RUBY
69
+
70
+ assert_equal %w[path/to/partial], dependencies
71
+ end
72
+ end
@@ -0,0 +1,32 @@
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
+ end
18
+
19
+ test 'index content' do
20
+ run_generator
21
+
22
+ assert_file 'app/views/posts/index.json.jbuilder' do |content|
23
+ assert_match /json\.array!\(@posts\) do \|post\|/, content
24
+ assert_match /json\.extract! post, :id, :title, :body/, content
25
+ assert_match /json\.url post_url\(post, format: :json\)/, content
26
+ end
27
+
28
+ assert_file 'app/views/posts/show.json.jbuilder' do |content|
29
+ assert_match /json\.extract! @post, :id, :title, :body, :created_at, :updated_at/, content
30
+ end
31
+ end
32
+ end