Begin unifying the interface between ActionController and ActionView

This commit is contained in:
Yehuda Katz
2009-01-22 16:18:10 -06:00
committed by Joshua Peek
parent cc0b5fa993
commit eb9af20b7c
51 changed files with 764 additions and 561 deletions

View File

@@ -371,6 +371,14 @@ module ActionMailer #:nodoc:
attr_reader :mail
attr_reader :template_name, :default_template_name, :action_name
def controller_path
self.class.controller_path
end
def formats
@template.formats
end
class << self
attr_writer :mailer_name
@@ -464,7 +472,7 @@ module ActionMailer #:nodoc:
# have not already been specified manually.
if @parts.empty?
Dir.glob("#{template_path}/#{@template}.*").each do |path|
template = template_root["#{mailer_name}/#{File.basename(path)}"]
template = template_root.find_template("#{mailer_name}/#{File.basename(path)}")
# Skip unless template has a multipart format
next unless template && template.multipart?
@@ -473,7 +481,7 @@ module ActionMailer #:nodoc:
:content_type => template.content_type,
:disposition => "inline",
:charset => charset,
:body => render_message(template, @body)
:body => render_template(template, @body)
)
end
unless @parts.empty?
@@ -487,7 +495,7 @@ module ActionMailer #:nodoc:
# normal template exists (or if there were no implicit parts) we render
# it.
template_exists = @parts.empty?
template_exists ||= template_root["#{mailer_name}/#{@template}"]
template_exists ||= template_root.find_template("#{mailer_name}/#{@template}")
@body = render_message(@template, @body) if template_exists
# Finally, if there are other message parts and a textual body exists,
@@ -512,6 +520,7 @@ module ActionMailer #:nodoc:
# no alternate has been given as the parameter, this will fail.
def deliver!(mail = @mail)
raise "no mail object available for delivery!" unless mail
unless logger.nil?
logger.info "Sent mail to #{Array(recipients).join(', ')}"
logger.debug "\n#{mail.encoded}"
@@ -543,27 +552,43 @@ module ActionMailer #:nodoc:
@mime_version = @@default_mime_version.dup if @@default_mime_version
end
def render_message(method_name, body)
if method_name.respond_to?(:content_type)
@current_template_content_type = method_name.content_type
def render_template(template, body)
if template.respond_to?(:content_type)
@current_template_content_type = template.content_type
end
@template = initialize_template_class(body)
layout = _pick_layout(layout, true) unless template.exempt_from_layout?
@template._render_template_with_layout(template, layout, {})
ensure
@current_template_content_type = nil
end
def render_message(method_name, body)
render :file => method_name, :body => body
ensure
@current_template_content_type = nil
end
def render(opts)
body = opts.delete(:body)
if opts[:file] && (opts[:file] !~ /\// && !opts[:file].respond_to?(:render))
opts[:file] = "#{mailer_name}/#{opts[:file]}"
end
layout, file = opts.delete(:layout), opts[:file]
begin
old_template, @template = @template, initialize_template_class(body)
layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false
@template.render(opts.merge(:layout => layout))
ensure
@template = old_template
@template = initialize_template_class(opts.delete(:body))
if file
prefix = mailer_name unless file =~ /\//
template = view_paths.find_by_parts(file, formats, prefix)
end
layout = _pick_layout(layout,
!template || !template.exempt_from_layout?)
if template
@template._render_template_with_layout(template, layout, opts)
elsif inline = opts[:inline]
@template._render_inline(inline, layout, opts)
end
end
end
@@ -575,12 +600,6 @@ module ActionMailer #:nodoc:
end
end
def candidate_for_layout?(options)
!self.view_paths.find_template(default_template_name, default_template_format).exempt_from_layout?
rescue ActionView::MissingTemplate
return true
end
def template_root
self.class.template_root
end
@@ -595,7 +614,7 @@ module ActionMailer #:nodoc:
def initialize_template_class(assigns)
template = ActionView::Base.new(view_paths, assigns, self)
template.template_format = default_template_format
template.formats = [default_template_format]
template
end

View File

@@ -566,13 +566,31 @@ class ActionMailerTest < Test::Unit::TestCase
TestMailer.deliver_signed_up(@recipient)
end
class FakeLogger
attr_reader :info_contents, :debug_contents
def initialize
@info_contents, @debug_contents = "", ""
end
def info(str)
@info_contents << str
end
def debug(str)
@debug_contents << str
end
end
def test_delivery_logs_sent_mail
mail = TestMailer.create_signed_up(@recipient)
logger = mock()
logger.expects(:info).with("Sent mail to #{@recipient}")
logger.expects(:debug).with("\n#{mail.encoded}")
TestMailer.logger = logger
# logger = mock()
# logger.expects(:info).with("Sent mail to #{@recipient}")
# logger.expects(:debug).with("\n#{mail.encoded}")
TestMailer.logger = FakeLogger.new
TestMailer.deliver_signed_up(@recipient)
assert(TestMailer.logger.info_contents =~ /Sent mail to #{@recipient}/)
assert_equal(TestMailer.logger.debug_contents, "\n#{mail.encoded}")
end
def test_unquote_quoted_printable_subject

View File

@@ -35,6 +35,8 @@ gem 'rack', '>= 0.9.0'
require 'rack'
require 'action_controller/rack_ext'
require File.join(File.dirname(__FILE__), "action_pack")
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.

View File

@@ -514,8 +514,8 @@ module ActionController #:nodoc:
def process(request, response, method = :perform_action, *arguments) #:nodoc:
response.request = request
initialize_template_class(response)
assign_shortcuts(request, response)
initialize_template_class(response)
initialize_current_url
assign_names
@@ -863,92 +863,84 @@ module ActionController #:nodoc:
def render(options = nil, extra_options = {}, &block) #:doc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
validate_render_arguments(options, extra_options, block_given?)
options = { :layout => true } if options.nil?
original, options = options, extra_options unless options.is_a?(Hash)
layout_name = options.delete(:layout)
if options.nil?
options = { :template => default_template, :layout => true }
elsif options == :update
options = extra_options.merge({ :update => true })
elsif options.is_a?(String) || options.is_a?(Symbol)
case options.to_s.index('/')
when 0
extra_options[:file] = options
when nil
extra_options[:action] = options
_process_options(options)
if block_given?
@template.send(:_evaluate_assigns_and_ivars)
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
response.content_type = Mime::JS
return render_for_text(generator.to_s)
end
if original
return render_for_name(original, layout_name, options) unless block_given?
end
if options.key?(:text)
return render_for_text(@template._render_text(options[:text],
_pick_layout(layout_name), options))
end
file, template = options.values_at(:file, :template)
if file || template
file = template.sub(/^\//, '') if template
return render_for_file(file, [layout_name, !!template], options)
end
if action_option = options[:action]
return render_for_action(action_option, [layout_name, true], options)
end
if inline = options[:inline]
render_for_text(@template._render_inline(inline, _pick_layout(layout_name), options))
elsif xml = options[:xml]
response.content_type ||= Mime::XML
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml)
elsif js = options[:js]
response.content_type ||= Mime::JS
render_for_text(js)
elsif json = options[:json]
json = json.to_json unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json)
elsif partial = options[:partial]
if partial == true
parts = [action_name_base, formats, controller_name, true]
elsif partial.is_a?(String)
parts = partial_parts(partial, options)
else
extra_options[:template] = options
return render_for_text(@template._render_partial(options))
end
options = extra_options
end
layout = pick_layout(options)
response.layout = layout.path_without_format_and_extension if layout
logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout
if content_type = options[:content_type]
response.content_type = content_type.to_s
end
if location = options[:location]
response.headers["Location"] = url_for(location)
end
if options.has_key?(:text)
text = layout ? @template.render(options.merge(:text => options[:text], :layout => layout)) : options[:text]
render_for_text(text, options[:status])
render_for_parts(parts, layout_name, options)
elsif options[:nothing]
render_for_text(nil)
else
if file = options[:file]
render_for_file(file, options[:status], layout, options[:locals] || {})
elsif template = options[:template]
render_for_file(template, options[:status], layout, options[:locals] || {})
elsif inline = options[:inline]
render_for_text(@template.render(options.merge(:layout => layout)), options[:status])
elsif action_name = options[:action]
render_for_file(default_template(action_name.to_s), options[:status], layout)
elsif xml = options[:xml]
response.content_type ||= Mime::XML
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
elsif js = options[:js]
response.content_type ||= Mime::JS
render_for_text(js, options[:status])
elsif json = options[:json]
json = json.to_json unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json, options[:status])
elsif options[:partial]
options[:partial] = default_template_name if options[:partial] == true
if layout
render_for_text(@template.render(:text => @template.render(options), :layout => layout), options[:status])
else
render_for_text(@template.render(options), options[:status])
end
elsif options[:update]
@template.send(:_evaluate_assigns_and_ivars)
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
response.content_type = Mime::JS
render_for_text(generator.to_s, options[:status])
elsif options[:nothing]
render_for_text(nil, options[:status])
else
render_for_file(default_template, options[:status], layout)
end
render_for_parts([action_name, formats, controller_path], layout_name, options)
end
end
def formats
@_request.formats.map {|f| f.symbol }.compact
end
def action_name_base(name = action_name)
(name.is_a?(String) ? name.sub(/^#{controller_path}\//, '') : name).to_s
end
# Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
# of sending it as the response body to the browser.
def render_to_string(options = nil, &block) #:doc:
@@ -1174,16 +1166,70 @@ module ActionController #:nodoc:
end
private
def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
path = template_path.respond_to?(:path_without_format_and_extension) ? template_path.path_without_format_and_extension : template_path
logger.info("Rendering #{path}" + (status ? " (#{status})" : '')) if logger
render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status
def _process_options(options)
if content_type = options[:content_type]
response.content_type = content_type.to_s
end
if location = options[:location]
response.headers["Location"] = url_for(location)
end
response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE)
end
def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
@performed_render = true
def render_for_name(name, layout, options)
case name.to_s.index('/')
when 0
render_for_file(name, layout, options)
when nil
render_for_action(name, layout, options)
else
render_for_file(name.sub(/^\//, ''), [layout, true], options)
end
end
def render_for_parts(parts, layout, options = {})
tmp = view_paths.find_by_parts(*parts)
layout = _pick_layout(*layout) unless tmp.exempt_from_layout?
render_for_text(
@template._render_template_with_layout(tmp, layout, options, parts[3]))
end
response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
def partial_parts(name, options)
segments = name.split("/")
parts = segments.pop.split(".")
case parts.size
when 1
parts
when 2, 3
extension = parts.delete_at(1).to_sym
if formats.include?(extension)
self.formats.replace [extension]
end
parts.pop if parts.size == 2
end
path = parts.join(".")
prefix = segments[0..-1].join("/")
prefix = prefix.blank? ? controller_path : prefix
parts = [path, formats, prefix]
parts.push options[:object] || true
end
def render_for_file(file, layout, options)
render_for_parts([file, [request.format.to_sym]], layout, options)
end
def render_for_action(name, layout, options)
parts = [action_name_base(name), formats, controller_name]
render_for_parts(parts, layout, options)
end
def render_for_text(text = nil, append_response = false) #:nodoc:
@performed_render = true
if append_response
response.body ||= ''
@@ -1197,18 +1243,8 @@ module ActionController #:nodoc:
end
end
def validate_render_arguments(options, extra_options, has_block)
if options && (has_block && options != :update) && !options.is_a?(String) && !options.is_a?(Hash) && !options.is_a?(Symbol)
raise RenderError, "You called render with invalid options : #{options.inspect}"
end
if !extra_options.is_a?(Hash)
raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
end
end
def initialize_template_class(response)
response.template = ActionView::Base.new(self.class.view_paths, {}, self)
@template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
response.template.helpers.send :include, self.class.master_helper_module
response.redirected_to = nil
@performed_render = @performed_redirect = false
@@ -1221,7 +1257,6 @@ module ActionController #:nodoc:
@_response.session = request.session
@_session = @_response.session
@template = @_response.template
@_headers = @_response.headers
end
@@ -1257,23 +1292,21 @@ module ActionController #:nodoc:
end
def perform_action
if action_methods.include?(action_name)
send(action_name)
default_render unless performed?
elsif respond_to? :method_missing
method_missing action_name
default_render unless performed?
else
begin
default_render
rescue ActionView::MissingTemplate => e
# Was the implicit template missing, or was it another template?
if e.path == default_template_name
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
else
raise e
end
end
if called = action_methods.include?(action_name)
ret = send(action_name)
elsif called = respond_to?(:method_missing)
ret = method_missing(action_name)
end
return (performed? ? ret : default_render) if called
begin
default_render
rescue ActionView::MissingTemplate => e
raise e unless e.path == action_name
# If the path is the same as the action_name, the action is completely missing
raise UnknownAction, "No action responded to #{action_name}. Actions: " +
"#{action_methods.sort.to_sentence}", caller
end
end
@@ -1337,6 +1370,7 @@ module ActionController #:nodoc:
path.split('/', 2).last
end
def template_path_includes_controller?(path)
self.controller_path.split('/')[-1] == path.split('/')[0]
end

View File

@@ -2,11 +2,7 @@ module ActionController #:nodoc:
module Layout #:nodoc:
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
class << self
alias_method_chain :inherited, :layout
end
end
base.class_inheritable_accessor :layout_name, :layout_conditions
end
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
@@ -159,122 +155,90 @@ module ActionController #:nodoc:
#
# This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
module ClassMethods
extend ActiveSupport::Memoizable
# If a layout is specified, all rendered actions will have their result rendered
# when the layout <tt>yield</tt>s. This layout can itself depend on instance variables assigned during action
# performance and have access to them as any normal template would.
def layout(template_name, conditions = {}, auto = false)
add_layout_conditions(conditions)
write_inheritable_attribute(:layout, template_name)
write_inheritable_attribute(:auto_layout, auto)
self.layout_name = template_name
end
def layout_conditions #:nodoc:
@layout_conditions ||= read_inheritable_attribute(:layout_conditions)
def memoized_default_layout(formats) #:nodoc:
self.layout_name || begin
layout = default_layout_name
layout.is_a?(String) ? find_layout(layout, formats) : layout
rescue ActionView::MissingTemplate
end
end
def default_layout(format) #:nodoc:
layout = read_inheritable_attribute(:layout)
return layout unless read_inheritable_attribute(:auto_layout)
find_layout(layout, format)
def default_layout(*args)
(@_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new)[args] ||= memoized_default_layout(*args)
end
def memoized_find_layout(layout, formats) #:nodoc:
return layout if layout.nil? || layout.respond_to?(:render)
prefix = layout.to_s =~ /layouts\// ? nil : "layouts"
view_paths.find_by_parts(layout.to_s, formats, prefix)
end
def find_layout(*args)
(@_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new)[args] ||= memoized_find_layout(*args)
end
def layout_list #:nodoc:
Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
end
memoize :layout_list
def find_layout(layout, *formats) #:nodoc:
return layout if layout.respond_to?(:render)
view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats)
rescue ActionView::MissingTemplate
nil
def default_layout_name
layout_match = name.underscore.sub(/_controller$/, '')
if layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
superclass.default_layout_name if superclass.respond_to?(:default_layout_name)
else
layout_match
end
end
memoize :default_layout_name
private
def inherited_with_layout(child)
inherited_without_layout(child)
unless child.name.blank?
layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
end
end
def add_layout_conditions(conditions)
write_inheritable_hash(:layout_conditions, normalize_conditions(conditions))
end
def normalize_conditions(conditions)
conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
# :except => :foo == :except => [:foo] == :except => "foo" == :except => ["foo"]
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
write_inheritable_hash(:layout_conditions, conditions)
end
end
# Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
# is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
# object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
def active_layout(passed_layout = nil)
layout = passed_layout || self.class.default_layout(default_template_format)
active_layout = case layout
when Symbol then __send__(layout)
when Proc then layout.call(self)
else layout
def active_layout(name)
name = self.class.default_layout(formats) if name == true
layout_name = case name
when Symbol then __send__(name)
when Proc then name.call(self)
else name
end
if active_layout
if layout = self.class.find_layout(active_layout, @template.template_format)
layout
else
raise ActionView::MissingTemplate.new(self.class.view_paths, active_layout)
end
end
self.class.find_layout(layout_name, formats)
end
def _pick_layout(layout_name, implicit = false)
return unless layout_name || implicit
layout_name = true if layout_name.nil?
active_layout(layout_name) if action_has_layout? && layout_name
end
private
def candidate_for_layout?(options)
template = options[:template] || default_template(options[:action])
if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty?
begin
!self.view_paths.find_template(template, default_template_format).exempt_from_layout?
rescue ActionView::MissingTemplate
true
end
end
rescue ActionView::MissingTemplate
false
end
def pick_layout(options)
if options.has_key?(:layout)
case layout = options.delete(:layout)
when FalseClass
nil
when NilClass, TrueClass
active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
else
active_layout(layout)
end
else
active_layout if action_has_layout? && candidate_for_layout?(options)
end
end
def action_has_layout?
if conditions = self.class.layout_conditions
case
when only = conditions[:only]
only.include?(action_name)
when except = conditions[:except]
!except.include?(action_name)
else
true
if only = conditions[:only]
return only.include?(action_name)
elsif except = conditions[:except]
return !except.include?(action_name)
end
else
true
end
true
end
def default_template_format
response.template.template_format
end
end
end

View File

@@ -109,16 +109,13 @@ module ActionController #:nodoc:
end
class Responder #:nodoc:
def initialize(controller)
@controller = controller
@request = controller.request
@response = controller.response
if ActionController::Base.use_accept_header
@mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
else
@mime_type_priority = [@request.format]
end
@mime_type_priority = @request.formats
@order = []
@responses = {}
@@ -130,7 +127,7 @@ module ActionController #:nodoc:
@order << mime_type
@responses[mime_type] ||= Proc.new do
@response.template.template_format = mime_type.to_sym
@response.template.formats = [mime_type.to_sym]
@response.content_type = mime_type.to_s
block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
end

View File

@@ -5,6 +5,10 @@ module Mime
EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
def self.[](type)
Type.lookup_by_extension(type.to_s)
end
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
#
# class PostsController < ActionController::Base
@@ -27,7 +31,7 @@ module Mime
# only needs to protect against these types.
@@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
cattr_reader :browser_generated_types
attr_reader :symbol
@@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
def self.unverifiable_types
@@ -172,6 +176,8 @@ module Mime
def ==(mime_type)
return false if mime_type.blank?
(@synonyms + [ self ]).any? do |synonym|
require "ruby-debug"
debugger if mime_type.is_a?(Array)
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
end
end
@@ -187,17 +193,13 @@ module Mime
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
# ActionController::RequestForgeryProtection.
def verify_request?
browser_generated?
@@browser_generated_types.include?(to_sym)
end
def html?
@@html_types.include?(to_sym) || @string =~ /html/
end
def browser_generated?
@@browser_generated_types.include?(to_sym)
end
private
def method_missing(method, *args)
if method.to_s =~ /(\w+)\?$/

View File

@@ -101,10 +101,16 @@ module ActionController
def accepts
header = @env['HTTP_ACCEPT'].to_s.strip
fallback = xhr? ? Mime::JS : Mime::HTML
if header.empty?
[content_type, Mime::ALL].compact
[content_type, fallback, Mime::ALL].compact
else
Mime::Type.parse(header)
ret = Mime::Type.parse(header)
if ret.last == Mime::ALL
ret.insert(-2, fallback)
end
ret
end
end
memoize :accepts
@@ -144,24 +150,33 @@ module ActionController
end
end
ONLY_ALL = [Mime::ALL].freeze
# Returns the Mime type for the \format used in the request.
#
# GET /posts/5.xml | request.format => Mime::XML
# GET /posts/5.xhtml | request.format => Mime::HTML
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
def format
def format(view_path = [])
@format ||=
if parameters[:format]
Mime::Type.lookup_by_extension(parameters[:format])
elsif ActionController::Base.use_accept_header
accepts.first
elsif xhr?
Mime::Type.lookup_by_extension("js")
else
Mime::Type.lookup_by_extension("html")
Mime[parameters[:format]]
elsif Base.use_accept_header && !(accepts == ONLY_ALL)
accepts.first
elsif xhr? then Mime::JS
else Mime::HTML
end
end
def formats
@formats =
if Base.use_accept_header
ret = Array(Mime[parameters[:format]] || accepts)
else
[format]
end
end
# Sets the \format by string extension, which can be used to force custom formats
# that are not controlled by the extension.

View File

@@ -125,11 +125,13 @@ module ActionController #:nodoc:
@template.instance_variable_set("@exception", exception)
@template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH)
@template.instance_variable_set("@contents",
@template.render(:file => template_path_for_local_rescue(exception)))
@template._render_template(template_path_for_local_rescue(exception)))
response.content_type = Mime::HTML
render_for_file(rescues_path("layout"),
response_code_for_rescue(exception))
response.status = interpret_status(response_code_for_rescue(exception))
content = @template._render_template(rescues_path("layout"))
render_for_text(content)
end
def rescue_action_without_handler(exception)
@@ -157,7 +159,7 @@ module ActionController #:nodoc:
end
def rescues_path(template_name)
RESCUES_TEMPLATE_PATH["rescues/#{template_name}.erb"]
RESCUES_TEMPLATE_PATH.find_template("rescues/#{template_name}.erb")
end
def template_path_for_local_rescue(exception)

