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.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.travis.yml +28 -0
- data/Appraisals +32 -0
- data/CHANGELOG.md +174 -0
- data/Gemfile +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +260 -0
- data/Rakefile +23 -0
- data/gemfiles/rails_3_0.gemfile +12 -0
- data/gemfiles/rails_3_1.gemfile +12 -0
- data/gemfiles/rails_3_2.gemfile +12 -0
- data/gemfiles/rails_4_0.gemfile +11 -0
- data/gemfiles/rails_4_1.gemfile +11 -0
- data/gemfiles/rails_4_2.gemfile +11 -0
- data/jbuilder.gemspec +18 -0
- data/lib/generators/rails/jbuilder_generator.rb +48 -0
- data/lib/generators/rails/scaffold_controller_generator.rb +12 -0
- data/lib/generators/rails/templates/controller.rb +84 -0
- data/lib/generators/rails/templates/index.json.jbuilder +5 -0
- data/lib/generators/rails/templates/show.json.jbuilder +3 -0
- data/lib/jbuilder/blank.rb +11 -0
- data/lib/jbuilder/dependency_tracker.rb +61 -0
- data/lib/jbuilder/errors.rb +17 -0
- data/lib/jbuilder/jbuilder.rb +7 -0
- data/lib/jbuilder/jbuilder_template.rb +145 -0
- data/lib/jbuilder/key_formatter.rb +34 -0
- data/lib/jbuilder/railtie.rb +21 -0
- data/lib/jbuilder.rb +313 -0
- data/test/jbuilder_dependency_tracker_test.rb +72 -0
- data/test/jbuilder_generator_test.rb +32 -0
- data/test/jbuilder_template_test.rb +343 -0
- data/test/jbuilder_test.rb +686 -0
- data/test/scaffold_controller_generator_test.rb +57 -0
- data/test/test_helper.rb +14 -0
- metadata +48 -8
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
|