Merge branch 'master' of git://github.com/lifo/docrails

This commit is contained in:
Rajinder Yadav
2010-10-15 22:19:39 -04:00
221 changed files with 3181 additions and 2200 deletions

View File

@@ -6,6 +6,7 @@ else
gem "arel", :git => "git://github.com/rails/arel.git"
end
gem "rack", :git => "git://github.com/rack/rack.git"
gem "rails", :path => File.dirname(__FILE__)
gem "rake", ">= 0.8.7"
@@ -54,6 +55,11 @@ platforms :jruby do
gem "activerecord-jdbcsqlite3-adapter"
# This is needed by now to let tests work on JRuby
# TODO: When the JRuby guys merge jruby-openssl in
# jruby this will be removed
gem "jruby-openssl"
group :db do
gem "activerecord-jdbcmysql-adapter"
gem "activerecord-jdbcpostgresql-adapter"

View File

@@ -26,6 +26,7 @@ $:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(
require 'abstract_controller'
require 'action_view'
require 'action_mailer/version'
# Common Active Support usage in Action Mailer
require 'active_support/core_ext/class'

View File

@@ -409,7 +409,7 @@ module ActionMailer #:nodoc:
protected
def set_payload_for_mail(payload, mail) #:nodoc:
payload[:mailer] = self.name
payload[:mailer] = name
payload[:message_id] = mail.message_id
payload[:subject] = mail.subject
payload[:to] = mail.to
@@ -421,11 +421,8 @@ module ActionMailer #:nodoc:
end
def method_missing(method, *args) #:nodoc:
if action_methods.include?(method.to_s)
new(method, *args).message
else
super
end
return super unless respond_to?(method)
new(method, *args).message
end
end

View File

@@ -247,8 +247,8 @@ module ActionMailer
[ nil, {} ]
else
ctype, *attrs = @content_type.split(/;\s*/)
attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h }
[ctype, {"charset" => @charset}.merge(attrs)]
attrs = Hash[attrs.map { |attr| attr.split(/\=/, 2) }]
[ctype, {"charset" => @charset}.merge!(attrs)]
end
end
end

View File

@@ -14,9 +14,9 @@ module ActionMailer
paths = app.config.paths
options = app.config.action_mailer
options.assets_dir ||= paths.public.to_a.first
options.javascripts_dir ||= paths.public.javascripts.to_a.first
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
options.assets_dir ||= paths["public"].first
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
# make sure readers methods get compiled
options.asset_path ||= nil

View File

@@ -6,7 +6,7 @@ class <%= class_name %> < ActionMailer::Base
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.<%= file_name %>.<%= action %>.subject
# en.<%= file_path.gsub("/",".") %>.<%= action %>.subject
#
def <%= action %>
@greeting = "Hi"

View File

@@ -328,7 +328,6 @@ class ActionMailerTest < Test::Unit::TestCase
mail
end
# Replacing logger work around for mocha bug. Should be fixed in mocha 0.3.3
def setup
set_delivery_method :test
ActionMailer::Base.perform_deliveries = true
@@ -336,14 +335,12 @@ class ActionMailerTest < Test::Unit::TestCase
ActionMailer::Base.deliveries.clear
ActiveSupport::Deprecation.silenced = true
@original_logger = TestMailer.logger
@recipient = 'test@localhost'
TestMailer.delivery_method = :test
end
def teardown
TestMailer.logger = @original_logger
ActiveSupport::Deprecation.silenced = false
restore_delivery_method
end
@@ -1097,4 +1094,4 @@ EOF
ensure
TestMailer.smtp_settings.merge!(:enable_starttls_auto => true)
end
end
end

View File

@@ -1,12 +1,20 @@
*Rails 3.1.0 (unreleased)*
* Added render :once. You can pass either a string or an array of strings and Rails will ensure they each of them are rendered just once. [José Valim]
* Deprecate old template handler API. The new API simply requires a template handler to respond to call. [José Valim]
* :rhtml and :rxml were finally removed as template handlers. [José Valim]
* Moved etag responsibility from ActionDispatch::Response to the middleware stack. [José Valim]
* Rely on Rack::Session stores API for more compatibility across the Ruby world. This is backwards incompatible since Rack::Session expects #get_session to accept 4 arguments and requires #destroy_session instead of simply #destroy. [José Valim]
* file_field automatically adds :multipart => true to the enclosing form. [Santiago Pastorino]
* Renames csrf_meta_tag -> csrf_meta_tags, and aliases csrf_meta_tag for backwards compatibility. [fxn]
* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used
for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply
to the browser only.
* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. [Yehuda Katz, Carl Lerche]
*Rails 3.0.0 (August 29, 2010)*

View File

@@ -8,15 +8,18 @@ Rake can be found at http://rake.rubyforge.org
== Running by hand
If you only want to run a single test suite, or don't want to bother with Rake,
you can do so with something like:
To run a single test suite
ruby -Itest test/controller/base_tests.rb
rake test TEST=path/to/test.rb
== Dependency on ActiveRecord and database setup
which can be further narrowed down to one test:
rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"
== Dependency on Active Record and database setup
Test cases in the test/controller/active_record/ directory depend on having
activerecord and sqlite installed. If ActiveRecord is not in
activerecord and sqlite installed. If Active Record is not in
actionpack/../activerecord directory, or the sqlite rubygem is not installed,
these tests are skipped.

View File

@@ -13,6 +13,7 @@ module AbstractController
class Base
attr_internal :response_body
attr_internal :action_name
attr_internal :formats
include ActiveSupport::Configurable
extend ActiveSupport::DescendantsTracker
@@ -61,13 +62,13 @@ module AbstractController
def action_methods
@action_methods ||= begin
# All public instance methods of this class, including ancestors
methods = public_instance_methods(true).map { |m| m.to_s }.to_set -
methods = (public_instance_methods(true) -
# Except for public instance methods of Base and its ancestors
internal_methods.map { |m| m.to_s } +
internal_methods +
# Be sure to include shadowed public instance methods of this class
public_instance_methods(false).map { |m| m.to_s } -
public_instance_methods(false)).uniq.map { |x| x.to_s } -
# And always exclude explicitly hidden actions
hidden_actions
hidden_actions.to_a
# Clear out AS callback method pollution
methods.reject { |method| method =~ /_one_time_conditions/ }

View File

@@ -12,7 +12,6 @@ module AbstractController
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
# it will trigger the lookup_context and consequently expire the cache.
# TODO Add some deprecation warnings to remove I18n.locale from controllers
class I18nProxy < ::I18n::Config #:nodoc:
attr_reader :i18n_config, :lookup_context
@@ -50,7 +49,7 @@ module AbstractController
if controller.respond_to?(:_helpers)
include controller._helpers
if controller.respond_to?(:_routes)
if controller.respond_to?(:_routes) && controller._routes
include controller._routes.url_helpers
include controller._routes.mounted_helpers
end
@@ -156,7 +155,7 @@ module AbstractController
options[:partial] = action_name
end
if (options.keys & [:partial, :file, :template]).empty?
if (options.keys & [:partial, :file, :template, :once]).empty?
options[:prefix] ||= _prefix
end

View File

@@ -119,6 +119,11 @@ module ActionController
headers["Location"] = url
end
# basic url_for that can be overridden for more robust functionality
def url_for(string)
string
end
def status
@_status
end

View File

@@ -66,7 +66,7 @@ module ActionController
# Examples:
# expires_in 20.minutes
# expires_in 3.hours, :public => true
# expires in 3.hours, 'max-stale' => 5.hours, :public => true
# expires_in 3.hours, 'max-stale' => 5.hours, :public => true
#
# This method will overwrite an existing Cache-Control header.
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.

View File

@@ -2,8 +2,6 @@ module ActionController
module Head
extend ActiveSupport::Concern
include ActionController::UrlFor
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
@@ -27,8 +25,8 @@ module ActionController
self.status = status
self.location = url_for(location) if location
self.content_type = Mime[formats.first]
self.content_type = Mime[formats.first] if formats
self.response_body = " "
end
end
end
end

View File