View File

@@ -6,6 +6,5 @@
</h1>
<pre><%=h @exception.clean_message %></pre>
<%= render :file => @rescues_path["rescues/_trace.erb"] %>
<%= render :file => @rescues_path["rescues/_request_and_response.erb"] %>
<%= @template._render_template(@rescues_path.find_template("rescues/_trace.erb")) %>
<%= @template._render_template(@rescues_path.find_template("rescues/_request_and_response.erb")) %>

View File

@@ -31,6 +31,8 @@ rescue LoadError
end
end
require File.join(File.dirname(__FILE__), "action_pack")
module ActionView
def self.load_all!
[Base, InlineTemplate, TemplateError]
@@ -38,15 +40,16 @@ module ActionView
autoload :Base, 'action_view/base'
autoload :Helpers, 'action_view/helpers'
autoload :InlineTemplate, 'action_view/inline_template'
autoload :Partials, 'action_view/partials'
autoload :InlineTemplate, 'action_view/template/inline'
autoload :Partials, 'action_view/render/partials'
autoload :PathSet, 'action_view/paths'
autoload :Renderable, 'action_view/renderable'
autoload :RenderablePartial, 'action_view/renderable_partial'
autoload :Template, 'action_view/template'
autoload :TemplateError, 'action_view/template_error'
autoload :TemplateHandler, 'action_view/template_handler'
autoload :TemplateHandlers, 'action_view/template_handlers'
autoload :Rendering, 'action_view/render/rendering'
autoload :Renderable, 'action_view/template/renderable'
autoload :RenderablePartial, 'action_view/template/partial'
autoload :Template, 'action_view/template/template'
autoload :TemplateError, 'action_view/template/error'
autoload :TemplateHandler, 'action_view/template/handler'
autoload :TemplateHandlers, 'action_view/template/handlers'
autoload :Helpers, 'action_view/helpers'
end

