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