diff options
Diffstat (limited to 'actionpack/lib/action_view/template/path.rb')
-rw-r--r-- | actionpack/lib/action_view/template/path.rb | 173 |
1 files changed, 119 insertions, 54 deletions
diff --git a/actionpack/lib/action_view/template/path.rb b/actionpack/lib/action_view/template/path.rb index 9709549b70..478bf96c9a 100644 --- a/actionpack/lib/action_view/template/path.rb +++ b/actionpack/lib/action_view/template/path.rb @@ -1,23 +1,82 @@ +require "pathname" + module ActionView class Template + # Abstract super class class Path - attr_reader :path, :paths - delegate :hash, :inspect, :to => :path - def initialize(options) - @cache = options[:cache] + @cache = options[:cache] + @cached = {} + end + + # Normalizes the arguments and passes it on to find_template + def find_by_parts(*args) + find_all_by_parts(*args).first + end + + def find_all_by_parts(name, details = {}, prefix = nil, partial = nil) + details[:locales] = [I18n.locale] + name = name.to_s.gsub(handler_matcher, '').split("/") + find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial) + end + + private + + # This is what child classes implement. No defaults are needed + # because Path guarentees that the arguments are present and + # normalized. + def find_templates(name, details, prefix, partial) + raise NotImplementedError + end + + def valid_handlers + @valid_handlers ||= TemplateHandlers.extensions + end + + def handler_matcher + @handler_matcher ||= begin + e = valid_handlers.join('|') + /\.(?:#{e})$/ + end + end + + def handler_glob + e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join + "{#{e}}" + end + + def formats_glob + @formats_glob ||= begin + formats = Mime::SET.map { |m| m.symbol } + '{' + formats.map { |l| ".#{l}," }.join + '}' + end + end + + def cached(key) + return yield unless @cache + return @cached[key] if @cached.key?(key) + @cached[key] = yield + end + end + + class FileSystemPath < Path + + def initialize(path, options = {}) + raise ArgumentError, "path already is a Path class" if path.is_a?(Path) + super(options) + @path = Pathname.new(path).expand_path end + # TODO: This is the currently needed API. Make this suck less + # ==== <suck> + attr_reader :path + def to_s - if defined?(RAILS_ROOT) - path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') - else - path.to_s - end + path.to_s end def to_str - path.to_str + path.to_s end def ==(path) @@ -27,59 +86,65 @@ module ActionView def eql?(path) to_str == path.to_str end - - def find_by_parts(name, extensions = nil, prefix = nil, partial = nil) - path = prefix ? "#{prefix}/" : "" - - name = name.to_s.split("/") - name[-1] = "_#{name[-1]}" if partial + # ==== </suck> - path << name.join("/") - - template = nil - - Array(extensions).each do |extension| - extensioned_path = extension ? "#{path}.#{extension}" : path - break if (template = find_template(extensioned_path)) + def find_templates(name, details, prefix, partial, root = "#{@path}/") + if glob = details_to_glob(name, details, prefix, partial, root) + cached(glob) do + Dir[glob].map do |path| + next if File.directory?(path) + source = File.read(path) + identifier = Pathname.new(path).expand_path.to_s + + Template.new(source, identifier, *path_to_details(path)) + end.compact + end end - template || find_template(path) end - + private - def create_template(file) - Template.new(file.split("#{self}/").last, self) - end - end - - class FileSystemPath < Path - def initialize(path, options = {}) - raise ArgumentError, "path already is a Path class" if path.is_a?(Path) - - super(options) - @path, @paths = path, {} + + # :api: plugin + def details_to_glob(name, details, prefix, partial, root) + path = "" + path << "#{prefix}/" unless prefix.empty? + path << (partial ? "_#{name}" : name) + + extensions = "" + [:locales, :formats].each do |k| + extensions << if exts = details[k] + '{' + exts.map {|e| ".#{e},"}.join + '}' + else + k == :formats ? formats_glob : '' + end + end - # **/*/** is a hax for symlinked directories - load_templates("#{@path}/{**/*,**}/**") if @cache + "#{root}#{path}#{extensions}#{handler_glob}" end + + # TODO: fix me + # :api: plugin + def path_to_details(path) + # [:erb, :format => :html, :locale => :en, :partial => true/false] + if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') + partial = m[1] == '_' + details = (m[2]||"").split('.').reject { |e| e.empty? } + handler = Template.handler_class_for_extension(m[3]) - private - - def load_template(template) - template.load! - template.accessible_paths.each do |path| - @paths[path] = template + format = Mime[details.last] && details.pop.to_sym + locale = details.last && details.pop.to_sym + + return handler, :format => format, :locale => locale, :partial => partial end end - - def find_template(path) - load_templates("#{@path}/#{path}{,.*}") unless @cache - @paths[path] - end - - def load_templates(glob) - Dir[glob].each do |file| - load_template(create_template(file)) unless File.directory?(file) - end + end + + class FileSystemPathWithFallback < FileSystemPath + + def find_templates(name, details, prefix, partial) + templates = super + return super(name, details, prefix, partial, '') if templates.empty? + templates end end |