lib/action_view/base.rb in actionpack-2.3.18 vs lib/action_view/base.rb in actionpack-3.0.0.beta

- old
+ new

@@ -1,18 +1,28 @@ +require 'active_support/core_ext/module/attr_internal' +require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/class/attribute' + module ActionView #:nodoc: class ActionViewError < StandardError #:nodoc: end class MissingTemplate < ActionViewError #:nodoc: attr_reader :path - def initialize(paths, path, template_format = nil) + def initialize(paths, path, details, partial) @path = path - full_template_path = path.include?('.') ? path : "#{path}.erb" display_paths = paths.compact.join(":") - template_type = (path =~ /layouts/i) ? 'layout' : 'template' - super("Missing #{template_type} #{full_template_path} in view path #{display_paths}") + template_type = if partial + "partial" + elsif path =~ /layouts/i + 'layout' + else + 'template' + end + + super("Missing #{template_type} #{path} with #{details.inspect} in view path #{display_paths}") end end # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used. @@ -158,23 +168,43 @@ # # This refreshes the sidebar, removes a person element and highlights the user list. # # See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details. class Base - include Helpers, Partials, ::ERB::Util + module Subclasses + end + + include Helpers, Rendering, Partials, ::ERB::Util + + def config + self.config = DEFAULT_CONFIG unless @config + @config + end + + def config=(config) + @config = ActiveSupport::OrderedOptions.new.merge(config) + end + extend ActiveSupport::Memoizable - attr_accessor :base_path, :assigns, :template_extension - attr_accessor :controller + attr_accessor :base_path, :assigns, :template_extension, :formats + attr_internal :captures - attr_writer :template_format + def reset_formats(formats) + @formats = formats - attr_accessor :output_buffer + if defined?(AbstractController::HashKey) + # This is expensive, but we need to reset this when the format is updated, + # which currently only happens + Thread.current[:format_locale_key] = + AbstractController::HashKey.get(self.class, formats, I18n.locale) + end + end class << self - delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' - delegate :logger, :to => 'ActionController::Base' + delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB' + delegate :logger, :to => 'ActionController::Base', :allow_nil => true end @@debug_rjs = false ## # :singleton-method: @@ -187,176 +217,99 @@ @@cache_template_loading = nil cattr_accessor :cache_template_loading # :nodoc: def self.xss_safe? - false + true end def self.cache_template_loading? ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading) end - attr_internal :request + attr_internal :request, :layout - delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers, - :flash, :logger, :action_name, :controller_name, :to => :controller - - module CompiledTemplates #:nodoc: - # holds compiled template code + def controller_path + @controller_path ||= controller && controller.controller_path end - include CompiledTemplates + delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers, + :flash, :action_name, :controller_name, :to => :controller + + delegate :logger, :to => :controller, :allow_nil => true + + delegate :find, :to => :view_paths + + include Context + def self.process_view_paths(value) ActionView::PathSet.new(Array(value)) end + class_attribute :helpers attr_reader :helpers - class ProxyModule < Module - def initialize(receiver) - @receiver = receiver - end + def self.for_controller(controller) + @views ||= {} - def include(*args) - super(*args) - @receiver.extend(*args) + # TODO: Decouple this so helpers are a separate concern in AV just like + # they are in AC. + if controller.class.respond_to?(:_helper_serial) + klass = @views[controller.class._helper_serial] ||= Class.new(self) do + # Try to make stack traces clearer + class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 + def self.name + "ActionView for #{controller.class}" + end + + def inspect + "#<#{self.class.name}>" + end + ruby_eval + + if controller.respond_to?(:_helpers) + include controller._helpers + self.helpers = controller._helpers + end + end + else + klass = self end + + klass.new(controller.class.view_paths, {}, controller) end - def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc: - @assigns = assigns_for_first_render - @assigns_added = nil - @controller = controller - @helpers = ProxyModule.new(self) - self.view_paths = view_paths + def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: + @config = nil + @formats = formats + @assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } + @helpers = self.class.helpers || Module.new - @_first_render = nil - @_current_render = nil + @_controller = controller + @_content_for = Hash.new {|h,k| h[k] = ActiveSupport::SafeBuffer.new } + @_virtual_path = nil + self.view_paths = view_paths end + attr_internal :controller, :template attr_reader :view_paths 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: - # - # * <tt>:partial</tt> - See ActionView::Partials. - # * <tt>:update</tt> - Calls update_page with the block given. - # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those. - # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller. - # * <tt>:text</tt> - Renders the text passed in out. - # - # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter - # as the locals hash. - def render(options = {}, local_assigns = {}, &block) #:nodoc: - local_assigns ||= {} - - case options - when Hash - options = options.reverse_merge(:locals => {}) - 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]) - elsif options[:partial] - render_partial(options) - elsif options[:inline] - InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals]) - elsif options[:text] - options[:text] - end - when :update - update_page(&block) - else - render_partial(:partial => options, :locals => local_assigns) - end + def punctuate_body!(part) + flush_output_buffer + response.body_parts << part + nil end - # The format to be used when choosing between multiple templates with - # the same name but differing formats. See +Request#template_format+ - # for more details. - def template_format - if defined? @template_format - @template_format - elsif controller && controller.respond_to?(:request) - @template_format = controller.request.template_format.to_sym - else - @template_format = :html + # Evaluates the local assigns and controller ivars, pushes them to the view. + def _evaluate_assigns_and_ivars #:nodoc: + if controller + variables = controller.instance_variable_names + variables -= controller.protected_instance_variables if controller.respond_to?(:protected_instance_variables) + variables.each { |name| instance_variable_set(name, controller.instance_variable_get(name)) } end end - - # Access the current template being rendered. - # Returns a ActionView::Template object. - def template - @_current_render - end - - def template=(template) #:nodoc: - @_first_render ||= template - @_current_render = template - end - - def with_template(current_template) - last_template, self.template = template, current_template - yield - ensure - self.template = last_template - end - - private - # Evaluates the local assigns and controller ivars, pushes them to the view. - def _evaluate_assigns_and_ivars #:nodoc: - unless @assigns_added - @assigns.each { |key, value| instance_variable_set("@#{key}", value) } - _copy_ivars_from_controller - @assigns_added = true - end - end - - def _copy_ivars_from_controller #:nodoc: - if @controller - variables = @controller.instance_variable_names - variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables) - variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) } - end - end - - def _set_controller_content_type(content_type) #:nodoc: - if controller.respond_to?(:response) - controller.response.content_type ||= content_type - end - end - - def _render_with_layout(options, local_assigns, &block) #:nodoc: - partial_layout = options.delete(:layout) - - if block_given? - begin - @_proc_for_layout = block - concat(render(options.merge(:partial => partial_layout))) - ensure - @_proc_for_layout = nil - end - else - begin - original_content_for_layout = @content_for_layout if defined?(@content_for_layout) - @content_for_layout = render(options) - - if (options[:inline] || options[:file] || options[:text]) - @cached_content_for_layout = @content_for_layout - render(:file => partial_layout, :locals => local_assigns) - else - render(options.merge(:partial => partial_layout)) - end - ensure - @content_for_layout = original_content_for_layout - end - end - end end end