View File

@@ -160,14 +160,13 @@ module ActionView #:nodoc:
#
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
include Helpers, Partials, ::ERB::Util
include Helpers, Rendering, Partials, ::ERB::Util
extend ActiveSupport::Memoizable
attr_accessor :base_path, :assigns, :template_extension
attr_accessor :base_path, :assigns, :template_extension, :formats
attr_accessor :controller
attr_writer :template_format
attr_accessor :output_buffer
class << self
@@ -184,9 +183,13 @@ module ActionView #:nodoc:
attr_internal :request
delegate :controller_path, :to => :controller, :allow_nil => true
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :logger, :action_name, :controller_name, :to => :controller
delegate :find_by_parts, :to => :view_paths
module CompiledTemplates #:nodoc:
# holds compiled template code
end
@@ -209,7 +212,8 @@ module ActionView #:nodoc:
end
end
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
@formats = formats || [:html]
@assigns = assigns_for_first_render
@assigns_added = nil
@_render_stack = []
@@ -223,55 +227,7 @@ module ActionView #:nodoc:
def view_paths=(paths)
@view_paths = self.class.process_view_paths(paths)
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]
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]
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
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
end
end
# Access the current template being rendered.
# Returns a ActionView::Template object.
def template
@@ -301,32 +257,5 @@ module ActionView #:nodoc:
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

