breezy 0.1.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.
- checksums.yaml +7 -0
- data/README.md +326 -0
- data/lib/assets/javascripts/breezy.coffee +4 -0
- data/lib/breezy.rb +71 -0
- data/lib/breezy/active_support.rb +21 -0
- data/lib/breezy/configuration.rb +21 -0
- data/lib/breezy/cookies.rb +15 -0
- data/lib/breezy/helpers.rb +28 -0
- data/lib/breezy/render.rb +36 -0
- data/lib/breezy/version.rb +3 -0
- data/lib/breezy/x_domain_blocker.rb +22 -0
- data/lib/breezy/xhr_headers.rb +56 -0
- data/lib/breezy/xhr_redirect.rb +13 -0
- data/lib/breezy/xhr_url_for.rb +11 -0
- data/test/blade_helper.rb +22 -0
- data/test/breezy_template_test.rb +1025 -0
- data/test/breezy_test.rb +171 -0
- data/test/configuration_test.rb +29 -0
- data/test/dependency_tracker_test.rb +66 -0
- data/test/engine_test.rb +7 -0
- data/test/render_test.rb +124 -0
- data/test/test_helper.rb +18 -0
- metadata +168 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module Breezy
|
2
|
+
# For non-GET requests, sets a request_method cookie containing
|
3
|
+
# the request method of the current request. The Breezy script
|
4
|
+
# will not initialize if this cookie is set.
|
5
|
+
module Cookies
|
6
|
+
private
|
7
|
+
def set_request_method_cookie
|
8
|
+
if request.get?
|
9
|
+
cookies.delete(:request_method)
|
10
|
+
else
|
11
|
+
cookies[:request_method] = request.request_method
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Breezy
|
2
|
+
module Helpers
|
3
|
+
def breezy_tag
|
4
|
+
if defined?(@breezy) && @breezy
|
5
|
+
"<script type='text/javascript'>Breezy.replace(#{@breezy});</script>".html_safe
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def breezy_snippet
|
10
|
+
if defined?(@breezy) && @breezy
|
11
|
+
snippet = @breezy.gsub(/\;$/, '')
|
12
|
+
"Breezy.replace(#{snippet});".html_safe
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def use_breezy_html
|
17
|
+
@_use_breezy_html = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def breezy_silient?
|
21
|
+
!!request.headers["X-SILENT"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def breezy_filter
|
25
|
+
request.params[:_breezy_filter] || (session && session[:breezy_filter])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Breezy
|
2
|
+
module Render
|
3
|
+
def default_render(*args)
|
4
|
+
if @_use_breezy_html
|
5
|
+
render(*args)
|
6
|
+
else
|
7
|
+
super
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def render(*args, &block)
|
12
|
+
render_options = args.extract_options!
|
13
|
+
breezy = render_options.delete(:breezy)
|
14
|
+
breezy = {} if breezy == true || @_use_breezy_html
|
15
|
+
|
16
|
+
if breezy
|
17
|
+
view_parts = _prefixes.reverse.push(action_name)[1..-1]
|
18
|
+
view_name = view_parts.map(&:camelize).join
|
19
|
+
|
20
|
+
breezy[:view] ||= view_name
|
21
|
+
render_options[:locals] ||= {}
|
22
|
+
render_options[:locals][:breezy] = breezy
|
23
|
+
end
|
24
|
+
|
25
|
+
if @_use_breezy_html && request.format == :html
|
26
|
+
original_formats = self.formats
|
27
|
+
|
28
|
+
@breezy = render_to_string(*args, render_options.merge(formats: [:js]))
|
29
|
+
self.formats = original_formats
|
30
|
+
render_options.reverse_merge!(formats: original_formats, template: 'breezy/response')
|
31
|
+
end
|
32
|
+
|
33
|
+
super(*args, render_options, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Breezy
|
2
|
+
# Changes the response status to 403 Forbidden if all of these conditions are true:
|
3
|
+
# - The current request originated from Breezy
|
4
|
+
# - The request is being redirected to a different domain
|
5
|
+
module XDomainBlocker
|
6
|
+
private
|
7
|
+
def same_origin?(a, b)
|
8
|
+
a = URI.parse URI.escape(a)
|
9
|
+
b = URI.parse URI.escape(b)
|
10
|
+
[a.scheme, a.host, a.port] == [b.scheme, b.host, b.port]
|
11
|
+
end
|
12
|
+
|
13
|
+
def abort_xdomain_redirect
|
14
|
+
to_uri = response.headers['Location']
|
15
|
+
current = request.headers['X-XHR-Referer']
|
16
|
+
unless to_uri.blank? || current.blank? || same_origin?(current, to_uri)
|
17
|
+
self.status = 403
|
18
|
+
end
|
19
|
+
rescue URI::InvalidURIError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Breezy
|
2
|
+
# Intercepts calls to _compute_redirect_to_location (used by redirect_to) for two purposes.
|
3
|
+
#
|
4
|
+
# 1. Corrects the behavior of redirect_to with the :back option by using the X-XHR-Referer
|
5
|
+
# request header instead of the standard Referer request header.
|
6
|
+
#
|
7
|
+
# 2. Stores the return value (the redirect target url) to persist through to the redirect
|
8
|
+
# request, where it will be used to set the X-XHR-Redirected-To response header. The
|
9
|
+
# Breezy script will detect the header and use replaceState to reflect the redirected
|
10
|
+
# url.
|
11
|
+
module XHRHeaders
|
12
|
+
if Rails.version >= '5.0'
|
13
|
+
def redirect_back(fallback_location:, **args)
|
14
|
+
if referer = request.headers["X-XHR-Referer"]
|
15
|
+
rsp = redirect_to referer, **args
|
16
|
+
store_for_breezy(self.location)
|
17
|
+
rsp
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def _compute_redirect_to_location(*args)
|
25
|
+
options, request = _normalize_redirect_params(args)
|
26
|
+
|
27
|
+
store_for_breezy begin
|
28
|
+
if options == :back && request.headers["X-XHR-Referer"]
|
29
|
+
super(*[(request if args.length == 2), request.headers["X-XHR-Referer"]].compact)
|
30
|
+
else
|
31
|
+
super(*args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def store_for_breezy(url)
|
38
|
+
session[:_breezy_redirect_to] = url if session && request.headers["X-XHR-Referer"]
|
39
|
+
url
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_xhr_redirected_to
|
43
|
+
if session && session[:_breezy_redirect_to]
|
44
|
+
response.headers['X-XHR-Redirected-To'] = session.delete :_breezy_redirect_to
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Ensure backwards compatibility
|
49
|
+
# Rails < 4.2: _compute_redirect_to_location(options)
|
50
|
+
# Rails >= 4.2: _compute_redirect_to_location(request, options)
|
51
|
+
def _normalize_redirect_params(args)
|
52
|
+
options, req = args.reverse
|
53
|
+
[options, req || request]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Breezy
|
2
|
+
module XHRRedirect
|
3
|
+
def call(env)
|
4
|
+
status, headers, body = super(env)
|
5
|
+
|
6
|
+
if env['rack.session'] && env['HTTP_X_XHR_REFERER']
|
7
|
+
env['rack.session'][:_breezy_redirect_to] = headers['Location']
|
8
|
+
end
|
9
|
+
|
10
|
+
[status, headers, body]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Breezy
|
2
|
+
# Corrects the behavior of url_for (and link_to, which uses url_for) with the :back
|
3
|
+
# option by using the X-XHR-Referer request header instead of the standard Referer
|
4
|
+
# request header.
|
5
|
+
module XHRUrlFor
|
6
|
+
def url_for(options = {})
|
7
|
+
options = (controller.request.headers["X-XHR-Referer"] || options) if options == :back
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative "dummy/application"
|
2
|
+
Dummy::Application.initialize!
|
3
|
+
|
4
|
+
module Blade
|
5
|
+
module Server
|
6
|
+
private
|
7
|
+
|
8
|
+
def app
|
9
|
+
Rack::Builder.app do
|
10
|
+
use Rack::ShowExceptions
|
11
|
+
|
12
|
+
map '/app' do
|
13
|
+
run Dummy::Application
|
14
|
+
end
|
15
|
+
|
16
|
+
map '/' do
|
17
|
+
run Blade::RackAdapter.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,1025 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "mocha/setup"
|
3
|
+
require "action_view"
|
4
|
+
require "action_view/testing/resolvers"
|
5
|
+
require "breezy_template"
|
6
|
+
|
7
|
+
BLOG_POST_PARTIAL = <<-JBUILDER
|
8
|
+
json.extract! blog_post, :id, :body
|
9
|
+
json.author do
|
10
|
+
first_name, last_name = blog_post.author_name.split(nil, 2)
|
11
|
+
json.first_name first_name
|
12
|
+
json.last_name last_name
|
13
|
+
end
|
14
|
+
JBUILDER
|
15
|
+
|
16
|
+
COLLECTION_PARTIAL = <<-JBUILDER
|
17
|
+
json.extract! collection, :id, :name
|
18
|
+
JBUILDER
|
19
|
+
|
20
|
+
PROFILE_PARTIAL = <<-JBUILDER
|
21
|
+
json.email email
|
22
|
+
JBUILDER
|
23
|
+
|
24
|
+
FOOTER_PARTIAL = <<-JBUILDER
|
25
|
+
json.terms "You agree"
|
26
|
+
JBUILDER
|
27
|
+
|
28
|
+
BlogPost = Struct.new(:id, :body, :author_name)
|
29
|
+
Collection = Struct.new(:id, :name)
|
30
|
+
blog_authors = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
|
31
|
+
BLOG_POST_COLLECTION = Array.new(10){ |i| BlogPost.new(i+1, "post body #{i+1}", blog_authors.next) }
|
32
|
+
COLLECTION_COLLECTION = Array.new(5){ |i| Collection.new(i+1, "collection #{i+1}") }
|
33
|
+
|
34
|
+
ActionView::Template.register_template_handler :breezy, BreezyTemplate::Handler
|
35
|
+
|
36
|
+
PARTIALS = {
|
37
|
+
"_partial.js.breezy" => "foo ||= 'hello'; json.content foo",
|
38
|
+
"_blog_post.js.breezy" => BLOG_POST_PARTIAL,
|
39
|
+
"_profile.js.breezy" => PROFILE_PARTIAL,
|
40
|
+
"_footer.js.breezy" => FOOTER_PARTIAL,
|
41
|
+
"_collection.js.breezy" => COLLECTION_PARTIAL
|
42
|
+
}
|
43
|
+
|
44
|
+
def strip_format(str)
|
45
|
+
str.strip_heredoc.gsub(/\n\s*/, "")
|
46
|
+
end
|
47
|
+
|
48
|
+
class BreezyTemplateTest < ActionView::TestCase
|
49
|
+
setup do
|
50
|
+
self.request_forgery = false
|
51
|
+
Breezy.configuration.track_assets = []
|
52
|
+
|
53
|
+
# this is a stub. Normally this would be set by the
|
54
|
+
# controller locals
|
55
|
+
self.breezy = {}
|
56
|
+
|
57
|
+
@context = self
|
58
|
+
Rails.cache.clear
|
59
|
+
end
|
60
|
+
|
61
|
+
cattr_accessor :request_forgery, :breezy
|
62
|
+
self.request_forgery = false
|
63
|
+
|
64
|
+
def breezy_filter
|
65
|
+
@breezy_filter
|
66
|
+
end
|
67
|
+
|
68
|
+
def request
|
69
|
+
@request
|
70
|
+
end
|
71
|
+
|
72
|
+
def jbuild(source, opts={})
|
73
|
+
@breezy_filter = opts[:breezy_filter]
|
74
|
+
@request = opts[:request] || action_controller_test_request
|
75
|
+
@rendered = []
|
76
|
+
partials = PARTIALS.clone
|
77
|
+
partials["test.js.breezy"] = source
|
78
|
+
resolver = ActionView::FixtureResolver.new(partials)
|
79
|
+
lookup_context.view_paths = [resolver]
|
80
|
+
lookup_context.formats = [:js]
|
81
|
+
template = ActionView::Template.new(source, "test", BreezyTemplate::Handler, virtual_path: "test")
|
82
|
+
template.render(self, {}).strip
|
83
|
+
end
|
84
|
+
|
85
|
+
def action_controller_test_request
|
86
|
+
if ::Rails.version.start_with?('5')
|
87
|
+
::ActionController::TestRequest.create
|
88
|
+
else
|
89
|
+
::ActionController::TestRequest.new
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def cache_keys
|
94
|
+
major_v = Rails::VERSION::MAJOR
|
95
|
+
minor_v = Rails::VERSION::MINOR
|
96
|
+
rails_v = "rails#{major_v}#{minor_v}"
|
97
|
+
path = File.expand_path("../fixtures/cache_keys.yaml", __FILE__)
|
98
|
+
keys = YAML.load_file(path)
|
99
|
+
keys[method_name][rails_v]
|
100
|
+
end
|
101
|
+
|
102
|
+
def undef_context_methods(*names)
|
103
|
+
self.class_eval do
|
104
|
+
names.each do |name|
|
105
|
+
undef_method name.to_sym if method_defined?(name.to_sym)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def protect_against_forgery?
|
111
|
+
self.request_forgery
|
112
|
+
end
|
113
|
+
|
114
|
+
def form_authenticity_token
|
115
|
+
"secret"
|
116
|
+
end
|
117
|
+
|
118
|
+
test "rendering" do
|
119
|
+
result = jbuild(<<-JBUILDER)
|
120
|
+
json.content "hello"
|
121
|
+
JBUILDER
|
122
|
+
|
123
|
+
expected = strip_format(<<-JS)
|
124
|
+
(function(){
|
125
|
+
return ({"data":{"content":"hello"}});
|
126
|
+
})()
|
127
|
+
JS
|
128
|
+
|
129
|
+
assert_equal expected, result
|
130
|
+
end
|
131
|
+
|
132
|
+
test "when rendering with duplicate keys, the last one wins" do
|
133
|
+
result = jbuild(<<-JBUILDER)
|
134
|
+
json.content do
|
135
|
+
json.miss 123
|
136
|
+
end
|
137
|
+
|
138
|
+
json.content do
|
139
|
+
json.hit 123
|
140
|
+
end
|
141
|
+
JBUILDER
|
142
|
+
|
143
|
+
|
144
|
+
expected = strip_format(<<-JS)
|
145
|
+
(function(){
|
146
|
+
return ({"data":{"content":{"hit":123}}});
|
147
|
+
})()
|
148
|
+
JS
|
149
|
+
|
150
|
+
assert_equal expected, result
|
151
|
+
end
|
152
|
+
|
153
|
+
test "when rendering with duplicate array values, the last one wins" do
|
154
|
+
result = jbuild(<<-JBUILDER)
|
155
|
+
json.content do
|
156
|
+
json.array! [1,2]
|
157
|
+
json.array! [3,4]
|
158
|
+
end
|
159
|
+
JBUILDER
|
160
|
+
|
161
|
+
expected = strip_format(<<-JS)
|
162
|
+
(function(){
|
163
|
+
return ({\"data\":{\"content\":[3,4]}});
|
164
|
+
})()
|
165
|
+
JS
|
166
|
+
|
167
|
+
assert_equal expected, result
|
168
|
+
end
|
169
|
+
|
170
|
+
test "render with asset tracking" do
|
171
|
+
Breezy.configuration.track_assets = ['test.js', 'test.css']
|
172
|
+
|
173
|
+
result = jbuild(<<-TEMPLATE)
|
174
|
+
json.content "hello"
|
175
|
+
TEMPLATE
|
176
|
+
|
177
|
+
expected = strip_format(<<-JS)
|
178
|
+
(function(){
|
179
|
+
return ({"data":{"content":"hello"},"assets":["/test.js","/test.css"]});
|
180
|
+
})()
|
181
|
+
JS
|
182
|
+
|
183
|
+
assert_equal expected, result
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
test "render with csrf token when request forgery is on" do
|
188
|
+
self.request_forgery = true
|
189
|
+
# csrf_meta_tags also delegate authenticity tokens to the controller
|
190
|
+
# here we provide a simple mock to the context
|
191
|
+
|
192
|
+
result = jbuild(<<-TEMPLATE)
|
193
|
+
json.content "hello"
|
194
|
+
TEMPLATE
|
195
|
+
|
196
|
+
expected = strip_format(<<-JS)
|
197
|
+
(function(){
|
198
|
+
return ({"data":{"content":"hello"},"csrf_token":"secret"});
|
199
|
+
})()
|
200
|
+
JS
|
201
|
+
|
202
|
+
assert_equal expected, result
|
203
|
+
end
|
204
|
+
|
205
|
+
test "wrapping jbuilder contents inside Breezy with additional options" do
|
206
|
+
Breezy.configuration.track_assets = ['test.js', 'test.css']
|
207
|
+
self.breezy = { title: 'this is fun' }
|
208
|
+
|
209
|
+
result = jbuild(<<-TEMPLATE)
|
210
|
+
json.content "hello"
|
211
|
+
TEMPLATE
|
212
|
+
|
213
|
+
expected = strip_format(<<-JS)
|
214
|
+
(function(){
|
215
|
+
return ({"data":{"content":"hello"},"title":"this is fun","assets":["/test.js","/test.css"]});
|
216
|
+
})()
|
217
|
+
JS
|
218
|
+
|
219
|
+
assert_equal expected, result
|
220
|
+
end
|
221
|
+
|
222
|
+
test "key_format! with parameter" do
|
223
|
+
result = jbuild(<<-JBUILDER)
|
224
|
+
json.key_format! camelize: [:lower]
|
225
|
+
json.camel_style "for JS"
|
226
|
+
JBUILDER
|
227
|
+
|
228
|
+
expected = strip_format(<<-JS)
|
229
|
+
(function(){
|
230
|
+
return ({"data":{"camelStyle":"for JS"}});
|
231
|
+
})()
|
232
|
+
JS
|
233
|
+
|
234
|
+
assert_equal expected, result
|
235
|
+
end
|
236
|
+
|
237
|
+
test "key_format! propagates to child elements" do
|
238
|
+
result = jbuild(<<-JBUILDER)
|
239
|
+
json.key_format! :upcase
|
240
|
+
json.level1 "one"
|
241
|
+
json.level2 do
|
242
|
+
json.value "two"
|
243
|
+
end
|
244
|
+
JBUILDER
|
245
|
+
|
246
|
+
expected = strip_format(<<-JS)
|
247
|
+
(function(){
|
248
|
+
return ({"data":{
|
249
|
+
"LEVEL1":"one",
|
250
|
+
"LEVEL2":{"VALUE":"two"}
|
251
|
+
}});
|
252
|
+
})()
|
253
|
+
JS
|
254
|
+
|
255
|
+
assert_equal expected, result
|
256
|
+
end
|
257
|
+
|
258
|
+
test "renders partial via the option through set!" do
|
259
|
+
@post = BLOG_POST_COLLECTION.first
|
260
|
+
Rails.cache.clear
|
261
|
+
|
262
|
+
result = jbuild(<<-JBUILDER)
|
263
|
+
json.post @post, partial: "blog_post", as: :blog_post
|
264
|
+
JBUILDER
|
265
|
+
|
266
|
+
expected = strip_format(<<-JS)
|
267
|
+
(function(){
|
268
|
+
return ({"data":{"post":{
|
269
|
+
"id":1,
|
270
|
+
"body":"post body 1",
|
271
|
+
"author":{"first_name":"David","last_name":"Heinemeier Hansson"}
|
272
|
+
}}});
|
273
|
+
})()
|
274
|
+
JS
|
275
|
+
|
276
|
+
assert_equal expected, result
|
277
|
+
end
|
278
|
+
|
279
|
+
test "renders a partial with no locals" do
|
280
|
+
result = jbuild(<<-JBUILDER)
|
281
|
+
json.footer partial: "footer"
|
282
|
+
JBUILDER
|
283
|
+
|
284
|
+
expected = strip_format(<<-JS)
|
285
|
+
(function(){
|
286
|
+
return ({"data":{"footer":{"terms":"You agree"}}});
|
287
|
+
})()
|
288
|
+
JS
|
289
|
+
assert_equal expected, result
|
290
|
+
end
|
291
|
+
|
292
|
+
test "renders a partial with locals" do
|
293
|
+
result = jbuild(<<-JBUILDER)
|
294
|
+
json.profile partial: "profile", locals: {email: "test@test.com"}
|
295
|
+
JBUILDER
|
296
|
+
|
297
|
+
expected = strip_format(<<-JS)
|
298
|
+
(function(){
|
299
|
+
return ({"data":{"profile":{"email":"test@test.com"}}});
|
300
|
+
})()
|
301
|
+
JS
|
302
|
+
assert_equal expected, result
|
303
|
+
end
|
304
|
+
|
305
|
+
test "renders a partial with locals and caches" do
|
306
|
+
result = jbuild(<<-JBUILDER)
|
307
|
+
json.profile 32, cache: "cachekey", partial: "profile", locals: {email: "test@test.com"}
|
308
|
+
JBUILDER
|
309
|
+
|
310
|
+
expected = strip_format(<<-JS)
|
311
|
+
(function(){
|
312
|
+
Breezy.cache("#{cache_keys[0]}", {"email":"test@test.com"});
|
313
|
+
return ({"data":{"profile":Breezy.cache("#{cache_keys[0]}")}});
|
314
|
+
})()
|
315
|
+
JS
|
316
|
+
|
317
|
+
assert_equal expected, result
|
318
|
+
end
|
319
|
+
|
320
|
+
test "renders a partial even without a :as to the value, this usage is rare" do
|
321
|
+
result = jbuild(<<-JBUILDER)
|
322
|
+
json.profile 32, partial: "profile", locals: {email: "test@test.com"}
|
323
|
+
JBUILDER
|
324
|
+
|
325
|
+
expected = strip_format(<<-JS)
|
326
|
+
(function(){
|
327
|
+
return ({"data":{"profile":{"email":"test@test.com"}}});
|
328
|
+
})()
|
329
|
+
JS
|
330
|
+
|
331
|
+
assert_equal expected, result
|
332
|
+
end
|
333
|
+
|
334
|
+
test "render array of partials without an :as to a member, this usage is very rare" do
|
335
|
+
result = jbuild(<<-JBUILDER)
|
336
|
+
json.array! [1,2], partial: "footer"
|
337
|
+
JBUILDER
|
338
|
+
|
339
|
+
expected = strip_format(<<-JS)
|
340
|
+
(function(){
|
341
|
+
return ({"data":[{"terms":"You agree"},{"terms":"You agree"}]});
|
342
|
+
})()
|
343
|
+
JS
|
344
|
+
|
345
|
+
assert_equal expected, result
|
346
|
+
end
|
347
|
+
|
348
|
+
test "render array of partials without an :as to a member and cache" do
|
349
|
+
result = jbuild(<<-JBUILDER)
|
350
|
+
json.array! [1,2], partial: "footer", cache: ->(i){ ['a', i] }
|
351
|
+
JBUILDER
|
352
|
+
|
353
|
+
expected = strip_format(<<-JS)
|
354
|
+
(function(){
|
355
|
+
Breezy.cache("#{cache_keys[0]}", {"terms":"You agree"});
|
356
|
+
Breezy.cache("#{cache_keys[1]}", {"terms":"You agree"});
|
357
|
+
return ({"data":[Breezy.cache("#{cache_keys[0]}"),Breezy.cache("#{cache_keys[1]}")]});
|
358
|
+
})()
|
359
|
+
JS
|
360
|
+
|
361
|
+
assert_equal expected, result
|
362
|
+
end
|
363
|
+
|
364
|
+
test "render array of partials" do
|
365
|
+
result = jbuild(<<-JBUILDER)
|
366
|
+
json.array! BLOG_POST_COLLECTION, partial: "blog_post", as: :blog_post
|
367
|
+
JBUILDER
|
368
|
+
|
369
|
+
expected = strip_format(<<-JS)
|
370
|
+
(function(){
|
371
|
+
return ({"data":[
|
372
|
+
{"id":1,"body":"post body 1","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
373
|
+
{"id":2,"body":"post body 2","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
374
|
+
{"id":3,"body":"post body 3","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
375
|
+
{"id":4,"body":"post body 4","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
376
|
+
{"id":5,"body":"post body 5","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
377
|
+
{"id":6,"body":"post body 6","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
378
|
+
{"id":7,"body":"post body 7","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
379
|
+
{"id":8,"body":"post body 8","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
380
|
+
{"id":9,"body":"post body 9","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
381
|
+
{"id":10,"body":"post body 10","author":{"first_name":"Pavel","last_name":"Pravosud"}}
|
382
|
+
]});
|
383
|
+
})()
|
384
|
+
JS
|
385
|
+
|
386
|
+
assert_equal expected, result
|
387
|
+
end
|
388
|
+
|
389
|
+
test "renders array of partials as empty array with nil-collection" do
|
390
|
+
result = jbuild(<<-JBUILDER)
|
391
|
+
json.array! nil, partial: "blog_post", as: :blog_post
|
392
|
+
JBUILDER
|
393
|
+
|
394
|
+
expected = strip_format(<<-JS)
|
395
|
+
(function(){
|
396
|
+
return ({"data":[]});
|
397
|
+
})()
|
398
|
+
JS
|
399
|
+
|
400
|
+
assert_equal expected, result
|
401
|
+
end
|
402
|
+
|
403
|
+
test "renders array of partials via set!" do
|
404
|
+
result = jbuild(<<-JBUILDER)
|
405
|
+
json.posts BLOG_POST_COLLECTION, partial: "blog_post", as: :blog_post
|
406
|
+
JBUILDER
|
407
|
+
|
408
|
+
expected = strip_format(<<-JS)
|
409
|
+
(function(){
|
410
|
+
return ({"data":{"posts":[
|
411
|
+
{"id":1,"body":"post body 1","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
412
|
+
{"id":2,"body":"post body 2","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
413
|
+
{"id":3,"body":"post body 3","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
414
|
+
{"id":4,"body":"post body 4","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
415
|
+
{"id":5,"body":"post body 5","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
416
|
+
{"id":6,"body":"post body 6","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
417
|
+
{"id":7,"body":"post body 7","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
418
|
+
{"id":8,"body":"post body 8","author":{"first_name":"Pavel","last_name":"Pravosud"}},
|
419
|
+
{"id":9,"body":"post body 9","author":{"first_name":"David","last_name":"Heinemeier Hansson"}},
|
420
|
+
{"id":10,"body":"post body 10","author":{"first_name":"Pavel","last_name":"Pravosud"}}
|
421
|
+
]}});
|
422
|
+
})()
|
423
|
+
JS
|
424
|
+
|
425
|
+
assert_equal expected, result
|
426
|
+
end
|
427
|
+
|
428
|
+
test "render as empty array if partials as a nil value" do
|
429
|
+
result = jbuild <<-JBUILDER
|
430
|
+
json.posts nil, partial: "blog_post", as: :blog_post
|
431
|
+
JBUILDER
|
432
|
+
|
433
|
+
expected = strip_format(<<-JS)
|
434
|
+
(function(){
|
435
|
+
return ({"data":{"posts":[]}});
|
436
|
+
})()
|
437
|
+
JS
|
438
|
+
assert_equal expected, result
|
439
|
+
end
|
440
|
+
|
441
|
+
test "caching a value at a node" do
|
442
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
443
|
+
|
444
|
+
result = jbuild(<<-JBUILDER)
|
445
|
+
json.hello(32, cache: ['b', 'c'])
|
446
|
+
JBUILDER
|
447
|
+
|
448
|
+
expected = strip_format(<<-JS)
|
449
|
+
(function(){
|
450
|
+
Breezy.cache("#{cache_keys[0]}", 32);
|
451
|
+
return ({"data":{"hello":Breezy.cache("#{cache_keys[0]}")}});
|
452
|
+
})()
|
453
|
+
JS
|
454
|
+
|
455
|
+
assert_equal expected, result
|
456
|
+
end
|
457
|
+
|
458
|
+
test "caching elements in a list" do
|
459
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
460
|
+
|
461
|
+
result = jbuild(<<-JBUILDER)
|
462
|
+
json.hello do
|
463
|
+
json.array! [4,5], cache: ->(i){ ['a', i] } do |x|
|
464
|
+
json.top "hello" + x.to_s
|
465
|
+
end
|
466
|
+
end
|
467
|
+
JBUILDER
|
468
|
+
|
469
|
+
expected = strip_format(<<-JS)
|
470
|
+
(function(){
|
471
|
+
Breezy.cache("#{cache_keys[0]}", {"top":"hello4"});
|
472
|
+
Breezy.cache("#{cache_keys[1]}", {"top":"hello5"});
|
473
|
+
return ({"data":{"hello":[Breezy.cache("#{cache_keys[0]}"),Breezy.cache("#{cache_keys[1]}")]}});
|
474
|
+
})()
|
475
|
+
JS
|
476
|
+
|
477
|
+
assert_equal expected, result
|
478
|
+
end
|
479
|
+
|
480
|
+
test "nested caching generates a depth-first list of cache nodes" do
|
481
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
482
|
+
|
483
|
+
result = jbuild(<<-JBUILDER)
|
484
|
+
json.hello(cache: ['a', 'b']) do
|
485
|
+
json.content(cache: ['d', 'z']) do
|
486
|
+
json.subcontent 'inner'
|
487
|
+
end
|
488
|
+
json.other(cache: ['e', 'z']) do
|
489
|
+
json.subcontent 'other'
|
490
|
+
end
|
491
|
+
end
|
492
|
+
JBUILDER
|
493
|
+
|
494
|
+
expected = strip_format(<<-JS)
|
495
|
+
(function(){
|
496
|
+
Breezy.cache("#{cache_keys[0]}", {"subcontent":"inner"});
|
497
|
+
Breezy.cache("#{cache_keys[1]}", {"subcontent":"other"});
|
498
|
+
Breezy.cache("#{cache_keys[2]}", {"content":Breezy.cache("#{cache_keys[0]}"),"other":Breezy.cache("#{cache_keys[1]}")});
|
499
|
+
return ({"data":{"hello":Breezy.cache("#{cache_keys[2]}")}});
|
500
|
+
})()
|
501
|
+
JS
|
502
|
+
|
503
|
+
assert_equal expected, result
|
504
|
+
end
|
505
|
+
|
506
|
+
test "caching an empty block generates no cache and no errors" do
|
507
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
508
|
+
|
509
|
+
result = nil
|
510
|
+
|
511
|
+
assert_nothing_raised do
|
512
|
+
result = jbuild(<<-JBUILDER)
|
513
|
+
json.hello do
|
514
|
+
json.array! [4,5], cache: ->(i){['a', i]} do |x|
|
515
|
+
end
|
516
|
+
end
|
517
|
+
JBUILDER
|
518
|
+
end
|
519
|
+
|
520
|
+
expected = strip_format(<<-JS)
|
521
|
+
(function(){
|
522
|
+
return ({\"data\":{\"hello\":[]}});
|
523
|
+
})()
|
524
|
+
JS
|
525
|
+
|
526
|
+
assert_equal expected, result
|
527
|
+
end
|
528
|
+
|
529
|
+
test "child! accepts cache options" do
|
530
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
531
|
+
|
532
|
+
result = jbuild(<<-JBUILDER)
|
533
|
+
json.comments do
|
534
|
+
json.child!(cache: ['e', 'z']) { json.content "hello" }
|
535
|
+
json.child! { json.content "world" }
|
536
|
+
end
|
537
|
+
JBUILDER
|
538
|
+
|
539
|
+
expected = strip_format(<<-JS)
|
540
|
+
(function(){
|
541
|
+
Breezy.cache("#{cache_keys[0]}", {"content":"hello"});
|
542
|
+
return ({"data":{"comments":[Breezy.cache("#{cache_keys[0]}"),{"content":"world"}]}});
|
543
|
+
})()
|
544
|
+
JS
|
545
|
+
|
546
|
+
assert_equal expected, result
|
547
|
+
end
|
548
|
+
|
549
|
+
test "fragment caching" do
|
550
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
551
|
+
|
552
|
+
jbuild(<<-JBUILDER)
|
553
|
+
json.post(cache: 'cachekey') do
|
554
|
+
json.name "Cache"
|
555
|
+
end
|
556
|
+
JBUILDER
|
557
|
+
|
558
|
+
result = jbuild(<<-JBUILDER)
|
559
|
+
json.post(cache: 'cachekey') do
|
560
|
+
json.name "Miss"
|
561
|
+
end
|
562
|
+
JBUILDER
|
563
|
+
|
564
|
+
expected = strip_format(<<-JS)
|
565
|
+
(function(){
|
566
|
+
Breezy.cache("#{cache_keys[0]}", {"name":"Cache"});
|
567
|
+
return ({"data":{"post":Breezy.cache("#{cache_keys[0]}")}});
|
568
|
+
})()
|
569
|
+
JS
|
570
|
+
|
571
|
+
assert_equal expected, result
|
572
|
+
end
|
573
|
+
|
574
|
+
test "fragment caching deserializes an array" do
|
575
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
576
|
+
|
577
|
+
result = jbuild <<-JBUILDER
|
578
|
+
json.content(cache: "cachekey") do
|
579
|
+
json.array! %w[a b c]
|
580
|
+
end
|
581
|
+
JBUILDER
|
582
|
+
|
583
|
+
expected = strip_format(<<-JS)
|
584
|
+
(function(){
|
585
|
+
Breezy.cache("#{cache_keys[0]}", ["a","b","c"]);
|
586
|
+
return ({"data":{"content":Breezy.cache("#{cache_keys[0]}")}});
|
587
|
+
})()
|
588
|
+
JS
|
589
|
+
|
590
|
+
assert_equal expected, result
|
591
|
+
end
|
592
|
+
|
593
|
+
test "fragment caching works with previous version of cache digests" do
|
594
|
+
undef_context_methods :cache_fragment_name
|
595
|
+
|
596
|
+
if !@context.class.method_defined? :fragment_name_with_digest
|
597
|
+
@context.class_eval do
|
598
|
+
def fragment_name_with_digest
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
@context.expects :fragment_name_with_digest
|
604
|
+
|
605
|
+
jbuild <<-JBUILDER
|
606
|
+
json.content(cache: "cachekey") do
|
607
|
+
json.name "Cache"
|
608
|
+
end
|
609
|
+
JBUILDER
|
610
|
+
end
|
611
|
+
|
612
|
+
test "fragment caching works with current cache digests" do
|
613
|
+
undef_context_methods :fragment_name_with_digest
|
614
|
+
|
615
|
+
@context.expects :cache_fragment_name
|
616
|
+
ActiveSupport::Cache.expects :expand_cache_key
|
617
|
+
|
618
|
+
jbuild <<-JBUILDER
|
619
|
+
json.content(cache: "cachekey") do
|
620
|
+
json.name "Cache"
|
621
|
+
end
|
622
|
+
JBUILDER
|
623
|
+
end
|
624
|
+
|
625
|
+
test "current cache digest option accepts options through the last element hash" do
|
626
|
+
undef_context_methods :fragment_name_with_digest
|
627
|
+
|
628
|
+
@context.expects(:cache_fragment_name)
|
629
|
+
.with("cachekey", skip_digest: true)
|
630
|
+
.returns("cachekey")
|
631
|
+
|
632
|
+
ActiveSupport::Cache.expects :expand_cache_key
|
633
|
+
|
634
|
+
jbuild <<-JBUILDER
|
635
|
+
json.content(cache: ["cachekey", skip_digest: true]) do
|
636
|
+
json.name "Cache"
|
637
|
+
end
|
638
|
+
JBUILDER
|
639
|
+
end
|
640
|
+
|
641
|
+
test "does not perform caching when controller.perform_caching is false" do
|
642
|
+
controller.perform_caching = false
|
643
|
+
|
644
|
+
result = jbuild <<-JBUILDER
|
645
|
+
json.content(cache: "cachekey") do
|
646
|
+
json.name "Cache"
|
647
|
+
end
|
648
|
+
JBUILDER
|
649
|
+
|
650
|
+
expected = strip_format(<<-JS)
|
651
|
+
(function(){
|
652
|
+
return ({"data":{"content":{"name":"Cache"}}});
|
653
|
+
})()
|
654
|
+
JS
|
655
|
+
|
656
|
+
assert_equal expected, result
|
657
|
+
end
|
658
|
+
|
659
|
+
test "invokes templates via params via set! and caches" do
|
660
|
+
@post = BLOG_POST_COLLECTION.first
|
661
|
+
|
662
|
+
result = jbuild(<<-JBUILDER)
|
663
|
+
json.post @post, partial: "blog_post", as: :blog_post, cache: ['a', 'b']
|
664
|
+
JBUILDER
|
665
|
+
|
666
|
+
expected = strip_format(<<-JS)
|
667
|
+
(function(){
|
668
|
+
Breezy.cache("#{cache_keys[0]}", {"id":1,"body":"post body 1","author":{"first_name":"David","last_name":"Heinemeier Hansson"}});
|
669
|
+
return ({"data":{"post":Breezy.cache("#{cache_keys[0]}")}});
|
670
|
+
})()
|
671
|
+
JS
|
672
|
+
|
673
|
+
assert_equal expected, result
|
674
|
+
end
|
675
|
+
|
676
|
+
test "shares partial caches (via the partial's digest) across multiple templates" do
|
677
|
+
@hit = BlogPost.new(1, "hit", "John Smith")
|
678
|
+
@miss = BlogPost.new(2, "miss", "John Smith")
|
679
|
+
|
680
|
+
jbuild(<<-JBUILDER)
|
681
|
+
json.post @hit, partial: "blog_post", as: :blog_post, cache: ['a', 'b']
|
682
|
+
JBUILDER
|
683
|
+
|
684
|
+
result = jbuild(<<-JBUILDER)
|
685
|
+
json.post @miss, partial: "blog_post", as: :blog_post, cache: ['a', 'b']
|
686
|
+
JBUILDER
|
687
|
+
|
688
|
+
expected = strip_format(<<-JS)
|
689
|
+
(function(){
|
690
|
+
Breezy.cache("#{cache_keys[0]}", {"id":1,"body":"hit","author":{"first_name":"John","last_name":"Smith"}});
|
691
|
+
return ({"data":{"post":Breezy.cache("#{cache_keys[0]}")}});
|
692
|
+
})()
|
693
|
+
JS
|
694
|
+
|
695
|
+
assert_equal expected, result
|
696
|
+
end
|
697
|
+
|
698
|
+
|
699
|
+
test "render array of partials and caches" do
|
700
|
+
result = jbuild(<<-JBUILDER)
|
701
|
+
json.array! BLOG_POST_COLLECTION, partial: "blog_post", as: :blog_post, cache: ->(d){ ['a', d.id] }
|
702
|
+
JBUILDER
|
703
|
+
Rails.cache.clear
|
704
|
+
|
705
|
+
expected = strip_format(<<-JS)
|
706
|
+
(function(){
|
707
|
+
Breezy.cache("#{cache_keys[0]}", {"id":1,"body":"post body 1","author":{"first_name":"David","last_name":"Heinemeier Hansson"}});
|
708
|
+
Breezy.cache("#{cache_keys[1]}", {"id":2,"body":"post body 2","author":{"first_name":"Pavel","last_name":"Pravosud"}});
|
709
|
+
Breezy.cache("#{cache_keys[2]}", {"id":3,"body":"post body 3","author":{"first_name":"David","last_name":"Heinemeier Hansson"}});
|
710
|
+
Breezy.cache("#{cache_keys[3]}", {"id":4,"body":"post body 4","author":{"first_name":"Pavel","last_name":"Pravosud"}});
|
711
|
+
Breezy.cache("#{cache_keys[4]}", {"id":5,"body":"post body 5","author":{"first_name":"David","last_name":"Heinemeier Hansson"}});
|
712
|
+
Breezy.cache("#{cache_keys[5]}", {"id":6,"body":"post body 6","author":{"first_name":"Pavel","last_name":"Pravosud"}});
|
713
|
+
Breezy.cache("#{cache_keys[6]}", {"id":7,"body":"post body 7","author":{"first_name":"David","last_name":"Heinemeier Hansson"}});
|
714
|
+
Breezy.cache("#{cache_keys[7]}", {"id":8,"body":"post body 8","author":{"first_name":"Pavel","last_name":"Pravosud"}});
|
715
|
+
Breezy.cache("#{cache_keys[8]}", {"id":9,"body":"post body 9","author":{"first_name":"David","last_name":"Heinemeier Hansson"}});
|
716
|
+
Breezy.cache("#{cache_keys[9]}", {"id":10,"body":"post body 10","author":{"first_name":"Pavel","last_name":"Pravosud"}});
|
717
|
+
return ({"data":[Breezy.cache("#{cache_keys[0]}"),Breezy.cache("#{cache_keys[1]}"),Breezy.cache("#{cache_keys[2]}"),Breezy.cache("#{cache_keys[3]}"),Breezy.cache("#{cache_keys[4]}"),Breezy.cache("#{cache_keys[5]}"),Breezy.cache("#{cache_keys[6]}"),Breezy.cache("#{cache_keys[7]}"),Breezy.cache("#{cache_keys[8]}"),Breezy.cache("#{cache_keys[9]}")]});
|
718
|
+
})()
|
719
|
+
JS
|
720
|
+
|
721
|
+
assert_equal expected, result
|
722
|
+
end
|
723
|
+
|
724
|
+
test "filtering for a node in the tree" do
|
725
|
+
result = jbuild(<<-JBUILDER)
|
726
|
+
json._filter_by_path('hit.hit2')
|
727
|
+
json.hit do
|
728
|
+
json.hit2 do
|
729
|
+
json.greeting 'hello world'
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
json.miss do
|
734
|
+
json.miss2 do
|
735
|
+
raise 'this should not be called'
|
736
|
+
json.greeting 'missed call'
|
737
|
+
end
|
738
|
+
end
|
739
|
+
JBUILDER
|
740
|
+
Rails.cache.clear
|
741
|
+
|
742
|
+
expected = strip_format(<<-JS)
|
743
|
+
(function(){
|
744
|
+
return (
|
745
|
+
{"data":{"greeting":"hello world"}}
|
746
|
+
);
|
747
|
+
})()
|
748
|
+
JS
|
749
|
+
|
750
|
+
assert_equal expected, result
|
751
|
+
end
|
752
|
+
|
753
|
+
test "filtering for a node in the tree via breezy_filter helper" do
|
754
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2')
|
755
|
+
json.hit do
|
756
|
+
json.hit2 do
|
757
|
+
json.greeting 'hello world'
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
json.miss do
|
762
|
+
json.miss2 do
|
763
|
+
raise 'this should not be called'
|
764
|
+
json.greeting 'missed call'
|
765
|
+
end
|
766
|
+
end
|
767
|
+
JBUILDER
|
768
|
+
Rails.cache.clear
|
769
|
+
|
770
|
+
expected = strip_format(<<-JS)
|
771
|
+
(function(){
|
772
|
+
return (
|
773
|
+
{"data":{"greeting":"hello world"},"action":"graft","path":"hit.hit2"}
|
774
|
+
);
|
775
|
+
})()
|
776
|
+
JS
|
777
|
+
|
778
|
+
assert_equal expected, result
|
779
|
+
end
|
780
|
+
|
781
|
+
test "filtering a cached node returns just that" do
|
782
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
783
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2')
|
784
|
+
json.hit do
|
785
|
+
json.hit2(cache: 'a') do
|
786
|
+
json.greeting 'hello world'
|
787
|
+
end
|
788
|
+
end
|
789
|
+
JBUILDER
|
790
|
+
Rails.cache.clear
|
791
|
+
|
792
|
+
expected = strip_format(<<-JS)
|
793
|
+
(function(){
|
794
|
+
Breezy.cache("#{cache_keys[0]}", {"greeting":"hello world"});
|
795
|
+
return ({"data":Breezy.cache("219dfba9f552f91402a22cf67c633582"),"action":"graft","path":"hit.hit2"});
|
796
|
+
})()
|
797
|
+
|
798
|
+
|
799
|
+
JS
|
800
|
+
|
801
|
+
assert_equal expected, result
|
802
|
+
end
|
803
|
+
|
804
|
+
test "filtering for a node of a AR relation in a tree by id via an appended where clause" do
|
805
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.id=1')
|
806
|
+
post = Post.create
|
807
|
+
post.comments.create title: 'first'
|
808
|
+
post.comments.create title: 'second'
|
809
|
+
|
810
|
+
post.comments.expects(:where).once().with('id'=>1).returns([{id: 1, title: 'first'}])
|
811
|
+
|
812
|
+
json.hit do
|
813
|
+
json.hit2 do
|
814
|
+
json.array! post.comments do |x|
|
815
|
+
raise 'this should be be called' if x[:title] == 'second'
|
816
|
+
json.title x[:title]
|
817
|
+
end
|
818
|
+
end
|
819
|
+
end
|
820
|
+
JBUILDER
|
821
|
+
|
822
|
+
Rails.cache.clear
|
823
|
+
|
824
|
+
expected = strip_format(<<-JS)
|
825
|
+
(function(){
|
826
|
+
return (
|
827
|
+
{"data":[{"title":"first"}],"action":"graft","path":"hit.hit2.id=1"}
|
828
|
+
);
|
829
|
+
})()
|
830
|
+
JS
|
831
|
+
assert_equal expected, result
|
832
|
+
end
|
833
|
+
|
834
|
+
|
835
|
+
test "filtering for a node of a AR relation in a tree by index via an appended where clause" do
|
836
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.0')
|
837
|
+
post = Post.create
|
838
|
+
post.comments.create title: 'first'
|
839
|
+
post.comments.create title: 'second'
|
840
|
+
|
841
|
+
offset = post.comments.offset(0)
|
842
|
+
post.comments.expects(:offset).once().with(0).returns(offset)
|
843
|
+
|
844
|
+
json.hit do
|
845
|
+
json.hit2 do
|
846
|
+
json.array! post.comments do |x|
|
847
|
+
raise 'this should be be called' if x[:title] == 'second'
|
848
|
+
json.title x[:title]
|
849
|
+
end
|
850
|
+
end
|
851
|
+
end
|
852
|
+
JBUILDER
|
853
|
+
|
854
|
+
Rails.cache.clear
|
855
|
+
|
856
|
+
expected = strip_format(<<-JS)
|
857
|
+
(function(){
|
858
|
+
return (
|
859
|
+
{"data":[{"title":"first"}],"action":"graft","path":"hit.hit2.0"}
|
860
|
+
);
|
861
|
+
})()
|
862
|
+
JS
|
863
|
+
assert_equal expected, result
|
864
|
+
end
|
865
|
+
|
866
|
+
test "filtering for a node in an array of a tree by id" do
|
867
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.id=1')
|
868
|
+
json.hit do
|
869
|
+
json.hit2 do
|
870
|
+
json.array! [{id: 1, name: 'hit' }, {id:2, name: 'miss'}] do |x|
|
871
|
+
raise 'this should be be called' if x[:name] == 'miss'
|
872
|
+
json.name x[:name]
|
873
|
+
end
|
874
|
+
end
|
875
|
+
end
|
876
|
+
JBUILDER
|
877
|
+
Rails.cache.clear
|
878
|
+
|
879
|
+
expected = strip_format(<<-JS)
|
880
|
+
(function(){
|
881
|
+
return (
|
882
|
+
{"data":[{"name":"hit"}],"action":"graft","path":"hit.hit2.id=1"}
|
883
|
+
);
|
884
|
+
})()
|
885
|
+
JS
|
886
|
+
|
887
|
+
assert_equal expected, result
|
888
|
+
end
|
889
|
+
|
890
|
+
test "filtering for a node in an array of a tree by index" do
|
891
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hit.hit2.0')
|
892
|
+
json.hit do
|
893
|
+
json.hit2 do
|
894
|
+
json.array! [{id: 1, name: 'hit' }, {id:2, name: 'miss'}] do |x|
|
895
|
+
raise 'this should be be called' if x[:name] == 'miss'
|
896
|
+
json.name x[:name]
|
897
|
+
end
|
898
|
+
end
|
899
|
+
end
|
900
|
+
JBUILDER
|
901
|
+
Rails.cache.clear
|
902
|
+
|
903
|
+
expected = strip_format(<<-JS)
|
904
|
+
(function(){
|
905
|
+
return (
|
906
|
+
{"data":[{"name":"hit"}],"action":"graft","path":"hit.hit2.0"}
|
907
|
+
);
|
908
|
+
})()
|
909
|
+
JS
|
910
|
+
|
911
|
+
assert_equal expected, result
|
912
|
+
end
|
913
|
+
|
914
|
+
test "rendering with node deferement" do
|
915
|
+
req = action_controller_test_request
|
916
|
+
req.path = '/some_url'
|
917
|
+
|
918
|
+
result = jbuild(<<-JBUILDER, request: req)
|
919
|
+
json.hit do
|
920
|
+
json.hit2 defer: true do
|
921
|
+
json.hit3 do
|
922
|
+
json.greeting 'hello world'
|
923
|
+
end
|
924
|
+
end
|
925
|
+
end
|
926
|
+
JBUILDER
|
927
|
+
Rails.cache.clear
|
928
|
+
|
929
|
+
expected = strip_format(<<-JS)
|
930
|
+
(function(){
|
931
|
+
Breezy.visit('/some_url?_breezy_filter=hit.hit2', {async: true, pushState: false});
|
932
|
+
return (
|
933
|
+
{"data":{"hit":{"hit2":null}}}
|
934
|
+
);
|
935
|
+
})()
|
936
|
+
JS
|
937
|
+
|
938
|
+
assert_equal expected, result
|
939
|
+
end
|
940
|
+
|
941
|
+
test "rendering with node array deferment" do
|
942
|
+
req = action_controller_test_request
|
943
|
+
req.path = '/some_url'
|
944
|
+
|
945
|
+
result = jbuild(<<-JBUILDER, request: req)
|
946
|
+
json.hit do
|
947
|
+
json.hit2 do
|
948
|
+
data = [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}]
|
949
|
+
json.array! data, key: :id do
|
950
|
+
json.greeting defer: true do
|
951
|
+
json.gree 'hi'
|
952
|
+
end
|
953
|
+
end
|
954
|
+
end
|
955
|
+
end
|
956
|
+
JBUILDER
|
957
|
+
Rails.cache.clear
|
958
|
+
|
959
|
+
expected = strip_format(<<-JS)
|
960
|
+
(function(){
|
961
|
+
Breezy.visit('/some_url?_breezy_filter=hit.hit2.id%3D1.greeting', {async: true, pushState: false});
|
962
|
+
Breezy.visit('/some_url?_breezy_filter=hit.hit2.id%3D2.greeting', {async: true, pushState: false});
|
963
|
+
return (
|
964
|
+
{"data":{"hit":{"hit2":[{"greeting":null},{"greeting":null}]}}}
|
965
|
+
);
|
966
|
+
})()
|
967
|
+
JS
|
968
|
+
|
969
|
+
assert_equal expected, result
|
970
|
+
end
|
971
|
+
|
972
|
+
test 'deferment does not work on values' do
|
973
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
974
|
+
|
975
|
+
result = jbuild(<<-JBUILDER)
|
976
|
+
json.hello(32, defer: true)
|
977
|
+
JBUILDER
|
978
|
+
|
979
|
+
expected = strip_format(<<-JS)
|
980
|
+
(function(){
|
981
|
+
return ({"data":{"hello":32}});
|
982
|
+
})()
|
983
|
+
JS
|
984
|
+
|
985
|
+
assert_equal expected, result
|
986
|
+
end
|
987
|
+
|
988
|
+
test 'deferment is disabled when filtering by keypath' do
|
989
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
990
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hello.world')
|
991
|
+
json.hello defer: true do
|
992
|
+
json.world 32
|
993
|
+
end
|
994
|
+
JBUILDER
|
995
|
+
|
996
|
+
expected = strip_format(<<-JS)
|
997
|
+
(function(){
|
998
|
+
return ({"data":{"world":32},"action":"graft","path":"hello.world"});
|
999
|
+
})()
|
1000
|
+
JS
|
1001
|
+
|
1002
|
+
assert_equal expected, result
|
1003
|
+
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
test 'deferment is enabled at the end of a keypath when filtering' do
|
1007
|
+
undef_context_methods :fragment_name_with_digest, :cache_fragment_name
|
1008
|
+
result = jbuild(<<-JBUILDER, breezy_filter: 'hello')
|
1009
|
+
json.hello defer: true do
|
1010
|
+
json.content defer: true do
|
1011
|
+
json.world 32
|
1012
|
+
end
|
1013
|
+
end
|
1014
|
+
JBUILDER
|
1015
|
+
|
1016
|
+
expected = strip_format(<<-JS)
|
1017
|
+
(function(){
|
1018
|
+
Breezy.visit('?_breezy_filter=hello.content', {async: true, pushState: false});
|
1019
|
+
return ({"data":{"content":null},"action":"graft","path":"hello"});
|
1020
|
+
})()
|
1021
|
+
JS
|
1022
|
+
|
1023
|
+
assert_equal expected, result
|
1024
|
+
end
|
1025
|
+
end
|