jbuilder 2.3.2 → 2.9.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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +49 -25
- data/Appraisals +19 -36
- data/CHANGELOG.md +72 -3
- data/CONTRIBUTING.md +14 -8
- data/Gemfile +0 -1
- data/MIT-LICENSE +1 -1
- data/README.md +47 -22
- data/Rakefile +1 -5
- data/gemfiles/rails_4_2.gemfile +3 -6
- data/gemfiles/rails_5_0.gemfile +10 -0
- data/gemfiles/rails_5_1.gemfile +10 -0
- data/gemfiles/rails_5_2.gemfile +10 -0
- data/gemfiles/rails_6_0.gemfile +10 -0
- data/gemfiles/rails_head.gemfile +10 -0
- data/jbuilder.gemspec +4 -5
- data/lib/generators/rails/jbuilder_generator.rb +9 -2
- data/lib/generators/rails/scaffold_controller_generator.rb +7 -1
- data/lib/generators/rails/templates/api_controller.rb +2 -2
- data/lib/generators/rails/templates/controller.rb +2 -2
- data/lib/generators/rails/templates/index.json.jbuilder +1 -4
- data/lib/generators/rails/templates/partial.json.jbuilder +2 -0
- data/lib/generators/rails/templates/show.json.jbuilder +1 -1
- data/lib/jbuilder.rb +7 -7
- data/lib/jbuilder/errors.rb +7 -0
- data/lib/jbuilder/jbuilder_template.rb +61 -8
- data/lib/jbuilder/key_formatter.rb +2 -2
- data/lib/jbuilder/railtie.rb +12 -5
- data/test/jbuilder_dependency_tracker_test.rb +1 -1
- data/test/jbuilder_generator_test.rb +18 -4
- data/test/jbuilder_template_test.rb +204 -275
- data/test/jbuilder_test.rb +38 -2
- data/test/scaffold_api_controller_generator_test.rb +24 -11
- data/test/scaffold_controller_generator_test.rb +31 -18
- data/test/test_helper.rb +26 -6
- metadata +12 -36
- data/gemfiles/rails_3_0.gemfile +0 -14
- data/gemfiles/rails_3_1.gemfile +0 -14
- data/gemfiles/rails_3_2.gemfile +0 -14
- data/gemfiles/rails_4_0.gemfile +0 -13
- data/gemfiles/rails_4_1.gemfile +0 -13
- data/gemfiles/rails_edge.gemfile +0 -13
data/Rakefile
CHANGED
@@ -12,11 +12,7 @@ else
|
|
12
12
|
|
13
13
|
test.libs << "test"
|
14
14
|
|
15
|
-
|
16
|
-
test.test_files = %w[test/jbuilder_template_test.rb test/jbuilder_test.rb]
|
17
|
-
else
|
18
|
-
test.test_files = FileList["test/*_test.rb"]
|
19
|
-
end
|
15
|
+
test.test_files = FileList["test/*_test.rb"]
|
20
16
|
end
|
21
17
|
|
22
18
|
task default: :test
|
data/gemfiles/rails_4_2.gemfile
CHANGED
@@ -3,11 +3,8 @@
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
5
|
gem "rake"
|
6
|
-
gem "mocha", :
|
6
|
+
gem "mocha", require: false
|
7
7
|
gem "appraisal"
|
8
|
-
gem "
|
9
|
-
gem "railties", "~> 4.2.0"
|
10
|
-
gem "actionpack", "~> 4.2.0"
|
11
|
-
gem "activemodel", "~> 4.2.0"
|
8
|
+
gem "rails", "~> 4.2.0"
|
12
9
|
|
13
|
-
gemspec :
|
10
|
+
gemspec path: "../"
|
data/jbuilder.gemspec
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'jbuilder'
|
3
|
-
s.version = '2.
|
4
|
-
s.authors =
|
5
|
-
s.email =
|
3
|
+
s.version = '2.9.1'
|
4
|
+
s.authors = 'David Heinemeier Hansson'
|
5
|
+
s.email = 'david@basecamp.com'
|
6
6
|
s.summary = 'Create JSON structures via a Builder-style DSL'
|
7
7
|
s.homepage = 'https://github.com/rails/jbuilder'
|
8
8
|
s.license = 'MIT'
|
9
9
|
|
10
10
|
s.required_ruby_version = '>= 1.9.3'
|
11
11
|
|
12
|
-
s.add_dependency 'activesupport', '>=
|
13
|
-
s.add_dependency 'multi_json', '~> 1.2'
|
12
|
+
s.add_dependency 'activesupport', '>= 4.2.0'
|
14
13
|
|
15
14
|
s.files = `git ls-files`.split("\n")
|
16
15
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
@@ -10,6 +10,8 @@ module Rails
|
|
10
10
|
|
11
11
|
argument :attributes, type: :array, default: [], banner: 'field:type field:type'
|
12
12
|
|
13
|
+
class_option :timestamps, type: :boolean, default: true
|
14
|
+
|
13
15
|
def create_root_folder
|
14
16
|
path = File.join('app/views', controller_file_path)
|
15
17
|
empty_directory path unless File.directory?(path)
|
@@ -20,6 +22,7 @@ module Rails
|
|
20
22
|
filename = filename_with_extensions(view)
|
21
23
|
template filename, File.join('app/views', controller_file_path, filename)
|
22
24
|
end
|
25
|
+
template filename_with_extensions('partial'), File.join('app/views', controller_file_path, filename_with_extensions("_#{singular_table_name}"))
|
23
26
|
end
|
24
27
|
|
25
28
|
|
@@ -32,8 +35,12 @@ module Rails
|
|
32
35
|
[name, :json, :jbuilder] * '.'
|
33
36
|
end
|
34
37
|
|
35
|
-
def
|
36
|
-
|
38
|
+
def full_attributes_list
|
39
|
+
if options[:timestamps]
|
40
|
+
attributes_list(attributes_names + %w(created_at updated_at))
|
41
|
+
else
|
42
|
+
attributes_list(attributes_names)
|
43
|
+
end
|
37
44
|
end
|
38
45
|
|
39
46
|
def attributes_list(attributes = attributes_names)
|
@@ -6,7 +6,13 @@ module Rails
|
|
6
6
|
class ScaffoldControllerGenerator
|
7
7
|
source_paths << File.expand_path('../templates', __FILE__)
|
8
8
|
|
9
|
-
hook_for :jbuilder, default: true
|
9
|
+
hook_for :jbuilder, type: :boolean, default: true
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def permitted_params
|
14
|
+
attributes_names.map { |name| ":#{name}" }.join(', ')
|
15
|
+
end unless private_method_defined? :permitted_params
|
10
16
|
end
|
11
17
|
end
|
12
18
|
end
|
@@ -54,9 +54,9 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|
54
54
|
# Never trust parameters from the scary internet, only allow the white list through.
|
55
55
|
def <%= "#{singular_table_name}_params" %>
|
56
56
|
<%- if attributes_names.empty? -%>
|
57
|
-
params
|
57
|
+
params.fetch(<%= ":#{singular_table_name}" %>, {})
|
58
58
|
<%- else -%>
|
59
|
-
params.require(<%= ":#{singular_table_name}" %>).permit(<%=
|
59
|
+
params.require(<%= ":#{singular_table_name}" %>).permit(<%= permitted_params %>)
|
60
60
|
<%- end -%>
|
61
61
|
end
|
62
62
|
end
|
@@ -75,9 +75,9 @@ class <%= controller_class_name %>Controller < ApplicationController
|
|
75
75
|
# Never trust parameters from the scary internet, only allow the white list through.
|
76
76
|
def <%= "#{singular_table_name}_params" %>
|
77
77
|
<%- if attributes_names.empty? -%>
|
78
|
-
params
|
78
|
+
params.fetch(<%= ":#{singular_table_name}" %>, {})
|
79
79
|
<%- else -%>
|
80
|
-
params.require(<%= ":#{singular_table_name}" %>).permit(<%=
|
80
|
+
params.require(<%= ":#{singular_table_name}" %>).permit(<%= permitted_params %>)
|
81
81
|
<%- end -%>
|
82
82
|
end
|
83
83
|
end
|
@@ -1,4 +1 @@
|
|
1
|
-
json.array!
|
2
|
-
json.extract! <%= singular_table_name %>, <%= attributes_list %>
|
3
|
-
json.url <%= singular_table_name %>_url(<%= singular_table_name %>, format: :json)
|
4
|
-
end
|
1
|
+
json.array! @<%= plural_table_name %>, partial: "<%= plural_table_name %>/<%= singular_table_name %>", as: :<%= singular_table_name %>
|
@@ -1 +1 @@
|
|
1
|
-
json.
|
1
|
+
json.partial! "<%= plural_table_name %>/<%= singular_table_name %>", <%= singular_table_name %>: @<%= singular_table_name %>
|
data/lib/jbuilder.rb
CHANGED
@@ -2,7 +2,7 @@ require 'jbuilder/jbuilder'
|
|
2
2
|
require 'jbuilder/blank'
|
3
3
|
require 'jbuilder/key_formatter'
|
4
4
|
require 'jbuilder/errors'
|
5
|
-
require '
|
5
|
+
require 'json'
|
6
6
|
require 'ostruct'
|
7
7
|
|
8
8
|
class Jbuilder
|
@@ -247,7 +247,7 @@ class Jbuilder
|
|
247
247
|
|
248
248
|
# Encodes the current builder as JSON.
|
249
249
|
def target!
|
250
|
-
|
250
|
+
@attributes.to_json
|
251
251
|
end
|
252
252
|
|
253
253
|
private
|
@@ -270,14 +270,14 @@ class Jbuilder
|
|
270
270
|
def _merge_values(current_value, updates)
|
271
271
|
if _blank?(updates)
|
272
272
|
current_value
|
273
|
-
elsif _blank?(current_value) || updates.nil?
|
273
|
+
elsif _blank?(current_value) || updates.nil? || current_value.empty? && ::Array === updates
|
274
274
|
updates
|
275
|
-
elsif ::Array === updates
|
276
|
-
|
277
|
-
elsif ::Hash === current_value
|
275
|
+
elsif ::Array === current_value && ::Array === updates
|
276
|
+
current_value + updates
|
277
|
+
elsif ::Hash === current_value && ::Hash === updates
|
278
278
|
current_value.merge(updates)
|
279
279
|
else
|
280
|
-
raise
|
280
|
+
raise MergeError.build(current_value, updates)
|
281
281
|
end
|
282
282
|
end
|
283
283
|
|
data/lib/jbuilder/errors.rb
CHANGED
@@ -11,6 +11,7 @@ class JbuilderTemplate < Jbuilder
|
|
11
11
|
|
12
12
|
def initialize(context, *args)
|
13
13
|
@context = context
|
14
|
+
@cached_root = nil
|
14
15
|
super(*args)
|
15
16
|
end
|
16
17
|
|
@@ -32,7 +33,7 @@ class JbuilderTemplate < Jbuilder
|
|
32
33
|
# end
|
33
34
|
def cache!(key=nil, options={})
|
34
35
|
if @context.controller.perform_caching
|
35
|
-
value =
|
36
|
+
value = _cache_fragment_for(key, options) do
|
36
37
|
_scope { yield self }
|
37
38
|
end
|
38
39
|
|
@@ -42,6 +43,27 @@ class JbuilderTemplate < Jbuilder
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
46
|
+
# Caches the json structure at the root using a string rather than the hash structure. This is considerably
|
47
|
+
# faster, but the drawback is that it only works, as the name hints, at the root. So you cannot
|
48
|
+
# use this approach to cache deeper inside the hierarchy, like in partials or such. Continue to use #cache! there.
|
49
|
+
#
|
50
|
+
# Example:
|
51
|
+
#
|
52
|
+
# json.cache_root! @person do
|
53
|
+
# json.extract! @person, :name, :age
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# # json.extra 'This will not work either, the root must be exclusive'
|
57
|
+
def cache_root!(key=nil, options={})
|
58
|
+
if @context.controller.perform_caching
|
59
|
+
raise "cache_root! can't be used after JSON structures have been defined" if @attributes.present?
|
60
|
+
|
61
|
+
@cached_root = _cache_fragment_for([ :root, key ], options) { yield; target! }
|
62
|
+
else
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
45
67
|
# Conditionally caches the json depending in the condition given as first parameter. Has the same
|
46
68
|
# signature as the `cache` helper method in `ActionView::Helpers::CacheHelper` and so can be used in
|
47
69
|
# the same way.
|
@@ -55,6 +77,10 @@ class JbuilderTemplate < Jbuilder
|
|
55
77
|
condition ? cache!(*args, &::Proc.new) : yield
|
56
78
|
end
|
57
79
|
|
80
|
+
def target!
|
81
|
+
@cached_root || super
|
82
|
+
end
|
83
|
+
|
58
84
|
def array!(collection = [], *args)
|
59
85
|
options = args.first
|
60
86
|
|
@@ -78,7 +104,7 @@ class JbuilderTemplate < Jbuilder
|
|
78
104
|
private
|
79
105
|
|
80
106
|
def _render_partial_with_options(options)
|
81
|
-
options.reverse_merge! locals:
|
107
|
+
options.reverse_merge! locals: options.except(:partial, :as, :collection)
|
82
108
|
options.reverse_merge! ::JbuilderTemplate.template_lookup_options
|
83
109
|
as = options[:as]
|
84
110
|
|
@@ -102,9 +128,35 @@ class JbuilderTemplate < Jbuilder
|
|
102
128
|
@context.render options
|
103
129
|
end
|
104
130
|
|
131
|
+
def _cache_fragment_for(key, options, &block)
|
132
|
+
key = _cache_key(key, options)
|
133
|
+
_read_fragment_cache(key, options) || _write_fragment_cache(key, options, &block)
|
134
|
+
end
|
135
|
+
|
136
|
+
def _read_fragment_cache(key, options = nil)
|
137
|
+
@context.controller.instrument_fragment_cache :read_fragment, key do
|
138
|
+
::Rails.cache.read(key, options)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def _write_fragment_cache(key, options = nil)
|
143
|
+
@context.controller.instrument_fragment_cache :write_fragment, key do
|
144
|
+
yield.tap do |value|
|
145
|
+
::Rails.cache.write(key, value, options)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
105
150
|
def _cache_key(key, options)
|
106
|
-
|
107
|
-
key =
|
151
|
+
name_options = options.slice(:skip_digest, :virtual_path)
|
152
|
+
key = _fragment_name_with_digest(key, name_options)
|
153
|
+
|
154
|
+
if @context.respond_to?(:combined_fragment_cache_key)
|
155
|
+
key = @context.combined_fragment_cache_key(key)
|
156
|
+
else
|
157
|
+
key = url_for(key).split('://', 2).last if ::Hash === key
|
158
|
+
end
|
159
|
+
|
108
160
|
::ActiveSupport::Cache.expand_cache_key(key, :jbuilder)
|
109
161
|
end
|
110
162
|
|
@@ -136,7 +188,7 @@ class JbuilderTemplate < Jbuilder
|
|
136
188
|
_scope{ _render_partial_with_options options.merge(collection: object) }
|
137
189
|
else
|
138
190
|
locals = ::Hash[options[:as], object]
|
139
|
-
_scope{
|
191
|
+
_scope{ _render_partial_with_options options.merge(locals: locals) }
|
140
192
|
end
|
141
193
|
|
142
194
|
set! name, value
|
@@ -170,11 +222,12 @@ end
|
|
170
222
|
|
171
223
|
class JbuilderHandler
|
172
224
|
cattr_accessor :default_format
|
173
|
-
self.default_format =
|
225
|
+
self.default_format = :json
|
174
226
|
|
175
|
-
def self.call(template)
|
227
|
+
def self.call(template, source = nil)
|
228
|
+
source ||= template.source
|
176
229
|
# this juggling is required to keep line numbers right in the error
|
177
|
-
%{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{
|
230
|
+
%{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{source}
|
178
231
|
json.target! unless (__already_defined && __already_defined != "method")}
|
179
232
|
end
|
180
233
|
end
|
data/lib/jbuilder/railtie.rb
CHANGED
@@ -3,17 +3,24 @@ require 'jbuilder/jbuilder_template'
|
|
3
3
|
|
4
4
|
class Jbuilder
|
5
5
|
class Railtie < ::Rails::Railtie
|
6
|
-
initializer :jbuilder do
|
6
|
+
initializer :jbuilder do
|
7
7
|
ActiveSupport.on_load :action_view do
|
8
8
|
ActionView::Template.register_template_handler :jbuilder, JbuilderHandler
|
9
9
|
require 'jbuilder/dependency_tracker'
|
10
10
|
end
|
11
11
|
|
12
|
-
if
|
12
|
+
if Rails::VERSION::MAJOR >= 5
|
13
|
+
module ::ActionController
|
14
|
+
module ApiRendering
|
15
|
+
include ActionView::Rendering
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
13
19
|
ActiveSupport.on_load :action_controller do
|
14
|
-
|
15
|
-
|
16
|
-
|
20
|
+
if self == ActionController::API
|
21
|
+
include ActionController::Helpers
|
22
|
+
include ActionController::ImplicitRender
|
23
|
+
end
|
17
24
|
end
|
18
25
|
end
|
19
26
|
end
|
@@ -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
|
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
|
@@ -14,19 +14,33 @@ class JbuilderGeneratorTest < Rails::Generators::TestCase
|
|
14
14
|
%w(index show).each do |view|
|
15
15
|
assert_file "app/views/posts/#{view}.json.jbuilder"
|
16
16
|
end
|
17
|
+
assert_file "app/views/posts/_post.json.jbuilder"
|
17
18
|
end
|
18
19
|
|
19
20
|
test 'index content' do
|
20
21
|
run_generator
|
21
22
|
|
22
23
|
assert_file 'app/views/posts/index.json.jbuilder' do |content|
|
23
|
-
assert_match
|
24
|
-
assert_match /json\.extract! post, :id, :title, :body/, content
|
25
|
-
assert_match /json\.url post_url\(post, format: :json\)/, content
|
24
|
+
assert_match %r{json\.array! @posts, partial: "posts/post", as: :post}, content
|
26
25
|
end
|
27
26
|
|
28
27
|
assert_file 'app/views/posts/show.json.jbuilder' do |content|
|
29
|
-
assert_match
|
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
|
30
44
|
end
|
31
45
|
end
|
32
46
|
end
|