View File

@@ -987,13 +987,13 @@ module ActionView
end
def render(*options_for_render)
old_format = @context && @context.template_format
@context.template_format = :html if @context
old_formats = @context && @context.formats
@context.formats = [:html] if @context
Hash === options_for_render.first ?
@context.render(*options_for_render) :
options_for_render.first.to_s
ensure
@context.template_format = old_format if @context
@context.formats = old_formats if @context
end
def javascript_object_for(object)

View File

@@ -32,14 +32,24 @@ module ActionView #:nodoc:
super(*objs.map { |obj| self.class.type_cast(obj) })
end
def find_by_parts(path, extension = nil, prefix = nil, partial = false)
template_path = path.sub(/^\//, '')
each do |load_path|
if template = load_path.find_by_parts(template_path, extension, prefix, partial)
return template
end
end
Template.new(path, self)
end
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}.#{format}"])
return template
elsif template = load_path[template_path]
if template = load_path.find_by_parts(template_path, format)
return template
end
end

View File

@@ -172,63 +172,128 @@ module ActionView
module Partials
extend ActiveSupport::Memoizable
private
def render_partial(options = {}) #:nodoc:
local_assigns = options[:locals] || {}
def _render_partial(options = {}) #:nodoc:
options[:locals] ||= {}
case partial_path = options[:partial]
when String, Symbol, NilClass
if options.has_key?(:collection)
render_partial_collection(options)
else
_pick_partial_template(partial_path).render_partial(self, options[:object], local_assigns)
case path = partial = options[:partial]
when *_array_like_objects
return _render_partial_collection(partial, options)
else
if partial.is_a?(ActionView::Helpers::FormBuilder)
path = partial.class.to_s.demodulize.underscore.sub(/_builder$/, '')
options[:locals].merge!(path.to_sym => partial)
elsif !partial.is_a?(String)
options[:object] = object = partial
path = ActionController::RecordIdentifier.partial_path(object, controller_path)
end
_, _, prefix, object = parts = partial_parts(path, options)
template = find_by_parts(*parts)
_render_partial_object(template, options, (object unless object == true))
end
end
private
def partial_parts(name, options)
segments = name.split("/")
parts = segments.pop.split(".")
case parts.size
when 1
parts
when 2, 3
extension = parts.delete_at(1).to_sym
if formats.include?(extension)
self.formats.replace [extension]
end
when ActionView::Helpers::FormBuilder
builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')
local_assigns.merge!(builder_partial_path.to_sym => partial_path)
render_partial(:partial => builder_partial_path, :object => options[:object], :locals => local_assigns)
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
render_partial_collection(options.except(:partial).merge(:collection => partial_path))
else
object = partial_path
render_partial(
:partial => ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path),
:object => object,
:locals => local_assigns
)
parts.pop if parts.size == 2
end
path = parts.join(".")
prefix = segments[0..-1].join("/")
prefix = prefix.blank? ? controller_path : prefix
parts = [path, formats, prefix]
parts.push options[:object] || true
end
def _render_partial_with_block(layout, block, options)
@_proc_for_layout = block
concat(_render_partial(options.merge(:partial => layout)))
ensure
@_proc_for_layout = nil
end
def _render_partial_with_layout(layout, options)
if layout
prefix = controller && !layout.include?("/") ? controller.controller_path : nil
layout = find_by_parts(layout, formats, prefix, true)
end
content = _render_partial(options)
return _render_content_with_layout(content, layout, options[:locals])
end
def _deprecated_ivar_assign(template)
if respond_to?(:controller)
ivar = :"@#{template.variable_name}"
object =
if controller.instance_variable_defined?(ivar)
ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
controller.instance_variable_get(ivar),
"#{ivar} will no longer be implicitly assigned to #{template.variable_name}")
end
end
end
def render_partial_collection(options = {}) #:nodoc:
return nil if options[:collection].blank?
def _array_like_objects
array_like = [Array]
if defined?(ActiveRecord)
array_like.push(ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope)
end
array_like
end
partial = options[:partial]
spacer = options[:spacer_template] ? render(:partial => options[:spacer_template]) : ''
local_assigns = options[:locals] ? options[:locals].clone : {}
as = options[:as]
def _render_partial_object(template, options, object = nil)
if options.key?(:collection)
_render_partial_collection(options.delete(:collection), options, template)
else
locals = (options[:locals] ||= {})
object ||= locals[:object] || locals[template.variable_name]
_set_locals(object, locals, template, options)
_render_template(template, locals)
end
end
index = 0
options[:collection].map do |object|
_partial_path ||= partial ||
ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
template = _pick_partial_template(_partial_path)
local_assigns[template.counter_name] = index
result = template.render_partial(self, object, local_assigns.dup, as)
def _set_locals(object, locals, template, options)
object ||= _deprecated_ivar_assign(template)
locals[:object] = locals[template.variable_name] = object
locals[options[:as]] = object if options[:as]
end
def _render_partial_collection(collection, options = {}, passed_template = nil) #:nodoc:
return nil if collection.blank?
spacer = options[:spacer_template] ? _render_partial(:partial => options[:spacer_template]) : ''
locals = (options[:locals] ||= {})
index, @_partial_path = 0, nil
collection.map do |object|
template = passed_template || begin
_partial_path =
ActionController::RecordIdentifier.partial_path(object, controller_path)
template = _pick_partial_template(_partial_path)
end
_set_locals(object, locals, template, options)
locals[template.counter_name] = index
index += 1
result
_render_template(template, locals)
end.join(spacer)
end
def _pick_partial_template(partial_path) #:nodoc:
if partial_path.include?('/')
path = File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}")
elsif controller
path = "#{controller.class.controller_path}/_#{partial_path}"
else
path = "_#{partial_path}"
end
self.view_paths.find_template(path, self.template_format)
prefix = controller_path unless partial_path.include?('/')
find_by_parts(partial_path, formats, prefix, true)
end
memoize :_pick_partial_template
end