@@ -214,7 +214,7 @@ module ActionController
def encode_credentials(http_method, credentials, password, password_is_ha1)
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
"Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
"Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
end
def decode_credentials_header(request)
@@ -423,14 +423,13 @@ module ActionController
# Returns nil if no token is found.
def token_and_options(request)
if header = request.authorization.to_s[/^Token (.*)/]
values = $1.split(',').
inject({}) do |memo, value|
value.strip! # remove any spaces between commas and values
key, value = value.split(/\=\"?/) # split key=value pairs
value.chomp!('"') # chomp trailing " in value
value.gsub!(/\\\"/, '"') # unescape remaining quotes
memo.update(key => value)
end
values = Hash[$1.split(',').map do |value|
value.strip! # remove any spaces between commas and values
key, value = value.split(/\=\"?/) # split key=value pairs
value.chomp!('"') # chomp trailing " in value
value.gsub!(/\\\"/, '"') # unescape remaining quotes
[key, value]
end]
[values.delete("token"), values.with_indifferent_access]
end
end
@@ -442,9 +441,8 @@ module ActionController
#
# Returns String.
def encode_credentials(token, options = {})
values = ["token=#{token.to_s.inspect}"]
options.each do |key, value|
values << "#{key}=#{value.to_s.inspect}"
values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
"#{key}=#{value.to_s.inspect}"
end
"Token #{values * ", "}"
end

View File

@@ -2,7 +2,6 @@ module ActionController
module Rendering
extend ActiveSupport::Concern
include ActionController::RackDelegation
include AbstractController::Rendering
# Before processing, set the request formats in current controller formats.

View File

@@ -101,10 +101,6 @@ module ActionController #:nodoc:
# send_data image.data, :type => image.content_type, :disposition => 'inline'
#
# See +send_file+ for more information on HTTP Content-* headers and caching.
#
# <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
# data to the browser, then use <tt>render :text => proc { ... }</tt>
# instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
send_file_headers! options.dup
render options.slice(:status, :content_type).merge(:text => data)

View File

@@ -21,10 +21,10 @@ module ActionController
paths = app.config.paths
options = app.config.action_controller
options.assets_dir ||= paths.public.to_a.first
options.javascripts_dir ||= paths.public.javascripts.to_a.first
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
options.page_cache_directory ||= paths.public.to_a.first
options.assets_dir ||= paths["public"].first
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
options.page_cache_directory ||= paths["public"].first
# make sure readers methods get compiled
options.asset_path ||= nil

View File

@@ -5,12 +5,14 @@ module ActionController
Module.new do
define_method(:inherited) do |klass|
super(klass)
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
klass.helpers_path = namespace._railtie.config.paths.app.helpers.to_a
paths = namespace._railtie.paths["app/helpers"].existent
else
klass.helpers_path = app.config.helpers_paths
paths = app.config.helpers_paths
end
klass.helpers_path = paths
klass.helper :all if klass.superclass == ActionController::Base
end
end

View File

@@ -187,15 +187,18 @@ module ActionController
end
end
class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc:
DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
def initialize(session = {})
@env, @by = nil, nil
replace(session.stringify_keys)
@loaded = true
end
def exists?; true; end
def exists?
true
end
end
# Superclass for ActionController functional tests. Functional tests allow you to
@@ -394,7 +397,7 @@ module ActionController
parameters ||= {}
@request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session = ActionController::TestSession.new(session) if session
@request.session["flash"] = @request.flash.update(flash || {})
@request.session["flash"].sweep

View File

@@ -50,8 +50,7 @@ module ActionDispatch
if cache_control = self["Cache-Control"]
cache_control.split(/,\s*/).each do |segment|
first, last = segment.split("=")
last ||= true
@cache_control[first.to_sym] = last
@cache_control[first.to_sym] = last || true
end
end
end
@@ -88,28 +87,9 @@ module ActionDispatch
def handle_conditional_get!
if etag? || last_modified? || !@cache_control.empty?
set_conditional_cache_control!
elsif nonempty_ok_response?
self.etag = body
if request && request.etag_matches?(etag)
self.status = 304
self.body = []
end
set_conditional_cache_control!
else
headers["Cache-Control"] = "no-cache"
end
end
def nonempty_ok_response?
@status == 200 && string_body?
end
def string_body?
!@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
end
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
def set_conditional_cache_control!

View File

@@ -54,11 +54,7 @@ module ActionDispatch
# the application should use), this \method returns the overridden
# value, not the original.
def request_method
@request_method ||= begin
method = env["REQUEST_METHOD"]
HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
method
end
@request_method ||= check_method(env["REQUEST_METHOD"])
end
# Returns a symbol form of the #request_method
@@ -70,11 +66,7 @@ module ActionDispatch
# even if it was overridden by middleware. See #request_method for
# more information.
def method
@method ||= begin
method = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
method
end
@method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
end
# Returns a symbol form of the #method
@@ -207,7 +199,7 @@ module ActionDispatch
# TODO This should be broken apart into AD::Request::Session and probably
# be included by the session middleware.
def reset_session
session.destroy if session
session.destroy if session && session.respond_to?(:destroy)
self.session = {}
@env['action_dispatch.request.flash_hash'] = nil
end
@@ -246,5 +238,12 @@ module ActionDispatch
def local?
LOCALHOST.any? { |local_ip| local_ip === remote_addr && local_ip === remote_ip }
end
private
def check_method(name)
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
name
end
end
end

View File

@@ -132,7 +132,7 @@ module ActionDispatch # :nodoc:
# information.
attr_accessor :charset, :content_type
CONTENT_TYPE = "Content-Type"
CONTENT_TYPE = "Content-Type"
cattr_accessor(:default_charset) { "utf-8" }

View File

@@ -2,27 +2,28 @@ require 'active_support/core_ext/object/blank'
module ActionDispatch
module Http
class UploadedFile < Tempfile
class UploadedFile
attr_accessor :original_filename, :content_type, :tempfile, :headers
def initialize(hash)
@original_filename = hash[:filename]
@content_type = hash[:type]
@headers = hash[:head]
# To the untrained eye, this may appear as insanity. Given the alternatives,
# such as busting the method cache on every request or breaking backwards
# compatibility with is_a?(Tempfile), this solution is the best available
# option.
#
# TODO: Deprecate is_a?(Tempfile) and define a real API for this parameter
tempfile = hash[:tempfile]
tempfile.instance_variables.each do |ivar|
instance_variable_set(ivar, tempfile.instance_variable_get(ivar))
end
@tempfile = hash[:tempfile]
raise(ArgumentError, ':tempfile is required') unless @tempfile
end
alias local_path path
def read(*args)
@tempfile.read(*args)
end
def rewind
@tempfile.rewind
end
def size
@tempfile.size
end
end
module Upload

View File

@@ -18,11 +18,6 @@ module ActionDispatch
@protocol ||= ssl? ? 'https://' : 'http://'
end
# Is this an SSL request?
def ssl?
@ssl ||= @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
end
# Returns the \host for this request, such as "example.com".
def raw_host_with_port
if forwarded = env["HTTP_X_FORWARDED_HOST"]

View File

@@ -1,5 +1,6 @@
require 'rack/utils'
require 'rack/request'
require 'rack/session/abstract/id'
require 'action_dispatch/middleware/cookies'
require 'active_support/core_ext/object/blank'
@@ -8,252 +9,76 @@ module ActionDispatch
class SessionRestoreError < StandardError #:nodoc:
end
class AbstractStore
ENV_SESSION_KEY = 'rack.session'.freeze
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
# thin wrapper around Hash that allows us to lazily
# load session id into session_options
class OptionsHash < Hash
def initialize(by, env, default_options)
@by = by
@env = env
@session_id_loaded = false
merge!(default_options)
end
def [](key)
if key == :id
load_session_id! unless key?(:id) || has_session_id?
end
super
end
private
def has_session_id?
@session_id_loaded
end
def load_session_id!
self[:id] = @by.send(:extract_session_id, @env)
@session_id_loaded = true
end
module DestroyableSession
def destroy
clear
options = @env[Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY] if @env
options ||= {}
@by.send(:destroy_session, @env, options[:id], options) if @by
options[:id] = nil
@loaded = false
end
end
class SessionHash < Hash
def initialize(by, env)
super()
@by = by
@env = env
@loaded = false
end
def [](key)
load_for_read!
super(key.to_s)
end
def has_key?(key)
load_for_read!
super(key.to_s)
end
def []=(key, value)
load_for_write!
super(key.to_s, value)
end
def clear
load_for_write!
super
end
def to_hash
load_for_read!
h = {}.replace(self)
h.delete_if { |k,v| v.nil? }
h
end
def update(hash)
load_for_write!
super(hash.stringify_keys)
end
def delete(key)
load_for_write!
super(key.to_s)
end
def inspect
load_for_read!
super
end
def exists?
return @exists if instance_variable_defined?(:@exists)
@exists = @by.send(:exists?, @env)
end
def loaded?
@loaded
end
def destroy
clear
@by.send(:destroy, @env) if defined?(@by) && @by
@env[ENV_SESSION_OPTIONS_KEY][:id] = nil if defined?(@env) && @env && @env[ENV_SESSION_OPTIONS_KEY]
@loaded = false
end
private
def load_for_read!
load! if !loaded? && exists?
end
def load_for_write!
load! unless loaded?
end
def load!
id, session = @by.send(:load_session, @env)
@env[ENV_SESSION_OPTIONS_KEY][:id] = id
replace(session.stringify_keys)
@loaded = true
end
end
DEFAULT_OPTIONS = {
:key => '_session_id',
:path => '/',
:domain => nil,
:expire_after => nil,
:secure => false,
:httponly => true,
:cookie_only => true
}
::Rack::Session::Abstract::SessionHash.send :include, DestroyableSession
module Compatibility
def initialize(app, options = {})
@app = app
@default_options = DEFAULT_OPTIONS.merge(options)
@key = @default_options.delete(:key).freeze
@cookie_only = @default_options.delete(:cookie_only)
ensure_session_key!
options[:key] ||= '_session_id'
super
end
def call(env)
prepare!(env)
response = @app.call(env)
session_data = env[ENV_SESSION_KEY]
options = env[ENV_SESSION_OPTIONS_KEY]
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after]
request = ActionDispatch::Request.new(env)
return response if (options[:secure] && !request.ssl?)
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
sid = options[:id] || generate_sid
session_data = session_data.to_hash
value = set_session(env, sid, session_data)
return response unless value
cookie = { :value => value }
unless options[:expire_after].nil?
cookie[:expires] = Time.now + options.delete(:expire_after)
end
set_cookie(request, cookie.merge!(options))
end
response
def generate_sid
ActiveSupport::SecureRandom.hex(16)
end
private
protected
def prepare!(env)
env[ENV_SESSION_KEY] = SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
end
def initialize_sid
@default_options.delete(:sidbits)
@default_options.delete(:secure_random)
end
end
def generate_sid
ActiveSupport::SecureRandom.hex(16)
end
module StaleSessionCheck
def load_session(env)
stale_session_check! { super }
end
def set_cookie(request, options)
if request.cookie_jar[@key] != options[:value] || !options[:expires].nil?
request.cookie_jar[@key] = options
def extract_session_id(env)
stale_session_check! { super }
end
def stale_session_check!
yield
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
end
retry
else
raise
end
end
end
def load_session(env)
stale_session_check! do
sid = current_session_id(env)
sid, session = get_session(env, sid)
[sid, session]
end
end
class AbstractStore < Rack::Session::Abstract::ID
include Compatibility
include StaleSessionCheck
def extract_session_id(env)
stale_session_check! do
request = ActionDispatch::Request.new(env)
sid = request.cookies[@key]
sid ||= request.params[@key] unless @cookie_only
sid
end
end
def destroy_session(env, sid, options)
ActiveSupport::Deprecation.warn "Implementing #destroy in session stores is deprecated. " <<
"Please implement destroy_session(env, session_id, options) instead."
destroy(env)
end
def current_session_id(env)
env[ENV_SESSION_OPTIONS_KEY][:id]
end
def ensure_session_key!
if @key.blank?
raise ArgumentError, 'A key is required to write a ' +
'cookie containing the session data. Use ' +
'config.session_store SESSION_STORE, { :key => ' +
'"_myapp_session" } in config/application.rb'
end
end
def stale_session_check!
yield
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
end
retry
else
raise
end
end
def exists?(env)
current_session_id(env).present?
end
def get_session(env, sid)
raise '#get_session needs to be implemented.'
end
def set_session(env, sid, session_data)
raise '#set_session needs to be implemented and should return ' <<
'the value to be stored in the cookie (usually the sid)'
end
def destroy(env)
raise '#destroy needs to be implemented.'
end
def destroy(env)
raise '#destroy needs to be implemented.'
end
end
end
end

View File

@@ -1,5 +1,7 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/object/blank'
require 'action_dispatch/middleware/session/abstract_store'
require 'rack/session/cookie'
module ActionDispatch
module Session
@@ -38,58 +40,32 @@ module ActionDispatch
# "rake secret" and set the key in config/initializers/secret_token.rb.
#
# Note that changing digest or secret invalidates all existing sessions!
class CookieStore < AbstractStore
def initialize(app, options = {})
super(app, options.merge!(:cookie_only => true))
freeze
end
class CookieStore < Rack::Session::Cookie
include Compatibility
include StaleSessionCheck
private
def load_session(env)
data = unpacked_cookie_data(env)
data = persistent_session_id!(data)
[data["session_id"], data]
end
def extract_session_id(env)
if data = unpacked_cookie_data(env)
data["session_id"]
else
nil
end
end
def unpacked_cookie_data(env)
env["action_dispatch.request.unsigned_session_cookie"] ||= begin
stale_session_check! do
request = ActionDispatch::Request.new(env)
if data = request.cookie_jar.signed[@key]
data.stringify_keys!
end
data || {}
def unpacked_cookie_data(env)
env["action_dispatch.request.unsigned_session_cookie"] ||= begin
stale_session_check! do
request = ActionDispatch::Request.new(env)
if data = request.cookie_jar.signed[@key]
data.stringify_keys!
end
data || {}
end
end
end
def set_cookie(request, options)
request.cookie_jar.signed[@key] = options
end
def set_session(env, sid, session_data, options)
persistent_session_id!(session_data, sid)
end
def set_session(env, sid, session_data)
persistent_session_id!(session_data, sid)
end
def destroy(env)
# session data is stored on client; nothing to do here
end
def persistent_session_id!(data, sid=nil)
data ||= {}
data["session_id"] ||= sid || generate_sid
data
end
def set_cookie(env, session_id, cookie)
request = ActionDispatch::Request.new(env)
request.cookie_jar.signed[@key] = cookie
end
end
end
end

View File

@@ -1,56 +1,17 @@
require 'action_dispatch/middleware/session/abstract_store'
require 'rack/session/memcache'
module ActionDispatch
module Session
class MemCacheStore < AbstractStore
class MemCacheStore < Rack::Session::Memcache
include Compatibility
include StaleSessionCheck
def initialize(app, options = {})
require 'memcache'
# Support old :expires option
options[:expire_after] ||= options[:expires]
super
@default_options = {
:namespace => 'rack:session',
:memcache_server => 'localhost:11211'
}.merge(@default_options)
@pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options)
unless @pool.servers.any? { |s| s.alive? }
raise "#{self} unable to find server during initialization."
end
@mutex = Mutex.new
super
end
private
def get_session(env, sid)
sid ||= generate_sid
begin
session = @pool.get(sid) || {}
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
session = {}
end
[sid, session]
end
def set_session(env, sid, session_data)
options = env['rack.session.options']
expiry = options[:expire_after] || 0
@pool.set(sid, session_data, expiry)
sid
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
false
end
def destroy(env)
if sid = current_session_id(env)
@pool.delete(sid)
end
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
false
end
end
end
end

View File

@@ -6,13 +6,13 @@ module ActionDispatch
@at, @root = at.chomp('/'), root.chomp('/')
@compiled_at = (Regexp.compile(/^#{Regexp.escape(at)}/) unless @at.blank?)
@compiled_root = Regexp.compile(/^#{Regexp.escape(root)}/)
@file_server = ::Rack::File.new(root)
@file_server = ::Rack::File.new(@root)
end
def match?(path)
path = path.dup
if @compiled_at.blank? || path.sub!(@compiled_at, '')
full_path = File.join(@root, ::Rack::Utils.unescape(path))
if !@compiled_at || path.sub!(@compiled_at, '')
full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path))
paths = "#{full_path}#{ext}"
matches = Dir[paths]

View File

@@ -171,13 +171,13 @@ module ActionDispatch
end
def blocks
block = @scope[:blocks] || []
if @options[:constraints].present? && !@options[:constraints].is_a?(Hash)
block = @options[:constraints]
else
block = nil
block << @options[:constraints]
end
((@scope[:blocks] || []) + [ block ]).compact
block
end
def constraints
@@ -345,11 +345,11 @@ module ActionDispatch
# Redirect any path to another path:
#
# match "/stories" => redirect("/posts")
def redirect(*args, &block)
def redirect(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
path = args.shift || block
path_proc = path.is_a?(Proc) ? path : proc { |params| params.empty? ? path : (path % params) }
path = args.shift || Proc.new
path_proc = path.is_a?(Proc) ? path : proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
status = options[:status] || 301
lambda do |env|
@@ -735,15 +735,15 @@ module ActionDispatch
resource_scope(SingletonResource.new(resources.pop, options)) do
yield if block_given?
collection_scope do
collection do
post :create
end if parent_resource.actions.include?(:create)
new_scope do
new do
get :new
end if parent_resource.actions.include?(:new)
member_scope do
member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
@@ -780,16 +780,16 @@ module ActionDispatch
resource_scope(Resource.new(resources.pop, options)) do
yield if block_given?
collection_scope do
collection do
get :index if parent_resource.actions.include?(:index)
post :create if parent_resource.actions.include?(:create)
end
new_scope do
new do
get :new
end if parent_resource.actions.include?(:new)
member_scope do
member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
@@ -813,12 +813,14 @@ module ActionDispatch
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
# route helpers.
def collection
unless @scope[:scope_level] == :resources
raise ArgumentError, "can't use collection outside resources scope"
unless resource_scope?
raise ArgumentError, "can't use collection outside resource(s) scope"
end
collection_scope do
yield
with_scope_level(:collection) do
scope(parent_resource.collection_scope) do
yield
end
end
end
@@ -838,8 +840,10 @@ module ActionDispatch
raise ArgumentError, "can't use member outside resource(s) scope"
end
member_scope do
yield
with_scope_level(:member) do
scope(parent_resource.member_scope) do
yield
end
end
end
@@ -848,8 +852,10 @@ module ActionDispatch
raise ArgumentError, "can't use new outside resource(s) scope"
end
new_scope do
yield
with_scope_level(:new) do
scope(parent_resource.new_scope(action_path(:new))) do
yield
end
end
end
@@ -1034,30 +1040,6 @@ module ActionDispatch
end
end
def new_scope
with_scope_level(:new) do
scope(parent_resource.new_scope(action_path(:new))) do
yield
end
end
end
def collection_scope
with_scope_level(:collection) do
scope(parent_resource.collection_scope) do
yield
end
end
end
def member_scope
with_scope_level(:member) do
scope(parent_resource.member_scope) do
yield
end
end
end
def nested_options
{}.tap do |options|
options[:as] = parent_resource.member_name
@@ -1130,7 +1112,7 @@ module ActionDispatch
end
candidate = name.select(&:present?).join("_").presence
candidate unless as.nil? && @set.routes.map(&:name).include?(candidate)
candidate unless as.nil? && @set.routes.find { |r| r.name == candidate }
end
end

View File

@@ -1,6 +1,7 @@
require 'rack/mount'
require 'forwardable'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/hash/slice'
module ActionDispatch
module Routing
@@ -511,7 +512,7 @@ module ActionDispatch
end
script_name = options.delete(:script_name)
path = (script_name.blank? ? _generate_prefix(options) : script_name).to_s
path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?

View File

@@ -81,14 +81,10 @@ module ActionDispatch
def normalize_argument_to_redirection(fragment)
case fragment
when %r{^\w[\w\d+.-]*:.*}
when %r{^\w[A-Za-z\d+.-]*:.*}
fragment
when String
if fragment =~ %r{^\w[\w\d+.-]*:.*}
fragment
else
@request.protocol + @request.host_with_port + fragment
end
@request.protocol + @request.host_with_port + fragment
when :back
raise RedirectBackError unless refer = @request.headers["Referer"]
refer

View File

@@ -11,9 +11,8 @@ begin
# formats are written, so you'll have two output files per test method.
class PerformanceTest < ActionDispatch::IntegrationTest
include ActiveSupport::Testing::Performance
include ActiveSupport::Testing::Default
end
end
rescue NameError
$stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks."
end
end

View File

@@ -38,11 +38,17 @@ module ActionView
autoload :Helpers
autoload :Base
autoload :LookupContext
autoload :Render
autoload :PathSet, "action_view/paths"
autoload :TestCase, "action_view/test_case"
autoload_under "renderer" do
autoload :AbstractRenderer
autoload :PartialRenderer
autoload :TemplateRenderer
end
autoload_under "render" do
autoload :Layouts
autoload :Partials
autoload :Rendering
end
@@ -51,6 +57,7 @@ module ActionView
autoload :Resolver
autoload :PathResolver
autoload :FileSystemResolver
autoload :FallbackFileSystemResolver
end
autoload_at "action_view/template/error" do

View File

@@ -155,10 +155,7 @@ module ActionView #:nodoc:
#
# See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods documentation for more details.
class Base
module Subclasses
end
include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
include Helpers, Rendering, Partials, ::ERB::Util, Context
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
@@ -177,13 +174,12 @@ module ActionView #:nodoc:
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
attr_accessor :base_path, :assigns, :template_extension, :lookup_context
attr_internal :captures, :request, :controller, :template, :config
attr_accessor :_template
attr_internal :request, :controller, :config, :assigns, :lookup_context
delegate :find_template, :template_exists?, :formats, :formats=, :locale, :locale=,
:view_paths, :view_paths=, :with_fallbacks, :update_details, :with_layout_format, :to => :lookup_context
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
delegate :logger, :to => :controller, :allow_nil => true
@@ -198,26 +194,30 @@ module ActionView #:nodoc:
end
def assign(new_assigns) # :nodoc:
self.assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
@_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
assign(assigns_for_first_render)
self.helpers = self.class.helpers || Module.new
if @_controller = controller
@_request = controller.request if controller.respond_to?(:request)
end
@_config = controller && controller.respond_to?(:config) ? controller.config.inheritable_copy : {}
self.helpers = Module.new unless self.class.helpers
@_config = {}
@_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
@output_buffer = nil
@lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
if @_controller = controller
@_request = controller.request if controller.respond_to?(:request)
@_config = controller.config.inheritable_copy if controller.respond_to?(:config)
end
@_lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
lookup_context : ActionView::LookupContext.new(lookup_context)
@lookup_context.formats = formats if formats
@_lookup_context.formats = formats if formats
end
def store_content_for(key, value)
@_content_for[key] = value
end
def controller_path

View File

@@ -152,7 +152,7 @@ module ActionView
#
# # Normally you'd calculate RELEASE_NUMBER at startup.
# RELEASE_NUMBER = 12345
# config.action_controller.asset_path_template = proc { |asset_path|
# config.action_controller.asset_path = proc { |asset_path|
# "/release-#{RELEASE_NUMBER}#{asset_path}"
# }
#
@@ -292,9 +292,6 @@ module ActionView
#
# * = The application.js file is only referenced if it exists
#
# Though it's not really recommended practice, if you need to extend the default JavaScript set for any reason
# (e.g., you're going to be using a certain .js file in every action), then take a look at the register_javascript_include_default method.
#
# You can also include all javascripts in the +javascripts+ directory using <tt>:all</tt> as the source:
#
# javascript_include_tag :all # =>

View File

@@ -791,7 +791,7 @@ module ActionView
options["incremental"] = true unless options.has_key?("incremental")
end
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("search", options)
InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options)
end
# Returns a text_field of type "tel".
@@ -1015,19 +1015,14 @@ module ActionView
module ClassMethods
def value(object, method_name)
object.send method_name unless object.nil?
object.send method_name if object
end
def value_before_type_cast(object, method_name)
unless object.nil?
if object.respond_to?(method_name)
object.send(method_name)
# FIXME: this is AR dependent
elsif object.respond_to?(method_name + "_before_type_cast")
object.send(method_name + "_before_type_cast")
else
raise NoMethodError, "Model #{object.class} does not respond to #{method_name}"
end
object.respond_to?(method_name + "_before_type_cast") ?
object.send(method_name + "_before_type_cast") :
object.send(method_name)
end
end

View File

@@ -297,7 +297,6 @@ module ActionView
def options_for_select(container, selected = nil)
return container if String === container
container = container.to_a if Hash === container
selected, disabled = extract_selected_and_disabled(selected).map do | r |
Array.wrap(r).map(&:to_s)
end

View File

@@ -546,7 +546,7 @@ module ActionView
end
def with_formats(*args)
@context ? @context.update_details(:formats => args) { yield } : yield
@context ? @context.lookup_context.update_details(:formats => args) { yield } : yield
end
def javascript_object_for(object)

View File

@@ -365,7 +365,7 @@ module ActionView
# <% end %>
def current_cycle(name = "default")
cycle = get_cycle(name)
cycle.current_value unless cycle.nil?
cycle.current_value if cycle
end
# Resets a cycle so that it starts from the first element the next time

View File

@@ -45,8 +45,8 @@ module ActionView
private
def scope_key_by_partial(key)
if key.to_s.first == "."
if @_virtual_path
@_virtual_path.gsub(%r{/_?}, ".") + key.to_s
if (path = @_template && @_template.virtual_path)
path.gsub(%r{/_?}, ".") + key.to_s
else
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
end

View File

@@ -476,39 +476,36 @@ module ActionView
html_options = html_options.stringify_keys
encode = html_options.delete("encode").to_s
cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
extras = []
extras << "cc=#{Rack::Utils.escape(cc).gsub("+", "%20")}" unless cc.nil?
extras << "bcc=#{Rack::Utils.escape(bcc).gsub("+", "%20")}" unless bcc.nil?
extras << "body=#{Rack::Utils.escape(body).gsub("+", "%20")}" unless body.nil?
extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}" unless subject.nil?
extras = %w{ cc bcc body subject }.map { |item|
option = html_options.delete(item) || next
"#{item}=#{Rack::Utils.escape(option).gsub("+", "%20")}"
}.compact
extras = extras.empty? ? '' : '?' + html_escape(extras.join('&'))
email_address_obfuscated = email_address.dup
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at")
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot")
string = ''
if encode == "javascript"
"document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".each_byte do |c|
string << sprintf("%%%x", c)
end
case encode
when "javascript"
string =
"document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".unpack('C*').map { |c|
sprintf("%%%x", c)
}.join
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
elsif encode == "hex"
email_address_encoded = ''
email_address_obfuscated.each_byte do |c|
email_address_encoded << sprintf("&#%d;", c)
end
when "hex"
email_address_encoded = email_address_obfuscated.unpack('C*').map {|c|
sprintf("&#%d;", c)
}.join
protocol = 'mailto:'
protocol.each_byte { |c| string << sprintf("&#%d;", c) }
email_address.each_byte do |c|
string = 'mailto:'.unpack('C*').map { |c|
sprintf("&#%d;", c)
}.join + email_address.unpack('C*').map { |c|
char = c.chr
string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
end
char =~ /\w/ ? sprintf("%%%x", c) : char
}.join
content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe)
else
content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)

