actionview_precompiler 0.2.2 → 0.3.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 +4 -4
- data/README.md +2 -2
- data/lib/actionview_precompiler/ast_parser/jruby.rb +58 -4
- data/lib/actionview_precompiler/ast_parser/ripper.rb +202 -0
- data/lib/actionview_precompiler/ast_parser/ruby26.rb +73 -4
- data/lib/actionview_precompiler/ast_parser/syntax_tree.rb +158 -0
- data/lib/actionview_precompiler/ast_parser.rb +19 -4
- data/lib/actionview_precompiler/controller_parser.rb +13 -0
- data/lib/actionview_precompiler/controller_scanner.rb +48 -0
- data/lib/actionview_precompiler/helper_parser.rb +13 -0
- data/lib/actionview_precompiler/helper_scanner.rb +34 -0
- data/lib/actionview_precompiler/precompiler.rb +47 -73
- data/lib/actionview_precompiler/render_parser.rb +143 -42
- data/lib/actionview_precompiler/template_file.rb +22 -0
- data/lib/actionview_precompiler/template_loader.rb +36 -0
- data/lib/actionview_precompiler/template_parser.rb +12 -17
- data/lib/actionview_precompiler/template_scanner.rb +44 -0
- data/lib/actionview_precompiler/version.rb +1 -1
- data/lib/actionview_precompiler.rb +31 -17
- metadata +17 -8
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module ActionviewPrecompiler
|
|
2
|
+
class HelperParser
|
|
3
|
+
def initialize(filename)
|
|
4
|
+
@filename = filename
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def render_calls
|
|
8
|
+
src = File.read(@filename)
|
|
9
|
+
return [] unless src.include?("render")
|
|
10
|
+
RenderParser.new(src).render_calls
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module ActionviewPrecompiler
|
|
2
|
+
class HelperScanner
|
|
3
|
+
def initialize(dir)
|
|
4
|
+
@dir = dir
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def template_renders
|
|
8
|
+
template_renders = []
|
|
9
|
+
|
|
10
|
+
each_helper do |fullpath|
|
|
11
|
+
parser = HelperParser.new(fullpath)
|
|
12
|
+
parser.render_calls.each do |render_call|
|
|
13
|
+
virtual_path = render_call.virtual_path
|
|
14
|
+
|
|
15
|
+
locals = render_call.locals_keys.map(&:to_s).sort
|
|
16
|
+
|
|
17
|
+
template_renders << [virtual_path, locals]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
template_renders.uniq
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def each_helper
|
|
27
|
+
Dir["#{@dir}/**/*_helper.rb"].sort.map do |fullpath|
|
|
28
|
+
next if File.directory?(fullpath)
|
|
29
|
+
|
|
30
|
+
yield fullpath
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -1,95 +1,69 @@
|
|
|
1
|
+
require "actionview_precompiler/template_scanner"
|
|
2
|
+
require "actionview_precompiler/controller_scanner"
|
|
3
|
+
require "actionview_precompiler/helper_scanner"
|
|
4
|
+
require "actionview_precompiler/template_loader"
|
|
5
|
+
|
|
1
6
|
module ActionviewPrecompiler
|
|
2
7
|
class Precompiler
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@relative_path = relative_path
|
|
10
|
-
@virtual_path = relative_path.slice(0, relative_path.index("."))
|
|
11
|
-
|
|
12
|
-
parsed = ParsedFilename.new(relative_path)
|
|
13
|
-
@prefix = parsed.prefix
|
|
14
|
-
@action = parsed.action
|
|
15
|
-
@partial = parsed.partial?
|
|
16
|
-
@details = parsed.details
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def partial?
|
|
20
|
-
@partial
|
|
21
|
-
end
|
|
8
|
+
def initialize(verbose: false)
|
|
9
|
+
@scanners = []
|
|
10
|
+
@loader = TemplateLoader.new
|
|
11
|
+
@verbose = verbose
|
|
12
|
+
@static_templates = []
|
|
13
|
+
@template_renders = nil
|
|
22
14
|
end
|
|
23
15
|
|
|
24
|
-
|
|
16
|
+
def scan_view_dir(view_dir)
|
|
17
|
+
@scanners << TemplateScanner.new(view_dir)
|
|
18
|
+
end
|
|
25
19
|
|
|
26
|
-
def
|
|
27
|
-
@
|
|
28
|
-
|
|
29
|
-
Dir["**/*", base: view_dir].map do |file|
|
|
30
|
-
fullpath = File.expand_path(file, view_dir)
|
|
31
|
-
next if File.directory?(fullpath)
|
|
20
|
+
def scan_controller_dir(controller_dir)
|
|
21
|
+
@scanners << ControllerScanner.new(controller_dir)
|
|
22
|
+
end
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
def scan_helper_dir(controller_dir)
|
|
25
|
+
@scanners << HelperScanner.new(controller_dir)
|
|
26
|
+
end
|
|
36
27
|
|
|
37
|
-
|
|
28
|
+
def add_template(virtual_path, locals = [])
|
|
29
|
+
locals = locals.map(&:to_s).sort
|
|
30
|
+
@static_templates << [virtual_path, locals]
|
|
38
31
|
end
|
|
39
32
|
|
|
40
|
-
def
|
|
41
|
-
|
|
33
|
+
def run
|
|
34
|
+
count = 0
|
|
35
|
+
template_renders.each do |virtual_path, locals|
|
|
36
|
+
debug "precompiling: #{virtual_path}"
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
locale: Array(template.details[:locale]),
|
|
46
|
-
variants: Array(template.details[:variant]),
|
|
47
|
-
formats: Array(template.details[:format]),
|
|
48
|
-
handlers: Array(template.details[:handler])
|
|
49
|
-
}
|
|
38
|
+
templates = @loader.load_template(virtual_path, locals)
|
|
39
|
+
count += templates.count
|
|
50
40
|
|
|
51
|
-
|
|
41
|
+
debug " No templates found at #{virtual_path}" if templates.empty?
|
|
52
42
|
end
|
|
53
43
|
|
|
54
|
-
|
|
44
|
+
debug "Precompiled #{count} Templates"
|
|
55
45
|
end
|
|
56
46
|
|
|
57
|
-
def
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
yield template, locals
|
|
65
|
-
end
|
|
66
|
-
elsif !template.partial?
|
|
67
|
-
# For now, guess that non-partials we haven't seen take no locals
|
|
68
|
-
yield template, []
|
|
69
|
-
else
|
|
70
|
-
# Locals unknown
|
|
71
|
-
end
|
|
47
|
+
def template_renders
|
|
48
|
+
return @template_renders if @template_renders
|
|
49
|
+
|
|
50
|
+
template_renders = []
|
|
51
|
+
|
|
52
|
+
@scanners.each do |scanner|
|
|
53
|
+
template_renders.concat scanner.template_renders
|
|
72
54
|
end
|
|
55
|
+
|
|
56
|
+
template_renders.concat @static_templates
|
|
57
|
+
|
|
58
|
+
template_renders.uniq!
|
|
59
|
+
|
|
60
|
+
@template_renders = template_renders
|
|
73
61
|
end
|
|
74
62
|
|
|
75
|
-
|
|
76
|
-
@locals_sets = {}
|
|
77
|
-
|
|
78
|
-
@templates.each do |template|
|
|
79
|
-
parser = TemplateParser.new(template.fullpath)
|
|
80
|
-
parser.render_calls.each do |render_call|
|
|
81
|
-
virtual_path = render_call.virtual_path
|
|
82
|
-
unless virtual_path.include?("/")
|
|
83
|
-
# Not necessarily true, since the perfix is based on the current
|
|
84
|
-
# controller, but is a safe bet most of the time.
|
|
85
|
-
virtual_path = "#{template.prefix}/#{virtual_path}"
|
|
86
|
-
end
|
|
87
|
-
@locals_sets[virtual_path] ||= []
|
|
88
|
-
@locals_sets[virtual_path] << render_call.locals_keys.map(&:to_s).sort
|
|
89
|
-
end
|
|
90
|
-
end
|
|
63
|
+
private
|
|
91
64
|
|
|
92
|
-
|
|
65
|
+
def debug(msg)
|
|
66
|
+
puts msg if @verbose
|
|
93
67
|
end
|
|
94
68
|
end
|
|
95
69
|
end
|
|
@@ -1,50 +1,84 @@
|
|
|
1
1
|
module ActionviewPrecompiler
|
|
2
|
-
RenderCall = Struct.new(:
|
|
3
|
-
def virtual_path
|
|
4
|
-
if render_type == :partial
|
|
5
|
-
@virtual_path ||= template.gsub(%r{(/|^)([^/]*)\z}, '\1_\2')
|
|
6
|
-
else
|
|
7
|
-
template
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
end
|
|
2
|
+
RenderCall = Struct.new(:virtual_path, :locals_keys)
|
|
11
3
|
|
|
12
4
|
class RenderParser
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def initialize(code)
|
|
5
|
+
def initialize(code, parser: ASTParser, from_controller: false)
|
|
16
6
|
@code = code
|
|
17
|
-
@
|
|
7
|
+
@parser = parser
|
|
8
|
+
@from_controller = from_controller
|
|
18
9
|
end
|
|
19
10
|
|
|
20
11
|
def render_calls
|
|
21
|
-
render_nodes =
|
|
22
|
-
render_nodes.map do |
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
render_nodes = @parser.parse_render_nodes(@code)
|
|
13
|
+
render_nodes.map do |method, nodes|
|
|
14
|
+
parse_method = case method
|
|
15
|
+
when :layout
|
|
16
|
+
:parse_layout
|
|
17
|
+
else
|
|
18
|
+
:parse_render
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
nodes.map { |n| send(parse_method, n) }
|
|
22
|
+
end.flatten.compact
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
private
|
|
28
26
|
|
|
27
|
+
# Convert
|
|
28
|
+
# render("foo", ...)
|
|
29
|
+
# into either
|
|
30
|
+
# render(template: "foo", ...)
|
|
31
|
+
# or
|
|
32
|
+
# render(partial: "foo", ...)
|
|
33
|
+
# depending on controller or view context
|
|
34
|
+
def normalize_args(string, options_hash)
|
|
35
|
+
if @from_controller
|
|
36
|
+
if options_hash
|
|
37
|
+
options = parse_hash_to_symbols(options_hash)
|
|
38
|
+
else
|
|
39
|
+
options = {}
|
|
40
|
+
end
|
|
41
|
+
return nil unless options
|
|
42
|
+
options.merge(template: string)
|
|
43
|
+
else
|
|
44
|
+
if options_hash
|
|
45
|
+
{ partial: string, locals: options_hash }
|
|
46
|
+
else
|
|
47
|
+
{ partial: string }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
29
52
|
def parse_render(node)
|
|
30
53
|
node = node.argument_nodes
|
|
31
|
-
if (node.length == 1 || node.length == 2) && node[0].
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if node.length == 2
|
|
54
|
+
if (node.length == 1 || node.length == 2) && !node[0].hash?
|
|
55
|
+
if node.length == 1
|
|
56
|
+
options = normalize_args(node[0], nil)
|
|
57
|
+
elsif node.length == 2
|
|
36
58
|
return unless node[1].hash?
|
|
37
|
-
options
|
|
59
|
+
options = normalize_args(node[0], node[1])
|
|
38
60
|
end
|
|
61
|
+
return nil unless options
|
|
39
62
|
return parse_render_from_options(options)
|
|
40
63
|
elsif node.length == 1 && node[0].hash?
|
|
41
64
|
options = parse_hash_to_symbols(node[0])
|
|
65
|
+
return nil unless options
|
|
42
66
|
return parse_render_from_options(options)
|
|
43
67
|
else
|
|
44
68
|
nil
|
|
45
69
|
end
|
|
46
70
|
end
|
|
47
71
|
|
|
72
|
+
def parse_layout(node)
|
|
73
|
+
return nil unless from_controller?
|
|
74
|
+
|
|
75
|
+
template = parse_str(node.argument_nodes[0])
|
|
76
|
+
return nil unless template
|
|
77
|
+
|
|
78
|
+
virtual_path = layout_to_virtual_path(template)
|
|
79
|
+
RenderCall.new(virtual_path, [])
|
|
80
|
+
end
|
|
81
|
+
|
|
48
82
|
def parse_hash(node)
|
|
49
83
|
node.hash? && node.to_hash
|
|
50
84
|
end
|
|
@@ -52,22 +86,28 @@ module ActionviewPrecompiler
|
|
|
52
86
|
def parse_hash_to_symbols(node)
|
|
53
87
|
hash = parse_hash(node)
|
|
54
88
|
return unless hash
|
|
55
|
-
hash.transform_keys do |
|
|
56
|
-
key = parse_sym(
|
|
89
|
+
hash.transform_keys do |key_node|
|
|
90
|
+
key = parse_sym(key_node)
|
|
57
91
|
return unless key
|
|
58
92
|
key
|
|
59
93
|
end
|
|
60
94
|
end
|
|
61
95
|
|
|
62
|
-
|
|
63
|
-
IGNORED_KEYS = [:formats]
|
|
64
|
-
ALL_KNOWN_KEYS = [*RENDER_TYPE_KEYS, *IGNORED_KEYS, :locals, :object, :collection, :as]
|
|
96
|
+
ALL_KNOWN_KEYS = [:partial, :template, :layout, :formats, :locals, :object, :collection, :as, :status, :content_type, :location, :spacer_template]
|
|
65
97
|
|
|
66
98
|
def parse_render_from_options(options_hash)
|
|
99
|
+
renders = []
|
|
67
100
|
keys = options_hash.keys
|
|
68
101
|
|
|
69
|
-
|
|
70
|
-
|
|
102
|
+
render_type_keys =
|
|
103
|
+
if from_controller?
|
|
104
|
+
[:partial, :template]
|
|
105
|
+
else
|
|
106
|
+
[:partial, :template, :layout]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if (keys & render_type_keys).size < 1
|
|
110
|
+
# Must have at least one of render keys
|
|
71
111
|
return nil
|
|
72
112
|
end
|
|
73
113
|
|
|
@@ -76,8 +116,25 @@ module ActionviewPrecompiler
|
|
|
76
116
|
return nil
|
|
77
117
|
end
|
|
78
118
|
|
|
79
|
-
render_type = (keys &
|
|
80
|
-
|
|
119
|
+
render_type = (keys & render_type_keys)[0]
|
|
120
|
+
|
|
121
|
+
node = options_hash[render_type]
|
|
122
|
+
if node.string?
|
|
123
|
+
template = node.to_string
|
|
124
|
+
else
|
|
125
|
+
if node.variable_reference?
|
|
126
|
+
dependency = node.variable_name.sub(/\A(?:\$|@{1,2})/, "")
|
|
127
|
+
elsif node.vcall?
|
|
128
|
+
dependency = node.variable_name
|
|
129
|
+
elsif node.call?
|
|
130
|
+
dependency = node.call_method_name
|
|
131
|
+
else
|
|
132
|
+
return
|
|
133
|
+
end
|
|
134
|
+
object_template = true
|
|
135
|
+
template = "#{dependency.pluralize}/#{dependency.singularize}"
|
|
136
|
+
end
|
|
137
|
+
|
|
81
138
|
return unless template
|
|
82
139
|
|
|
83
140
|
if options_hash.key?(:locals)
|
|
@@ -93,7 +150,13 @@ module ActionviewPrecompiler
|
|
|
93
150
|
locals_keys = []
|
|
94
151
|
end
|
|
95
152
|
|
|
96
|
-
if
|
|
153
|
+
if spacer_template = render_template_with_spacer?(options_hash)
|
|
154
|
+
virtual_path = partial_to_virtual_path(:partial, spacer_template)
|
|
155
|
+
# Locals keys should not include collection keys
|
|
156
|
+
renders << RenderCall.new(virtual_path, locals_keys.dup)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
if options_hash.key?(:object) || options_hash.key?(:collection) || object_template
|
|
97
160
|
return nil if options_hash.key?(:object) && options_hash.key?(:collection)
|
|
98
161
|
return nil unless options_hash.key?(:partial)
|
|
99
162
|
|
|
@@ -112,7 +175,28 @@ module ActionviewPrecompiler
|
|
|
112
175
|
end
|
|
113
176
|
end
|
|
114
177
|
|
|
115
|
-
|
|
178
|
+
virtual_path = partial_to_virtual_path(render_type, template)
|
|
179
|
+
renders << RenderCall.new(virtual_path, locals_keys)
|
|
180
|
+
|
|
181
|
+
# Support for rendering multiple templates (i.e. a partial with a layout)
|
|
182
|
+
if layout_template = render_template_with_layout?(render_type, options_hash)
|
|
183
|
+
virtual_path = if from_controller?
|
|
184
|
+
layout_to_virtual_path(layout_template)
|
|
185
|
+
else
|
|
186
|
+
if !layout_template.include?("/") &&
|
|
187
|
+
partial_prefix = template.match(%r{(.*)/([^/]*)\z})
|
|
188
|
+
# TODO: use the file path that this render call was found in to
|
|
189
|
+
# generate the partial prefix instead of rendered partial.
|
|
190
|
+
partial_prefix = partial_prefix[1]
|
|
191
|
+
layout_template = "#{partial_prefix}/#{layout_template}"
|
|
192
|
+
end
|
|
193
|
+
partial_to_virtual_path(:layout, layout_template)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
renders << RenderCall.new(virtual_path, locals_keys)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
renders
|
|
116
200
|
end
|
|
117
201
|
|
|
118
202
|
def parse_str(node)
|
|
@@ -123,21 +207,38 @@ module ActionviewPrecompiler
|
|
|
123
207
|
node.symbol? && node.to_symbol
|
|
124
208
|
end
|
|
125
209
|
|
|
210
|
+
private
|
|
211
|
+
|
|
126
212
|
def debug(message)
|
|
127
213
|
warn message
|
|
128
214
|
end
|
|
129
215
|
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
216
|
+
def from_controller?
|
|
217
|
+
@from_controller
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def render_template_with_layout?(render_type, options_hash)
|
|
221
|
+
if render_type != :layout && options_hash.key?(:layout)
|
|
222
|
+
parse_str(options_hash[:layout])
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def render_template_with_spacer?(options_hash)
|
|
227
|
+
if !from_controller? && options_hash.key?(:spacer_template)
|
|
228
|
+
parse_str(options_hash[:spacer_template])
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def partial_to_virtual_path(render_type, partial_path)
|
|
233
|
+
if render_type == :partial || render_type == :layout
|
|
234
|
+
partial_path.gsub(%r{(/|^)([^/]*)\z}, '\1_\2')
|
|
235
|
+
else
|
|
236
|
+
partial_path
|
|
135
237
|
end
|
|
136
|
-
renders
|
|
137
238
|
end
|
|
138
239
|
|
|
139
|
-
def
|
|
140
|
-
|
|
240
|
+
def layout_to_virtual_path(layout_path)
|
|
241
|
+
"layouts/#{layout_path}"
|
|
141
242
|
end
|
|
142
243
|
end
|
|
143
244
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ActionviewPrecompiler
|
|
2
|
+
class TemplateFile
|
|
3
|
+
attr_reader :fullpath, :relative_path, :virtual_path
|
|
4
|
+
attr_reader :action, :prefix, :details
|
|
5
|
+
|
|
6
|
+
def initialize(fullpath, relative_path)
|
|
7
|
+
@fullpath = fullpath
|
|
8
|
+
@relative_path = relative_path
|
|
9
|
+
@virtual_path = relative_path.slice(0, relative_path.index("."))
|
|
10
|
+
|
|
11
|
+
parsed = ParsedFilename.new(relative_path)
|
|
12
|
+
@prefix = parsed.prefix
|
|
13
|
+
@action = parsed.action
|
|
14
|
+
@partial = parsed.partial?
|
|
15
|
+
@details = parsed.details
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def partial?
|
|
19
|
+
@partial
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module ActionviewPrecompiler
|
|
2
|
+
class TemplateLoader
|
|
3
|
+
VIRTUAL_PATH_REGEX = %r{\A(?:(?<prefix>.*)\/)?(?<partial>_)?(?<action>[^\/\.]+)}
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
target = ActionController::Base
|
|
7
|
+
@lookup_context = ActionView::LookupContext.new(target.view_paths)
|
|
8
|
+
@view_context_class = target.view_context_class
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def load_template(virtual_path, locals)
|
|
12
|
+
templates = find_all_templates(virtual_path, locals)
|
|
13
|
+
templates.each do |template|
|
|
14
|
+
template.send(:compile!, @view_context_class)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def find_all_templates(virtual_path, locals)
|
|
21
|
+
match = virtual_path.match(VIRTUAL_PATH_REGEX)
|
|
22
|
+
if match
|
|
23
|
+
action = match[:action]
|
|
24
|
+
prefix = match[:prefix] ? [match[:prefix]] : []
|
|
25
|
+
partial = !!match[:partial]
|
|
26
|
+
|
|
27
|
+
# Assume templates with different details take same locals
|
|
28
|
+
details = {}
|
|
29
|
+
|
|
30
|
+
@lookup_context.find_all(action, prefix, partial, locals, details)
|
|
31
|
+
else
|
|
32
|
+
[]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -2,8 +2,6 @@ require "action_view"
|
|
|
2
2
|
|
|
3
3
|
module ActionviewPrecompiler
|
|
4
4
|
class TemplateParser
|
|
5
|
-
include ASTParser
|
|
6
|
-
|
|
7
5
|
attr_reader :filename, :basename, :handler
|
|
8
6
|
|
|
9
7
|
class FakeTemplate
|
|
@@ -14,30 +12,27 @@ module ActionviewPrecompiler
|
|
|
14
12
|
def type
|
|
15
13
|
nil
|
|
16
14
|
end
|
|
15
|
+
|
|
16
|
+
def format
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def initialize(filename)
|
|
20
22
|
@filename = filename
|
|
21
23
|
@basename = File.basename(filename)
|
|
22
24
|
handler_ext = @basename.split(".").last
|
|
23
|
-
@handler =
|
|
24
|
-
@is_partial = !!@basename.start_with?("_")
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def partial?
|
|
28
|
-
@is_partial
|
|
25
|
+
@handler = HANDLERS_FOR_EXTENSION[handler_ext]
|
|
29
26
|
end
|
|
30
27
|
|
|
31
28
|
def render_calls
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def compiled_source
|
|
40
|
-
@handler.call(FakeTemplate.new, File.read(@filename))
|
|
29
|
+
src = File.read(@filename)
|
|
30
|
+
if src.include?("render")
|
|
31
|
+
compiled_source = @handler.call(FakeTemplate.new, File.read(@filename))
|
|
32
|
+
RenderParser.new(compiled_source).render_calls
|
|
33
|
+
else
|
|
34
|
+
[]
|
|
35
|
+
end
|
|
41
36
|
end
|
|
42
37
|
end
|
|
43
38
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "actionview_precompiler/template_file"
|
|
2
|
+
|
|
3
|
+
module ActionviewPrecompiler
|
|
4
|
+
class TemplateScanner
|
|
5
|
+
attr_reader :view_dir
|
|
6
|
+
|
|
7
|
+
def initialize(view_dir)
|
|
8
|
+
@view_dir = view_dir
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def template_renders
|
|
12
|
+
template_renders = []
|
|
13
|
+
|
|
14
|
+
each_template do |template|
|
|
15
|
+
parser = TemplateParser.new(template.fullpath)
|
|
16
|
+
parser.render_calls.each do |render_call|
|
|
17
|
+
virtual_path = render_call.virtual_path
|
|
18
|
+
unless virtual_path.include?("/")
|
|
19
|
+
# Not necessarily true, since the perfix is based on the current
|
|
20
|
+
# controller, but is a safe bet most of the time.
|
|
21
|
+
virtual_path = "#{template.prefix}/#{virtual_path}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
locals = render_call.locals_keys.map(&:to_s).sort
|
|
25
|
+
|
|
26
|
+
template_renders << [virtual_path, locals]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
template_renders.uniq
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def each_template
|
|
36
|
+
Dir["**/*", base: view_dir].sort.map do |file|
|
|
37
|
+
fullpath = File.expand_path(file, view_dir)
|
|
38
|
+
next if File.directory?(fullpath)
|
|
39
|
+
|
|
40
|
+
yield TemplateFile.new(fullpath, file)
|
|
41
|
+
end.compact
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,33 +1,47 @@
|
|
|
1
|
+
require "action_controller"
|
|
2
|
+
require "action_view"
|
|
3
|
+
|
|
1
4
|
require "actionview_precompiler/version"
|
|
2
5
|
require "actionview_precompiler/ast_parser"
|
|
3
6
|
require "actionview_precompiler/template_parser"
|
|
4
7
|
require "actionview_precompiler/render_parser"
|
|
8
|
+
require "actionview_precompiler/controller_parser"
|
|
9
|
+
require "actionview_precompiler/helper_parser"
|
|
5
10
|
require "actionview_precompiler/precompiler"
|
|
6
11
|
require "actionview_precompiler/parsed_filename"
|
|
7
12
|
|
|
8
13
|
module ActionviewPrecompiler
|
|
9
14
|
class Error < StandardError; end
|
|
10
15
|
|
|
16
|
+
# Allow overriding from ActionView default handlers if necessary
|
|
17
|
+
HANDLERS_FOR_EXTENSION = Hash.new do |h, ext|
|
|
18
|
+
h[ext] = ActionView::Template.handler_for_extension(ext)
|
|
19
|
+
end
|
|
20
|
+
|
|
11
21
|
def self.precompile(verbose: false)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
templates = lookup_context.find_all(*args)
|
|
22
|
-
templates.each do |template|
|
|
23
|
-
puts "precompiling: #{template.inspect}" if verbose
|
|
24
|
-
count += 1
|
|
25
|
-
template.send(:compile!, mod)
|
|
22
|
+
precompiler = Precompiler.new(verbose: verbose)
|
|
23
|
+
|
|
24
|
+
if block_given?
|
|
25
|
+
# Custom configuration
|
|
26
|
+
yield precompiler
|
|
27
|
+
else
|
|
28
|
+
# Scan view dirs
|
|
29
|
+
ActionController::Base.view_paths.each do |view_path|
|
|
30
|
+
precompiler.scan_view_dir view_path.path
|
|
26
31
|
end
|
|
27
|
-
end
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
# If we have an application, scan controllers
|
|
34
|
+
if Rails.respond_to?(:application)
|
|
35
|
+
Rails.application.paths["app/controllers"].each do |path|
|
|
36
|
+
precompiler.scan_controller_dir path.to_s
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Rails.application.paths["app/helpers"].each do |path|
|
|
40
|
+
precompiler.scan_helper_dir path.to_s
|
|
41
|
+
end
|
|
42
|
+
end
|
|
31
43
|
end
|
|
44
|
+
|
|
45
|
+
precompiler.run
|
|
32
46
|
end
|
|
33
47
|
end
|