View File

@@ -0,0 +1,119 @@
module ActionView
module Rendering
# 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 ||= {}
@exempt_from_layout = true
case options
when Hash
options[:locals] ||= {}
layout = options[:layout]
return _render_partial_with_layout(layout, options) if options.key?(:partial)
return _render_partial_with_block(layout, block, options) if block_given?
layout = find_by_parts(layout, formats) if layout
if file = options[:file]
template = find_by_parts(file, formats)
_render_template_with_layout(template, layout, :locals => options[:locals])
elsif inline = options[:inline]
_render_inline(inline, layout, options)
elsif text = options[:text]
_render_text(text, layout, options)
end
when :update
update_page(&block)
when String, NilClass
_render_partial(:partial => options, :locals => local_assigns)
end
end
def _render_content_with_layout(content, layout, locals)
return content unless layout
locals ||= {}
if controller && layout
response.layout = layout.path_without_format_and_extension if controller.respond_to?(:response)
logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger
end
begin
original_content_for_layout = @content_for_layout if defined?(@content_for_layout)
@content_for_layout = content
@cached_content_for_layout = @content_for_layout
_render_template(layout, locals)
ensure
@content_for_layout = original_content_for_layout
end
end
def _render_template(template, local_assigns = {})
template.compile(local_assigns)
@_render_stack.push(template)
_evaluate_assigns_and_ivars
_set_controller_content_type(template.mime_type) if template.respond_to?(:mime_type)
result = send(template.method_name(local_assigns), local_assigns) do |*names|
if !instance_variable_defined?(:"@content_for_#{names.first}") &&
instance_variable_defined?(:@_proc_for_layout) && (proc = @_proc_for_layout)
capture(*names, &proc)
elsif instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
instance_variable_get(ivar)
end
end
@_render_stack.pop
result
rescue Exception => e
raise e if !template.filename || template.is_a?(InlineTemplate)
if TemplateError === e
e.sub_template_of(template)
raise e
else
raise TemplateError.new(template, assigns, e)
end
end
def _render_inline(inline, layout, options)
content = _render_template(InlineTemplate.new(options[:inline], options[:type]), options[:locals] || {})
layout ? _render_content_with_layout(content, layout, options[:locals]) : content
end
def _render_text(text, layout, options)
layout ? _render_content_with_layout(text, layout, options[:locals]) : text
end
def _render_template_with_layout(template, layout = nil, options = {}, partial = false)
if controller && logger
logger.info("Rendering #{template.path_without_extension}" +
(options[:status] ? " (#{options[:status]})" : ''))
end
content = if partial
object = partial unless partial == true
_render_partial_object(template, options, object)
else
_render_template(template, options[:locals] || {})
end
return content unless layout && !template.exempt_from_layout?
_render_content_with_layout(content, layout, options[:locals] || {})
end
end
end

