breezy_template 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ module BreezyTemplate
2
+ module DefermentExtension
3
+ ACTIVE_MODES = [:auto, :manual].freeze
4
+
5
+ def set!(key, value = BLANK, *args)
6
+ if ::Kernel.block_given? && _deferment_options?
7
+ if _deferment_auto?
8
+ @js.push(_breezy_visit_current(@path))
9
+ end
10
+ return _set_value key, nil
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def _breezy_visit_current(path)
17
+ uri = ::URI.parse(@request_path)
18
+ qry = ::URI.decode_www_form(uri.query || '') << ["_breezy_filter", path.join('.')]
19
+ uri.query = ::URI.encode_www_form(qry)
20
+ "defers.push({url:'#{uri}'});"
21
+ end
22
+
23
+ def _deferment_options?
24
+ !!@extensions[:defer] && (@search_path.nil? || @search_path.size == 0)
25
+
26
+ end
27
+
28
+ def _deferment_options
29
+ @extensions[:defer]
30
+ end
31
+
32
+ def _deferment_auto?
33
+ _deferment_options[0] == :auto
34
+ end
35
+
36
+ def _set_request_url(request_path)
37
+ @request_path = request_path
38
+ end
39
+
40
+ def _extended_options?(value)
41
+ _deferment_options? || super
42
+ end
43
+
44
+ def _mapping_element(element, options)
45
+ if _deferment_options?
46
+ if options.has_key? :key
47
+ id_name = options[:key]
48
+ id_val = element[id_name]
49
+ ::Hash[id_name, id_val]
50
+ else
51
+ nil
52
+ end
53
+ else
54
+ super
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,47 @@
1
+ # This was taken from jbuilder
2
+
3
+ dependency_tracker = false
4
+
5
+ begin
6
+ require 'action_view'
7
+ require 'action_view/dependency_tracker'
8
+ dependency_tracker = ::ActionView::DependencyTracker
9
+ rescue LoadError
10
+ begin
11
+ require 'cache_digests'
12
+ dependency_tracker = ::CacheDigests::DependencyTracker
13
+ rescue LoadError
14
+ end
15
+ end
16
+
17
+ if dependency_tracker
18
+ class BreezyTemplate::Template
19
+ module DependencyTrackerMethods
20
+ # Matches:
21
+ # json.partial! partial: "comments/comment"
22
+ # json.comments @post.comments, partial: "comments/comment", as: :comment
23
+ # json.array! @posts, partial: "posts/post", as: :post
24
+ # = render partial: "account"
25
+ #
26
+ INDIRECT_RENDERS = /
27
+ (?::partial\s*=>|partial:) # partial: or :partial =>
28
+ \s* # optional whitespace
29
+ (['"])([^'"]+)\1 # quoted value
30
+ /x
31
+
32
+ def dependencies
33
+ indirect_dependencies + explicit_dependencies
34
+ end
35
+
36
+ private
37
+
38
+ def indirect_dependencies
39
+ source.scan(INDIRECT_RENDERS).map(&:second)
40
+ end
41
+ end
42
+ end
43
+
44
+ ::BreezyTemplate::DependencyTracker = Class.new(dependency_tracker::ERBTracker)
45
+ ::BreezyTemplate::DependencyTracker.send :include, ::BreezyTemplate::Template::DependencyTrackerMethods
46
+ dependency_tracker.register_tracker :breezy, ::BreezyTemplate::DependencyTracker
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'action_view'
2
+ require 'action_view/digestor'
3
+
4
+ module BreezyTemplate
5
+ module PartialDigestor
6
+ if ::Rails.version >= '5.0'
7
+ def _partial_digestor(options)
8
+ name = options[:name]
9
+ finder = options[:finder]
10
+ ::ActionView::Digestor.digest(name: name, finder: finder)
11
+ end
12
+ elsif ::Rails.version >= '4.1'
13
+ def _partial_digestor(options)
14
+ name = options[:name]
15
+ finder = options[:finder]
16
+ ::ActionView::PartialDigestor.new(name: name, finder: finder).digest
17
+ end
18
+ elsif ::Rails.version >= '4.0'
19
+ def _partial_digestor(options={})
20
+ name = options[:name]
21
+ finder = options[:finder]
22
+ ::ActionView::PartialDigestor.new(name, finder.formats.last, finder).digest
23
+ end
24
+ else
25
+ def _partial_digestor(options)
26
+ options[:name]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ module BreezyTemplate
2
+ class Handler
3
+ cattr_accessor :default_format
4
+ self.default_format = Mime[:js]
5
+
6
+ def self.call(template)
7
+ # this juggling is required to keep line numbers right in the error
8
+ %{__already_defined = defined?(json); json||=::BreezyTemplate::Template.new(self);json._filter_by_path(breezy_filter) if defined?(breezy_filter); json._set_request_url(request.path);#{template.source}
9
+ if !(__already_defined && __already_defined != "method")
10
+ json.merge!({data: json.found! || json.empty! })
11
+ json.key_format! :downcase
12
+ if defined?(breezy) && breezy
13
+ breezy.each do |k, v|
14
+ json.set! k, v
15
+ end
16
+ end
17
+
18
+ if protect_against_forgery?
19
+ json.csrf_token form_authenticity_token
20
+ end
21
+
22
+ if ::BreezyTemplate.configuration.track_assets.any?
23
+ json.assets do
24
+ json.array! (::BreezyTemplate.configuration.track_assets || []).map{|assets|
25
+ asset_path(assets)
26
+ }
27
+ end
28
+ end
29
+
30
+ if defined?(breezy_filter) && !!breezy_filter
31
+ json.action 'graft'
32
+ json.path breezy_filter
33
+ end
34
+
35
+ if defined?(session) && session
36
+ session.delete(:breezy_filter)
37
+ end
38
+
39
+ json.joints ::BreezyTemplate::PartialExtension::JointVar.new
40
+ json.defers ::BreezyTemplate::PartialExtension::DeferVar.new
41
+
42
+ json.target!
43
+ end
44
+ }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,123 @@
1
+ module BreezyTemplate
2
+ module PartialExtension
3
+ class DeferVar
4
+ def initialize
5
+ @digest = "defers"
6
+ end
7
+
8
+ def to_json(*)
9
+ @digest
10
+ end
11
+
12
+ def as_json(*)
13
+ self
14
+ end
15
+
16
+ def encode_json(*)
17
+ @digest
18
+ end
19
+ end
20
+
21
+ class JointVar
22
+ def initialize
23
+ @digest = "joints"
24
+ end
25
+
26
+ def to_json(*)
27
+ @digest
28
+ end
29
+
30
+ def as_json(*)
31
+ self
32
+ end
33
+
34
+ def encode_json(*)
35
+ @digest
36
+ end
37
+ end
38
+
39
+ def set!(key, value = BLANK, *args)
40
+ options = args.first || {}
41
+ options = _normalize_options(options)
42
+ if args.one? && _partial_options?(options)
43
+ _set_inline_partial key, value, options
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ def array!(collection = [], *attributes)
50
+ options = attributes.first || {}
51
+ options = _normalize_options(options)
52
+
53
+ if attributes.one? && _partial_options?(options)
54
+ _render_partial_with_options(options.merge(collection: collection))
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ def _extended_options?(value)
61
+ _partial_options?(value) || super
62
+ end
63
+
64
+ def _partial_options?(options)
65
+ ::Hash === options && options.key?(:partial)
66
+ end
67
+
68
+ def _partial_digest(partial)
69
+ lookup_context = @context.lookup_context
70
+ name = lookup_context.find(partial, lookup_context.prefixes, true).virtual_path
71
+ _partial_digestor({name: name, finder: lookup_context})
72
+ end
73
+
74
+ def _set_inline_partial(name, object, options)
75
+ value = if object.nil? && options.empty?
76
+ []
77
+ else
78
+ locals = {}
79
+ locals[options[:as]] = object if !_blank?(object) && options.key?(:as)
80
+ locals.merge!(options[:locals]) if options.key? :locals
81
+
82
+ _scope{ _render_partial options.merge(locals: locals) }
83
+ end
84
+
85
+ set! name, value
86
+ end
87
+
88
+ def _render_partial(options)
89
+ joint = options[:joint]
90
+ if joint
91
+ joint = joint.to_sym
92
+ path = @path.dup.join('.')
93
+ @js.push "joints['#{joint}'] ||= []; joints['#{joint}'].push('#{path}');"
94
+ @joints[joint]
95
+ end
96
+
97
+ options[:locals].merge! json: self
98
+ @context.render options
99
+ end
100
+
101
+ def _render_partial_with_options(options)
102
+ ary_opts = options.dup
103
+ options.reverse_merge! locals: {}
104
+ options.reverse_merge! ::BreezyTemplate::Template.template_lookup_options
105
+ as = options[:as]
106
+
107
+ if options.key?(:collection)
108
+ collection = options.delete(:collection)
109
+ locals = options.delete(:locals)
110
+
111
+ ary_opts.delete(:partial)
112
+ array! collection, ary_opts do |member|
113
+ member_locals = locals.clone
114
+ member_locals.merge! collection: collection
115
+ member_locals.merge! as.to_sym => member if as
116
+ _render_partial options.merge(locals: member_locals)
117
+ end
118
+ else
119
+ _render_partial options
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,99 @@
1
+ module BreezyTemplate
2
+ module SearchExtension
3
+ def found!
4
+ found = @found
5
+ @found = nil
6
+ @search_path = []
7
+ found
8
+ end
9
+
10
+ def _filter_by_path(search_path)
11
+ if search_path.is_a? ::String
12
+ return _filter_by_path(search_path.split('.'))
13
+ end
14
+ @search_path = search_path
15
+ end
16
+
17
+ def _mapping_element(element, options)
18
+ if @search_path && !@search_path.empty?
19
+ original_search_path = @search_path
20
+ @search_path = original_search_path[1..-1]
21
+ if @search_path.size == 0
22
+ @found = super
23
+ else
24
+ yield element
25
+ end
26
+
27
+ @search_path = original_search_path
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def _prepare_collection_for_map(collection)
34
+ if @search_path && !@search_path.empty?
35
+ id_name, id_val = @search_path.first.split('=')
36
+
37
+ if (defined? ::ActiveRecord) && collection.is_a?(::ActiveRecord::Relation)
38
+ if id_val
39
+ id_val = id_val.to_i
40
+ collection = collection.where(::Hash[id_name, id_val])
41
+ else
42
+ index = id_name.to_i
43
+ collection = collection.offset(index).limit(1)
44
+ end
45
+ else
46
+ if id_val
47
+ id_val = id_val.to_i
48
+ found = collection.find do |ele|
49
+ ele[id_name] == id_val || ele[id_name.to_sym] == id_val
50
+ end
51
+ else
52
+ index = id_name.to_i
53
+ found = collection[index]
54
+ end
55
+
56
+ if found
57
+ collection = [found]
58
+ else
59
+ collection = []
60
+ end
61
+ end
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ def set!(key, value = BLANK, *args)
68
+ return if @found
69
+ options = args.first || {}
70
+ options = _normalize_options(options)
71
+
72
+ if @search_path && !@search_path.empty?
73
+ if key.to_s == @search_path.first
74
+ original_search_path = @search_path
75
+ @search_path = original_search_path[1..-1]
76
+ if @search_path.size == 0
77
+ @found = super
78
+ else
79
+ if ::Kernel.block_given?
80
+ yield self
81
+ elsif _partial_options?(options)
82
+ without_track = args.dup
83
+ # without_track.first.delete(:track)
84
+ super(key, value, *without_track)
85
+ else
86
+ ::Kernel.raise 'This should not happen'
87
+ end
88
+ end
89
+
90
+ @search_path = original_search_path
91
+ end
92
+
93
+ return _blank
94
+ else
95
+ super
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module BreezyTemplate
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,1239 @@
1
+ require "test_helper"
2
+ require "mocha"
3
+
4
+ require "action_view"
5
+ require "action_view/testing/resolvers"
6
+ require "breezy_template"
7
+ require 'byebug'
8
+ require 'mocha/test_unit'
9
+
10
+ BLOG_POST_PARTIAL = <<-JBUILDER
11
+ json.extract! blog_post, :id, :body
12
+ json.author do
13
+ first_name, last_name = blog_post.author_name.split(nil, 2)
14
+ json.first_name first_name
15
+ json.last_name last_name
16
+ end
17
+ JBUILDER
18
+
19
+ COLLECTION_PARTIAL = <<-JBUILDER
20
+ json.extract! collection, :id, :name
21
+ JBUILDER
22
+
23
+ PROFILE_PARTIAL = <<-JBUILDER
24
+ json.email email
25
+ JBUILDER
26
+
27
+ FOOTER_PARTIAL = <<-JBUILDER
28
+ json.terms "You agree"
29
+ JBUILDER
30
+
31
+ BlogPost = Struct.new(:id, :body, :author_name)
32
+ Collection = Struct.new(:id, :name)
33
+ blog_authors = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
34
+ BLOG_POST_COLLECTION = Array.new(10){ |i| BlogPost.new(i+1, "post body #{i+1}", blog_authors.next) }
35
+ COLLECTION_COLLECTION = Array.new(5){ |i| Collection.new(i+1, "collection #{i+1}") }
36
+
37
+ ActionView::Template.register_template_handler :breezy, BreezyTemplate::Handler
38
+
39
+ PARTIALS = {
40
+ "_partial.js.breezy" => "foo ||= 'hello'; json.content foo",
41
+ "_blog_post.js.breezy" => BLOG_POST_PARTIAL,
42
+ "_profile.js.breezy" => PROFILE_PARTIAL,
43
+ "_footer.js.breezy" => FOOTER_PARTIAL,
44
+ "_collection.js.breezy" => COLLECTION_PARTIAL
45
+ }
46
+
47
+ def strip_format(str)
48
+ str.strip_heredoc.gsub(/\n\s*/, "")
49
+ end
50
+
51
+ class BreezyTemplateTest < ActionView::TestCase
52
+ setup do
53
+ self.request_forgery = false
54
+ BreezyTemplate.configuration.track_assets = []
55
+
56
+ # this is a stub. Normally this would be set by the
57
+ # controller locals
58
+ self.breezy = {}
59
+
60
+ @context = self
61
+ Rails.cache.clear
62
+ end
63
+
64
+ teardown do
65
+ # Mocha didn't auto teardown??
66
+ Mocha::Mockery.teardown
67
+ end
68
+
69
+ cattr_accessor :request_forgery, :breezy
70
+ self.request_forgery = false
71
+
72
+ def breezy_filter
73
+ @breezy_filter
74
+ end
75
+
76
+ def request
77
+ @request
78
+ end
79
+
80
+ # Stub out a couple of methods that'll get called from cache_fragment_name
81
+ def view_cache_dependencies
82
+ []
83
+ end
84
+
85
+ def jbuild(source, opts={})
86
+ @breezy_filter = opts[:breezy_filter]
87
+ @request = opts[:request] || action_controller_test_request
88
+ @rendered = []
89
+ partials = PARTIALS.clone
90
+ partials["test.js.breezy"] = source
91
+ resolver = ActionView::FixtureResolver.new(partials)
92
+ lookup_context.view_paths = [resolver]
93
+ lookup_context.formats = [:js]
94
+ template = ActionView::Template.new(source, "test", BreezyTemplate::Handler, virtual_path: "test")
95
+ template.render(self, {}).strip
96
+ end
97
+
98
+ def action_controller_test_request
99
+ if ::Rails.version.start_with?('5')
100
+ ::ActionController::TestRequest.create
101
+ else
102
+ ::ActionController::TestRequest.new
103
+ end
104
+ end
105
+
106
+ def cache_keys
107
+ major_v = Rails::VERSION::MAJOR
108
+ minor_v = Rails::VERSION::MINOR
109
+ rails_v = "rails#{major_v}#{minor_v}"
110
+ path = File.expand_path("../fixtures/cache_keys.yaml", __FILE__)
111
+ keys = YAML.load_file(path)
112
+ keys[method_name][rails_v]
113
+ end
114
+
115
+ def undef_context_methods(*names)
116
+ self.class_eval do
117
+ names.each do |name|
118
+ undef_method name.to_sym if method_defined?(name.to_sym)
119
+ end
120
+ end
121
+ end
122
+
123
+ def protect_against_forgery?
124
+ self.request_forgery
125
+ end
126
+
127
+ def form_authenticity_token
128
+ "secret"
129
+ end
130
+
131
+ test "rendering" do
132
+ result = jbuild(<<-JBUILDER)
133
+ json.content "hello"
134
+ JBUILDER
135
+
136
+ expected = strip_format(<<-JS)
137
+ (function(){
138
+ var joints={};
139
+ var cache={};
140
+ var defers=[];
141
+ return ({"data":{"content":"hello"},"joints":joints,"defers":defers});
142
+ })()
143
+ JS
144
+
145
+ assert_equal expected, result
146
+ end
147
+
148
+ test "when rendering with duplicate keys, the last one wins" do
149
+ result = jbuild(<<-JBUILDER)
150
+ json.content do
151
+ json.miss 123
152
+ end
153
+
154
+ json.content do
155
+ json.hit 123
156
+ end
157
+ JBUILDER
158
+
159
+
160
+ expected = strip_format(<<-JS)
161
+ (function(){
162
+ var joints={};
163
+ var cache={};
164
+ var defers=[];
165
+ return ({"data":{"content":{"hit":123}},"joints":joints,"defers":defers});
166
+ })()
167
+ JS
168
+
169
+ assert_equal expected, result
170
+ end
171
+
172
+ test "when rendering with duplicate array values, the last one wins" do
173
+ result = jbuild(<<-JBUILDER)
174
+ json.content do
175
+ json.array! [1,2]
176
+ json.array! [3,4]
177
+ end
178
+ JBUILDER
179
+
180
+ expected = strip_format(<<-JS)
181
+ (function(){
182
+ var joints={};
183
+ var cache={};
184
+ var defers=[];
185
+ return ({\"data\":{\"content\":[3,4]},"joints":joints,"defers":defers});
186
+ })()
187
+ JS
188
+
189
+ assert_equal expected, result
190
+ end
191
+ #
192
+ test "render with asset tracking" do
193
+ BreezyTemplate.configuration.track_assets = ['test.js', 'test.css']
194
+
195
+ result = jbuild(<<-TEMPLATE)
196
+ json.content "hello"
197
+ TEMPLATE
198
+
199
+ expected = strip_format(<<-JS)
200
+ (function(){
201
+ var joints={};
202
+ var cache={};
203
+ var defers=[];
204
+ return ({"data":{"content":"hello"},"assets":["/test.js","/test.css"],"joints":joints,"defers":defers});
205
+ })()
206
+ JS
207
+
208
+ assert_equal expected, result
209
+ end
210
+
211
+
212
+ test "render with csrf token when request forgery is on" do
213
+ self.request_forgery = true
214
+ # csrf_meta_tags also delegate authenticity tokens to the controller
215
+ # here we provide a simple mock to the context
216
+
217
+ result = jbuild(<<-TEMPLATE)
218
+ json.content "hello"
219
+ TEMPLATE
220
+
221
+ expected = strip_format(<<-JS)
222
+ (function(){
223
+ var joints={};
224
+ var cache={};
225
+ var defers=[];
226
+ return ({"data":{"content":"hello"},"csrf_token":"secret","joints":joints,"defers":defers});
227
+ })()
228
+ JS
229
+
230
+ assert_equal expected, result
231
+ end
232
+
233
+ test "wrapping jbuilder contents inside Breezy with additional options" do
234
+ BreezyTemplate.configuration.track_assets = ['test.js', 'test.css']
235
+ self.breezy = { title: 'this is fun' }
236
+
237
+ result = jbuild(<<-TEMPLATE)
238
+ json.content "hello"
239
+ TEMPLATE
240
+
241
+ expected = strip_format(<<-JS)
242
+ (function(){
243
+ var joints={};
244
+ var cache={};
245
+ var defers=[];
246
+ return ({"data":{"content":"hello"},"title":"this is fun","assets":["/test.js","/test.css"],"joints":joints,"defers":defers});
247
+ })()
248
+ JS
249
+
250
+ assert_equal expected, result
251
+ end
252
+
253
+ test "key_format! with parameter" do
254
+ result = jbuild(<<-JBUILDER)
255
+ json.key_format! camelize: [:lower]
256
+ json.camel_style "for JS"
257
+ JBUILDER
258
+
259
+ expected = strip_format(<<-JS)
260
+ (function(){
261
+ var joints={};
262
+ var cache={};
263
+ var defers=[];
264
+ return ({"data":{"camelStyle":"for JS"},"joints":joints,"defers":defers});
265
+ })()
266
+ JS
267
+
268
+ assert_equal expected, result
269
+ end
270
+
271
+ test "key_format! propagates to child elements" do
272
+ result = jbuild(<<-JBUILDER)
273
+ json.key_format! :upcase
274
+ json.level1 "one"
275
+ json.level2 do
276
+ json.value "two"
277
+ end
278
+ JBUILDER
279
+
280
+ expected = strip_format(<<-JS)
281
+ (function(){
282
+ var joints={};
283
+ var cache={};
284
+ var defers=[];
285
+ return ({"data":{
286
+ "LEVEL1":"one",
287
+ "LEVEL2":{"VALUE":"two"}
288
+ },"joints":joints,"defers":defers});
289
+ })()
290
+ JS
291
+
292
+ assert_equal expected, result
293
+ end
294
+
295
+ test "renders partial via the option through set!" do
296
+ @post = BLOG_POST_COLLECTION.first
297
+ Rails.cache.clear
298
+
299
+ result = jbuild(<<-JBUILDER)
300
+ json.post @post, partial: "blog_post", as: :blog_post, joint: :header
301
+ JBUILDER
302
+
303
+ expected = strip_format(<<-JS)
304
+ (function(){
305
+ var joints={};
306
+ var cache={};
307
+ var defers=[];
308
+ joints['header'] ||= []; joints['header'].push('post');
309
+ return ({"data":{"post":{
310
+ "id":1,
311
+ "body":"post body 1",
312
+ "author":{"first_name":"David","last_name":"Heinemeier Hansson"}
313
+ }},"joints":joints,"defers":defers});
314
+ })()
315
+ JS
316
+
317
+ assert_equal expected, result
318
+ end
319
+
320
+ test "renders a partial with no locals" do
321
+ result = jbuild(<<-JBUILDER)
322
+ json.footer nil, partial: "footer"
323
+ JBUILDER
324
+
325
+ expected = strip_format(<<-JS)
326
+ (function(){
327
+ var joints={};
328
+ var cache={};
329
+ var defers=[];
330
+ return ({"data":{"footer":{"terms":"You agree"}},"joints":joints,"defers":defers});
331
+ })()
332
+ JS
333
+ assert_equal expected, result
334
+ end
335
+
336
+ test "renders a partial with locals" do
337
+ result = jbuild(<<-JBUILDER)
338
+ json.profile nil, partial: "profile", locals: {email: "test@test.com"}
339
+ JBUILDER
340
+
341
+ expected = strip_format(<<-JS)
342
+ (function(){
343
+ var joints={};
344
+ var cache={};
345
+ var defers=[];
346
+ return ({"data":{"profile":{"email":"test@test.com"}},"joints":joints,"defers":defers});
347
+ })()
348
+ JS
349
+ assert_equal expected, result
350
+ end
351
+
352
+ test "renders a partial with locals and caches" do
353
+ result = jbuild(<<-JBUILDER)
354
+ json.wrap! :cache, 'cachekey'
355
+ json.profile 32, partial: "profile", locals: {email: "test@test.com"}
356
+ JBUILDER
357
+
358
+ expected = strip_format(<<-JS)
359
+ (function(){
360
+ var joints={};
361
+ var cache={};
362
+ var defers=[];
363
+ cache["#{cache_keys[0]}"]={"email":"test@test.com"};
364
+ return ({"data":{"profile":cache["#{cache_keys[0]}"]},"joints":joints,"defers":defers});
365
+ })()
366
+ JS
367
+
368
+ assert_equal expected, result
369
+ end
370
+
371
+ test "renders a partial even without a :as to the value, this usage is rare" do
372
+ result = jbuild(<<-JBUILDER)
373
+ json.profile 32, partial: "profile", locals: {email: "test@test.com"}
374
+ JBUILDER
375
+
376
+ expected = strip_format(<<-JS)
377
+ (function(){
378
+ var joints={};
379
+ var cache={};
380
+ var defers=[];
381
+ return ({"data":{"profile":{"email":"test@test.com"}},"joints":joints,"defers":defers});
382
+ })()
383
+ JS
384
+
385
+ assert_equal expected, result
386
+ end
387
+
388
+ test "render array of partials without an :as to a member, this usage is very rare" do
389
+ result = jbuild(<<-JBUILDER)
390
+ json.array! [1,2], partial: "footer"
391
+ JBUILDER
392
+
393
+ expected = strip_format(<<-JS)
394
+ (function(){
395
+ var joints={};
396
+ var cache={};
397
+ var defers=[];
398
+ return ({"data":[{"terms":"You agree"},{"terms":"You agree"}],"joints":joints,"defers":defers});
399
+ })()
400
+ JS
401
+
402
+ assert_equal expected, result
403
+ end
404
+
405
+ test "render array of partials without an :as to a member and cache" do
406
+ result = jbuild(<<-JBUILDER)
407
+ json.wrap! :cache, ->(i){ ['a', i] }
408
+ json.array! [1,2], partial: "footer"
409
+ JBUILDER
410
+
411
+ expected = strip_format(<<-JS)
412
+ (function(){
413
+ var joints={};
414
+ var cache={};
415
+ var defers=[];
416
+ cache["#{cache_keys[0]}"]={"terms":"You agree"};
417
+ cache["#{cache_keys[1]}"]={"terms":"You agree"};
418
+ return ({"data":[cache["#{cache_keys[0]}"],cache["#{cache_keys[1]}"]],"joints":joints,"defers":defers});
419
+ })()
420
+ JS
421
+
422
+ assert_equal expected, result
423
+ end
424
+
425
+ test "render array of partials" do
426
+ result = jbuild(<<-JBUILDER)
427
+ json.array! BLOG_POST_COLLECTION, partial: "blog_post", as: :blog_post
428
+ JBUILDER
429
+
430
+ expected = strip_format(<<-JS)
431
+ (function(){
432
+ var joints={};
433
+ var cache={};
434
+ var defers=[];
435
+ return ({"data":[
436
+ {"id":1,"body":"post body 1","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
437
+ {"id":2,"body":"post body 2","author":{"first_name":"Pavel","last_name":"Pravosud"}},
438
+ {"id":3,"body":"post body 3","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
439
+ {"id":4,"body":"post body 4","author":{"first_name":"Pavel","last_name":"Pravosud"}},
440
+ {"id":5,"body":"post body 5","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
441
+ {"id":6,"body":"post body 6","author":{"first_name":"Pavel","last_name":"Pravosud"}},
442
+ {"id":7,"body":"post body 7","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
443
+ {"id":8,"body":"post body 8","author":{"first_name":"Pavel","last_name":"Pravosud"}},
444
+ {"id":9,"body":"post body 9","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
445
+ {"id":10,"body":"post body 10","author":{"first_name":"Pavel","last_name":"Pravosud"}}
446
+ ],"joints":joints,"defers":defers});
447
+ })()
448
+ JS
449
+
450
+ assert_equal expected, result
451
+ end
452
+
453
+ test "renders array of partials as empty array with nil-collection" do
454
+ result = jbuild(<<-JBUILDER)
455
+ json.array! nil, partial: "blog_post", as: :blog_post
456
+ JBUILDER
457
+
458
+ expected = strip_format(<<-JS)
459
+ (function(){
460
+ var joints={};
461
+ var cache={};
462
+ var defers=[];
463
+ return ({"data":[],"joints":joints,"defers":defers});
464
+ })()
465
+ JS
466
+
467
+ assert_equal expected, result
468
+ end
469
+
470
+ test "renders the partial and ignores the value" do
471
+ result = jbuild <<-JBUILDER
472
+ json.posts nil, partial: "footer"
473
+ JBUILDER
474
+
475
+ expected = strip_format(<<-JS)
476
+ (function(){
477
+ var joints={};
478
+ var cache={};
479
+ var defers=[];
480
+ return ({"data":{"posts":{"terms":"You agree"}},"joints":joints,"defers":defers});
481
+ })()
482
+ JS
483
+ assert_equal expected, result
484
+ end
485
+
486
+ test "caching a value at a node" do
487
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
488
+
489
+ result = jbuild(<<-JBUILDER)
490
+ json.wrap! :cache, ['b', 'c']
491
+ json.hello 32
492
+ JBUILDER
493
+
494
+ expected = strip_format(<<-JS)
495
+ (function(){
496
+ var joints={};
497
+ var cache={};
498
+ var defers=[];
499
+ cache["#{cache_keys[0]}"]=32;
500
+ return ({"data":{"hello":cache["#{cache_keys[0]}"]},"joints":joints,"defers":defers});
501
+ })()
502
+ JS
503
+
504
+ assert_equal expected, result
505
+ end
506
+
507
+ test "caching elements in a list" do
508
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
509
+
510
+ result = jbuild(<<-JBUILDER)
511
+ json.hello do
512
+ json.wrap! :cache, ->(i){ ['a', i] }
513
+ json.array! [4,5] do |x|
514
+ json.top "hello" + x.to_s
515
+ end
516
+ end
517
+ JBUILDER
518
+
519
+ expected = strip_format(<<-JS)
520
+ (function(){
521
+ var joints={};
522
+ var cache={};
523
+ var defers=[];
524
+ cache["#{cache_keys[0]}"]={"top":"hello4"};
525
+ cache["#{cache_keys[1]}"]={"top":"hello5"};
526
+ return ({"data":{"hello":[cache["#{cache_keys[0]}"],cache["#{cache_keys[1]}"]]},"joints":joints,"defers":defers});
527
+ })()
528
+ JS
529
+
530
+ assert_equal expected, result
531
+ end
532
+
533
+ test "nested caching generates a depth-first list of cache nodes" do
534
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
535
+
536
+ result = jbuild(<<-JBUILDER)
537
+ json.wrap! :cache, ['a', 'b']
538
+ json.hello do
539
+ json.wrap! :cache, ['d', 'z']
540
+ json.content do
541
+ json.subcontent 'inner'
542
+ end
543
+ json.wrap! :cache, ['e', 'z']
544
+ json.other do
545
+ json.subcontent 'other'
546
+ end
547
+ end
548
+ JBUILDER
549
+
550
+ expected = strip_format(<<-JS)
551
+ (function(){
552
+ var joints={};
553
+ var cache={};
554
+ var defers=[];
555
+ cache["#{cache_keys[0]}"]={"subcontent":"inner"};
556
+ cache["#{cache_keys[1]}"]={"subcontent":"other"};
557
+ cache["#{cache_keys[2]}"]={"content":cache["#{cache_keys[0]}"],"other":cache["#{cache_keys[1]}"]};
558
+ return ({"data":{"hello":cache["#{cache_keys[2]}"]},"joints":joints,"defers":defers});
559
+ })()
560
+ JS
561
+
562
+ assert_equal expected, result
563
+ end
564
+
565
+ test "caching an empty block generates no cache and no errors" do
566
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
567
+
568
+ result = nil
569
+
570
+ assert_nothing_raised do
571
+ result = jbuild(<<-JBUILDER)
572
+ json.hello do
573
+ json.array! [4,5], cache: ->(i){['a', i]} do |x|
574
+ end
575
+ end
576
+ JBUILDER
577
+ end
578
+
579
+ expected = strip_format(<<-JS)
580
+ (function(){
581
+ var joints={};
582
+ var cache={};
583
+ var defers=[];
584
+ return ({\"data\":{\"hello\":[]},"joints":joints,"defers":defers});
585
+ })()
586
+ JS
587
+
588
+ assert_equal expected, result
589
+ end
590
+ #
591
+ # test "child! accepts cache options" do
592
+ # undef_context_methods :fragment_name_with_digest, :cache_fragment_name
593
+ #
594
+ # result = jbuild(<<-JBUILDER)
595
+ # json.comments do
596
+ # json.child!(cache: ['e', 'z']) { json.content "hello" }
597
+ # json.child! { json.content "world" }
598
+ # end
599
+ # JBUILDER
600
+ #
601
+ # expected = strip_format(<<-JS)
602
+ # (function(){
603
+ # cache["#{cache_keys[0]}", {"content":"hello"});
604
+ # return ({"data":{"comments":[cache["#{cache_keys[0]}"),{"content":"world"}]}});
605
+ # })()
606
+ # JS
607
+ #
608
+ # assert_equal expected, result
609
+ # end
610
+
611
+ test "fragment caching" do
612
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
613
+
614
+ jbuild(<<-JBUILDER)
615
+ json.wrap! :cache, ['cachekey']
616
+ json.post do
617
+ json.name "Cache"
618
+ end
619
+ JBUILDER
620
+
621
+ result = jbuild(<<-JBUILDER)
622
+ json.wrap! :cache, ['cachekey']
623
+ json.post do
624
+ json.name "Miss"
625
+ end
626
+ JBUILDER
627
+
628
+ expected = strip_format(<<-JS)
629
+ (function(){
630
+ var joints={};
631
+ var cache={};
632
+ var defers=[];
633
+ cache["#{cache_keys[0]}"]={"name":"Cache"};
634
+ return ({"data":{"post":cache["#{cache_keys[0]}"]},"joints":joints,"defers":defers});
635
+ })()
636
+ JS
637
+
638
+ assert_equal expected, result
639
+ end
640
+
641
+ test "fragment caching deserializes an array" do
642
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
643
+
644
+ result = jbuild <<-JBUILDER
645
+ json.wrap! :cache, "cachekey"
646
+ json.content do
647
+ json.array! %w[a b c]
648
+ end
649
+ JBUILDER
650
+
651
+ expected = strip_format(<<-JS)
652
+ (function(){
653
+ var joints={};
654
+ var cache={};
655
+ var defers=[];
656
+ cache["#{cache_keys[0]}"]=["a","b","c"];
657
+ return ({"data":{"content":cache["#{cache_keys[0]}"]},"joints":joints,"defers":defers});
658
+ })()
659
+ JS
660
+
661
+ assert_equal expected, result
662
+ end
663
+ #
664
+ test "fragment caching works with previous version of cache digests" do
665
+ undef_context_methods :cache_fragment_name
666
+
667
+ if !@context.class.method_defined? :fragment_name_with_digest
668
+ @context.class_eval do
669
+ def fragment_name_with_digest
670
+ end
671
+ end
672
+ end
673
+
674
+ @context.expects :fragment_name_with_digest
675
+
676
+ jbuild <<-JBUILDER
677
+ json.wrap! :cache, 'cachekey'
678
+ json.content do
679
+ json.name "Cache"
680
+ end
681
+ JBUILDER
682
+ end
683
+
684
+ test "fragment caching works with current cache digests" do
685
+ undef_context_methods :fragment_name_with_digest
686
+
687
+ @context.expects :cache_fragment_name
688
+ ActiveSupport::Cache.expects :expand_cache_key
689
+
690
+ jbuild <<-JBUILDER
691
+ json.wrap! :cache, 'cachekey'
692
+ json.content do
693
+ json.name "Cache"
694
+ end
695
+ JBUILDER
696
+ end
697
+
698
+ test "current cache digest option accepts options through the last element hash" do
699
+ undef_context_methods :fragment_name_with_digest
700
+
701
+ @context.expects(:cache_fragment_name)
702
+ .with("cachekey", skip_digest: true)
703
+ .returns("cachekey")
704
+
705
+ ActiveSupport::Cache.expects :expand_cache_key
706
+
707
+ jbuild <<-JBUILDER
708
+ json.wrap! :cache, 'cachekey', skip_digest: true
709
+ json.content do
710
+ json.name "Cache"
711
+ end
712
+ JBUILDER
713
+ end
714
+
715
+ test "does not perform caching when controller.perform_caching is false" do
716
+ controller.perform_caching = false
717
+
718
+ result = jbuild <<-JBUILDER
719
+ json.wrap! :cache, 'cachekey'
720
+ json.content do
721
+ json.name "Cache"
722
+ end
723
+ JBUILDER
724
+
725
+ expected = strip_format(<<-JS)
726
+ (function(){
727
+ var joints={};
728
+ var cache={};
729
+ var defers=[];
730
+ return ({"data":{"content":{"name":"Cache"}},"joints":joints,"defers":defers});
731
+ })()
732
+ JS
733
+
734
+ assert_equal expected, result
735
+ end
736
+
737
+ test "invokes templates via params via set! and caches" do
738
+ @post = BLOG_POST_COLLECTION.first
739
+
740
+ result = jbuild(<<-JBUILDER)
741
+ json.wrap! :cache, ['a', 'b']
742
+ json.post @post, partial: "blog_post", as: :blog_post
743
+ JBUILDER
744
+
745
+ expected = strip_format(<<-JS)
746
+ (function(){
747
+ var joints={};
748
+ var cache={};
749
+ var defers=[];
750
+ cache["#{cache_keys[0]}"]={"id":1,"body":"post body 1","author":{"first_name":"David","last_name":"Heinemeier Hansson"}};
751
+ return ({"data":{"post":cache["#{cache_keys[0]}"]},"joints":joints,"defers":defers});
752
+ })()
753
+ JS
754
+
755
+ assert_equal expected, result
756
+ end
757
+
758
+ test "shares partial caches (via the partial's digest) across multiple templates" do
759
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
760
+
761
+ @hit = BlogPost.new(1, "hit", "John Smith")
762
+ @miss = BlogPost.new(2, "miss", "John Smith")
763
+
764
+ cat = jbuild(<<-JBUILDER)
765
+ json.wrap! :cache, ['a', 'b']
766
+ json.post @hit, partial: "blog_post", as: :blog_post
767
+ JBUILDER
768
+
769
+ result = jbuild(<<-JBUILDER)
770
+ json.wrap! :cache, ['a', 'b']
771
+ json.post @miss, partial: "blog_post", as: :blog_post
772
+ JBUILDER
773
+
774
+ expected = strip_format(<<-JS)
775
+ (function(){
776
+ var joints={};
777
+ var cache={};
778
+ var defers=[];
779
+ cache["#{cache_keys[0]}"]={"id":1,"body":"hit","author":{"first_name":"John","last_name":"Smith"}};
780
+ return ({"data":{"post":cache["#{cache_keys[0]}"]},"joints":joints,"defers":defers});
781
+ })()
782
+ JS
783
+
784
+ assert_equal expected, result
785
+ end
786
+
787
+
788
+ test "render array of partials and caches" do
789
+ result = jbuild(<<-JBUILDER)
790
+ json.wrap! :cache, ->(d){ ['a', d.id] }
791
+ json.array! BLOG_POST_COLLECTION, partial: "blog_post", as: :blog_post
792
+ JBUILDER
793
+ Rails.cache.clear
794
+
795
+ expected = strip_format(<<-JS)
796
+ (function(){
797
+ var joints={};
798
+ var cache={};
799
+ var defers=[];
800
+ cache["#{cache_keys[0]}"]={"id":1,"body":"post body 1","author":{"first_name":"David","last_name":"Heinemeier Hansson"}};
801
+ cache["#{cache_keys[1]}"]={"id":2,"body":"post body 2","author":{"first_name":"Pavel","last_name":"Pravosud"}};
802
+ cache["#{cache_keys[2]}"]={"id":3,"body":"post body 3","author":{"first_name":"David","last_name":"Heinemeier Hansson"}};
803
+ cache["#{cache_keys[3]}"]={"id":4,"body":"post body 4","author":{"first_name":"Pavel","last_name":"Pravosud"}};
804
+ cache["#{cache_keys[4]}"]={"id":5,"body":"post body 5","author":{"first_name":"David","last_name":"Heinemeier Hansson"}};
805
+ cache["#{cache_keys[5]}"]={"id":6,"body":"post body 6","author":{"first_name":"Pavel","last_name":"Pravosud"}};
806
+ cache["#{cache_keys[6]}"]={"id":7,"body":"post body 7","author":{"first_name":"David","last_name":"Heinemeier Hansson"}};
807
+ cache["#{cache_keys[7]}"]={"id":8,"body":"post body 8","author":{"first_name":"Pavel","last_name":"Pravosud"}};
808
+ cache["#{cache_keys[8]}"]={"id":9,"body":"post body 9","author":{"first_name":"David","last_name":"Heinemeier Hansson"}};
809
+ cache["#{cache_keys[9]}"]={"id":10,"body":"post body 10","author":{"first_name":"Pavel","last_name":"Pravosud"}};
810
+ return ({"data":[cache["#{cache_keys[0]}"],cache["#{cache_keys[1]}"],cache["#{cache_keys[2]}"],cache["#{cache_keys[3]}"],cache["#{cache_keys[4]}"],cache["#{cache_keys[5]}"],cache["#{cache_keys[6]}"],cache["#{cache_keys[7]}"],cache["#{cache_keys[8]}"],cache["#{cache_keys[9]}"]],"joints":joints,"defers":defers});
811
+ })()
812
+ JS
813
+ assert_equal expected, result
814
+ end
815
+
816
+ test "filtering for a node in the tree" do
817
+ result = jbuild(<<-JBUILDER)
818
+ json._filter_by_path('hit.hit2')
819
+ json.hit do
820
+ json.hit2 do
821
+ json.greeting 'hello world'
822
+ end
823
+ end
824
+
825
+ json.miss do
826
+ json.miss2 do
827
+ raise 'this should not be called'
828
+ json.greeting 'missed call'
829
+ end
830
+ end
831
+ JBUILDER
832
+ Rails.cache.clear
833
+
834
+ expected = strip_format(<<-JS)
835
+ (function(){
836
+ var joints={};
837
+ var cache={};
838
+ var defers=[];
839
+ return (
840
+ {"data":{"greeting":"hello world"},"joints":joints,"defers":defers}
841
+ );
842
+ })()
843
+ JS
844
+
845
+ assert_equal expected, result
846
+ end
847
+
848
+ test "filtering for a raw value is also possble" do
849
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2')
850
+ json.hit do
851
+ json.hit2 23
852
+ end
853
+
854
+ json.miss do
855
+ json.miss2 do
856
+ raise 'this should not be called'
857
+ json.greeting 'missed call'
858
+ end
859
+ end
860
+ JBUILDER
861
+ Rails.cache.clear
862
+
863
+ expected = strip_format(<<-JS)
864
+ (function(){
865
+ var joints={};
866
+ var cache={};
867
+ var defers=[];
868
+ return (
869
+ {"data":23,"action":"graft","path":"hit.hit2","joints":joints,"defers":defers}
870
+ );
871
+ })()
872
+ JS
873
+
874
+ assert_equal expected, result
875
+ end
876
+
877
+ test "filter with partial" do
878
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.terms')
879
+ json.hit do
880
+ json.hit2 nil, partial: "footer"
881
+ end
882
+ JBUILDER
883
+
884
+ expected = strip_format(<<-JS)
885
+ (function(){
886
+ var joints={};
887
+ var cache={};
888
+ var defers=[];
889
+ return (
890
+ {"data":"You agree","action":"graft","path":"hit.hit2.terms","joints":joints,"defers":defers}
891
+ );
892
+ })()
893
+ JS
894
+ assert_equal expected, result
895
+ end
896
+
897
+ test "filtering for a node in the tree via breezy_filter helper" do
898
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2')
899
+ json.hit do
900
+ json.hit2 do
901
+ json.greeting 'hello world'
902
+ end
903
+ end
904
+
905
+ json.miss do
906
+ json.miss2 do
907
+ raise 'this should not be called'
908
+ json.greeting 'missed call'
909
+ end
910
+ end
911
+ JBUILDER
912
+ Rails.cache.clear
913
+
914
+ expected = strip_format(<<-JS)
915
+ (function(){
916
+ var joints={};
917
+ var cache={};
918
+ var defers=[];
919
+ return (
920
+ {"data":{"greeting":"hello world"},"action":"graft","path":"hit.hit2","joints":joints,"defers":defers}
921
+ );
922
+ })()
923
+ JS
924
+
925
+ assert_equal expected, result
926
+ end
927
+
928
+ test "filtering a cached node returns just that" do
929
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
930
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2')
931
+ json.hit do
932
+ json.wrap! :cache, ['a']
933
+ json.hit2 do
934
+ json.greeting 'hello world'
935
+ end
936
+ end
937
+ JBUILDER
938
+ Rails.cache.clear
939
+
940
+ expected = strip_format(<<-JS)
941
+ (function(){
942
+ var joints={};
943
+ var cache={};
944
+ var defers=[];
945
+ cache["#{cache_keys[0]}"]={"greeting":"hello world"};
946
+ return ({"data":cache["219dfba9f552f91402a22cf67c633582"],"action":"graft","path":"hit.hit2","joints":joints,"defers":defers});
947
+ })()
948
+
949
+
950
+ JS
951
+
952
+ assert_equal expected, result
953
+ end
954
+
955
+ test "filtering for a node of a AR relation in a tree by id via an appended where clause" do
956
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.id=1')
957
+ post = Post.create
958
+ post.comments.create title: 'first'
959
+ post.comments.create title: 'second'
960
+
961
+ post.comments.expects(:where).once().with('id'=>1).returns([{id: 1, title: 'first'}])
962
+
963
+ json.hit do
964
+ json.hit2 do
965
+ json.array! post.comments do |x|
966
+ raise 'this should be be called' if x[:title] == 'second'
967
+ json.title x[:title]
968
+ end
969
+ end
970
+ end
971
+ JBUILDER
972
+
973
+ Rails.cache.clear
974
+
975
+ expected = strip_format(<<-JS)
976
+ (function(){
977
+ var joints={};
978
+ var cache={};
979
+ var defers=[];
980
+ return (
981
+ {"data":{"title":"first"},"action":"graft","path":"hit.hit2.id=1","joints":joints,"defers":defers}
982
+ );
983
+ })()
984
+ JS
985
+ assert_equal expected, result
986
+ end
987
+
988
+
989
+ test "filtering for a node of a AR relation in a tree by index via an appended where clause" do
990
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.0')
991
+ post = Post.create
992
+ post.comments.create title: 'first'
993
+ post.comments.create title: 'second'
994
+
995
+ offset = post.comments.offset(0)
996
+ post.comments.expects(:offset).once().with(0).returns(offset)
997
+
998
+ json.hit do
999
+ json.hit2 do
1000
+ json.array! post.comments do |x|
1001
+ raise 'this should be be called' if x[:title] == 'second'
1002
+ json.title x[:title]
1003
+ end
1004
+ end
1005
+ end
1006
+ JBUILDER
1007
+
1008
+ Rails.cache.clear
1009
+
1010
+ expected = strip_format(<<-JS)
1011
+ (function(){
1012
+ var joints={};
1013
+ var cache={};
1014
+ var defers=[];
1015
+ return (
1016
+ {"data":{"title":"first"},"action":"graft","path":"hit.hit2.0","joints":joints,"defers":defers}
1017
+ );
1018
+ })()
1019
+ JS
1020
+ assert_equal expected, result
1021
+ end
1022
+
1023
+ test "filtering for a node in an array of a tree by id" do
1024
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.id=1')
1025
+ json.hit do
1026
+ json.hit2 do
1027
+ json.array! [{id: 1, name: 'hit' }, {id:2, name: 'miss'}] do |x|
1028
+ raise 'this should be be called' if x[:name] == 'miss'
1029
+ json.name x[:name]
1030
+ end
1031
+ end
1032
+ end
1033
+ JBUILDER
1034
+ Rails.cache.clear
1035
+
1036
+ expected = strip_format(<<-JS)
1037
+ (function(){
1038
+ var joints={};
1039
+ var cache={};
1040
+ var defers=[];
1041
+ return (
1042
+ {"data":{"name":"hit"},"action":"graft","path":"hit.hit2.id=1","joints":joints,"defers":defers}
1043
+ );
1044
+ })()
1045
+ JS
1046
+
1047
+ assert_equal expected, result
1048
+ end
1049
+
1050
+ test "filtering for a node in an array of a tree by index" do
1051
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.0')
1052
+ json.hit do
1053
+ json.hit2 do
1054
+ json.array! [{id: 1, name: 'hit' }, {id:2, name: 'miss'}] do |x|
1055
+ raise 'this should be be called' if x[:name] == 'miss'
1056
+ json.name x[:name]
1057
+ end
1058
+ end
1059
+ end
1060
+ JBUILDER
1061
+ Rails.cache.clear
1062
+
1063
+ expected = strip_format(<<-JS)
1064
+ (function(){
1065
+ var joints={};
1066
+ var cache={};
1067
+ var defers=[];
1068
+ return (
1069
+ {"data":{"name":"hit"},"action":"graft","path":"hit.hit2.0","joints":joints,"defers":defers}
1070
+ );
1071
+ })()
1072
+ JS
1073
+
1074
+ assert_equal expected, result
1075
+ end
1076
+
1077
+ test "rendering with node deferement" do
1078
+ req = action_controller_test_request
1079
+ req.path = '/some_url'
1080
+
1081
+ result = jbuild(<<-JBUILDER, request: req)
1082
+ json.hit do
1083
+ json.wrap! :defer, :auto
1084
+ json.hit2 do
1085
+ json.hit3 do
1086
+ json.greeting 'hello world'
1087
+ end
1088
+ end
1089
+ end
1090
+ JBUILDER
1091
+ Rails.cache.clear
1092
+
1093
+ expected = strip_format(<<-JS)
1094
+ (function(){
1095
+ var joints={};
1096
+ var cache={};
1097
+ var defers=[];
1098
+ defers.push({url:'/some_url?_breezy_filter=hit.hit2'});
1099
+ return (
1100
+ {"data":{"hit":{"hit2":null}},"joints":joints,"defers":defers}
1101
+ );
1102
+ })()
1103
+ JS
1104
+
1105
+ assert_equal expected, result
1106
+ end
1107
+
1108
+ test "rendering with manual node deferement" do
1109
+ req = action_controller_test_request
1110
+ req.path = '/some_url'
1111
+
1112
+ result = jbuild(<<-JBUILDER, request: req)
1113
+ json.hit do
1114
+ json.wrap! :defer, :manual
1115
+ json.hit2 do
1116
+ json.hit3 do
1117
+ json.greeting 'hello world'
1118
+ end
1119
+ end
1120
+ end
1121
+ JBUILDER
1122
+ Rails.cache.clear
1123
+
1124
+ expected = strip_format(<<-JS)
1125
+ (function(){
1126
+ var joints={};
1127
+ var cache={};
1128
+ var defers=[];
1129
+ return (
1130
+ {"data":{"hit":{"hit2":null}},"joints":joints,"defers":defers}
1131
+ );
1132
+ })()
1133
+ JS
1134
+
1135
+ assert_equal expected, result
1136
+ end
1137
+
1138
+ test "rendering with node array deferment" do
1139
+ req = action_controller_test_request
1140
+ req.path = '/some_url'
1141
+
1142
+ result = jbuild(<<-JBUILDER, request: req)
1143
+ json.hit do
1144
+ json.hit2 do
1145
+ data = [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}]
1146
+ json.array! data, key: :id do
1147
+ json.wrap! :defer, :auto
1148
+ json.greeting do
1149
+ json.gree 'hi'
1150
+ end
1151
+ end
1152
+ end
1153
+ end
1154
+ JBUILDER
1155
+ Rails.cache.clear
1156
+
1157
+ expected = strip_format(<<-JS)
1158
+ (function(){
1159
+ var joints={};
1160
+ var cache={};
1161
+ var defers=[];
1162
+ defers.push({url:'/some_url?_breezy_filter=hit.hit2.id%3D1.greeting'});
1163
+ defers.push({url:'/some_url?_breezy_filter=hit.hit2.id%3D2.greeting'});
1164
+ return (
1165
+ {"data":{"hit":{"hit2":[{"greeting":null},{"greeting":null}]}},"joints":joints,"defers":defers}
1166
+ );
1167
+ })()
1168
+ JS
1169
+
1170
+ assert_equal expected, result
1171
+ end
1172
+
1173
+ test 'deferment does not work on values' do
1174
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
1175
+
1176
+ result = jbuild(<<-JBUILDER)
1177
+ json.wrap! :defer, :auto
1178
+ json.hello(32, defer: :auto)
1179
+ JBUILDER
1180
+
1181
+ expected = strip_format(<<-JS)
1182
+ (function(){
1183
+ var joints={};
1184
+ var cache={};
1185
+ var defers=[];
1186
+ return ({"data":{"hello":32},"joints":joints,"defers":defers});
1187
+ })()
1188
+ JS
1189
+
1190
+ assert_equal expected, result
1191
+ end
1192
+
1193
+ test 'deferment is disabled when filtering by keypath' do
1194
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
1195
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hello.world')
1196
+ json.wrap! :defer, :auto
1197
+ json.hello defer: :auto do
1198
+ json.world 32
1199
+ end
1200
+ JBUILDER
1201
+
1202
+ expected = strip_format(<<-JS)
1203
+ (function(){
1204
+ var joints={};
1205
+ var cache={};
1206
+ var defers=[];
1207
+ return ({"data":32,"action":"graft","path":"hello.world","joints":joints,"defers":defers});
1208
+ })()
1209
+ JS
1210
+
1211
+ assert_equal expected, result
1212
+
1213
+ end
1214
+
1215
+ test 'deferment is enabled at the end of a keypath when filtering' do
1216
+ undef_context_methods :fragment_name_with_digest, :cache_fragment_name
1217
+ result = jbuild(<<-JBUILDER, breezy_filter: 'hello')
1218
+ json.wrap! :defer, :auto
1219
+ json.hello do
1220
+ json.wrap! :defer, :auto
1221
+ json.content do
1222
+ json.world 32
1223
+ end
1224
+ end
1225
+ JBUILDER
1226
+
1227
+ expected = strip_format(<<-JS)
1228
+ (function(){
1229
+ var joints={};
1230
+ var cache={};
1231
+ var defers=[];
1232
+ defers.push({url:'?_breezy_filter=hello.content'});
1233
+ return ({"data":{"content":null},"action":"graft","path":"hello","joints":joints,"defers":defers});
1234
+ })()
1235
+ JS
1236
+
1237
+ assert_equal expected, result
1238
+ end
1239
+ end