View File

@@ -10,7 +10,7 @@ module ActionView
# this key is generated just once during the request, it speeds up all cache accesses.
class LookupContext #:nodoc:
mattr_accessor :fallbacks
@@fallbacks = [FileSystemResolver.new(""), FileSystemResolver.new("/")]
@@fallbacks = FallbackFileSystemResolver.instances
mattr_accessor :registered_details
self.registered_details = []
@@ -61,6 +61,7 @@ module ActionView
def initialize(view_paths, details = {})
@details, @details_key = { :handlers => default_handlers }, nil
@frozen_formats, @skip_default_locale = false, false
@cache = details.key?(:cache) ? details.delete(:cache) : true
self.view_paths = view_paths
self.registered_detail_setters.each do |key, setter|
@@ -77,17 +78,17 @@ module ActionView
@view_paths = ActionView::Base.process_view_paths(paths)
end
def find(name, prefix = nil, partial = false)
@view_paths.find(*args_for_lookup(name, prefix, partial))
def find(name, prefix = nil, partial = false, keys = [])
@view_paths.find(*args_for_lookup(name, prefix, partial, keys))
end
alias :find_template :find
def find_all(name, prefix = nil, partial = false)
@view_paths.find_all(*args_for_lookup(name, prefix, partial))
def find_all(name, prefix = nil, partial = false, keys = [])
@view_paths.find_all(*args_for_lookup(name, prefix, partial, keys))
end
def exists?(name, prefix = nil, partial = false)
@view_paths.exists?(*args_for_lookup(name, prefix, partial))
def exists?(name, prefix = nil, partial = false, keys = [])
@view_paths.exists?(*args_for_lookup(name, prefix, partial, keys))
end
alias :template_exists? :exists?
@@ -106,9 +107,9 @@ module ActionView
protected
def args_for_lookup(name, prefix, partial) #:nodoc:
def args_for_lookup(name, prefix, partial, keys) #:nodoc:
name, prefix = normalize_name(name, prefix)
[name, prefix, partial || false, @details, details_key]
[name, prefix, partial || false, @details, keys, details_key]
end
# Support legacy foo.erb names even though we now ignore .erb
@@ -130,10 +131,20 @@ module ActionView
end
module Details
attr_accessor :cache
# Calculate the details key. Remove the handlers from calculation to improve performance
# since the user cannot modify it explicitly.
def details_key #:nodoc:
@details_key ||= DetailsKey.get(@details)
@details_key ||= DetailsKey.get(@details) if @cache
end
# Temporary skip passing the details_key forward.
def disable_cache
old_value, @cache = @cache, false
yield
ensure
@cache = old_value
end
# Freeze the current formats in the lookup context. By freezing them, you are guaranteeing

