1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
# encoding: utf-8
# This is so that templates compiled in this file are UTF-8
require 'set'
require "action_view/template/path"
module ActionView
class Template
extend TemplateHandlers
attr_reader :source, :identifier, :handler
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
@details = details
end
def render(view, locals, &blk)
method_name = compile(locals, view)
view.send(method_name, locals, &blk)
end
# TODO: Figure out how to abstract this
def variable_name
identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
# TODO: Figure out how to abstract this
def counter_name
"#{variable_name}_counter".to_sym
end
# TODO: kill hax
def partial?
@details[:partial]
end
# TODO: Move out of Template
def mime_type
Mime::Type.lookup_by_extension(@details[:format]) if @details[:format]
end
private
def compile(locals, view)
method_name = build_method_name(locals)
return method_name if view.respond_to?(method_name)
locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
source = <<-end_src
def #{method_name}(local_assigns)
old_output_buffer = output_buffer;#{locals_code};#{@handler.call(self)}
ensure
self.output_buffer = old_output_buffer
end
end_src
begin
ActionView::Base::CompiledTemplates.module_eval(source, identifier, 0)
method_name
rescue Exception => e # errors from template code
if logger = (view && view.logger)
logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
logger.debug "Function body: #{source}"
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
end
raise ActionView::TemplateError.new(self, {}, e)
end
end
def build_method_name(locals)
# TODO: is locals.keys.hash reliably the same?
"_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
end
end
end
if false
module ActionView #:nodoc:
class Template
extend TemplateHandlers
extend ActiveSupport::Memoizable
module Loading
def load!
@cached = true
# freeze
end
end
include Loading
include Renderable
# Templates that are exempt from layouts
@@exempt_from_layout = Set.new([/\.rjs$/])
# Don't render layouts for templates with the given extensions.
def self.exempt_from_layout(*extensions)
regexps = extensions.collect do |extension|
extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
end
@@exempt_from_layout.merge(regexps)
end
attr_accessor :template_path, :filename, :load_path, :base_path
attr_accessor :locale, :name, :format, :extension
delegate :to_s, :to => :path
def initialize(template_path, load_paths = [])
template_path = template_path.dup
@load_path, @filename = find_full_path(template_path, load_paths)
@name = template_path.to_s.split("/").last.split(".").first
# @base_path, @name, @locale, @format, @extension = split(template_path)
@base_path.to_s.gsub!(/\/$/, '') # Push to split method
# Extend with partial super powers
extend RenderablePartial if @name =~ /^_/
end
def accessible_paths
paths = []
if valid_extension?(extension)
paths << path
paths << path_without_extension
if multipart?
formats = format.split(".")
paths << "#{path_without_format_and_extension}.#{formats.first}"
paths << "#{path_without_format_and_extension}.#{formats.second}"
end
else
# template without explicit template handler should only be reachable through its exact path
paths << template_path
end
paths
end
def relative_path
path = File.expand_path(filename)
path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
path
end
memoize :relative_path
def source
File.read(filename)
end
memoize :source
def exempt_from_layout?
@@exempt_from_layout.any? { |exempted| path =~ exempted }
end
def path_without_extension
[base_path, [name, locale, format].compact.join('.')].compact.join('/')
end
memoize :path_without_extension
def path_without_format_and_extension
[base_path, [name, locale].compact.join('.')].compact.join('/')
end
memoize :path_without_format_and_extension
def path
[base_path, [name, locale, format, extension].compact.join('.')].compact.join('/')
end
memoize :path
def mime_type
Mime::Type.lookup_by_extension(format) if format && defined?(::Mime)
end
memoize :mime_type
def multipart?
format && format.include?('.')
end
def content_type
format && format.gsub('.', '/')
end
private
def format_and_extension
(extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
end
memoize :format_and_extension
def mtime
File.mtime(filename)
end
memoize :mtime
def method_segment
relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
end
memoize :method_segment
def stale?
File.mtime(filename) > mtime
end
def recompile?
!@cached
end
def valid_extension?(extension)
!Template.registered_template_handler(extension).nil?
end
def valid_locale?(locale)
I18n.available_locales.include?(locale.to_sym)
end
def find_full_path(path, load_paths)
load_paths = Array(load_paths) + [nil]
load_paths.each do |load_path|
file = load_path ? "#{load_path.to_str}/#{path}" : path
return load_path, file if File.file?(file)
end
raise MissingTemplate.new(load_paths, path)
end
end
end
end
|