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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1034948b51df6045cd4ebed1fec5278d578b6232
|
4
|
+
data.tar.gz: 37f2073cb2c299d22fa2d12b0b022d34d531ed7e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ceab85197a0d71c53c3b4ac25263bc392fcf700b1a23e88f5dcb8a79f6634b7bc6d6e93e10d3ff0f2222a3c5d8b3345e02b66978d44e452463ae53aa05c567a4
|
7
|
+
data.tar.gz: 7fc963e83b28dfdbb7a2216815e7e3319089c7bb6745ffe92f3e37d583490d492adc8607cc097b21c905052d5077ba5a6994474f60c28773315be42c0659fc64
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'jbuilder'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'action_view'
|
4
|
+
require 'active_support'
|
5
|
+
require 'breezy_template/active_support'
|
6
|
+
require 'breezy_template/digestor'
|
7
|
+
require 'breezy_template/configuration'
|
8
|
+
require 'breezy_template/handler'
|
9
|
+
require 'breezy_template/partial_extension'
|
10
|
+
require 'breezy_template/cache_extension'
|
11
|
+
require 'breezy_template/deferment_extension'
|
12
|
+
require 'breezy_template/search_extension'
|
13
|
+
|
14
|
+
module BreezyTemplate
|
15
|
+
class Engine < ::Rails::Engine
|
16
|
+
initializer :breezy_template do |app|
|
17
|
+
ActiveSupport.on_load(:action_view) do
|
18
|
+
ActionView::Template.register_template_handler :breezy, BreezyTemplate::Handler
|
19
|
+
ActionView::Template.register_template_handler :props, BreezyTemplate::Handler
|
20
|
+
require 'breezy_template/dependency_tracker'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Template < ::Jbuilder
|
26
|
+
include PartialDigestor
|
27
|
+
prepend PartialExtension
|
28
|
+
|
29
|
+
prepend CacheExtension
|
30
|
+
prepend SearchExtension
|
31
|
+
prepend DefermentExtension
|
32
|
+
|
33
|
+
class << self
|
34
|
+
attr_accessor :template_lookup_options
|
35
|
+
end
|
36
|
+
|
37
|
+
self.template_lookup_options = { handlers: [:breezy] }
|
38
|
+
|
39
|
+
def initialize(context, *args)
|
40
|
+
@context = context
|
41
|
+
@js = []
|
42
|
+
@path = []
|
43
|
+
@joints = {}
|
44
|
+
@extensions = {}
|
45
|
+
super(*args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def empty!
|
49
|
+
attributes = @attributes
|
50
|
+
@attributes = {}
|
51
|
+
attributes
|
52
|
+
end
|
53
|
+
|
54
|
+
def wrap!(name, *args)
|
55
|
+
@extensions[name.to_sym] = args
|
56
|
+
end
|
57
|
+
|
58
|
+
def _blank
|
59
|
+
BLANK
|
60
|
+
end
|
61
|
+
|
62
|
+
def method_missing(*args)
|
63
|
+
key = args[0]
|
64
|
+
@path.push(key)
|
65
|
+
if ::Kernel.block_given?
|
66
|
+
args = _args_for_set_with_block(*args)
|
67
|
+
set!(*args, &::Proc.new)
|
68
|
+
else
|
69
|
+
args = _args_for_set(*args)
|
70
|
+
set!(*args)
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
# ::Byebug.byebug
|
74
|
+
@extensions = {}
|
75
|
+
@path.pop
|
76
|
+
end
|
77
|
+
|
78
|
+
def _scope
|
79
|
+
parent_extensions = @extensions
|
80
|
+
@extensions = {}
|
81
|
+
parent_attributes, parent_formatter = @attributes, @key_formatter
|
82
|
+
@attributes = BLANK
|
83
|
+
yield
|
84
|
+
@attributes
|
85
|
+
ensure
|
86
|
+
# ::Byebug.byebug
|
87
|
+
@extensions = parent_extensions
|
88
|
+
@attributes, @key_formatter = parent_attributes, parent_formatter
|
89
|
+
end
|
90
|
+
|
91
|
+
def set!(key, value = BLANK, *args)
|
92
|
+
_ensure_valid_key(key)
|
93
|
+
result = if ::Kernel.block_given?
|
94
|
+
_result(value, &::Proc.new)
|
95
|
+
else
|
96
|
+
_result(value)
|
97
|
+
end
|
98
|
+
|
99
|
+
_set_value key, result
|
100
|
+
end
|
101
|
+
|
102
|
+
def _ensure_valid_key(key)
|
103
|
+
current_value = _blank? ? _blank : @attributes.fetch(_key(key), _blank)
|
104
|
+
raise NullError.build(key) if current_value.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
def _result(value)
|
108
|
+
if ::Kernel.block_given?
|
109
|
+
_scope { yield self }
|
110
|
+
elsif ::Jbuilder === value
|
111
|
+
# json.age 32
|
112
|
+
# json.person another_jbuilder
|
113
|
+
# { "age": 32, "person": { ... }
|
114
|
+
value.attributes!
|
115
|
+
else
|
116
|
+
# json.age 32
|
117
|
+
# { "age": 32 }
|
118
|
+
value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def _set_value(key, result)
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
def array!(collection = [], *attributes)
|
127
|
+
options = attributes.first || {}
|
128
|
+
options = _normalize_options(options)
|
129
|
+
|
130
|
+
collection = [] if collection.nil?
|
131
|
+
collection = _prepare_collection_for_map(collection)
|
132
|
+
|
133
|
+
array = if ::Kernel.block_given?
|
134
|
+
_map_collection(collection, options, &::Proc.new)
|
135
|
+
elsif attributes.any?
|
136
|
+
_map_collection(collection, options) { |element| extract! element, *attributes }
|
137
|
+
else
|
138
|
+
collection.to_a
|
139
|
+
end
|
140
|
+
|
141
|
+
@extensions = {}
|
142
|
+
merge! array #remove this depednacy
|
143
|
+
end
|
144
|
+
|
145
|
+
def target!
|
146
|
+
js = _breezy_return(@attributes)
|
147
|
+
|
148
|
+
@js.push(js)
|
149
|
+
"(function(){var joints={};var cache={};var defers=[];#{@js.join}})()"
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
def _merge_values(current_value, updates)
|
154
|
+
# Always use the new values. This is because cached values
|
155
|
+
# are no longer a Ruby object. They are JS values and can't
|
156
|
+
# be merged.
|
157
|
+
|
158
|
+
updates
|
159
|
+
end
|
160
|
+
|
161
|
+
def _prepare_collection_for_map(collection)
|
162
|
+
collection
|
163
|
+
end
|
164
|
+
|
165
|
+
def _args_for_set_with_block(*args)
|
166
|
+
key = args[0]
|
167
|
+
#todo: check this
|
168
|
+
if ::Hash === args[1] && _extended_options?(args[1])
|
169
|
+
options = args[1]
|
170
|
+
[key, BLANK, options]
|
171
|
+
else
|
172
|
+
[key, BLANK]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def _args_for_set(*args)
|
177
|
+
if args.length == 3
|
178
|
+
key, value, options = args
|
179
|
+
|
180
|
+
[key, value, options]
|
181
|
+
else
|
182
|
+
key, value = args
|
183
|
+
|
184
|
+
[key, value]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def _extended_options?(value)
|
189
|
+
false
|
190
|
+
end
|
191
|
+
|
192
|
+
def _mapping_element(element, options)
|
193
|
+
_scope { yield element }
|
194
|
+
end
|
195
|
+
|
196
|
+
def _map_collection(collection, options)
|
197
|
+
@path.push(nil)
|
198
|
+
collection.map.with_index do |element, i|
|
199
|
+
if options.has_key? :key
|
200
|
+
id_name = options[:key]
|
201
|
+
id_val = element[id_name]
|
202
|
+
@path[-1] = "#{id_name}=#{id_val}"
|
203
|
+
else
|
204
|
+
@path[-1] = i
|
205
|
+
end
|
206
|
+
|
207
|
+
_mapping_element(element, options, &::Proc.new)
|
208
|
+
|
209
|
+
end - [BLANK]
|
210
|
+
ensure
|
211
|
+
@path.pop
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
def _breezy_return(results)
|
216
|
+
"return (#{_dump(results)});"
|
217
|
+
end
|
218
|
+
|
219
|
+
def _dump(value)
|
220
|
+
::MultiJson.dump(value)
|
221
|
+
end
|
222
|
+
|
223
|
+
def _normalize_options(options)
|
224
|
+
options = options.dup
|
225
|
+
key = options[:cache]
|
226
|
+
opts = {}
|
227
|
+
return options if !key
|
228
|
+
|
229
|
+
if ::Array === key && key.length == 2 && ::Hash === key.last
|
230
|
+
return options
|
231
|
+
end
|
232
|
+
|
233
|
+
if options[:partial]
|
234
|
+
opts[:partial] = options[:partial]
|
235
|
+
end
|
236
|
+
|
237
|
+
key = case key
|
238
|
+
when ::Array
|
239
|
+
key
|
240
|
+
when ::Proc
|
241
|
+
key
|
242
|
+
else
|
243
|
+
[key]
|
244
|
+
end
|
245
|
+
|
246
|
+
options[:cache] = [key, opts]
|
247
|
+
options
|
248
|
+
end
|
249
|
+
|
250
|
+
def _fragment_name_with_digest(key, options)
|
251
|
+
if @context.respond_to?(:cache_fragment_name)
|
252
|
+
# Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
|
253
|
+
# should be used instead.
|
254
|
+
@context.cache_fragment_name(key, options)
|
255
|
+
elsif @context.respond_to?(:fragment_name_with_digest)
|
256
|
+
# Backwards compatibility for period of time when fragment_name_with_digest was made public.
|
257
|
+
@context.fragment_name_with_digest(key)
|
258
|
+
else
|
259
|
+
key
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
def _logger
|
265
|
+
::ActionView::Base.logger
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# https://github.com/rails/jbuilder/issues/204
|
2
|
+
if Rails.version >= '4.1'
|
3
|
+
module ActiveSupport
|
4
|
+
module JSON
|
5
|
+
module Encoding
|
6
|
+
class JSONGemEncoder
|
7
|
+
alias_method :original_jsonify, :jsonify
|
8
|
+
|
9
|
+
def jsonify(value)
|
10
|
+
if ::BreezyTemplate::Template::Digest === value
|
11
|
+
value
|
12
|
+
elsif ::BreezyTemplate::Template::JointVar === value
|
13
|
+
value
|
14
|
+
elsif ::BreezyTemplate::Template::DeferVar === value
|
15
|
+
value
|
16
|
+
else
|
17
|
+
original_jsonify(value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module BreezyTemplate
|
2
|
+
module CacheExtension
|
3
|
+
class Digest
|
4
|
+
def initialize(digest)
|
5
|
+
@digest = "cache[\"#{digest}\"]"
|
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
|
+
def _result(value)
|
22
|
+
if _cache_options?
|
23
|
+
_cache(*_cache_options) { super }
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def set!(key, value = BLANK, *args)
|
30
|
+
options = args.first || {}
|
31
|
+
options = _normalize_options(options)
|
32
|
+
if options[:partial] && _cache_options?
|
33
|
+
_cache_options[1] ||= {}
|
34
|
+
_cache_options[1][:_partial] = options[:partial]
|
35
|
+
end
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def array!(collection=[], *attributes)
|
40
|
+
options = attributes.first || {}
|
41
|
+
options = _normalize_options(options)
|
42
|
+
|
43
|
+
if options[:partial] && _cache_options?
|
44
|
+
_cache_options[1] ||= {}
|
45
|
+
_cache_options[1][:_partial] = options[:partial]
|
46
|
+
end
|
47
|
+
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
def _mapping_element(element, opts={})
|
52
|
+
if _cache_options
|
53
|
+
key = _cache_options.first
|
54
|
+
if ::Proc === key
|
55
|
+
key = key.call(element)
|
56
|
+
end
|
57
|
+
|
58
|
+
_cache(key, opts) {
|
59
|
+
_scope { yield element }
|
60
|
+
}
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def _cache_options?
|
67
|
+
!!@extensions[:cache]
|
68
|
+
end
|
69
|
+
|
70
|
+
def _cache_options
|
71
|
+
@extensions[:cache]
|
72
|
+
end
|
73
|
+
|
74
|
+
def _extended_options?(value)
|
75
|
+
_cache_options? || super
|
76
|
+
end
|
77
|
+
|
78
|
+
def _breezy_set_cache(key, value)
|
79
|
+
"cache[\"#{key}\"]=#{_dump(value)};"
|
80
|
+
end
|
81
|
+
|
82
|
+
def _cache_key(key, options={})
|
83
|
+
key = _fragment_name_with_digest(key, options)
|
84
|
+
key = url_for(key).split('://', 2).last if ::Hash === key
|
85
|
+
key = ::ActiveSupport::Cache.expand_cache_key(key, :jbuilder)
|
86
|
+
|
87
|
+
::Digest::MD5.hexdigest(key.to_s).tap do |digest|
|
88
|
+
_logger.try :debug, "Cache key :#{key} was digested to #{digest}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def _cache(key=nil, options={})
|
93
|
+
return yield self if !@context.controller.perform_caching || key.nil?
|
94
|
+
|
95
|
+
parent_js = @js
|
96
|
+
key = _cache_key(key, options)
|
97
|
+
@js = []
|
98
|
+
|
99
|
+
blank_or_value = begin
|
100
|
+
::Rails.cache.fetch(key, options) do
|
101
|
+
result = yield self
|
102
|
+
if result != _blank
|
103
|
+
@js << _breezy_set_cache(key, result)
|
104
|
+
@js.join
|
105
|
+
else
|
106
|
+
_blank
|
107
|
+
end
|
108
|
+
end
|
109
|
+
ensure
|
110
|
+
@js = parent_js
|
111
|
+
end
|
112
|
+
|
113
|
+
if blank_or_value == _blank
|
114
|
+
_blank
|
115
|
+
else
|
116
|
+
v = blank_or_value
|
117
|
+
@js.push(v)
|
118
|
+
Digest.new(key)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def _fragment_name_with_digest(key, options)
|
123
|
+
if _cache_options? && _cache_options[1] && _cache_options[1][:_partial] && !options[:skip_digest]
|
124
|
+
partial = _cache_options[1][:_partial]
|
125
|
+
[key, _partial_digest(partial)]
|
126
|
+
else
|
127
|
+
super
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module BreezyTemplate
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :track_assets
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@track_assets = ['application.js', 'application.css']
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configuration
|
11
|
+
@configuration ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configuration=(config)
|
15
|
+
@configuration = config
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configure
|
19
|
+
yield configuration
|
20
|
+
end
|
21
|
+
end
|