mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
c1304098cc
* A new module (ActiveSupport::Autoload) is provide that extends autoloading with new behavior. * All autoloads in modules that have extended ActiveSupport::Autoload will be eagerly required in threadsafe environments * Autoloads can optionally leave off the path if the path is the same as full_constant_name.underscore * It is possible to specify that a group of autoloads live under an additional path. For instance, all of ActionDispatch's middlewares are ActionDispatch::MiddlewareName, but they live under "action_dispatch/middlewares/middleware_name" * It is possible to specify that a group of autoloads are all found at the same path. For instance, a number of exceptions might all be declared there. * One consequence of this is that testing-related constants are not autoloaded. To get the testing helpers for a given component, require "component_name/test_case". For instance, "action_controller/test_case". * test_help.rb, which is automatically required by a Rails application's test helper, requires the test_case.rb for all active components, so this change will not be disruptive in existing or new applications.
165 lines
No EOL
4.4 KiB
Ruby
165 lines
No EOL
4.4 KiB
Ruby
require "pathname"
|
|
require "active_support/core_ext/class"
|
|
require "action_view/template"
|
|
|
|
module ActionView
|
|
# Abstract superclass
|
|
class Resolver
|
|
|
|
class_inheritable_accessor(:registered_details)
|
|
self.registered_details = {}
|
|
|
|
def self.register_detail(name, options = {})
|
|
registered_details[name] = lambda do |val|
|
|
val ||= yield
|
|
val |= [nil] unless options[:allow_nil] == false
|
|
val
|
|
end
|
|
end
|
|
|
|
register_detail(:locale) { [I18n.locale] }
|
|
register_detail(:formats) { Mime::SET.symbols }
|
|
register_detail(:handlers, :allow_nil => false) do
|
|
Template::Handlers.extensions
|
|
end
|
|
|
|
def initialize(options = {})
|
|
@cache = options[:cache]
|
|
@cached = {}
|
|
end
|
|
|
|
# Normalizes the arguments and passes it on to find_template
|
|
def find(*args)
|
|
find_all(*args).first
|
|
end
|
|
|
|
def find_all(name, details = {}, prefix = nil, partial = nil)
|
|
details = normalize_details(details)
|
|
name, prefix = normalize_name(name, prefix)
|
|
|
|
cached([name, details, prefix, partial]) do
|
|
find_templates(name, details, prefix, partial)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
# This is what child classes implement. No defaults are needed
|
|
# because Resolver guarantees that the arguments are present and
|
|
# normalized.
|
|
def find_templates(name, details, prefix, partial)
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def normalize_details(details)
|
|
details = details.dup
|
|
# TODO: Refactor this concern out of the resolver
|
|
details.delete(:formats) if details[:formats] == [:"*/*"]
|
|
registered_details.each do |k, v|
|
|
details[k] = v.call(details[k])
|
|
end
|
|
details
|
|
end
|
|
|
|
# Support legacy foo.erb names even though we now ignore .erb
|
|
# as well as incorrectly putting part of the path in the template
|
|
# name instead of the prefix.
|
|
def normalize_name(name, prefix)
|
|
handlers = Template::Handlers.extensions.join('|')
|
|
name = name.to_s.gsub(/\.(?:#{handlers})$/, '')
|
|
|
|
parts = name.split('/')
|
|
return parts.pop, [prefix, *parts].compact.join("/")
|
|
end
|
|
|
|
def cached(key)
|
|
return yield unless @cache
|
|
return @cached[key] if @cached.key?(key)
|
|
@cached[key] = yield
|
|
end
|
|
end
|
|
|
|
class PathResolver < Resolver
|
|
|
|
EXTENSION_ORDER = [:locale, :formats, :handlers]
|
|
|
|
def to_s
|
|
@path.to_s
|
|
end
|
|
alias to_path to_s
|
|
|
|
def find_templates(name, details, prefix, partial)
|
|
path = build_path(name, details, prefix, partial)
|
|
query(path, EXTENSION_ORDER.map { |ext| details[ext] })
|
|
end
|
|
|
|
private
|
|
|
|
def build_path(name, details, prefix, partial)
|
|
path = ""
|
|
path << "#{prefix}/" unless prefix.empty?
|
|
path << (partial ? "_#{name}" : name)
|
|
path
|
|
end
|
|
|
|
def query(path, exts)
|
|
query = "#{@path}/#{path}"
|
|
exts.each do |ext|
|
|
query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}'
|
|
end
|
|
|
|
Dir[query].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
|
|
|
|
# # 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])
|
|
|
|
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
|
|
end
|
|
|
|
class FileSystemResolver < PathResolver
|
|
def initialize(path, options = {})
|
|
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
|
super(options)
|
|
@path = Pathname.new(path).expand_path
|
|
end
|
|
end
|
|
|
|
# OMG HAX
|
|
# TODO: remove hax
|
|
class FileSystemResolverWithFallback < Resolver
|
|
def initialize(path, options = {})
|
|
super(options)
|
|
@paths = [FileSystemResolver.new(path, options), FileSystemResolver.new("", options), FileSystemResolver.new("/", options)]
|
|
end
|
|
|
|
def find_templates(*args)
|
|
@paths.each do |p|
|
|
template = p.find_templates(*args)
|
|
return template unless template.empty?
|
|
end
|
|
[]
|
|
end
|
|
|
|
def to_s
|
|
@paths.first.to_s
|
|
end
|
|
end
|
|
end |