View File

@@ -10,8 +10,8 @@ module ActionView #:nodoc:
METHOD
end
def find(path, prefix = nil, partial = false, details = {}, key = nil)
template = find_all(path, prefix, partial, details, key).first
def find(path, prefix = nil, partial = false, details = {}, keys = [], key = nil)
template = find_all(path, prefix, partial, details, keys, key).first
raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
template
end

View File

@@ -1,83 +0,0 @@
module ActionView
# = Action View Layouts
module Layouts
# Returns the contents that are yielded to a layout, given a name or a block.
#
# You can think of a layout as a method that is called with a block. If the user calls
# <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
# If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
#
# The user can override this default by passing a block to the layout:
#
# # The template
# <%= render :layout => "my_layout" do %>
# Content
# <% end %>
#
# # The layout
# <html>
# <%= yield %>
# </html>
#
# In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
# this method returns the block that was passed in to <tt>render :layout</tt>, and the response
# would be
#
# <html>
# Content
# </html>
#
# Finally, the block can take block arguments, which can be passed in by +yield+:
#
# # The template
# <%= render :layout => "my_layout" do |customer| %>
# Hello <%= customer.name %>
# <% end %>
#
# # The layout
# <html>
# <%= yield Struct.new(:name).new("David") %>
# </html>
#
# In this case, the layout would receive the block passed into <tt>render :layout</tt>,
# and the struct specified would be passed into the block as an argument. The result
# would be
#
# <html>
# Hello David
# </html>
#
def _layout_for(*args, &block) #:nodoc:
name = args.first
if name.is_a?(Symbol)
@_content_for[name].html_safe
elsif block
capture(*args, &block)
else
@_content_for[:layout].html_safe
end
end
# This is the method which actually finds the layout using details in the lookup
# context object. If no layout is found, it checks if at least a layout with
# the given name exists across all details before raising the error.
def find_layout(layout)
begin
with_layout_format do
layout =~ /^\// ?
with_fallbacks { find_template(layout) } : find_template(layout)
end
rescue ActionView::MissingTemplate => e
update_details(:formats => nil) do
raise unless template_exists?(layout)
end
end
end
# Contains the logic that actually renders the layout.
def _render_layout(layout, locals, &block) #:nodoc:
layout.render(self, locals){ |*name| _layout_for(*name, &block) }
end
end
end

View File

@@ -210,165 +210,12 @@ module ActionView
# <%- end -%>
# <% end %>
module Partials
extend ActiveSupport::Concern
class PartialRenderer
PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
def initialize(view_context, options, block)
@view = view_context
@partial_names = PARTIAL_NAMES[@view.controller.class.name]
setup(options, block)
end
def setup(options, block)
partial = options[:partial]
@options = options
@locals = options[:locals] || {}
@block = block
if String === partial
@object = options[:object]
@path = partial
@collection = collection
else
@object = partial
if @collection = collection
paths = @collection_paths = @collection.map { |o| partial_path(o) }
@path = paths.uniq.size == 1 ? paths.first : nil
else
@path = partial_path
end
end
end
def render
identifier = ((@template = find_template) ? @template.identifier : @path)
if @collection
ActiveSupport::Notifications.instrument("render_collection.action_view",
:identifier => identifier || "collection", :count => @collection.size) do
render_collection
end
else
content = ActiveSupport::Notifications.instrument("render_partial.action_view",
:identifier => identifier) do
render_partial
end
if !@block && (layout = @options[:layout])
content = @view._render_layout(find_template(layout), @locals){ content }
end
content
end
end
def render_collection
return nil if @collection.blank?
if @options.key?(:spacer_template)
spacer = find_template(@options[:spacer_template]).render(@view, @locals)
end
result = @template ? collection_with_template : collection_without_template
result.join(spacer).html_safe
end
def collection_with_template(template = @template)
segments, locals, template = [], @locals, @template
if @options[:as]
as = @options[:as]
counter = "#{as}_counter".to_sym
else
as = template.variable_name
counter = template.counter_name
end
locals[counter] = -1
@collection.each do |object|
locals[counter] += 1
locals[as] = object
segments << template.render(@view, locals)
end
segments
end
def collection_without_template(collection_paths = @collection_paths)
segments, locals = [], @locals
index, template = -1, nil
if @options[:as]
as = @options[:as]
counter = "#{as}_counter"
end
@collection.each_with_index do |object, i|
template = find_template(collection_paths[i])
locals[as || template.variable_name] = object
locals[counter || template.counter_name] = (index += 1)
segments << template.render(@view, locals)
end
@template = template
segments
end
def render_partial(object = @object)
locals, view, template = @locals, @view, @template
object ||= locals[template.variable_name]
locals[@options[:as] || template.variable_name] = object
template.render(view, locals) do |*name|
view._layout_for(*name, &@block)
end
end
private
def collection
if @object.respond_to?(:to_ary)
@object
elsif @options.key?(:collection)
@options[:collection] || []
end
end
def find_template(path=@path)
return path unless path.is_a?(String)
prefix = @view.controller_path unless path.include?(?/)
@view.find_template(path, prefix, true)
end
def partial_path(object = @object)
@partial_names[object.class.name] ||= begin
object = object.to_model if object.respond_to?(:to_model)
object.class.model_name.partial_path.dup.tap do |partial|
path = @view.controller_path
partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
end
end
end
end
def _render_partial(options, &block) #:nodoc:
if defined?(@renderer)
@renderer.setup(options, block)
else
@renderer = PartialRenderer.new(self, options, block)
end
@renderer.render
_partial_renderer.setup(options, block).render
end
def _partial_renderer #:nodoc:
@_partial_renderer ||= PartialRenderer.new(self)
end
end
end

View File

@@ -10,6 +10,7 @@ module ActionView
# * <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.
# * <tt>:once</tt> - Accepts a string or an array of strings and Rails will ensure they each of them are rendered just once.
#
# 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.
@@ -20,10 +21,10 @@ module ActionView
_render_partial(options.merge(:partial => options[:layout]), &block)
elsif options.key?(:partial)
_render_partial(options)
elsif options.key?(:once)
_render_once(options)
else
template = _determine_template(options)
lookup_context.freeze_formats(template.formats, true)
_render_template(template, options[:layout], options)
_render_template(options)
end
when :update
update_page(&block)
@@ -32,36 +33,74 @@ module ActionView
end
end
# Determine the template to be rendered using the given options.
def _determine_template(options) #:nodoc:
if options.key?(:inline)
handler = Template.handler_class_for_extension(options[:type] || "erb")
Template.new(options[:inline], "inline template", handler, {})
elsif options.key?(:text)
Template::Text.new(options[:text], formats.try(:first))
elsif options.key?(:file)
with_fallbacks { find_template(options[:file], options[:prefix]) }
elsif options.key?(:template)
options[:template].respond_to?(:render) ?
options[:template] : find_template(options[:template], options[:prefix])
# Returns the contents that are yielded to a layout, given a name or a block.
#
# You can think of a layout as a method that is called with a block. If the user calls
# <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
# If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
#
# The user can override this default by passing a block to the layout:
#
# # The template
# <%= render :layout => "my_layout" do %>
# Content
# <% end %>
#
# # The layout
# <html>
# <%= yield %>
# </html>
#
# In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
# this method returns the block that was passed in to <tt>render :layout</tt>, and the response
# would be
#
# <html>
# Content
# </html>
#
# Finally, the block can take block arguments, which can be passed in by +yield+:
#
# # The template
# <%= render :layout => "my_layout" do |customer| %>
# Hello <%= customer.name %>
# <% end %>
#
# # The layout
# <html>
# <%= yield Struct.new(:name).new("David") %>
# </html>
#
# In this case, the layout would receive the block passed into <tt>render :layout</tt>,
# and the struct specified would be passed into the block as an argument. The result
# would be
#
# <html>
# Hello David
# </html>
#
def _layout_for(*args, &block)
name = args.first
if name.is_a?(Symbol)
@_content_for[name].html_safe
elsif block
capture(*args, &block)
else
@_content_for[:layout].html_safe
end
end
# Renders the given template. An string representing the layout can be
# supplied as well.
def _render_template(template, layout = nil, options = {}) #:nodoc:
locals = options[:locals] || {}
layout = find_layout(layout) if layout
def _render_once(options) #:nodoc:
_template_renderer.render_once(options)
end
ActiveSupport::Notifications.instrument("render_template.action_view",
:identifier => template.identifier, :layout => layout.try(:virtual_path)) do
def _render_template(options) #:nodoc:
_template_renderer.render(options)
end
content = template.render(self, locals) { |*name| _layout_for(*name) }
@_content_for[:layout] = content if layout
content = _render_layout(layout, locals) if layout
content
end
def _template_renderer #:nodoc:
@_template_renderer ||= TemplateRenderer.new(self)
end
end
end

View File

@@ -0,0 +1,36 @@
module ActionView
class AbstractRenderer #:nodoc:
attr_reader :vew, :lookup_context
delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
:with_layout_format, :formats, :to => :lookup_context
def initialize(view)
@view = view
@lookup_context = view.lookup_context
end
def render
raise NotImplementedError
end
# Checks if the given path contains a format and if so, change
# the lookup context to take this new format into account.
def wrap_formats(value)
return yield unless value.is_a?(String)
@@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
if value.sub!(@@formats_regexp, "")
update_details(:formats => [$1.to_sym]){ yield }
else
yield
end
end
protected
def instrument(name, options={})
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
end
end
end

View File

