1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/lib/action_view/template/resolver.rb
José Valim b2600bfc18 Remove locals dependency from template.
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.
2010-10-07 21:31:31 +02:00

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