breezy_template 0.2.0

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.
@@ -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