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
|
# encoding: utf-8
# This is so that templates compiled in this file are UTF-8
require 'set'
require "action_view/template/resolver"
module ActionView
class Template
extend TemplateHandlers
attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
@details = details
@method_names = {}
format = details.delete(:format) || begin
# TODO: Clean this up
handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
end
@mime_type = Mime::Type.lookup_by_extension(format.to_s)
@formats = [format.to_sym]
@formats << :html if format == :js
@details[:formats] = Array.wrap(format.to_sym)
end
def render(view, locals, &block)
ActiveSupport::Notifications.instrument(:render_template, :identifier => identifier) do
method_name = compile(locals, view)
view.send(method_name, locals, &block)
end
rescue Exception => e
if e.is_a?(TemplateError)
e.sub_template_of(self)
raise e
else
raise TemplateError.new(self, view.assigns, e)
end
end
# TODO: Figure out how to abstract this
def variable_name
@variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
# TODO: Figure out how to abstract this
def counter_name
@counter_name ||= "#{variable_name}_counter".to_sym
end
# TODO: kill hax
def partial?
@details[:partial]
end
def inspect
if defined?(Rails.root)
identifier.sub("#{Rails.root}/", '')
else
identifier
end
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
code = @handler.call(self)
if code.sub!(/\A(#.*coding.*)\n/, '')
encoding_comment = $1
elsif defined?(Encoding) && Encoding.respond_to?(:default_external)
encoding_comment = "#coding:#{Encoding.default_external}"
end
source = <<-end_src
def #{method_name}(local_assigns)
old_output_buffer = output_buffer;#{locals_code};#{code}
ensure
self.output_buffer = old_output_buffer
end
end_src
if encoding_comment
source = "#{encoding_comment}\n#{source}"
line = -1
else
line = 0
end
begin
ActionView::CompiledTemplates.module_eval(source, identifier, line)
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
class LocalsKey
@hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } }
def self.get(*locals)
@hash_keys[*locals] ||= new(klass, format, locale)
end
attr_accessor :hash
def initialize(klass, format, locale)
@hash = locals.hash
end
alias_method :eql?, :equal?
end
def build_method_name(locals)
# TODO: is locals.keys.hash reliably the same?
@method_names[locals.keys.hash] ||=
"_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
end
end
end
|