aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view
diff options
context:
space:
mode:
authorthedarkone <nobody>2009-02-12 19:35:14 +0100
committerJoshua Peek <josh@joshpeek.com>2009-02-12 13:04:12 -0600
commit3942cb406e1d5db0ac00e03153809cc8dc4cc4db (patch)
tree3fda08b1509e81f209d5353180feec2086139a3f /actionpack/lib/action_view
parentb1d41bdfb06cb5f606f515965316a348d9bc9b47 (diff)
downloadrails-3942cb406e1d5db0ac00e03153809cc8dc4cc4db.tar.gz
rails-3942cb406e1d5db0ac00e03153809cc8dc4cc4db.tar.bz2
rails-3942cb406e1d5db0ac00e03153809cc8dc4cc4db.zip
Port fast reloadable templates from rails-dev-boost.
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r--actionpack/lib/action_view/base.rb13
-rw-r--r--actionpack/lib/action_view/partials.rb1
-rw-r--r--actionpack/lib/action_view/paths.rb23
-rw-r--r--actionpack/lib/action_view/reloadable_template.rb120
-rw-r--r--actionpack/lib/action_view/renderable.rb30
-rw-r--r--actionpack/lib/action_view/template.rb88
6 files changed, 187 insertions, 88 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 3134807a08..4198725e0d 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -182,10 +182,15 @@ module ActionView #:nodoc:
# that alert()s the caught exception (and then re-raises it).
cattr_accessor :debug_rjs
- # Specify whether to check whether modified templates are recompiled without a restart
+ # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
+ # Automaticaly reloading templates are not thread safe and should only be used in development mode.
@@cache_template_loading = false
cattr_accessor :cache_template_loading
+ def self.cache_template_loading?
+ ActionController::Base.allow_concurrency || cache_template_loading
+ end
+
attr_internal :request
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
@@ -226,6 +231,8 @@ module ActionView #:nodoc:
def view_paths=(paths)
@view_paths = self.class.process_view_paths(paths)
+ # we might be using ReloadableTemplates, so we need to let them know this a new request
+ @view_paths.load!
end
# Returns the result of a render that's dictated by the options hash. The primary options are:
@@ -247,8 +254,8 @@ module ActionView #:nodoc:
if options[:layout]
_render_with_layout(options, local_assigns, &block)
elsif options[:file]
- template = self.view_paths.find_template(options[:file], template_format)
- template.render_template(self, options[:locals])
+ tempalte = self.view_paths.find_template(options[:file], template_format)
+ tempalte.render_template(self, options[:locals])
elsif options[:partial]
render_partial(options)
elsif options[:inline]
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index 6fe4dbf375..9e5e0f786e 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -235,5 +235,6 @@ module ActionView
self.view_paths.find_template(path, self.template_format)
end
+ memoize :_pick_partial_template
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index e14b21221c..41f9f486e5 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -2,12 +2,16 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
- Template::Path.new(obj)
+ if Base.cache_template_loading?
+ Template::EagerPath.new(obj.to_s)
+ else
+ ReloadableTemplate::ReloadablePath.new(obj.to_s)
+ end
else
obj
end
end
-
+
def initialize(*args)
super(*args).map! { |obj| self.class.type_cast(obj) }
end
@@ -31,9 +35,14 @@ module ActionView #:nodoc:
def unshift(*objs)
super(*objs.map { |obj| self.class.type_cast(obj) })
end
+
+ def load!
+ each(&:load!)
+ end
- def find_template(template_path, format = nil)
- return template_path if template_path.respond_to?(:render)
+ def find_template(original_template_path, format = nil)
+ return original_template_path if original_template_path.respond_to?(:render)
+ template_path = original_template_path.sub(/^\//, '')
each do |load_path|
if format && (template = load_path["#{template_path}.#{I18n.locale}.#{format}"])
@@ -52,11 +61,9 @@ module ActionView #:nodoc:
end
end
- if File.exist?(template_path)
- return Template.new(template_path, template_path[0] == 47 ? "" : ".")
- end
+ return Template.new(original_template_path, original_template_path =~ /\A\// ? "" : ".") if File.file?(original_template_path)
- raise MissingTemplate.new(self, template_path, format)
+ raise MissingTemplate.new(self, original_template_path, format)
end
end
end
diff --git a/actionpack/lib/action_view/reloadable_template.rb b/actionpack/lib/action_view/reloadable_template.rb
new file mode 100644
index 0000000000..3081be60fd
--- /dev/null
+++ b/actionpack/lib/action_view/reloadable_template.rb
@@ -0,0 +1,120 @@
+module ActionView #:nodoc:
+ class ReloadableTemplate < Template
+
+ class TemplateDeleted < ActionView::ActionViewError
+ end
+
+ class ReloadablePath < Template::Path
+
+ def initialize(path)
+ super
+ @paths = {}
+ new_request!
+ end
+
+ def new_request!
+ @disk_cache = {}
+ end
+ alias_method :load!, :new_request!
+
+ def [](path)
+ if found_template = @paths[path]
+ begin
+ found_template.reset_cache_if_stale!
+ rescue TemplateDeleted
+ unregister_template(found_template)
+ self[path]
+ end
+ else
+ load_all_templates_from_dir(templates_dir_from_path(path))
+ @paths[path]
+ end
+ end
+
+ def register_template_from_file(template_file_path)
+ if !@paths[template_relative_path = template_file_path.split("#{@path}/").last] && File.file?(template_file_path)
+ register_template(ReloadableTemplate.new(template_relative_path, self))
+ end
+ end
+
+ def register_template(template)
+ template.accessible_paths.each do |path|
+ @paths[path] = template
+ end
+ end
+
+ # remove (probably deleted) template from cache
+ def unregister_template(template)
+ template.accessible_paths.each do |template_path|
+ @paths.delete(template_path) if @paths[template_path] == template
+ end
+ # fill in any newly created gaps
+ @paths.values.uniq.each do |template|
+ template.accessible_paths.each {|path| @paths[path] ||= template}
+ end
+ end
+
+ # load all templates from the directory of the requested template
+ def load_all_templates_from_dir(dir)
+ # hit disk only once per template-dir/request
+ @disk_cache[dir] ||= template_files_from_dir(dir).each {|template_file| register_template_from_file(template_file)}
+ end
+
+ def templates_dir_from_path(path)
+ dirname = File.dirname(path)
+ File.join(@path, dirname == '.' ? '' : dirname)
+ end
+
+ # get all the template filenames from the dir
+ def template_files_from_dir(dir)
+ Dir.glob(File.join(dir, '*'))
+ end
+
+ end
+
+ module Unfreezable
+ def freeze; self; end
+ end
+
+ def initialize(*args)
+ super
+ @compiled_methods = []
+
+ # we don't ever want to get frozen
+ extend Unfreezable
+ end
+
+ def mtime
+ File.mtime(filename)
+ end
+
+ attr_accessor :previously_last_modified
+
+ def stale?
+ previously_last_modified.nil? || previously_last_modified < mtime
+ rescue Errno::ENOENT => e
+ undef_my_compiled_methods!
+ raise TemplateDeleted
+ end
+
+ def reset_cache_if_stale!
+ if stale?
+ flush_cache 'source', 'compiled_source'
+ undef_my_compiled_methods!
+ @previously_last_modified = mtime
+ end
+ self
+ end
+
+ def undef_my_compiled_methods!
+ @compiled_methods.each {|comp_method| ActionView::Base::CompiledTemplates.send(:remove_method, comp_method)}
+ @compiled_methods.clear
+ end
+
+ def compile!(render_symbol, local_assigns)
+ super
+ @compiled_methods << render_symbol
+ end
+
+ end
+end
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index 16cdd0162e..41080ed629 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -16,18 +16,8 @@ module ActionView
memoize :handler
def compiled_source
- @compiled_at = Time.now
handler.call(self)
end
- memoize :compiled_source
-
- def compiled_at
- @compiled_at
- end
-
- def defined_at
- @defined_at ||= {}
- end
def method_name_without_locals
['_run', extension, method_segment].compact.join('_')
@@ -71,12 +61,8 @@ module ActionView
def compile(local_assigns)
render_symbol = method_name(local_assigns)
- if self.is_a?(InlineTemplate)
+ if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
compile!(render_symbol, local_assigns)
- else
- if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?(render_symbol)
- recompile!(render_symbol, local_assigns)
- end
end
end
@@ -93,7 +79,6 @@ module ActionView
begin
ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
- defined_at[render_symbol] = Time.now if respond_to?(:reloadable?) && reloadable?
rescue Errno::ENOENT => e
raise e # Missing template file, re-raise for Base to rescue
rescue Exception => e # errors from template code
@@ -107,17 +92,8 @@ module ActionView
end
end
- def recompile?(render_symbol)
- !cached? || redefine?(render_symbol) || stale?
- end
-
- def recompile!(render_symbol, local_assigns)
- compiled_source(:reload) if compiled_at.nil? || compiled_at < mtime
- compile!(render_symbol, local_assigns)
- end
-
- def redefine?(render_symbol)
- compiled_at && defined_at[render_symbol] && compiled_at > defined_at[render_symbol]
+ def recompile?
+ false
end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index f2d3998d16..b8e2165ddf 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -6,14 +6,12 @@ module ActionView #:nodoc:
def initialize(path)
raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
- @path = path.freeze
+ @path = expand_path(path).freeze
end
- def load!
- @paths = {}
- templates_in_path do |template|
- load_template(template)
- end
+ def expand_path(path)
+ # collapse any directory dots in path ('.' or '..')
+ path.starts_with?('/') ? File.expand_path(path) : File.expand_path(path, '/').from(1)
end
def to_s
@@ -46,43 +44,51 @@ module ActionView #:nodoc:
# etc. A format must be supplied to match a formated file. +hello/index+
# will never match +hello/index.html.erb+.
def [](path)
- load! if @paths.nil?
- @paths[path] || find_template(path)
end
-
- private
- def templates_in_path
- (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
- yield create_template(file) unless File.directory?(file)
- end
+
+ def load!
+ end
+
+ def self.new_and_loaded(path)
+ returning new(path) do |path|
+ path.load!
end
+ end
+ end
- def create_template(file)
- Template.new(file.split("#{self}/").last, self)
- end
+ class EagerPath < Path
+ def initialize(path)
+ super
+ end
- def load_template(template)
+ def load!
+ return if @loaded
+
+ @paths = {}
+ templates_in_path do |template|
template.load!
template.accessible_paths.each do |path|
@paths[path] = template
end
end
+ @paths.freeze
+ @loaded = true
+ end
- def matching_templates(template_path)
- Dir.glob("#{@path}/#{template_path}.*").each do |file|
+ def [](path)
+ load! unless @loaded
+ @paths[path]
+ end
+
+ private
+ def templates_in_path
+ (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
yield create_template(file) unless File.directory?(file)
end
end
- def find_template(path)
- return nil if Base.cache_template_loading || ActionController::Base.allow_concurrency
- matching_templates(path) do |template|
- if template.accessible_paths.include?(path)
- load_template(template)
- return template
- end
- end
- nil
+ def create_template(file)
+ Template.new(file.split("#{self}/").last, self)
end
end
@@ -171,14 +177,10 @@ module ActionView #:nodoc:
@@exempt_from_layout.any? { |exempted| path =~ exempted }
end
- def mtime
- File.mtime(filename)
- end
- memoize :mtime
-
def source
File.read(filename)
end
+ memoize :source
def method_segment
relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
@@ -192,27 +194,13 @@ module ActionView #:nodoc:
if TemplateError === e
e.sub_template_of(self)
raise e
- elsif Errno::ENOENT === e
- raise MissingTemplate.new(view.view_paths, filename.sub("#{RAILS_ROOT}/#{load_path}/", ""))
else
raise TemplateError.new(self, view.assigns, e)
end
end
- def stale?
- reloadable? && (mtime < mtime(:reload))
- end
-
def load!
- reloadable? ? memoize_all : freeze
- end
-
- def reloadable?
- !(Base.cache_template_loading || ActionController::Base.allow_concurrency)
- end
-
- def cached?
- ActionController::Base.perform_caching || !reloadable?
+ freeze
end
private
@@ -262,5 +250,5 @@ module ActionView #:nodoc:
[base_path, name, locale, format, extension]
end
- end
+ end
end