From 243513f4d17e62186ef0499edece1588c79220b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 7 Oct 2010 13:26:58 +0200 Subject: Get rid of ruby warnings in Resolvers. Move a few methods up to the abstract class. --- actionpack/lib/action_view/template/resolver.rb | 45 +++++++++++++------------ 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'actionpack/lib/action_view/template/resolver.rb') diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index a261e08dbc..a031d0dc33 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -6,7 +6,6 @@ module ActionView # = Action View Resolver class Resolver def initialize - @path = nil @cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } } end @@ -35,6 +34,23 @@ module ActionView 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) return yield unless key && caching? @cached[key][prefix][name][partial] ||= yield @@ -44,25 +60,13 @@ module ActionView class PathResolver < Resolver EXTENSION_ORDER = [:locale, :formats, :handlers] - def to_s - @path.to_s - end - alias :to_path :to_s - - private + 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 build_path(name, prefix, partial, details) - path = "" - path << "#{prefix}/" unless prefix.empty? - path << (partial ? "_#{name}" : name) - path - end - def query(path, exts, formats) query = File.join(@path, path) @@ -86,13 +90,7 @@ module ActionView def extract_handler_and_format(path, default_formats) pieces = File.basename(path).split(".") pieces.shift - - handler = Template.handler_class_for_extension(pieces.pop) - format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym - format ||= handler.default_format if handler.respond_to?(:default_format) - format ||= default_formats - - [handler, format] + retrieve_handler_and_format(pieces.pop, pieces.pop, default_formats) end end @@ -103,6 +101,11 @@ module ActionView @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 -- cgit v1.2.3 From b2600bfc181664fcfe448d100ca054017b0576dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 7 Oct 2010 15:50:20 +0200 Subject: 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. --- actionpack/lib/action_view/template/resolver.rb | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'actionpack/lib/action_view/template/resolver.rb') diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index a031d0dc33..9ec39b16f0 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -6,8 +6,8 @@ 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] = {} } } } + @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 @@ -15,8 +15,8 @@ module ActionView end # Normalizes the arguments and passes it on to find_template. - def find_all(name, prefix=nil, partial=false, details={}, key=nil) - cached(key, prefix, name, partial) do + 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 @@ -51,9 +51,25 @@ module ActionView [handler, format] end - def cached(key, prefix, name, partial) - return yield unless key && caching? - @cached[key][prefix][name][partial] ||= yield + 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 -- cgit v1.2.3 From 38d78f99d52801d8392a7229b40edae74cc3d142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 10 Oct 2010 09:24:17 +0200 Subject: Resolvers now consider timestamps. Before this patch, every request in development caused the template to be compiled, regardless if it was updated in the filesystem or not. This patch now checks the timestamp and only compiles it again if any change was done. While this probably won't show any difference for current setups, but it will be useful for asset template handlers (like SASS), as compiling their templates is slower than ERb, Haml, etc. --- actionpack/lib/action_view/template/resolver.rb | 67 +++++++++++++++++-------- 1 file changed, 46 insertions(+), 21 deletions(-) (limited to 'actionpack/lib/action_view/template/resolver.rb') diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 9ec39b16f0..5c6877a923 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -16,7 +16,7 @@ module ActionView # 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 + cached(key, [name, prefix, partial], details, locals) do find_templates(name, prefix, partial, details) end end @@ -35,37 +35,55 @@ module ActionView end # Helpers that builds a path. Useful for building virtual paths. - def build_path(name, prefix, partial, details) + def build_path(name, prefix, partial) 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) + # Hnadles templates caching. If a key is given and caching is on + # always check the cache before hitting the resolver. Otherwise, + # it always hits the resolver but check if the resolver is fresher + # before returning it. + def cached(key, path_info, details, locals) #:nodoc: + name, prefix, partial = path_info locals = sort_locals(locals) - unless key && caching? - yield.each { |t| t.locals = locals } + + if key && caching? + @cached[key][name][prefix][partial][locals] ||= decorate(yield, path_info, details, locals) else - @cached[key][prefix][name][partial][locals] ||= yield.each { |t| t.locals = locals } + fresh = decorate(yield, path_info, details, locals) + return fresh unless key + + scope = @cached[key][name][prefix][partial] + cache = scope[locals] + mtime = cache && cache.map(&:updated_at).max + + if !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime } + scope[locals] = fresh + else + cache + end + end + end + + # Ensures all the resolver information is set in the template. + def decorate(templates, path_info, details, locals) #:nodoc: + cached = nil + templates.each do |t| + t.locals = locals + t.formats = details[:formats] || [:html] if t.formats.empty? + t.virtual_path ||= (cached ||= build_path(*path_info)) end end - if :locale.respond_to?("<=>") - def sort_locals(locals) + if :symbol.respond_to?("<=>") + def sort_locals(locals) #:nodoc: locals.sort.freeze end else - def sort_locals(locals) + def sort_locals(locals) #:nodoc: locals = locals.map{ |l| l.to_s } locals.sort! locals.freeze @@ -79,7 +97,7 @@ module ActionView private def find_templates(name, prefix, partial, details) - path = build_path(name, prefix, partial, details) + path = build_path(name, prefix, partial) query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats]) end @@ -96,17 +114,24 @@ module ActionView contents = File.open(p, "rb") {|io| io.read } Template.new(contents, File.expand_path(p), handler, - :virtual_path => path, :format => format) + :virtual_path => path, :format => format, :updated_at => mtime(p)) end end + # Returns the file mtime from the filesystem. + def mtime(p) + File.stat(p).mtime + 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) + handler = Template.handler_class_for_extension(pieces.pop) + format = pieces.last && Mime[pieces.last] + [handler, format] end end -- cgit v1.2.3 From 5ec27189b8b433145baa7270cf4219c5041f6a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 10 Oct 2010 23:11:50 +0200 Subject: Do not allow templates coming from Fallback resolvers to store a virtual path. --- actionpack/lib/action_view/template/resolver.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'actionpack/lib/action_view/template/resolver.rb') diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 5c6877a923..7707dbcf98 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -135,6 +135,7 @@ module ActionView end end + # A resolver that loads files from the filesystem. class FileSystemResolver < PathResolver def initialize(path) raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) @@ -152,4 +153,16 @@ module ActionView end alias :== :eql? end + + # The same as FileSystemResolver but does not allow templates to store + # a virtual path since it is invalid for such resolvers. + class FallbackFileSystemResolver < FileSystemResolver + def self.instances + [new(""), new("/")] + end + + def decorate(*) + super.each { |t| t.virtual_path = nil } + end + end end -- cgit v1.2.3