@@ -0,0 +1,166 @@
require 'action_view/renderer/abstract_renderer'
module ActionView
class PartialRenderer < AbstractRenderer #:nodoc:
PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
def initialize(view)
super
@partial_names = PARTIAL_NAMES[@view.controller.class.name]
end
def setup(options, block)
partial = options[:partial]
@options = options
@locals = options[:locals] || {}
@block = block
if String === partial
@object = options[:object]
@path = partial
@collection = collection
else
@object = partial
if @collection = collection_from_object || collection
paths = @collection_data = @collection.map { |o| partial_path(o) }
@path = paths.uniq.size == 1 ? paths.first : nil
else
@path = partial_path
end
end
if @path
@variable, @variable_counter = retrieve_variable(@path)
else
paths.map! { |path| retrieve_variable(path).unshift(path) }
end
self
end
def render
wrap_formats(@path) do
identifier = ((@template = find_partial) ? @template.identifier : @path)
if @collection
instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
render_collection
end
else
instrument(:partial, :identifier => identifier) do
render_partial
end
end
end
end
def render_collection
return nil if @collection.blank?
if @options.key?(:spacer_template)
spacer = find_template(@options[:spacer_template]).render(@view, @locals)
end
result = @template ? collection_with_template : collection_without_template
result.join(spacer).html_safe
end
def render_partial
locals, view, block = @locals, @view, @block
object, as = @object, @variable
if !block && (layout = @options[:layout])
layout = find_template(layout)
end
object ||= locals[as]
locals[as] = object
content = @template.render(view, locals) do |*name|
view._layout_for(*name, &block)
end
content = layout.render(view, locals){ content } if layout
content
end
private
def collection
if @options.key?(:collection)
@options[:collection] || []
end
end
def collection_from_object
if @object.respond_to?(:to_ary)
@object
end
end
def find_partial
if path = @path
locals = @locals.keys
locals << @variable
locals << @variable_counter if @collection
find_template(path, locals)
end
end
def find_template(path=@path, locals=@locals.keys)
prefix = @view.controller_path unless path.include?(?/)
@lookup_context.find_template(path, prefix, true, locals)
end
def collection_with_template
segments, locals, template = [], @locals, @template
as, counter = @variable, @variable_counter
locals[counter] = -1
@collection.each do |object|
locals[counter] += 1
locals[as] = object
segments << template.render(@view, locals)
end
segments
end
def collection_without_template
segments, locals, collection_data = [], @locals, @collection_data
index, template, cache = -1, nil, {}
keys = @locals.keys
@collection.each_with_index do |object, i|
path, *data = collection_data[i]
template = (cache[path] ||= find_template(path, keys + data))
locals[data[0]] = object
locals[data[1]] = (index += 1)
segments << template.render(@view, locals)
end
@template = template
segments
end
def partial_path(object = @object)
@partial_names[object.class.name] ||= begin
object = object.to_model if object.respond_to?(:to_model)
object.class.model_name.partial_path.dup.tap do |partial|
path = @view.controller_path
partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
end
end
end
def retrieve_variable(path)
variable = @options[:as] || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
variable_counter = :"#{variable}_counter" if @collection
[variable, variable_counter]
end
end
end

View File

@@ -0,0 +1,97 @@
require 'set'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/array/wrap'
require 'action_view/renderer/abstract_renderer'
module ActionView
class TemplateRenderer < AbstractRenderer #:nodoc:
attr_reader :rendered
def initialize(view)
super
@rendered = Set.new
end
def render(options)
wrap_formats(options[:template] || options[:file]) do
template = determine_template(options)
render_template(template, options[:layout], options[:locals])
end
end
def render_once(options)
paths, locals = options[:once], options[:locals] || {}
layout, keys, prefix = options[:layout], locals.keys, options[:prefix]
raise "render :once expects a String or an Array to be given" unless paths
render_with_layout(layout, locals) do
contents = []
Array.wrap(paths).each do |path|
template = find_template(path, prefix, false, keys)
contents << render_template(template, nil, locals) if @rendered.add?(template)
end
contents.join("\n")
end
end
# Determine the template to be rendered using the given options.
def determine_template(options) #:nodoc:
keys = options[:locals].try(:keys) || []
if options.key?(:text)
Template::Text.new(options[:text], formats.try(:first))
elsif options.key?(:file)
with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
elsif options.key?(:inline)
handler = Template.handler_class_for_extension(options[:type] || "erb")
Template::Inline.new(options[:inline], handler, :locals => keys)
elsif options.key?(:template)
options[:template].respond_to?(:render) ?
options[:template] : find_template(options[:template], options[:prefix], false, keys)
end
end
# Renders the given template. An string representing the layout can be
# supplied as well.
def render_template(template, layout_name = nil, locals = {}) #:nodoc:
lookup_context.freeze_formats(template.formats, true)
view, locals = @view, locals || {}
render_with_layout(layout_name, locals) do |layout|
instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
template.render(view, locals) { |*name| view._layout_for(*name) }
end
end
end
def render_with_layout(path, locals) #:nodoc:
layout = path && find_layout(path, locals.keys)
content = yield(layout)
if layout
view = @view
view.store_content_for(:layout, content)
layout.render(view, locals){ |*name| view._layout_for(*name) }
else
content
end
end
# This is the method which actually finds the layout using details in the lookup
# context object. If no layout is found, it checks if at least a layout with
# the given name exists across all details before raising the error.
def find_layout(layout, keys)
begin
with_layout_format do
layout =~ /^\// ?
with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys)
end
rescue ActionView::MissingTemplate => e
update_details(:formats => nil) do
raise unless template_exists?(layout)
end
end
end
end
end

View File

@@ -93,14 +93,18 @@ module ActionView
autoload :Error
autoload :Handler
autoload :Handlers
autoload :Inline
autoload :Text
end
extend Template::Handlers
attr_reader :source, :identifier, :handler, :virtual_path, :formats,
:original_encoding
attr_accessor :locals, :formats, :virtual_path
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
# This finalizer is needed (and exactly with a proc inside another proc)
# otherwise templates leak in development.
Finalizer = proc do |method_name, mod|
proc do
mod.module_eval do
@@ -110,49 +114,83 @@ module ActionView
end
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
@original_encoding = nil
@method_names = {}
format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
format = details[:format] || :html
@formats = Array.wrap(format).map(&:to_sym)
@virtual_path = details[:virtual_path].try(:sub, ".#{format}", "")
@source = source
@identifier = identifier
@handler = handler
@compiled = false
@original_encoding = nil
@locals = details[:locals] || []
@virtual_path = details[:virtual_path]
@updated_at = details[:updated_at] || Time.now
@formats = Array.wrap(format).map(&:to_sym)
end
# Render a template. If the template was not compiled yet, it is done
# exactly before rendering.
#
# This method is instrumented as "!render_template.action_view". Notice that
# we use a bang in this instrumentation because you don't want to
# consume this in production. This is only slow if it's being listened to.
def render(view, locals, &block)
# Notice that we use a bang in this instrumentation because you don't want to
# consume this in production. This is only slow if it's being listened to.
old_template, view._template = view._template, self
ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
if view.is_a?(ActionView::CompiledTemplates)
mod = ActionView::CompiledTemplates
else
mod = view.singleton_class
end
method_name = compile(locals, view, mod)
compile!(view)
view.send(method_name, locals, &block)
end
rescue Exception => e
if e.is_a?(Template::Error)
e.sub_template_of(self)
raise e
else
raise Template::Error.new(self, view.respond_to?(:assigns) ? view.assigns : {}, e)
end
handle_render_error(view, e)
ensure
view._template = old_template
end
def mime_type
@mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
end
def variable_name
@variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
# Receives a view object and return a template similar to self by using @virtual_path.
#
# This method is useful if you have a template object but it does not contain its source
# anymore since it was already compiled. In such cases, all you need to do is to call
# refresh passing in the view object.
#
# Notice this method raises an error if the template to be refreshed does not have a
# virtual path set (true just for inline templates).
def refresh(view)
raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
lookup = view.lookup_context
pieces = @virtual_path.split("/")
name = pieces.pop
partial = !!name.sub!(/^_/, "")
lookup.disable_cache do
lookup.find_template(name, pieces.join, partial, @locals)
end
end
def counter_name
@counter_name ||= "#{variable_name}_counter".to_sym
# Expires this template by setting his updated_at date to Jan 1st, 1970.
def expire!
@updated_at = Time.utc(1970)
end
# Receives a view context and renders a template exactly like self by using
# the @virtual_path. It raises an error if no @virtual_path was given.
def rerender(view)
raise "A template needs to have a virtual path in order to be rerendered" unless @virtual_path
name = @virtual_path.dup
if name.sub!(/(^|\/)_([^\/]*)$/, '\1\2')
view.render :partial => name
else
view.render :template => @virtual_path
end
end
def hash
identifier.hash
end
def eql?(other)
other.is_a?(Template) && other.identifier == identifier
end
def inspect
@@ -164,7 +202,27 @@ module ActionView
end
end
private
protected
# Compile a template. This method ensures a template is compiled
# just once and removes the source after it is compiled.
def compile!(view) #:nodoc:
return if @compiled
if view.is_a?(ActionView::CompiledTemplates)
mod = ActionView::CompiledTemplates
else
mod = view.singleton_class
end
compile(view, mod)
# Just discard the source if we have a virtual path. This
# means we can get the template back.
@source = nil if @virtual_path
@compiled = true
end
# Among other things, this method is responsible for properly setting
# the encoding of the source. Until this point, we assume that the
# source is BINARY data. If no additional information is supplied,
@@ -185,11 +243,8 @@ module ActionView
# encode the source into Encoding.default_internal. In general,
# this means that templates will be UTF-8 inside of Rails,
# regardless of the original source encoding.
def compile(locals, view, mod)
method_name = build_method_name(locals)
return method_name if view.respond_to?(method_name)
locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
def compile(view, mod) #:nodoc:
method_name = self.method_name
if source.encoding_aware?
# Look for # encoding: *. If we find one, we'll encode the
@@ -229,9 +284,9 @@ module ActionView
# encoding of the code
source = <<-end_src
def #{method_name}(local_assigns)
_old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
_old_output_buffer = @output_buffer;#{locals_code};#{code}
ensure
@_virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
@output_buffer = _old_output_buffer
end
end_src
@@ -254,8 +309,6 @@ module ActionView
begin
mod.module_eval(source, identifier, 0)
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
method_name
rescue Exception => e # errors from template code
if logger = (view && view.logger)
logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
@@ -267,12 +320,27 @@ module ActionView
end
end
def build_method_name(locals)
@method_names[locals.keys.hash] ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
def handle_render_error(view, e) #:nodoc:
if e.is_a?(Template::Error)
e.sub_template_of(self)
raise e
else
assigns = view.respond_to?(:assigns) ? view.assigns : {}
template = @virtual_path ? refresh(view) : self
raise Template::Error.new(template, assigns, e)
end
end
def identifier_method_name
@identifier_method_name ||= inspect.gsub(/[^a-z_]/, '_')
def locals_code #:nodoc:
@locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
end
def method_name #:nodoc:
@method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
end
def identifier_method_name #:nodoc:
inspect.gsub(/[^a-z_]/, '_')
end
end
end

View File

@@ -43,8 +43,9 @@ module ActionView
end
class Template
# The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a
# bunch of intimate details and uses it to report a very precise exception message.
# The Template::Error exception is raised when the compilation or rendering of the template
# fails. This exception then gathers a bunch of intimate details and uses it to report a
# precise exception message.
class Error < ActionViewError #:nodoc:
SOURCE_CODE_RADIUS = 3

View File

@@ -1,4 +1,4 @@
require "action_dispatch/http/mime_type"
require 'action_dispatch/http/mime_type'
require 'active_support/core_ext/class/attribute'
# Legacy TemplateHandler stub
@@ -7,6 +7,8 @@ module ActionView
module Handlers #:nodoc:
module Compilable
def self.included(base)
ActiveSupport::Deprecation.warn "Including Compilable in your template handler is deprecated. " <<
"All the API your template handler needs to implement is to respond to #call."
base.extend(ClassMethods)
end
@@ -26,6 +28,12 @@ module ActionView
class_attribute :default_format
self.default_format = Mime::HTML
def self.inherited(base)
ActiveSupport::Deprecation.warn "Inheriting from ActionView::Template::Handler is deprecated. " <<
"All the API your template handler needs to implement is to respond to #call."
super
end
def self.call(template)
raise "Need to implement #{self.class.name}#call(template)"
end

View File

@@ -7,13 +7,9 @@ module ActionView #:nodoc:
autoload :Builder, 'action_view/template/handlers/builder'
def self.extended(base)
base.register_default_template_handler :erb, ERB
base.register_template_handler :rjs, RJS
base.register_template_handler :builder, Builder
# TODO: Depreciate old template extensions
base.register_template_handler :rhtml, ERB
base.register_template_handler :rxml, Builder
base.register_default_template_handler :erb, ERB.new
base.register_template_handler :rjs, RJS.new
base.register_template_handler :builder, Builder.new
end
@@template_handlers = {}