View File

@@ -1,47 +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
def render(view, local_assigns = {})
if defined? ActionController
ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do
super
end
else
super
end
end
def render_partial(view, object = nil, local_assigns = {}, as = nil)
object ||= local_assigns[:object] || local_assigns[variable_name]
if object.nil? && view.respond_to?(:controller)
ivar = :"@#{variable_name}"
object =
if view.controller.instance_variable_defined?(ivar)
ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
view.controller.instance_variable_get(ivar),
"#{ivar} will no longer be implicitly assigned to #{variable_name}")
end
end
# Ensure correct object is reassigned to other accessors
local_assigns[:object] = local_assigns[variable_name] = object
local_assigns[as] = object if as
render_template(view, local_assigns)
end
end
end

View File

@@ -1,8 +1,8 @@
module ActionView #:nodoc:
module TemplateHandlers #:nodoc:
autoload :ERB, 'action_view/template_handlers/erb'
autoload :RJS, 'action_view/template_handlers/rjs'
autoload :Builder, 'action_view/template_handlers/builder'
autoload :ERB, 'action_view/template/handlers/erb'
autoload :RJS, 'action_view/template/handlers/rjs'
autoload :Builder, 'action_view/template/handlers/builder'
def self.extended(base)
base.register_default_template_handler :erb, TemplateHandlers::ERB

View File

@@ -4,7 +4,7 @@ module ActionView
include Compilable
def compile(template)
"@template_format = :html;" +
"@formats = [:html];" +
"controller.response.content_type ||= Mime::JS;" +
"update_page do |page|;#{template.source}\nend"
end

View File

@@ -0,0 +1,18 @@
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

View File

@@ -23,28 +23,6 @@ module ActionView
end
memoize :method_name_without_locals
def render(view, local_assigns = {})
compile(local_assigns)
stack = view.instance_variable_get(:@_render_stack)
stack.push(self)
view.send(:_evaluate_assigns_and_ivars)
view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
result = view.send(method_name(local_assigns), local_assigns) do |*names|
ivar = :@_proc_for_layout
if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
view.capture(*names, &proc)
elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
view.instance_variable_get(ivar)
end
end
stack.pop
result
end
def method_name(local_assigns)
if local_assigns && local_assigns.any?
method_name = method_name_without_locals.dup
@@ -55,16 +33,16 @@ module ActionView
method_name.to_sym
end
private
# Compile and evaluate the template's code (if necessary)
def compile(local_assigns)
render_symbol = method_name(local_assigns)
# Compile and evaluate the template's code (if necessary)
def compile(local_assigns)
render_symbol = method_name(local_assigns)
if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
compile!(render_symbol, local_assigns)
end
if !Base::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

View File

