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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
|
module ActionView #:nodoc:
class Template
class Path
attr_reader :path, :paths
delegate :hash, :inspect, :to => :path
def initialize(path)
raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
@path = path.freeze
end
def to_s
if defined?(RAILS_ROOT)
path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
else
path.to_s
end
end
def to_str
path.to_str
end
def ==(path)
to_str == path.to_str
end
def eql?(path)
to_str == path.to_str
end
# Returns a ActionView::Template object for the given path string. The
# input path should be relative to the view path directory,
# +hello/index.html.erb+. This method also has a special exception to
# match partial file names without a handler extension. So
# +hello/index.html+ will match the first template it finds with a
# known template extension, +hello/index.html.erb+. Template extensions
# should not be confused with format extensions +html+, +js+, +xml+,
# etc. A format must be supplied to match a formated file. +hello/index+
# will never match +hello/index.html.erb+.
def find_template(path)
templates_in_path do |template|
if template.accessible_paths.include?(path)
return template
end
end
nil
end
def find_by_parts(name, extensions = nil, prefix = nil, partial = nil)
path = prefix ? "#{prefix}/" : ""
name = name.split("/")
name[-1] = "_#{name[-1]}" if partial
path << name.join("/")
template = nil
Array(extensions).each do |extension|
extensioned_path = extension ? "#{path}.#{extension}" : path
template = find_template(extensioned_path) || find_template(path)
break if template
end
template || find_template(path)
end
private
def templates_in_path
(Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
yield create_template(file) unless File.directory?(file)
end
end
def create_template(file)
Template.new(file.split("#{self}/").last, self)
end
end
class EagerPath < Path
def initialize(path)
super
@paths = {}
templates_in_path do |template|
template.load!
template.accessible_paths.each do |path|
@paths[path] = template
end
end
@paths.freeze
end
def find_template(path)
@paths[path]
end
end
extend TemplateHandlers
extend ActiveSupport::Memoizable
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 :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)
@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 = []
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
paths
end
def format_and_extension
(extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
end
memoize :format_and_extension
def multipart?
format && format.include?('.')
end
def content_type
format.gsub('.', '/')
end
def mime_type
Mime::Type.lookup_by_extension(format) if format
end
memoize :mime_type
def path
[base_path, [name, locale, format, extension].compact.join('.')].compact.join('/')
end
memoize :path
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 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 exempt_from_layout?
@@exempt_from_layout.any? { |exempted| path =~ exempted }
end
def mtime
File.mtime(filename)
end
memoize :mtime
def source
File.read(filename)
end
memoize :source
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 load!
@cached = true
freeze
end
private
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
# Returns file split into an array
# [base_path, name, locale, format, extension]
def split(file)
if m = file.match(/^(.*\/)?([^\.]+)\.(.*)$/)
base_path = m[1]
name = m[2]
extensions = m[3]
else
return
end
locale = nil
format = nil
extension = nil
if m = extensions.match(/^(\w+)?\.?(\w+)?\.?(\w+)?\.?/)
if valid_locale?(m[1]) && m[2] && valid_extension?(m[3]) # All three
locale = m[1]
format = m[2]
extension = m[3]
elsif m[1] && m[2] && valid_extension?(m[3]) # Multipart formats
format = "#{m[1]}.#{m[2]}"
extension = m[3]
elsif valid_locale?(m[1]) && valid_extension?(m[2]) # locale and extension
locale = m[1]
extension = m[2]
elsif valid_extension?(m[2]) # format and extension
format = m[1]
extension = m[2]
elsif valid_extension?(m[1]) # Just extension
extension = m[1]
else # No extension
format = m[1]
end
end
[base_path, name, locale, format, extension]
end
end
end
|