View File

@@ -1,11 +1,11 @@
module ActionView
module Template::Handlers
class Builder < Template::Handler
include Compilable
class Builder
# Default format used by Builder.
class_attribute :default_format
self.default_format = Mime::XML
def compile(template)
def call(template)
require 'builder'
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
"self.output_buffer = xml.target!;" +

View File

@@ -1,6 +1,7 @@
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/string/output_safety'
require "action_view/template"
require 'action_view/template'
require 'action_view/template/handler'
require 'erubis'
module ActionView
@@ -47,28 +48,31 @@ module ActionView
end
end
class ERB < Handler
include Compilable
##
# :singleton-method:
class ERB
# Specify trim mode for the ERB compiler. Defaults to '-'.
# See ERb documentation for suitable values.
cattr_accessor :erb_trim_mode
class_attribute :erb_trim_mode
self.erb_trim_mode = '-'
# Default format used by ERB.
class_attribute :default_format
self.default_format = Mime::HTML
cattr_accessor :erb_implementation
# Default implemenation used.
class_attribute :erb_implementation
self.erb_implementation = Erubis
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
def self.handles_encoding?
def self.call(template)
new.call(template)
end
def handles_encoding?
true
end
def compile(template)
def call(template)
if template.source.encoding_aware?
# First, convert to BINARY, so in case the encoding is
# wrong, we can still find an encoding tag
@@ -94,6 +98,7 @@ module ActionView
end
private
def valid_encoding(string, encoding)
# If a magic encoding comment was found, tag the
# String with this encoding. This is for a case

View File

@@ -1,17 +1,13 @@
module ActionView
module Template::Handlers
class RJS < Template::Handler
include Compilable
class RJS
# Default format used by RJS.
class_attribute :default_format
self.default_format = Mime::JS
def compile(template)
def call(template)
"update_page do |page|;#{template.source}\nend"
end
def default_format
Mime::JS
end
end
end
end

View File

@@ -0,0 +1,20 @@
require 'digest/md5'
module ActionView
class Template
class Inline < ::ActionView::Template
def initialize(source, handler, options={})
super(source, "inline template", handler, options)
end
def md5_source
@md5_source ||= Digest::MD5.hexdigest(source)
end
def eql?(other)
other.is_a?(Inline) && other.md5_source == md5_source
end
end
end
end

View File

@@ -6,9 +6,8 @@ module ActionView
# = Action View Resolver
class Resolver
def initialize
@path = nil
@cached = Hash.new { |h1,k1| h1[k1] =
Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
@cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
end
def clear_cache
@@ -16,8 +15,8 @@ module ActionView
end
# Normalizes the arguments and passes it on to find_template.
def find_all(name, prefix=nil, partial=false, details={}, key=nil)
cached(key, prefix, name, partial) do
def find_all(name, prefix=nil, partial=false, details={}, locals=[], key=nil)
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details)
end
end
@@ -35,34 +34,73 @@ module ActionView
raise NotImplementedError
end
def cached(key, prefix, name, partial)
return yield unless key && caching?
@cached[key][prefix][name][partial] ||= yield
# Helpers that builds a path. Useful for building virtual paths.
def build_path(name, prefix, partial)
path = ""
path << "#{prefix}/" unless prefix.empty?
path << (partial ? "_#{name}" : name)
path
end
# Hnadles templates caching. If a key is given and caching is on
# always check the cache before hitting the resolver. Otherwise,
# it always hits the resolver but check if the resolver is fresher
# before returning it.
def cached(key, path_info, details, locals) #:nodoc:
name, prefix, partial = path_info
locals = sort_locals(locals)
if key && caching?
@cached[key][name][prefix][partial][locals] ||= decorate(yield, path_info, details, locals)
else
fresh = decorate(yield, path_info, details, locals)
return fresh unless key
scope = @cached[key][name][prefix][partial]
cache = scope[locals]
mtime = cache && cache.map(&:updated_at).max
if !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime }
scope[locals] = fresh
else
cache
end
end
end
# Ensures all the resolver information is set in the template.
def decorate(templates, path_info, details, locals) #:nodoc:
cached = nil
templates.each do |t|
t.locals = locals
t.formats = details[:formats] || [:html] if t.formats.empty?
t.virtual_path ||= (cached ||= build_path(*path_info))
end
end
if :symbol.respond_to?("<=>")
def sort_locals(locals) #:nodoc:
locals.sort.freeze
end
else
def sort_locals(locals) #:nodoc:
locals = locals.map{ |l| l.to_s }
locals.sort!
locals.freeze
end
end
end
class PathResolver < Resolver
EXTENSION_ORDER = [:locale, :formats, :handlers]
def to_s
@path.to_s
end
alias :to_path :to_s
private
private
def find_templates(name, prefix, partial, details)
path = build_path(name, prefix, partial, details)
path = build_path(name, prefix, partial)
query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats])
end
def build_path(name, prefix, partial, details)
path = ""
path << "#{prefix}/" unless prefix.empty?
path << (partial ? "_#{name}" : name)
path
end
def query(path, exts, formats)
query = File.join(@path, path)
@@ -76,26 +114,28 @@ module ActionView
contents = File.open(p, "rb") {|io| io.read }
Template.new(contents, File.expand_path(p), handler,
:virtual_path => path, :format => format)
:virtual_path => path, :format => format, :updated_at => mtime(p))
end
end
# Returns the file mtime from the filesystem.
def mtime(p)
File.stat(p).mtime
end
# Extract handler and formats from path. If a format cannot be a found neither
# from the path, or the handler, we should return the array of formats given
# to the resolver.
def extract_handler_and_format(path, default_formats)
pieces = File.basename(path).split(".")
pieces.shift
handler = Template.handler_class_for_extension(pieces.pop)
format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
format ||= handler.default_format if handler.respond_to?(:default_format)
format ||= default_formats
handler = Template.handler_class_for_extension(pieces.pop)
format = pieces.last && Mime[pieces.last]
[handler, format]
end
end
# A resolver that loads files from the filesystem.
class FileSystemResolver < PathResolver
def initialize(path)
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
@@ -103,9 +143,26 @@ module ActionView
@path = File.expand_path(path)
end
def to_s
@path.to_s
end
alias :to_path :to_s
def eql?(resolver)
self.class.equal?(resolver.class) && to_path == resolver.to_path
end
alias :== :eql?
end
# The same as FileSystemResolver but does not allow templates to store
# a virtual path since it is invalid for such resolvers.
class FallbackFileSystemResolver < FileSystemResolver
def self.instances
[new(""), new("/")]
end
def decorate(*)
super.each { |t| t.virtual_path = nil }
end
end
end

View File

@@ -25,10 +25,6 @@ module ActionView #:nodoc:
def formats
[@mime_type.to_sym]
end
def partial?
false
end
end
end
end

View File

