mirror of
https://github.com/github/rails.git
synced 2026-01-10 07:08:08 -05:00
Begin unifying the interface between ActionController and ActionView
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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+)\?$/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")) %>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
119
actionpack/lib/action_view/render/rendering.rb
Normal file
119
actionpack/lib/action_view/render/rendering.rb
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
18
actionpack/lib/action_view/template/partial.rb
Normal file
18
actionpack/lib/action_view/template/partial.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
1
actionpack/test/fixtures/layout_tests/views/goodbye.rhtml
vendored
Normal file
1
actionpack/test/fixtures/layout_tests/views/goodbye.rhtml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello.rhtml
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
26
activesupport/lib/active_support/concurrent_hash.rb
Normal file
26
activesupport/lib/active_support/concurrent_hash.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
9
activesupport/lib/active_support/mini.rb
Normal file
9
activesupport/lib/active_support/mini.rb
Normal 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'
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user