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.
- checksums.yaml +7 -0
- data/lib/breezy_template.rb +268 -0
- data/lib/breezy_template/active_support.rb +24 -0
- data/lib/breezy_template/cache_extension.rb +131 -0
- data/lib/breezy_template/configuration.rb +21 -0
- data/lib/breezy_template/deferment_extension.rb +58 -0
- data/lib/breezy_template/dependency_tracker.rb +47 -0
- data/lib/breezy_template/digestor.rb +30 -0
- data/lib/breezy_template/handler.rb +47 -0
- data/lib/breezy_template/partial_extension.rb +123 -0
- data/lib/breezy_template/search_extension.rb +99 -0
- data/lib/breezy_template/version.rb +3 -0
- data/test/breezy_template_test.rb +1239 -0
- data/test/dependency_tracker_test.rb +66 -0
- data/test/test_helper.rb +17 -0
- metadata +115 -0
@@ -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,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
|