@@ -103,7 +103,7 @@ module ActionView
end
def render(options = {}, local_assigns = {}, &block)
view.assign(_assigns)
view.assign(view_assigns)
@rendered << output = view.render(options, local_assigns, &block)
output
end
@@ -169,15 +169,19 @@ module ActionView
alias_method :_view, :view
EXCLUDE_IVARS = %w{
INTERNAL_IVARS = %w{
@__name__
@_assertion_wrapped
@_assertions
@_result
@_routes
@controller
@layouts
@locals
@method_name
@output_buffer
@partials
@passed
@rendered
@request
@routes
@@ -187,12 +191,24 @@ module ActionView
@view_context_class
}
def _instance_variables
instance_variables.map(&:to_s) - EXCLUDE_IVARS
def _user_defined_ivars
instance_variables.map(&:to_s) - INTERNAL_IVARS
end
# Returns a Hash of instance variables and their values, as defined by
# the user in the test case, which are then assigned to the view being
# rendered. This is generally intended for internal use and extension
# frameworks.
def view_assigns
Hash[_user_defined_ivars.map do |var|
[var[1, var.length].to_sym, instance_variable_get(var)]
end]
end
def _assigns
_instance_variables.map { |var| [var[1..-1].to_sym, instance_variable_get(var)] }
ActiveSupport::Deprecation.warn "ActionView::TestCase#_assigns is deprecated and will be removed in future versions. " <<
"Please use view_assigns instead."
view_assigns
end
def _routes

View File

@@ -13,26 +13,29 @@ module ActionView #:nodoc:
@hash = hash
end
private
private
def query(path, exts, formats)
query = Regexp.escape(path)
query = ""
exts.each do |ext|
query << '(' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
end
query = /^(#{Regexp.escape(path)})#{query}$/
templates = []
@hash.select { |k,v| k =~ /^#{query}$/ }.each do |_path, source|
@hash.each do |_path, array|
source, updated_at = array
next unless _path =~ query
handler, format = extract_handler_and_format(_path, formats)
templates << Template.new(source, _path, handler,
:virtual_path => _path, :format => format)
:virtual_path => $1, :format => format, :updated_at => updated_at)
end
templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
end
end
class NullResolver < ActionView::PathResolver
class NullResolver < PathResolver
def query(path, exts, formats)
handler, format = extract_handler_and_format(path, formats)
[ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format)]

View File

@@ -308,3 +308,38 @@ module ActionView
end
end
end
class Workshop
extend ActiveModel::Naming
include ActiveModel::Conversion
attr_accessor :id
def initialize(id)
@id = id
end
def persisted?
id.present?
end
def to_s
id.to_s
end
end
module ActionDispatch
class ShowExceptions
private
remove_method :public_path
def public_path
"#{FIXTURE_LOAD_PATH}/public"
end
remove_method :logger
# Silence logger
def logger
nil
end
end
end

View File

@@ -32,6 +32,8 @@ class ActionPackAssertionsController < ActionController::Base
def redirect_to_path() redirect_to '/some/path' end
def redirect_invalid_external_route() redirect_to 'ht_tp://www.rubyonrails.org' end
def redirect_to_named_route() redirect_to route_one_url end
def redirect_external() redirect_to "http://www.rubyonrails.org"; end
@@ -368,6 +370,11 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
end
def test_redirect_invalid_external_route
process :redirect_invalid_external_route
assert_redirected_to "http://test.hostht_tp://www.rubyonrails.org"
end
def test_redirected_to_url_full_url
process :redirect_to_path
assert_redirected_to 'http://test.host/some/path'

View File

@@ -25,6 +25,10 @@ class CaptureController < ActionController::Base
render :layout => "talk_from_action"
end
def proper_block_detection
@todo = "some todo"
end
def rescue_action(e) raise end
end
@@ -66,8 +70,8 @@ class CaptureTest < ActionController::TestCase
end
def test_proper_block_detection
@todo = "some todo"
get :proper_block_detection
assert_equal "some todo", @response.body
end
private

View File

@@ -29,18 +29,18 @@ class OldContentTypeController < ActionController::Base
render :text => "hello world!"
end
def render_default_for_rhtml
def render_default_for_erb
end
def render_default_for_rxml
def render_default_for_builder
end
def render_default_for_rjs
end
def render_change_for_rxml
def render_change_for_builder
response.content_type = Mime::HTML
render :action => "render_default_for_rxml"
render :action => "render_default_for_builder"
end
def render_default_content_types_for_respond_to
@@ -108,23 +108,23 @@ class ContentTypeTest < ActionController::TestCase
assert_equal "utf-8", @response.charset, @response.headers.inspect
end
def test_nil_default_for_rhtml
def test_nil_default_for_erb
OldContentTypeController.default_charset = nil
get :render_default_for_rhtml
get :render_default_for_erb
assert_equal Mime::HTML, @response.content_type
assert_nil @response.charset, @response.headers.inspect
ensure
OldContentTypeController.default_charset = "utf-8"
end
def test_default_for_rhtml
get :render_default_for_rhtml
def test_default_for_erb
get :render_default_for_erb
assert_equal Mime::HTML, @response.content_type
assert_equal "utf-8", @response.charset
end
def test_default_for_rxml
get :render_default_for_rxml
def test_default_for_builder
get :render_default_for_builder
assert_equal Mime::XML, @response.content_type
assert_equal "utf-8", @response.charset
end
@@ -135,8 +135,8 @@ class ContentTypeTest < ActionController::TestCase
assert_equal "utf-8", @response.charset
end
def test_change_for_rxml
get :render_change_for_rxml
def test_change_for_builder
get :render_change_for_builder
assert_equal Mime::HTML, @response.content_type
assert_equal "utf-8", @response.charset
end

View File

@@ -314,6 +314,7 @@ class FilterTest < ActionController::TestCase
def initialize
@@execution_log = ""
super()
end
before_filter { |c| c.class.execution_log << " before procfilter " }
@@ -757,12 +758,12 @@ class ControllerWithSymbolAsFilter < PostsController
def without_exception
# Do stuff...
1 + 1
wtf = 1 + 1
yield
# Do stuff...
1 + 1
wtf += 1
end
end

View File

@@ -167,7 +167,7 @@ end
class IntegrationTestTest < Test::Unit::TestCase
def setup
@test = ::ActionDispatch::IntegrationTest.new(:default_test)
@test = ::ActionDispatch::IntegrationTest.new(:app)
@test.class.stubs(:fixture_table_names).returns([])
@session = @test.open_session
end

View File

@@ -46,13 +46,13 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_application_layout_is_default_when_no_controller_match
@controller = ProductController.new
get :hello
assert_equal 'layout_test.rhtml hello.rhtml', @response.body
assert_equal 'layout_test.erb hello.erb', @response.body
end
def test_controller_name_layout_name_match
@controller = ItemController.new
get :hello
assert_equal 'item.rhtml hello.rhtml', @response.body
assert_equal 'item.erb hello.erb', @response.body
end
def test_third_party_template_library_auto_discovers_layout
@@ -65,13 +65,13 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_namespaced_controllers_auto_detect_layouts1
@controller = ControllerNameSpace::NestedController.new
get :hello
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
assert_equal 'controller_name_space/nested.erb hello.erb', @response.body
end
def test_namespaced_controllers_auto_detect_layouts2
@controller = MultipleExtensions.new
get :hello
assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
assert_equal 'multiple_extensions.html.erb hello.erb', @response.body.strip
end
end
@@ -79,7 +79,7 @@ class DefaultLayoutController < LayoutTest
end
class AbsolutePathLayoutController < LayoutTest
layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.rhtml')
layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.erb')
end
class HasOwnLayoutController < LayoutTest
@@ -137,7 +137,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
end
def test_layout_except_exception_when_included
@@ -149,7 +149,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
end
def test_layout_set_when_using_render
@@ -173,7 +173,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_absolute_pathed_layout
@controller = AbsolutePathLayoutController.new
get :hello
assert_equal "layout_test.rhtml hello.rhtml", @response.body.strip
assert_equal "layout_test.erb hello.erb", @response.body.strip
end
end
@@ -184,7 +184,7 @@ class RenderWithTemplateOptionController < LayoutTest
end
class SetsNonExistentLayoutFile < LayoutTest
layout "nofile.rhtml"
layout "nofile.erb"
end
class LayoutExceptionRaised < ActionController::TestCase

View File

@@ -23,7 +23,7 @@ module Another
def with_fragment_cache
render :inline => "<%= cache('foo'){ 'bar' } %>"
end
def with_fragment_cache_and_percent_in_key
render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>"
end
@@ -151,8 +151,8 @@ class ACLogSubscriberTest < ActionController::TestCase
wait
assert_equal 4, logs.size
assert_match /Exist fragment\? views\/foo%bar/, logs[1]
assert_match /Write fragment views\/foo%bar/, logs[2]
assert_match(/Exist fragment\? views\/foo%bar/, logs[1])
assert_match(/Write fragment views\/foo%bar/, logs[2])
ensure
@controller.config.perform_caching = true
end

View File

@@ -24,4 +24,19 @@ module BareMetalTest
assert_equal "Hello world", string
end
end
class HeadController < ActionController::Metal
include ActionController::Head
def index
head :not_found
end
end
class HeadTest < ActiveSupport::TestCase
test "head works on its own" do
status, headers, body = HeadController.action(:index).call(Rack::MockRequest.env_for("/"))
assert_equal 404, status
end
end
end

View File

@@ -1,46 +0,0 @@
require 'abstract_unit'
module Etags
class BasicController < ActionController::Base
self.view_paths = [ActionView::FixtureResolver.new(
"etags/basic/base.html.erb" => "Hello from without_layout.html.erb",
"layouts/etags.html.erb" => "teh <%= yield %> tagz"
)]
def without_layout
render :action => "base"
end
def with_layout
render :action => "base", :layout => "etags"
end
end
class EtagTest < Rack::TestCase
describe "Rendering without any special etag options returns an etag that is an MD5 hash of its text"
test "an action without a layout" do
get "/etags/basic/without_layout"
body = "Hello from without_layout.html.erb"
assert_body body
assert_header "Etag", etag_for(body)
assert_status 200
end
test "an action with a layout" do
get "/etags/basic/with_layout"
body = "teh Hello from without_layout.html.erb tagz"
assert_body body
assert_header "Etag", etag_for(body)
assert_status 200
end
private
def etag_for(text)
%("#{Digest::MD5.hexdigest(text)}")
end
end
end

View File

@@ -0,0 +1,72 @@
require 'abstract_unit'
module RenderTemplate
class RenderOnceController < ActionController::Base
layout false
RESOLVER = ActionView::FixtureResolver.new(
"test/a.html.erb" => "a",
"test/b.html.erb" => "<>",
"test/c.html.erb" => "c",
"test/one.html.erb" => "<%= render :once => 'test/result' %>",
"test/two.html.erb" => "<%= render :once => 'test/result' %>",
"test/three.html.erb" => "<%= render :once => 'test/result' %>",
"test/result.html.erb" => "YES!",
"layouts/test.html.erb" => "l<%= yield %>l"
)
self.view_paths = [RESOLVER]
def multiple
render :once => %w(test/a test/b test/c)
end
def once
render :once => %w(test/one test/two test/three)
end
def duplicate
render :once => %w(test/a test/a test/a)
end
def with_layout
render :once => %w(test/a test/b test/c), :layout => "test"
end
end
module Tests
def test_mutliple_arguments_get_all_rendered
get :multiple
assert_response "a\n<>\nc"
end
def test_referenced_templates_get_rendered_once
get :once
assert_response "YES!\n\n"
end
def test_duplicated_templates_get_rendered_once
get :duplicate
assert_response "a"
end
def test_layout_wraps_all_rendered_templates
get :with_layout
assert_response "la\n<>\ncl"
end
end
class TestWithResolverCache < Rack::TestCase
testing RenderTemplate::RenderOnceController
include Tests
end
class TestWithoutResolverCache < Rack::TestCase
testing RenderTemplate::RenderOnceController
include Tests
def setup
RenderTemplate::RenderOnceController::RESOLVER.stubs(:caching?).returns(false)
end
end
end

View File

@@ -5,10 +5,17 @@ module RenderPartial
class BasicController < ActionController::Base
self.view_paths = [ActionView::FixtureResolver.new(
"render_partial/basic/_basic.html.erb" => "BasicPartial!",
"render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>"
"render_partial/basic/_basic.html.erb" => "BasicPartial!",
"render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>",
"render_partial/basic/with_json.html.erb" => "<%= render 'with_json.json' %>",
"render_partial/basic/_with_json.json.erb" => "<%= render 'final' %>",
"render_partial/basic/_final.json.erb" => "{ final: json }"
)]
def html_with_json_inside_json
render :action => "with_json"
end
def changing
@test_unchanged = 'hello'
render :action => "basic"
@@ -22,6 +29,12 @@ module RenderPartial
get :changing
assert_response("goodbyeBasicPartial!goodbye")
end
test "rendering a template with renders another partial with other format that renders other partial in the same format" do
get :html_with_json_inside_json
assert_content_type "text/html; charset=utf-8"
assert_response "{ final: json }"
end
end
end

View File

@@ -8,13 +8,21 @@ module RenderTemplate
"shared.html.erb" => "Elastica",
"locals.html.erb" => "The secret is <%= secret %>",
"xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend",
"with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>"
"with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>",
"test/with_json.html.erb" => "<%= render :template => 'test/with_json.json' %>",
"test/with_json.json.erb" => "<%= render :template => 'test/final' %>",
"test/final.json.erb" => "{ final: json }",
"test/with_error.html.erb" => "<%= idontexist %>"
)]
def index
render :template => "test/basic"
end
def html_with_json_inside_json
render :template => "test/with_json"
end
def index_without_key
render "test/basic"
end
@@ -42,6 +50,10 @@ module RenderTemplate
def with_raw
render :template => "with_raw"
end
def with_error
render :template => "test/with_error"
end
end
class TestWithoutLayout < Rack::TestCase
@@ -88,6 +100,18 @@ module RenderTemplate
assert_body "Hello <strong>this is raw</strong>"
assert_status 200
end
test "rendering a template with renders another template with other format that renders other template in the same format" do
get :html_with_json_inside_json
assert_content_type "text/html; charset=utf-8"
assert_response "{ final: json }"
end
test "rendering a template with error properly exceprts the code" do
get :with_error
assert_status 500
assert_match "undefined local variable or method `idontexist'", response.body
end
end
class WithLayoutController < ::ApplicationController

View File

@@ -1,30 +1,5 @@
require 'abstract_unit'
class Comment
extend ActiveModel::Naming
include ActiveModel::Conversion
attr_reader :id
def to_key; id ? [id] : nil end
def save; @id = 1 end
def new_record?; @id.nil? end
def name
@id.nil? ? 'new comment' : "comment ##{@id}"
end
end
class Sheep
extend ActiveModel::Naming
include ActiveModel::Conversion
attr_reader :id
def to_key; id ? [id] : nil end
def save; @id = 1 end
def new_record?; @id.nil? end
def name
@id.nil? ? 'new sheep' : "sheep ##{@id}"
end
end
require 'controller/fake_models'
class RecordIdentifierTest < Test::Unit::TestCase
include ActionController::RecordIdentifier

View File

@@ -3,24 +3,6 @@ require 'abstract_unit'
class WorkshopsController < ActionController::Base
end
class Workshop
extend ActiveModel::Naming
include ActiveModel::Conversion
attr_accessor :id
def initialize(id)
@id = id
end
def persisted?
id.present?
end
def to_s
id.to_s
end
end
class RedirectController < ActionController::Base
def simple_redirect
redirect_to :action => "hello_world"

View File

