diff options
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r-- | actionpack/lib/action_view/base.rb | 11 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/date_helper.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 30 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_options_helper.rb | 107 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/text_helper.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/translation_helper.rb | 20 | ||||
-rw-r--r-- | actionpack/lib/action_view/paths.rb | 18 | ||||
-rw-r--r-- | actionpack/lib/action_view/reloadable_template.rb | 120 | ||||
-rw-r--r-- | actionpack/lib/action_view/renderable.rb | 5 | ||||
-rw-r--r-- | actionpack/lib/action_view/template.rb | 103 |
10 files changed, 329 insertions, 89 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 70a0ba91a7..4198725e0d 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -182,6 +182,15 @@ module ActionView #:nodoc: # that alert()s the caught exception (and then re-raises it). cattr_accessor :debug_rjs + # 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, @@ -222,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: diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index b4c1adbe76..b7ef1fb90d 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -931,7 +931,7 @@ module ActionView end def default_datetime(options) - return if options[:include_blank] + return if options[:include_blank] || options[:prompt] case options[:default] when nil diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 37400bdbf7..4fef2b443e 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -755,7 +755,9 @@ module ActionView end options["checked"] = "checked" if checked add_default_name_and_id(options) - tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value) + hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value) + checkbox = tag("input", options) + hidden + checkbox end def to_boolean_select_tag(options = {}) @@ -964,24 +966,34 @@ module ActionView def fields_for_with_nested_attributes(association_name, args, block) name = "#{object_name}[#{association_name}_attributes]" association = @object.send(association_name) + explicit_object = args.first if args.first.respond_to?(:new_record?) if association.is_a?(Array) - children = args.first.respond_to?(:new_record?) ? [args.first] : association + children = explicit_object ? [explicit_object] : association + explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash) children.map do |child| - child_name = "#{name}[#{ child.new_record? ? new_child_id : child.id }]" - @template.fields_for(child_name, child, *args, &block) + fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index}]", child, args, block) end.join else - object = args.first.respond_to?(:new_record?) ? args.first : association + fields_for_nested_model(name, explicit_object || association, args, block) + end + end + + def fields_for_nested_model(name, object, args, block) + if object.new_record? @template.fields_for(name, object, *args, &block) + else + @template.fields_for(name, object, *args) do |builder| + @template.concat builder.hidden_field(:id) + block.call(builder) + end end end - def new_child_id - value = (@child_counter ||= 1) - @child_counter += 1 - "new_#{value}" + def nested_child_index + @nested_child_index ||= -1 + @nested_child_index += 1 end end end diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 54c82cbd1d..6b385ef77d 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -6,9 +6,7 @@ module ActionView module Helpers # Provides a number of methods for turning different kinds of containers into a set of option tags. # == Options - # The <tt>collection_select</tt>, <tt>country_select</tt>, <tt>select</tt>, - # and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, - # a hash. + # The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash: # # * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element. # @@ -28,7 +26,7 @@ module ActionView # # Example with @post.person_id => 2: # - # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, {:include_blank => 'None'}) + # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:include_blank => 'None'}) # # could become: # @@ -43,7 +41,7 @@ module ActionView # # Example: # - # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, {:prompt => 'Select Person'}) + # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:prompt => 'Select Person'}) # # could become: # @@ -68,6 +66,36 @@ module ActionView # <option value="rock">rock</option> # <option value="country">country</option> # </select> + # + # * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output. + # + # Example: + # + # select("post", "category", Post::CATEGORIES, {:disabled => 'restricted'}) + # + # could become: + # + # <select name="post[category]"> + # <option></option> + # <option>joke</option> + # <option>poem</option> + # <option disabled="disabled">restricted</option> + # </select> + # + # When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled. + # + # Example: + # + # collection_select(:post, :category_id, Category.all, :id, :name, {:disabled => lambda{|category| category.archived? }}) + # + # If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return: + # <select name="post[category_id]"> + # <option value="1" disabled="disabled">2008 stuff</option> + # <option value="2" disabled="disabled">Christmas</option> + # <option value="3">Jokes</option> + # <option value="4">Poems</option> + # </select> + # module FormOptionsHelper include ERB::Util @@ -76,7 +104,7 @@ module ActionView # See options_for_select for the required format of the choices parameter. # # Example with @post.person_id => 1: - # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank => true }) + # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { :include_blank => true }) # # could become: # @@ -94,7 +122,8 @@ module ActionView # In addition, this allows a single partial to be used to generate form inputs for both edit and create forms. # # By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection - # or <tt>:selected => nil</tt> to leave all options unselected. + # or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option + # tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled. def select(object, method, choices, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options) end @@ -120,7 +149,7 @@ module ActionView # end # # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>): - # collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true}) + # collection_select(:post, :author_id, Author.all, :id, :name_with_initial, {:prompt => true}) # # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return: # <select name="post[author_id]"> @@ -186,14 +215,29 @@ module ActionView # options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"]) # <option selected="selected">VISA</option>\n<option>MasterCard</option>\n<option selected="selected">Discover</option> # + # If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value + # or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags. + # + # Examples: + # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :disabled => "Super Platinum") + # <option value="Free">Free</option>\n<option value="Basic">Basic</option>\n<option value="Advanced">Advanced</option>\n<option value="Super Platinum" disabled="disabled">Super Platinum</option> + # + # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :disabled => ["Advanced", "Super Platinum"]) + # <option value="Free">Free</option>\n<option value="Basic">Basic</option>\n<option value="Advanced" disabled="disabled">Advanced</option>\n<option value="Super Platinum" disabled="disabled">Super Platinum</option> + # + # options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], :selected => "Free", :disabled => "Super Platinum") + # <option value="Free" selected="selected">Free</option>\n<option value="Basic">Basic</option>\n<option value="Advanced">Advanced</option>\n<option value="Super Platinum" disabled="disabled">Super Platinum</option> + # # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. def options_for_select(container, selected = nil) container = container.to_a if Hash === container + selected, disabled = extract_selected_and_disabled(selected) options_for_select = container.inject([]) do |options, element| text, value = option_text_and_value(element) selected_attribute = ' selected="selected"' if option_value_selected?(value, selected) - options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>) + disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled) + options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}>#{html_escape(text.to_s)}</option>) end options_for_select.join("\n") @@ -209,8 +253,15 @@ module ActionView # This is more often than not used inside a #select_tag like this example: # select_tag 'person', options_from_collection_for_select(@people, 'id', 'name') # - # If +selected+ is specified, the element returning a match on +value_method+ will get the selected option tag. - # Be sure to specify the same class as the +value_method+ when specifying a selected option. + # If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+ + # will be selected option tag(s). + # + # If +selected+ is specified as a Proc, those members of the collection that return true for the anonymous + # function are the selected values. + # + # +selected+ can also be a hash, specifying both <tt>:selected</tt> and/or <tt>:disabled</tt> values as required. + # + # Be sure to specify the same class as the +value_method+ when specifying selected or disabled options. # Failure to do this will produce undesired results. Example: # options_from_collection_for_select(@people, 'id', 'name', '1') # Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string) @@ -220,7 +271,12 @@ module ActionView options = collection.map do |element| [element.send(text_method), element.send(value_method)] end - options_for_select(options, selected) + selected, disabled = extract_selected_and_disabled(selected) + select_deselect = {} + select_deselect[:selected] = extract_values_from_collection(collection, value_method, selected) + select_deselect[:disabled] = extract_values_from_collection(collection, value_method, disabled) + + options_for_select(options, select_deselect) end # Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but @@ -238,7 +294,8 @@ module ActionView # +collection+, returns a value to be used as the contents of its <tt><option></tt> tag. # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags, # which will have the +selected+ attribute set. Corresponds to the return value of one of the calls - # to +option_key_method+. If +nil+, no selection is made. + # to +option_key_method+. If +nil+, no selection is made. Can also be a hash if disabled values are + # to be specified. # # Example object structure for use with this method: # class Continent < ActiveRecord::Base @@ -388,6 +445,24 @@ module ActionView value == selected end end + + def extract_selected_and_disabled(selected) + if selected.is_a?(Hash) + [selected[:selected], selected[:disabled]] + else + [selected, nil] + end + end + + def extract_values_from_collection(collection, value_method, selected) + if selected.is_a?(Proc) + collection.map do |element| + element.send(value_method) if selected.call(element) + end.compact + else + selected + end + end end class InstanceTag #:nodoc: @@ -398,16 +473,18 @@ module ActionView add_default_name_and_id(html_options) value = value(object) selected_value = options.has_key?(:selected) ? options[:selected] : value - content_tag("select", add_options(options_for_select(choices, selected_value), options, selected_value), html_options) + disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil + content_tag("select", add_options(options_for_select(choices, :selected => selected_value, :disabled => disabled_value), options, selected_value), html_options) end def to_collection_select_tag(collection, value_method, text_method, options, html_options) html_options = html_options.stringify_keys add_default_name_and_id(html_options) value = value(object) + disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil selected_value = options.has_key?(:selected) ? options[:selected] : value content_tag( - "select", add_options(options_from_collection_for_select(collection, value_method, text_method, selected_value), options, value), html_options + "select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options ) end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index b1eb6891fa..63fe0c1c57 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -107,7 +107,7 @@ module ActionView text else match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') - text.gsub(/(#{match})(?!(?:[^<]*?)?(?:["'])[^<>]*>)/i, options[:highlighter]) + text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter]) end end diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index dc41ef5305..4aed10f640 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -3,19 +3,37 @@ require 'action_view/helpers/tag_helper' module ActionView module Helpers module TranslationHelper + # Delegates to I18n#translate but also performs two additional functions. First, it'll catch MissingTranslationData exceptions + # and turn them into inline spans that contains the missing key, such that you can see in a view what is missing where. + # + # Second, it'll scope the key by the current partial if the key starts with a period. So if you call translate(".foo") from the + # people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo"). This makes it less repetitive + # to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't + # prepend the key with a period, nothing is converted. def translate(key, options = {}) options[:raise] = true - I18n.translate(key, options) + I18n.translate(scope_key_by_partial(key), options) rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') end alias :t :translate + # Delegates to I18n.localize with no additional functionality. def localize(*args) I18n.localize *args end alias :l :localize + + + private + def scope_key_by_partial(key) + if key.to_s.first == "." + template.path_without_format_and_extension.gsub(%r{/_?}, ".") + key.to_s + else + key + end + end end end end
\ No newline at end of file diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index c7d6fd696a..41f9f486e5 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -2,16 +2,16 @@ module ActionView #:nodoc: class PathSet < Array #:nodoc: def self.type_cast(obj) if obj.is_a?(String) - if !Object.const_defined?(:Rails) || Rails.configuration.cache_classes - Template::EagerPath.new(obj) + if Base.cache_template_loading? + Template::EagerPath.new(obj.to_s) else - Template::Path.new(obj) + ReloadableTemplate::ReloadablePath.new(obj.to_s) end else obj end end - + def initialize(*args) super(*args).map! { |obj| self.class.type_cast(obj) } end @@ -35,6 +35,10 @@ module ActionView #:nodoc: def unshift(*objs) super(*objs.map { |obj| self.class.type_cast(obj) }) end + + def load! + each(&:load!) + end def find_template(original_template_path, format = nil) return original_template_path if original_template_path.respond_to?(:render) @@ -50,12 +54,16 @@ module ActionView #:nodoc: elsif template = load_path[template_path] return template # Try to find html version if the format is javascript + elsif format == :js && template = load_path["#{template_path}.#{I18n.locale}.html"] + return template elsif format == :js && template = load_path["#{template_path}.html"] return template end end - Template.new(original_template_path, self) + return Template.new(original_template_path, original_template_path =~ /\A\// ? "" : ".") if File.file?(original_template_path) + + 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 153e14f68b..41080ed629 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + module ActionView # NOTE: The template that this mixin is being included into is frozen # so you cannot set or modify any instance variables @@ -16,7 +18,6 @@ module ActionView def compiled_source handler.call(self) end - memoize :compiled_source def method_name_without_locals ['_run', extension, method_segment].compact.join('_') @@ -78,6 +79,8 @@ module ActionView begin ActionView::Base::CompiledTemplates.module_eval(source, filename, 0) + rescue Errno::ENOENT => e + raise e # Missing template file, re-raise for Base to rescue rescue Exception => e # errors from template code if logger = defined?(ActionController) && Base.logger logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 553158b82a..b8e2165ddf 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -6,7 +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 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 @@ -39,30 +44,26 @@ 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) - templates_in_path do |template| - if template.accessible_paths.include?(path) - return template - end - end - nil 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 create_template(file) - Template.new(file.split("#{self}/").last, self) + + def load! + end + + def self.new_and_loaded(path) + returning new(path) do |path| + path.load! end + end end class EagerPath < Path def initialize(path) super + end + def load! + return if @loaded + @paths = {} templates_in_path do |template| template.load! @@ -71,11 +72,24 @@ module ActionView #:nodoc: end end @paths.freeze + @loaded = true end 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 create_template(file) + Template.new(file.split("#{self}/").last, self) + end end extend TemplateHandlers @@ -97,9 +111,9 @@ module ActionView #:nodoc: attr_accessor :locale, :name, :format, :extension delegate :to_s, :to => :path - def initialize(template_path, load_paths = []) + def initialize(template_path, load_path) template_path = template_path.dup - @load_path, @filename = find_full_path(template_path, load_paths) + @load_path, @filename = load_path, File.join(load_path, template_path) @base_path, @name, @locale, @format, @extension = split(template_path) @base_path.to_s.gsub!(/\/$/, '') # Push to split method @@ -163,11 +177,6 @@ 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 @@ -190,16 +199,7 @@ module ActionView #:nodoc: end end - def stale? - File.mtime(filename) > mtime - end - - def recompile? - !@cached - end - def load! - @cached = true freeze end @@ -212,15 +212,6 @@ module ActionView #:nodoc: I18n.available_locales.include?(locale.to_sym) end - def find_full_path(path, load_paths) - load_paths = Array(load_paths) + [nil] - load_paths.each do |load_path| - file = load_path ? "#{load_path.to_str}/#{path}" : path - return load_path, file if File.file?(file) - end - raise MissingTemplate.new(load_paths, path) - end - # Returns file split into an array # [base_path, name, locale, format, extension] def split(file) @@ -236,24 +227,24 @@ module ActionView #:nodoc: format = nil extension = nil - if m = extensions.match(/^(\w+)?\.?(\w+)?\.?(\w+)?\.?/) - if valid_locale?(m[1]) && m[2] && valid_extension?(m[3]) # All three - locale = m[1] - format = m[2] - extension = m[3] - elsif m[1] && m[2] && valid_extension?(m[3]) # Multipart formats - format = "#{m[1]}.#{m[2]}" - extension = m[3] - elsif valid_locale?(m[1]) && valid_extension?(m[2]) # locale and extension - locale = m[1] - extension = m[2] - elsif valid_extension?(m[2]) # format and extension + if m = extensions.split(".") + if valid_locale?(m[0]) && m[1] && valid_extension?(m[2]) # All three + locale = m[0] format = m[1] extension = m[2] - elsif valid_extension?(m[1]) # Just extension + elsif m[0] && m[1] && valid_extension?(m[2]) # Multipart formats + format = "#{m[0]}.#{m[1]}" + extension = m[2] + elsif valid_locale?(m[0]) && valid_extension?(m[1]) # locale and extension + locale = m[0] extension = m[1] + elsif valid_extension?(m[1]) # format and extension + format = m[0] + extension = m[1] + elsif valid_extension?(m[0]) # Just extension + extension = m[0] else # No extension - format = m[1] + format = m[0] end end |