jwtbuilder 0.0.1 → 2.2.12.jwt

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