diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2009-09-21 21:14:04 +0100 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2009-09-21 21:14:04 +0100 |
commit | 340be9bddd8e5902e0218a0101a40a17a4afd558 (patch) | |
tree | ef4de25f3f8eb610dc2235f0762b01cb1d464efd /actionpack/lib/action_view | |
parent | b31cdb55422226cd45a2234a4b54986f1f611151 (diff) | |
parent | 1bbb9b2db05730194edfd7d2cef9f5fcb9d79e50 (diff) | |
download | rails-340be9bddd8e5902e0218a0101a40a17a4afd558.tar.gz rails-340be9bddd8e5902e0218a0101a40a17a4afd558.tar.bz2 rails-340be9bddd8e5902e0218a0101a40a17a4afd558.zip |
Merge commit 'mainstream/master'
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r-- | actionpack/lib/action_view/helpers/asset_tag_helper.rb | 9 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 26 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/tag_helper.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/url_helper.rb | 1 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/handlers/builder.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/inline.rb | 19 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/partial.rb | 18 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/renderable.rb | 93 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/resolver.rb | 185 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/template.rb | 6 |
10 files changed, 129 insertions, 232 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index c71840d41f..95f00cda39 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -1,3 +1,4 @@ +require 'thread' require 'cgi' require 'action_view/helpers/url_helper' require 'action_view/helpers/tag_helper' @@ -286,7 +287,9 @@ module ActionView end javascript_src_tag(joined_javascript_name, options) else - ensure_javascript_sources!(expand_javascript_sources(sources, recursive)).collect { |source| javascript_src_tag(source, options) }.join("\n") + sources = expand_javascript_sources(sources, recursive) + ensure_javascript_sources!(sources) if cache + sources.collect { |source| javascript_src_tag(source, options) }.join("\n") end end @@ -435,7 +438,9 @@ module ActionView end stylesheet_tag(joined_stylesheet_name, options) else - ensure_stylesheet_sources!(expand_stylesheet_sources(sources, recursive)).collect { |source| stylesheet_tag(source, options) }.join("\n") + sources = expand_stylesheet_sources(sources, recursive) + ensure_stylesheet_sources!(sources) if cache + sources.collect { |source| stylesheet_tag(source, options) }.join("\n") end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 81029102b1..32b9c4a7dd 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -449,6 +449,15 @@ module ActionView # <% end %> # <% end %> # + # Or a collection to be used: + # + # <% form_for @person, :url => { :action => "update" } do |person_form| %> + # ... + # <% person_form.fields_for :projects, @active_projects do |project_fields| %> + # Name: <%= project_fields.text_field :name %> + # <% end %> + # <% end %> + # # When projects is already an association on Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # @@ -1037,18 +1046,21 @@ 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.to_model if args.first.respond_to?(:to_model) + association = args.first.to_model if args.first.respond_to?(:to_model) + + if association.respond_to?(:new_record?) + association = [association] if @object.send(association_name).is_a?(Array) + elsif !association.is_a?(Array) + association = @object.send(association_name) + end if association.is_a?(Array) - children = explicit_object ? [explicit_object] : association explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash) - - children.map do |child| + association.map do |child| fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block) end.join - else - fields_for_nested_model(name, explicit_object || association, args, block) + elsif association + fields_for_nested_model(name, association, args, block) end end diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index ff5a2134ff..7fae0f6b8d 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -106,7 +106,7 @@ module ActionView # escape_once("<< Accept & Checkout") # # => "<< Accept & Checkout" def escape_once(html) - html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } + ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } end private diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index b07304e361..204d4d71e1 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -1,4 +1,5 @@ require 'action_view/helpers/javascript_helper' +require 'active_support/core_ext/array/access' require 'active_support/core_ext/hash/keys' module ActionView diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index 5f381f7bf0..ba0d17b5af 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -6,7 +6,7 @@ module ActionView self.default_format = Mime::XML def compile(template) - require 'builder' + require 'active_support/vendor/builder' "xml = ::Builder::XmlMarkup.new(:indent => 2);" + "self.output_buffer = xml.target!;" + template.source + diff --git a/actionpack/lib/action_view/template/inline.rb b/actionpack/lib/action_view/template/inline.rb deleted file mode 100644 index 54efa543c8..0000000000 --- a/actionpack/lib/action_view/template/inline.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActionView #:nodoc: - class InlineTemplate #:nodoc: - include Renderable - - attr_reader :source, :extension, :method_segment - - def initialize(source, type = nil) - @source = source - @extension = type - @method_segment = "inline_#{@source.hash.abs}" - end - - private - # Always recompile inline templates - def recompile? - true - end - end -end diff --git a/actionpack/lib/action_view/template/partial.rb b/actionpack/lib/action_view/template/partial.rb deleted file mode 100644 index 30dec1dc5b..0000000000 --- a/actionpack/lib/action_view/template/partial.rb +++ /dev/null @@ -1,18 +0,0 @@ -module ActionView - # NOTE: The template that this mixin is being included into is frozen - # so you cannot set or modify any instance variables - module RenderablePartial #:nodoc: - extend ActiveSupport::Memoizable - - def variable_name - name.sub(/\A_/, '').to_sym - end - memoize :variable_name - - def counter_name - "#{variable_name}_counter".to_sym - end - memoize :counter_name - - end -end diff --git a/actionpack/lib/action_view/template/renderable.rb b/actionpack/lib/action_view/template/renderable.rb deleted file mode 100644 index 7687578165..0000000000 --- a/actionpack/lib/action_view/template/renderable.rb +++ /dev/null @@ -1,93 +0,0 @@ -# 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 - module Renderable #:nodoc: - extend ActiveSupport::Memoizable - - def render(view, locals) - compile(locals) - view.send(method_name(locals), locals) {|*args| yield(*args) } - end - - def load! - names = CompiledTemplates.instance_methods.grep(/#{method_name_without_locals}/) - names.each do |name| - CompiledTemplates.class_eval do - remove_method(name) - end - end - super - end - - private - - def filename - 'compiled-template' - end - - def handler - Template.handler_class_for_extension(extension) - end - memoize :handler - - def compiled_source - handler.call(self) - end - memoize :compiled_source - - def method_name_without_locals - ['_run', extension, method_segment].compact.join('_') - end - memoize :method_name_without_locals - - def method_name(local_assigns) - if local_assigns && local_assigns.any? - method_name = method_name_without_locals.dup - method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}" - else - method_name = method_name_without_locals - end - method_name.to_sym - end - - # Compile and evaluate the template's code (if necessary) - def compile(local_assigns) - render_symbol = method_name(local_assigns) - - if !CompiledTemplates.method_defined?(render_symbol) || recompile? - compile!(render_symbol, local_assigns) - end - end - - private - def compile!(render_symbol, local_assigns) - locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join - - source = <<-end_src - def #{render_symbol}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{compiled_source} - ensure - self.output_buffer = old_output_buffer - end - end_src - - begin - ActionView::CompiledTemplates.module_eval(source, filename.to_s, 0) - rescue Exception => e # errors from template code - if logger = defined?(ActionController) && Base.logger - logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" - logger.debug "Function body: #{source}" - logger.debug "Backtrace: #{e.backtrace.join("\n")}" - end - - raise ActionView::TemplateError.new(self, {}, e) - end - end - - def recompile? - false - end - end -end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 0b4c62d4d0..f5591ead09 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -1,9 +1,28 @@ require "pathname" +require "active_support/core_ext/class" require "action_view/template/template" module ActionView # Abstract superclass class Resolver + + class_inheritable_accessor(:registered_details) + self.registered_details = {} + + def self.register_detail(name, options = {}) + registered_details[name] = lambda do |val| + val ||= yield + val |= [nil] unless options[:allow_nil] == false + val + end + end + + register_detail(:locale) { [I18n.locale] } + register_detail(:formats) { Mime::SET.symbols } + register_detail(:handlers, :allow_nil => false) do + TemplateHandlers.extensions + end + def initialize(options = {}) @cache = options[:cache] @cached = {} @@ -11,15 +30,18 @@ module ActionView # Normalizes the arguments and passes it on to find_template def find(*args) - find_all_by_parts(*args).first + find_all(*args).first end - - def find_all_by_parts(name, details = {}, prefix = nil, partial = nil) - details[:locales] = [I18n.locale] - name = name.to_s.gsub(handler_matcher, '').split("/") - find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial) + + def find_all(name, details = {}, prefix = nil, partial = nil) + details = normalize_details(details) + name, prefix = normalize_name(name, prefix) + + cached([name, details, prefix, partial]) do + find_templates(name, details, prefix, partial) + end end - + private # This is what child classes implement. No defaults are needed @@ -28,29 +50,26 @@ module ActionView def find_templates(name, details, prefix, partial) raise NotImplementedError end - - def valid_handlers - @valid_handlers ||= TemplateHandlers.extensions - end - def handler_matcher - @handler_matcher ||= begin - e = valid_handlers.join('|') - /\.(?:#{e})$/ + def normalize_details(details) + details = details.dup + # TODO: Refactor this concern out of the resolver + details.delete(:formats) if details[:formats] == [:"*/*"] + registered_details.each do |k, v| + details[k] = v.call(details[k]) end + details end - def handler_glob - @handler_glob ||= begin - e = TemplateHandlers.extensions.map{|h| ".#{h}"}.join(",") - "{#{e}}" - end - end - - def formats_glob - @formats_glob ||= begin - '{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}' - end + # Support legacy foo.erb names even though we now ignore .erb + # as well as incorrectly putting part of the path in the template + # name instead of the prefix. + def normalize_name(name, prefix) + handlers = TemplateHandlers.extensions.join('|') + name = name.to_s.gsub(/\.(?:#{handlers})$/, '') + + parts = name.split('/') + return parts.pop, [prefix, *parts].compact.join("/") end def cached(key) @@ -60,80 +79,49 @@ module ActionView end end - class FileSystemResolver < Resolver - - def self.cached_glob - @@cached_glob ||= {} - end - - def initialize(path, options = {}) - raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) - super(options) - @path = Pathname.new(path).expand_path - end + class PathResolver < Resolver - # TODO: This is the currently needed API. Make this suck less - # ==== <suck> - attr_reader :path + EXTENSION_ORDER = [:locale, :formats, :handlers] def to_s - path.to_s + @path.to_s end + alias to_path to_s - def to_str - path.to_s + def find_templates(name, details, prefix, partial) + path = build_path(name, details, prefix, partial) + query(path, EXTENSION_ORDER.map { |ext| details[ext] }) end - def ==(path) - to_str == path.to_str - end + private - def eql?(path) - to_str == path.to_str + def build_path(name, details, prefix, partial) + path = "" + path << "#{prefix}/" unless prefix.empty? + path << (partial ? "_#{name}" : name) + path end - # ==== </suck> - - def find_templates(name, details, prefix, partial, root = "#{@path}/") - if glob = details_to_glob(name, details, prefix, partial, root) - cached(glob) do - Dir[glob].map do |path| - next if File.directory?(path) - source = File.read(path) - identifier = Pathname.new(path).expand_path.to_s - Template.new(source, identifier, *path_to_details(path)) - end.compact - end + def query(path, exts) + query = "#{@path}/#{path}" + exts.each do |ext| + query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}' end - end - - private - # :api: plugin - def details_to_glob(name, details, prefix, partial, root) - self.class.cached_glob[[name, prefix, partial, details, root]] ||= begin - path = "" - path << "#{prefix}/" unless prefix.empty? - path << (partial ? "_#{name}" : name) - - extensions = "" - [:locales, :formats].each do |k| - extensions << if exts = details[k] - '{' + exts.map {|e| ".#{e},"}.join + '}' - else - k == :formats ? formats_glob : '' - end - end - - "#{root}#{path}#{extensions}#{handler_glob}" - end + Dir[query].map do |path| + next if File.directory?(path) + source = File.read(path) + identifier = Pathname.new(path).expand_path.to_s + + Template.new(source, identifier, *path_to_details(path)) + end.compact end - # TODO: fix me - # :api: plugin + # # TODO: fix me + # # :api: plugin def path_to_details(path) # [:erb, :format => :html, :locale => :en, :partial => true/false] - if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') + if m = path.match(%r'(?:^|/)(_)?[\w-]+(\.[\w-]+)*\.(\w+)$') partial = m[1] == '_' details = (m[2]||"").split('.').reject { |e| e.empty? } handler = Template.handler_class_for_extension(m[3]) @@ -146,13 +134,32 @@ module ActionView end end - class FileSystemResolverWithFallback < FileSystemResolver + class FileSystemResolver < PathResolver + def initialize(path, options = {}) + raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) + super(options) + @path = Pathname.new(path).expand_path + end + end - def find_templates(name, details, prefix, partial) - templates = super - return super(name, details, prefix, partial, '') if templates.empty? - templates + # OMG HAX + # TODO: remove hax + class FileSystemResolverWithFallback < Resolver + def initialize(path, options = {}) + super(options) + @paths = [FileSystemResolver.new(path, options), FileSystemResolver.new("", options), FileSystemResolver.new("/", options)] end + def find_templates(*args) + @paths.each do |p| + template = p.find_templates(*args) + return template unless template.empty? + end + [] + end + + def to_s + @paths.first.to_s + end end end
\ No newline at end of file diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index 7d6964e3e3..80c1bab7d5 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -27,8 +27,10 @@ module ActionView end def render(view, locals, &block) - method_name = compile(locals, view) - view.send(method_name, locals, &block) + ActiveSupport::Orchestra.instrument(:render_template, :identifier => identifier) do + method_name = compile(locals, view) + view.send(method_name, locals, &block) + end.result rescue Exception => e if e.is_a?(TemplateError) e.sub_template_of(self) |