@@ -99,11 +99,6 @@ class TestController < ActionController::Base
render :template => "test/hello_world"
end
def render_hello_world_with_etag_set
response.etag = "hello_world"
render :template => "test/hello_world"
end
# :ported: compatibility
def render_hello_world_with_forward_slash
render :template => "/test/hello_world"
@@ -1386,119 +1381,6 @@ class ExpiresInRenderTest < ActionController::TestCase
end
end
class EtagRenderTest < ActionController::TestCase
tests TestController
def setup
super
@request.host = "www.nextangle.com"
@expected_bang_etag = etag_for(expand_key([:foo, 123]))
end
def test_render_blank_body_shouldnt_set_etag
get :blank_response
assert !@response.etag?
end
def test_render_200_should_set_etag
get :render_hello_world_from_variable
assert_equal etag_for("hello david"), @response.headers['ETag']
assert_equal "max-age=0, private, must-revalidate", @response.headers['Cache-Control']
end
def test_render_against_etag_request_should_304_when_match
@request.if_none_match = etag_for("hello david")
get :render_hello_world_from_variable
assert_equal 304, @response.status.to_i
assert @response.body.empty?
end
def test_render_against_etag_request_should_have_no_content_length_when_match
@request.if_none_match = etag_for("hello david")
get :render_hello_world_from_variable
assert !@response.headers.has_key?("Content-Length")
end
def test_render_against_etag_request_should_200_when_no_match
@request.if_none_match = etag_for("hello somewhere else")
get :render_hello_world_from_variable
assert_equal 200, @response.status.to_i
assert !@response.body.empty?
end
def test_render_should_not_set_etag_when_last_modified_has_been_specified
get :render_hello_world_with_last_modified_set
assert_equal 200, @response.status.to_i
assert_not_nil @response.last_modified
assert_nil @response.etag
assert_present @response.body
end
def test_render_with_etag
get :render_hello_world_from_variable
expected_etag = etag_for('hello david')
assert_equal expected_etag, @response.headers['ETag']
@response = ActionController::TestResponse.new
@request.if_none_match = expected_etag
get :render_hello_world_from_variable
assert_equal 304, @response.status.to_i
@response = ActionController::TestResponse.new
@request.if_none_match = "\"diftag\""
get :render_hello_world_from_variable
assert_equal 200, @response.status.to_i
end
def render_with_404_shouldnt_have_etag
get :render_custom_code
assert_nil @response.headers['ETag']
end
def test_etag_should_not_be_changed_when_already_set
get :render_hello_world_with_etag_set
assert_equal etag_for("hello_world"), @response.headers['ETag']
end
def test_etag_should_govern_renders_with_layouts_too
get :builder_layout_test
assert_equal "<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body
assert_equal etag_for("<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n"), @response.headers['ETag']
end
def test_etag_with_bang_should_set_etag
get :conditional_hello_with_bangs
assert_equal @expected_bang_etag, @response.headers["ETag"]
assert_response :success
end
def test_etag_with_bang_should_obey_if_none_match
@request.if_none_match = @expected_bang_etag
get :conditional_hello_with_bangs
assert_response :not_modified
end
def test_etag_with_public_true_should_set_header
get :conditional_hello_with_public_header
assert_equal "public", @response.headers['Cache-Control']
end
def test_etag_with_public_true_should_set_header_and_retain_other_headers
get :conditional_hello_with_public_header_and_expires_at
assert_equal "max-age=60, public", @response.headers['Cache-Control']
end
protected
def etag_for(text)
%("#{Digest::MD5.hexdigest(text)}")
end
def expand_key(args)
ActiveSupport::Cache.expand_cache_key(args)
end
end
class LastModifiedRenderTest < ActionController::TestCase
tests TestController

View File

@@ -1,21 +1,5 @@
require 'abstract_unit'
module ActionDispatch
class ShowExceptions
private
remove_method :public_path
def public_path
"#{FIXTURE_LOAD_PATH}/public"
end
remove_method :logger
# Silence logger
def logger
nil
end
end
end
class RescueController < ActionController::Base
class NotAuthorized < StandardError
end

View File

@@ -1,8 +1,23 @@
require 'abstract_unit'
require 'rack/test'
module TestGenerationPrefix
class Post
extend ActiveModel::Naming
def to_param
"1"
end
def self.model_name
klass = "Post"
def klass.name; self end
ActiveModel::Name.new(klass)
end
end
class WithMountedEngine < ActionDispatch::IntegrationTest
require 'rack/test'
include Rack::Test::Methods
class BlogEngine
@@ -55,21 +70,6 @@ module TestGenerationPrefix
# force draw
RailsApplication.routes
class Post
extend ActiveModel::Naming
def to_param
"1"
end
def self.model_name
klass = "Post"
def klass.name; self end
ActiveModel::Name.new(klass)
end
end
class ::InsideEngineGeneratingController < ActionController::Base
include BlogEngine.routes.url_helpers
include RailsApplication.routes.mounted_helpers
@@ -253,4 +253,65 @@ module TestGenerationPrefix
assert_equal "http://www.example.com/awesome/blog/posts/1", path
end
end
class EngineMountedAtRoot < ActionDispatch::IntegrationTest
include Rack::Test::Methods
class BlogEngine
def self.routes
@routes ||= begin
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
match "/posts/:id", :to => "posts#show", :as => :post
end
routes
end
end
def self.call(env)
env['action_dispatch.routes'] = routes
routes.call(env)
end
end
class RailsApplication
def self.routes
@routes ||= begin
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
mount BlogEngine => "/"
end
routes
end
end
def self.call(env)
env['action_dispatch.routes'] = routes
routes.call(env)
end
end
# force draw
RailsApplication.routes
class ::PostsController < ActionController::Base
include BlogEngine.routes.url_helpers
include RailsApplication.routes.mounted_helpers
def show
render :text => post_path(:id => params[:id])
end
end
def app
RailsApplication
end
test "generating path inside engine" do
get "/posts/1"
assert_equal "/posts/1", last_response.body
end
end
end

View File

@@ -36,7 +36,6 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
assert_equal 'bar', params['foo']
file = params['file']
assert_kind_of Tempfile, file
assert_equal 'file.txt', file.original_filename
assert_equal "text/plain", file.content_type
assert_equal 'contents', file.read
@@ -49,8 +48,6 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
file = params['file']
foo = params['foo']
assert_kind_of Tempfile, file
assert_equal 'file.txt', file.original_filename
assert_equal "text/plain", file.content_type
@@ -64,8 +61,6 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
file = params['file']
assert_kind_of Tempfile, file
assert_equal 'file.txt', file.original_filename
assert_equal "text/plain", file.content_type
assert_equal(('a' * 20480), file.read)
@@ -77,13 +72,11 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
assert_equal 'bar', params['foo']
file = params['file']
assert_kind_of Tempfile, file
assert_equal 'file.csv', file.original_filename
assert_nil file.content_type
assert_equal 'contents', file.read
file = params['flowers']
assert_kind_of Tempfile, file
assert_equal 'flowers.jpg', file.original_filename
assert_equal "image/jpeg", file.content_type
assert_equal 19512, file.size

View File

@@ -11,9 +11,7 @@ class ResponseTest < ActiveSupport::TestCase
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
"Cache-Control" => "max-age=0, private, must-revalidate",
"ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"'
"Content-Type" => "text/html; charset=utf-8"
}, headers)
parts = []
@@ -27,9 +25,7 @@ class ResponseTest < ActiveSupport::TestCase
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
"Cache-Control" => "max-age=0, private, must-revalidate",
"ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"'
"Content-Type" => "text/html; charset=utf-8"
}, headers)
end
@@ -41,8 +37,7 @@ class ResponseTest < ActiveSupport::TestCase
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
"Cache-Control" => "no-cache"
"Content-Type" => "text/html; charset=utf-8"
}, headers)
parts = []

View File

@@ -53,18 +53,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def rescue_action(e) raise end
end
def test_raises_argument_error_if_missing_session_key
assert_raise(ArgumentError, nil.inspect) {
ActionDispatch::Session::CookieStore.new(nil,
:key => nil, :secret => SessionSecret)
}
assert_raise(ArgumentError, ''.inspect) {
ActionDispatch::Session::CookieStore.new(nil,
:key => '', :secret => SessionSecret)
}
end
def test_setting_session_value
with_test_route_set do
get '/set_session_value'

View File

@@ -1,19 +1,5 @@
require 'abstract_unit'
module ActionDispatch
class ShowExceptions
private
def public_path
"#{FIXTURE_LOAD_PATH}/public"
end
# Silence logger
def logger
nil
end
end
end
class ShowExceptionsTest < ActionDispatch::IntegrationTest
Boomer = lambda do |env|
req = ActionDispatch::Request.new(env)

View File

@@ -2,30 +2,37 @@ require 'abstract_unit'
module StaticTests
def test_serves_dynamic_content
assert_equal "Hello, World!", get("/nofile")
assert_equal "Hello, World!", get("/nofile").body
end
def test_serves_static_index_at_root
assert_equal "/index.html", get("/index.html")
assert_equal "/index.html", get("/index")
assert_equal "/index.html", get("/")
assert_html "/index.html", get("/index.html")
assert_html "/index.html", get("/index")
assert_html "/index.html", get("/")
assert_html "/index.html", get("")
end
def test_serves_static_file_in_directory
assert_equal "/foo/bar.html", get("/foo/bar.html")
assert_equal "/foo/bar.html", get("/foo/bar/")
assert_equal "/foo/bar.html", get("/foo/bar")
assert_html "/foo/bar.html", get("/foo/bar.html")
assert_html "/foo/bar.html", get("/foo/bar/")
assert_html "/foo/bar.html", get("/foo/bar")
end
def test_serves_static_index_file_in_directory
assert_equal "/foo/index.html", get("/foo/index.html")
assert_equal "/foo/index.html", get("/foo/")
assert_equal "/foo/index.html", get("/foo")
assert_html "/foo/index.html", get("/foo/index.html")
assert_html "/foo/index.html", get("/foo/")
assert_html "/foo/index.html", get("/foo")
end
private
def assert_html(body, response)
assert_equal body, response.body
assert_equal "text/html", response.headers["Content-Type"]
end
def get(path)
Rack::MockRequest.new(@app).request("GET", path).body
Rack::MockRequest.new(@app).request("GET", path)
end
end
@@ -59,16 +66,16 @@ class MultipleDirectorisStaticTest < ActiveSupport::TestCase
include StaticTests
test "serves files from other mounted directories" do
assert_equal "/blog/index.html", get("/blog/index.html")
assert_equal "/blog/index.html", get("/blog/index")
assert_equal "/blog/index.html", get("/blog/")
assert_html "/blog/index.html", get("/blog/index.html")
assert_html "/blog/index.html", get("/blog/index")
assert_html "/blog/index.html", get("/blog/")
assert_equal "/blog/blog.html", get("/blog/blog/")
assert_equal "/blog/blog.html", get("/blog/blog.html")
assert_equal "/blog/blog.html", get("/blog/blog")
assert_html "/blog/blog.html", get("/blog/blog/")
assert_html "/blog/blog.html", get("/blog/blog.html")
assert_html "/blog/blog.html", get("/blog/blog")
assert_equal "/blog/subdir/index.html", get("/blog/subdir/index.html")
assert_equal "/blog/subdir/index.html", get("/blog/subdir/")
assert_equal "/blog/subdir/index.html", get("/blog/subdir")
assert_html "/blog/subdir/index.html", get("/blog/subdir/index.html")
assert_html "/blog/subdir/index.html", get("/blog/subdir/")
assert_html "/blog/subdir/index.html", get("/blog/subdir")
end
end

View File

@@ -0,0 +1,58 @@
require 'abstract_unit'
module ActionDispatch
class UploadedFileTest < ActiveSupport::TestCase
def test_constructor_with_argument_error
assert_raises(ArgumentError) do
Http::UploadedFile.new({})
end
end
def test_original_filename
uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
assert_equal 'foo', uf.original_filename
end
def test_content_type
uf = Http::UploadedFile.new(:type => 'foo', :tempfile => Object.new)
assert_equal 'foo', uf.content_type
end
def test_headers
uf = Http::UploadedFile.new(:head => 'foo', :tempfile => Object.new)
assert_equal 'foo', uf.headers
end
def test_tempfile
uf = Http::UploadedFile.new(:tempfile => 'foo')
assert_equal 'foo', uf.tempfile
end
def test_delegates_to_tempfile
tf = Class.new { def read; 'thunderhorse' end }
uf = Http::UploadedFile.new(:tempfile => tf.new)
assert_equal 'thunderhorse', uf.read
end
def test_delegates_to_tempfile_with_params
tf = Class.new { def read *args; args end }
uf = Http::UploadedFile.new(:tempfile => tf.new)
assert_equal %w{ thunder horse }, uf.read(*%w{ thunder horse })
end
def test_delegate_respects_respond_to?
tf = Class.new { def read; yield end; private :read }
uf = Http::UploadedFile.new(:tempfile => tf.new)
assert_raises(NoMethodError) do
uf.read
end
end
def test_respond_to?
tf = Class.new { def read; yield end }
uf = Http::UploadedFile.new(:tempfile => tf.new)
assert uf.respond_to?(:headers), 'responds to headers'
assert uf.respond_to?(:read), 'responds to read'
end
end
end

View File

@@ -0,0 +1 @@
alt/hello.erb

View File

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

View File

@@ -0,0 +1 @@
controller_name_space/nested.erb <%= yield %>

View File

@@ -1 +0,0 @@
controller_name_space/nested.rhtml <%= yield %>

View File

@@ -0,0 +1 @@
item.erb <%= yield %>

View File

@@ -1 +0,0 @@
item.rhtml <%= yield %>

View File

@@ -0,0 +1 @@
layout_test.erb <%= yield %>

View File

@@ -1 +0,0 @@
layout_test.rhtml <%= yield %>

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More