mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
b2600bfc18
This means that templates does not need to store its source anymore, allowing us to reduce the ammount of memory taken by our Rails processes. Naively speaking, if your app/views contains 2MB of files, each of your processes (after being hit by a bunch of requests) will take 2MB less of memory after this commit. This is extremely important for the upcoming features. Since Rails will also render CSS and JS files, their source won't be stored as well allowing us to decrease the ammount of memory taken.
130 lines
3.7 KiB
Ruby
130 lines
3.7 KiB
Ruby
require "pathname"
|
|
require "active_support/core_ext/class"
|
|
require "action_view/template"
|
|
|
|
module ActionView
|
|
# = Action View Resolver
|
|
class Resolver
|
|
def initialize
|
|
@cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
|
|
h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
|
|
end
|
|
|
|
def clear_cache
|
|
@cached.clear
|
|
end
|
|
|
|
# Normalizes the arguments and passes it on to find_template.
|
|
def find_all(name, prefix=nil, partial=false, details={}, locals=[], key=nil)
|
|
cached(key, prefix, name, partial, locals) do
|
|
find_templates(name, prefix, partial, details)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def caching?
|
|
@caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
|
|
end
|
|
|
|
# This is what child classes implement. No defaults are needed
|
|
# because Resolver guarantees that the arguments are present and
|
|
# normalized.
|
|
def find_templates(name, prefix, partial, details)
|
|
raise NotImplementedError
|
|
end
|
|
|
|
# Helpers that builds a path. Useful for building virtual paths.
|
|
def build_path(name, prefix, partial, details)
|
|
path = ""
|
|
path << "#{prefix}/" unless prefix.empty?
|
|
path << (partial ? "_#{name}" : name)
|
|
path
|
|
end
|
|
|
|
# Get the handler and format from the given parameters.
|
|
def retrieve_handler_and_format(handler, format, default_formats=nil)
|
|
handler = Template.handler_class_for_extension(handler)
|
|
format = format && Mime[format]
|
|
format ||= handler.default_format if handler.respond_to?(:default_format)
|
|
format ||= default_formats
|
|
[handler, format]
|
|
end
|
|
|
|
def cached(key, prefix, name, partial, locals)
|
|
locals = sort_locals(locals)
|
|
unless key && caching?
|
|
yield.each { |t| t.locals = locals }
|
|
else
|
|
@cached[key][prefix][name][partial][locals] ||= yield.each { |t| t.locals = locals }
|
|
end
|
|
end
|
|
|
|
if :locale.respond_to?("<=>")
|
|
def sort_locals(locals)
|
|
locals.sort.freeze
|
|
end
|
|
else
|
|
def sort_locals(locals)
|
|
locals = locals.map{ |l| l.to_s }
|
|
locals.sort!
|
|
locals.freeze
|
|
end
|
|
end
|
|
end
|
|
|
|
class PathResolver < Resolver
|
|
EXTENSION_ORDER = [:locale, :formats, :handlers]
|
|
|
|
private
|
|
|
|
def find_templates(name, prefix, partial, details)
|
|
path = build_path(name, prefix, partial, details)
|
|
query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats])
|
|
end
|
|
|
|
def query(path, exts, formats)
|
|
query = File.join(@path, path)
|
|
|
|
exts.each do |ext|
|
|
query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << ',}'
|
|
end
|
|
|
|
Dir[query].reject { |p| File.directory?(p) }.map do |p|
|
|
handler, format = extract_handler_and_format(p, formats)
|
|
|
|
contents = File.open(p, "rb") {|io| io.read }
|
|
|
|
Template.new(contents, File.expand_path(p), handler,
|
|
:virtual_path => path, :format => format)
|
|
end
|
|
end
|
|
|
|
# Extract handler and formats from path. If a format cannot be a found neither
|
|
# from the path, or the handler, we should return the array of formats given
|
|
# to the resolver.
|
|
def extract_handler_and_format(path, default_formats)
|
|
pieces = File.basename(path).split(".")
|
|
pieces.shift
|
|
retrieve_handler_and_format(pieces.pop, pieces.pop, default_formats)
|
|
end
|
|
end
|
|
|
|
class FileSystemResolver < PathResolver
|
|
def initialize(path)
|
|
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
|
|
super()
|
|
@path = File.expand_path(path)
|
|
end
|
|
|
|
def to_s
|
|
@path.to_s
|
|
end
|
|
alias :to_path :to_s
|
|
|
|
def eql?(resolver)
|
|
self.class.equal?(resolver.class) && to_path == resolver.to_path
|
|
end
|
|
alias :== :eql?
|
|
end
|
|
end
|