@@ -38,7 +38,7 @@ module ActionView #:nodoc:
# should not be confused with format extensions +html+, +js+, +xml+,
# etc. A format must be supplied to match a formated file. +hello/index+
# will never match +hello/index.html.erb+.
def [](path)
def find_template(path)
templates_in_path do |template|
if template.accessible_paths.include?(path)
return template
@@ -47,6 +47,24 @@ module ActionView #:nodoc:
nil
end
def find_by_parts(name, extensions = nil, prefix = nil, partial = nil)
path = prefix ? "#{prefix}/" : ""
name = name.split("/")
name[-1] = "_#{name[-1]}" if partial
path << name.join("/")
template = nil
Array(extensions).each do |extension|
extensioned_path = extension ? "#{path}.#{extension}" : path
template = find_template(extensioned_path) || find_template(path)
break if template
end
template
end
private
def templates_in_path
(Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
@@ -73,7 +91,7 @@ module ActionView #:nodoc:
@paths.freeze
end
def [](path)
def find_template(path)
@paths[path]
end
end
@@ -177,18 +195,6 @@ module ActionView #:nodoc:
end
memoize :method_segment
def render_template(view, local_assigns = {})
render(view, local_assigns)
rescue Exception => e
raise e unless filename
if TemplateError === e
e.sub_template_of(self)
raise e
else
raise TemplateError.new(self, view.assigns, e)
end
end
def stale?
File.mtime(filename) > mtime
end

View File

@@ -7,18 +7,15 @@ module ActionView
@_rendered = { :template => nil, :partials => Hash.new(0) }
initialize_without_template_tracking(*args)
end
end
module Renderable
alias_method :render_without_template_tracking, :render
def render(view, local_assigns = {})
if respond_to?(:path) && !is_a?(InlineTemplate)
rendered = view.instance_variable_get(:@_rendered)
rendered[:partials][self] += 1 if is_a?(RenderablePartial)
rendered[:template] ||= self
alias_method :_render_template_without_template_tracking, :_render_template
def _render_template(template, local_assigns = {})
if template.respond_to?(:path) && !template.is_a?(InlineTemplate)
@_rendered[:partials][template] += 1 if template.is_a?(RenderablePartial)
@_rendered[:template] ||= template
end
render_without_template_tracking(view, local_assigns)
end
_render_template_without_template_tracking(template, local_assigns)
end
end
class TestCase < ActiveSupport::TestCase

View File

@@ -55,7 +55,7 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_third_party_template_library_auto_discovers_layout
@controller = ThirdPartyTemplateLibraryController.new
get :hello
assert_equal 'layouts/third_party_template_library.mab', @controller.active_layout.to_s
assert_equal 'layouts/third_party_template_library.mab', @controller.active_layout(true).to_s
assert_equal 'layouts/third_party_template_library', @response.layout
assert_response :success
assert_equal 'Mab', @response.body
@@ -64,14 +64,14 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_namespaced_controllers_auto_detect_layouts
@controller = ControllerNameSpace::NestedController.new
get :hello
assert_equal 'layouts/controller_name_space/nested', @controller.active_layout.to_s
assert_equal 'layouts/controller_name_space/nested', @controller.active_layout(true).to_s
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
end
def test_namespaced_controllers_auto_detect_layouts
@controller = MultipleExtensions.new
get :hello
assert_equal 'layouts/multiple_extensions.html.erb', @controller.active_layout.to_s
assert_equal 'layouts/multiple_extensions.html.erb', @controller.active_layout(true).to_s
assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
end
end
@@ -83,6 +83,14 @@ class HasOwnLayoutController < LayoutTest
layout 'item'
end
class OnlyLayoutController < LayoutTest
layout 'item', :only => "hello"
end
class ExceptLayoutController < LayoutTest
layout 'item', :except => "goodbye"
end
class SetsLayoutInRenderController < LayoutTest
def hello
render :layout => 'third_party_template_library'
@@ -107,6 +115,30 @@ class LayoutSetInResponseTest < ActionController::TestCase
get :hello
assert_equal 'layouts/item', @response.layout
end
def test_layout_only_exception_when_included
@controller = OnlyLayoutController.new
get :hello
assert_equal 'layouts/item', @response.layout
end
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
assert_equal nil, @response.layout
end
def test_layout_except_exception_when_included
@controller = ExceptLayoutController.new
get :hello
assert_equal 'layouts/item', @response.layout
end
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
assert_equal nil, @response.layout
end
def test_layout_set_when_using_render
@controller = SetsLayoutInRenderController.new

View File

@@ -437,7 +437,7 @@ class MimeControllerTest < ActionController::TestCase
unless args.empty?
@action = args.first[:action]
end
response.body = "#{@action} - #{@template.template_format}"
response.body = "#{@action} - #{@template.formats}"
end
end

View File

@@ -342,7 +342,7 @@ class TestController < ActionController::Base
end
def accessing_params_in_template_with_layout
render :layout => nil, :inline => "Hello: <%= params[:name] %>"
render :layout => true, :inline => "Hello: <%= params[:name] %>"
end
def render_with_explicit_template
@@ -1612,7 +1612,7 @@ class RenderingLoggingTest < ActionController::TestCase
@controller.logger = MockLogger.new
get :layout_test
logged = @controller.logger.logged.find_all {|l| l =~ /render/i }
assert_equal "Rendering template within layouts/standard", logged[0]
assert_equal "Rendering test/hello_world", logged[1]
assert_equal "Rendering test/hello_world", logged[0]
assert_equal "Rendering template within layouts/standard", logged[1]
end
end

View File

@@ -29,8 +29,8 @@ class ViewLoadPathsTest < ActionController::TestCase
@controller = TestController.new
# Following is needed in order to setup @controller.template object properly
@controller.send :initialize_template_class, @response
@controller.send :assign_shortcuts, @request, @response
@controller.send :initialize_template_class, @response
# Track the last warning.
@old_behavior = ActiveSupport::Deprecation.behavior

View File

@@ -0,0 +1 @@
hello.rhtml

View File

@@ -3,7 +3,7 @@ require 'abstract_unit'
class JavaScriptHelperTest < ActionView::TestCase
tests ActionView::Helpers::JavaScriptHelper
attr_accessor :template_format, :output_buffer
attr_accessor :formats, :output_buffer
def setup
@template = self

View File

@@ -25,7 +25,7 @@ class Author::Nested < Author; end
class PrototypeHelperBaseTest < ActionView::TestCase
attr_accessor :template_format, :output_buffer
attr_accessor :formats, :output_buffer
def setup
@template = self

View File

@@ -138,7 +138,7 @@ module RenderTestCases
# TODO: The reason for this test is unclear, improve documentation
def test_render_missing_xml_partial_and_raise_missing_template
@view.template_format = :xml
@view.formats = [:xml]
assert_raise(ActionView::MissingTemplate) { @view.render(:partial => "test/layout_for_partial") }
end

View File

@@ -32,6 +32,7 @@ module ActiveSupport
autoload :BufferedLogger, 'active_support/buffered_logger'
autoload :Cache, 'active_support/cache'
autoload :Callbacks, 'active_support/callbacks'
autoload :ConcurrentHash, 'active_support/concurrent_hash'
autoload :Deprecation, 'active_support/deprecation'
autoload :Duration, 'active_support/duration'
autoload :Gzip, 'active_support/gzip'

View File

@@ -67,14 +67,14 @@ module ActiveSupport
end
for severity in Severity.constants
class_eval <<-EOT, __FILE__, __LINE__
def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
end # end
#
def #{severity.downcase}? # def debug?
#{severity} >= @level # DEBUG >= @level
end # end
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
end # end
def #{severity.downcase}? # def debug?
#{severity} >= @level # DEBUG >= @level
end # end
EOT
end

View File

@@ -204,7 +204,7 @@ module ActiveSupport
module ClassMethods
def define_callbacks(*callbacks)
callbacks.each do |callback|
class_eval <<-"end_eval"
class_eval <<-"end_eval", __FILE__, __LINE__ + 1
def self.#{callback}(*methods, &block) # def self.before_save(*methods, &block)
callbacks = CallbackChain.build(:#{callback}, *methods, &block) # callbacks = CallbackChain.build(:before_save, *methods, &block)
@#{callback}_callbacks ||= CallbackChain.new # @before_save_callbacks ||= CallbackChain.new

View File

@@ -0,0 +1,26 @@
module ActiveSupport
class ConcurrentHash
def initialize(hash = {})
@backup_cache = hash.dup
@frozen_cache = hash.dup.freeze
@mutex = Mutex.new
end
def []=(k,v)
@mutex.synchronize { @backup_cache[k] = v }
@frozen_cache = @backup_cache.dup.freeze
end
def [](k)
if @frozen_cache.key?(k)
@frozen_cache[k]
else
@mutex.synchronize { @backup_cache[k] }
end
end
def empty?
@backup_cache.empty?
end
end
end

View File

@@ -10,7 +10,7 @@ class Class
def cattr_reader(*syms)
syms.flatten.each do |sym|
next if sym.is_a?(Hash)
class_eval(<<-EOS, __FILE__, __LINE__)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
unless defined? @@#{sym} # unless defined? @@hair_colors
@@#{sym} = nil # @@hair_colors = nil
end # end
@@ -29,7 +29,7 @@ class Class
def cattr_writer(*syms)
options = syms.extract_options!
syms.flatten.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
unless defined? @@#{sym} # unless defined? @@hair_colors
@@#{sym} = nil # @@hair_colors = nil
end # end

View File

@@ -8,7 +8,7 @@ class Class
def superclass_delegating_reader(*names)
class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name
names.each do |name|
class_eval <<-EOS
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{name} # def self.only_reader
if defined?(@#{name}) # if defined?(@only_reader)
@#{name} # @only_reader
@@ -32,10 +32,10 @@ class Class
def superclass_delegating_writer(*names)
names.each do |name|
class_eval <<-EOS
def self.#{name}=(value) # def self.only_writer=(value)
@#{name} = value # @only_writer = value
end # end
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{name}=(value) # def self.property=(value)
@#{name} = value # @property = value
end # end
EOS
end
end

View File

@@ -10,14 +10,14 @@ class Class # :nodoc:
def class_inheritable_reader(*syms)
syms.each do |sym|
next if sym.is_a?(Hash)
class_eval <<-EOS
def self.#{sym} # def self.before_add_for_comments
read_inheritable_attribute(:#{sym}) # read_inheritable_attribute(:before_add_for_comments)
end # end
#
def #{sym} # def before_add_for_comments
self.class.#{sym} # self.class.before_add_for_comments
end # end
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{sym} # def self.after_add
read_inheritable_attribute(:#{sym}) # read_inheritable_attribute(:after_add)
end # end
def #{sym} # def after_add
self.class.#{sym} # self.class.after_add
end # end
EOS
end
end
@@ -25,7 +25,7 @@ class Class # :nodoc:
def class_inheritable_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{sym}=(obj) # def self.color=(obj)
write_inheritable_attribute(:#{sym}, obj) # write_inheritable_attribute(:color, obj)
end # end
@@ -42,7 +42,7 @@ class Class # :nodoc:
def class_inheritable_array_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{sym}=(obj) # def self.levels=(obj)
write_inheritable_array(:#{sym}, obj) # write_inheritable_array(:levels, obj)
end # end
@@ -59,7 +59,7 @@ class Class # :nodoc:
def class_inheritable_hash_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{sym}=(obj) # def self.nicknames=(obj)
write_inheritable_hash(:#{sym}, obj) # write_inheritable_hash(:nicknames, obj)
end # end

View File

@@ -14,7 +14,7 @@ class Module
def mattr_reader(*syms)
syms.each do |sym|
next if sym.is_a?(Hash)
class_eval(<<-EOS, __FILE__, __LINE__)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
unless defined? @@#{sym} # unless defined? @@pagination_options
@@#{sym} = nil # @@pagination_options = nil
end # end
@@ -33,7 +33,7 @@ class Module
def mattr_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
unless defined? @@#{sym} # unless defined? @@pagination_options
@@#{sym} = nil # @@pagination_options = nil
end # end

View File

@@ -3,9 +3,9 @@ class Proc #:nodoc:
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
define_method(method_name, &block) # define_method("__bind_1230458026_720454", &block)
method = instance_method(method_name) # method = instance_method("__bind_1230458026_720454")
remove_method(method_name) # remove_method("__bind_1230458026_720454")
method
end.bind(object)
end

View File

@@ -55,7 +55,11 @@ module ActiveSupport #:nodoc:
unless '1.8.7 and later'.respond_to?(:chars)
def chars
ActiveSupport::Deprecation.warn('String#chars has been deprecated in favor of String#mb_chars.', caller)
# FIXME:
# ActiveSupport::Deprecation refers to RAILS_ENV
# and is a show stopper for 3rd party applications
# that only want ActiveSupport
ActiveSupport::Deprecation.warn('String#chars has been deprecated in favor of String#mb_chars.', caller) if defined?(ActiveSupport::Deprecation)
mb_chars
end
end

View File

@@ -89,7 +89,7 @@ module ActiveSupport
method_names = method_names + options.keys
method_names.each do |method_name|
alias_method_chain(method_name, :deprecation) do |target, punctuation|
class_eval(<<-EOS, __FILE__, __LINE__)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block)
::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn(
self.class.deprecated_method_warning( # self.class.deprecated_method_warning(

View File

@@ -58,7 +58,7 @@ module ActiveSupport
original_method = :"_unmemoized_#{symbol}"
memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)
class_eval <<-EOS, __FILE__, __LINE__
class_eval <<-EOS, __FILE__, __LINE__ + 1
include InstanceMethods # include InstanceMethods
#
if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)

View File

@@ -0,0 +1,9 @@
$LOAD_PATH.unshift File.dirname(__FILE__)
require "core_ext/blank"
# whole object.rb pulls up rarely used introspection extensions
require "core_ext/object/metaclass"
require 'core_ext/array'
require 'core_ext/hash'
require 'core_ext/module/attribute_accessors'
require 'core_ext/string/inflections'

View File

@@ -23,11 +23,11 @@ module ActiveSupport #:nodoc:
# Lazy load the Unicode database so it's only loaded when it's actually used
ATTRIBUTES.each do |attr_name|
class_eval(<<-EOS, __FILE__, __LINE__)
def #{attr_name} # def codepoints
load # load
@#{attr_name} # @codepoints
end # end
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{attr_name} # def codepoints
load # load
@#{attr_name} # @codepoints
end # end
EOS
end

View File

@@ -237,10 +237,10 @@ module ActiveSupport
end
%w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
class_eval <<-EOV
def #{method_name} # def year
time.#{method_name} # time.year
end # end
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method_name} # def month
time.#{method_name} # time.month
end # end
EOV
end