aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2010-01-17 03:20:30 +0530
committerPratik Naik <pratiknaik@gmail.com>2010-01-17 03:20:30 +0530
commitb04230e3bbf912d60601e9e7b797c4cd43581d51 (patch)
tree97a2f784a2ec2bfae4f960af56a9280dad6f7774 /actionpack
parent867829b187969607aa12f2b0457f25da9c204db0 (diff)
parent6e3bee6cf1f0d2684152292db0a8b757249824fd (diff)
downloadrails-b04230e3bbf912d60601e9e7b797c4cd43581d51.tar.gz
rails-b04230e3bbf912d60601e9e7b797c4cd43581d51.tar.bz2
rails-b04230e3bbf912d60601e9e7b797c4cd43581d51.zip
Merge remote branch 'mainstream/master'
Conflicts: actionpack/lib/action_controller/metal/flash.rb
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG8
-rw-r--r--actionpack/lib/abstract_controller.rb1
-rw-r--r--actionpack/lib/abstract_controller/base.rb5
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb6
-rw-r--r--actionpack/lib/abstract_controller/url_for.rb156
-rw-r--r--actionpack/lib/action_controller.rb13
-rw-r--r--actionpack/lib/action_controller/base.rb9
-rw-r--r--actionpack/lib/action_controller/caching.rb11
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb18
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb8
-rw-r--r--actionpack/lib/action_controller/deprecated/dispatcher.rb31
-rw-r--r--actionpack/lib/action_controller/dispatch/dispatcher.rb52
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb12
-rw-r--r--actionpack/lib/action_controller/metal/filter_parameter_logging.rb22
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb177
-rw-r--r--actionpack/lib/action_controller/metal/head.rb3
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb102
-rw-r--r--actionpack/lib/action_controller/metal/logger.rb89
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb9
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb4
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb4
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb38
-rw-r--r--actionpack/lib/action_controller/railtie.rb67
-rw-r--r--actionpack/lib/action_controller/railties/subscriber.rb60
-rw-r--r--actionpack/lib/action_controller/test_case.rb7
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb226
-rw-r--r--actionpack/lib/action_dispatch.rb13
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb123
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb101
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb50
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb391
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb80
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb48
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb129
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb14
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb174
-rw-r--r--actionpack/lib/action_dispatch/middleware/head.rb18
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/string_coercion.rb29
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb245
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb118
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb9
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb1
-rw-r--r--actionpack/lib/action_view.rb2
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb10
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb68
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb13
-rw-r--r--actionpack/lib/action_view/locale/en.yml40
-rw-r--r--actionpack/lib/action_view/railtie.rb17
-rw-r--r--actionpack/lib/action_view/railties/subscriber.rb24
-rw-r--r--actionpack/lib/action_view/render/partials.rb17
-rw-r--r--actionpack/lib/action_view/render/rendering.rb24
-rw-r--r--actionpack/lib/action_view/template/text.rb2
-rw-r--r--actionpack/lib/action_view/test_case.rb3
-rw-r--r--actionpack/test/abstract/url_for_test.rb272
-rw-r--r--actionpack/test/abstract_unit.rb30
-rw-r--r--actionpack/test/active_record_unit.rb1
-rw-r--r--actionpack/test/activerecord/controller_runtime_test.rb50
-rw-r--r--actionpack/test/activerecord/polymorphic_routes_test.rb2
-rw-r--r--actionpack/test/controller/base_test.rb88
-rw-r--r--actionpack/test/controller/caching_test.rb14
-rw-r--r--actionpack/test/controller/dispatcher_test.rb80
-rw-r--r--actionpack/test/controller/filter_params_test.rb12
-rw-r--r--actionpack/test/controller/flash_test.rb42
-rw-r--r--actionpack/test/controller/integration_test.rb13
-rw-r--r--actionpack/test/controller/logging_test.rb80
-rw-r--r--actionpack/test/controller/mime_responds_test.rb130
-rw-r--r--actionpack/test/controller/resources_test.rb52
-rw-r--r--actionpack/test/controller/routing_test.rb4
-rw-r--r--actionpack/test/controller/subscriber_test.rb192
-rw-r--r--actionpack/test/controller/url_rewriter_test.rb283
-rw-r--r--actionpack/test/dispatch/callbacks_test.rb107
-rw-r--r--actionpack/test/dispatch/request_test.rb4
-rw-r--r--actionpack/test/dispatch/routing_test.rb263
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb23
-rw-r--r--actionpack/test/dispatch/string_coercion_test.rb40
-rw-r--r--actionpack/test/fixtures/respond_with/using_defaults.js.rjs1
-rw-r--r--actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs1
-rw-r--r--actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder1
-rw-r--r--actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb (renamed from actionpack/test/fixtures/respond_with/using_defaults.html.erb)0
-rw-r--r--actionpack/test/lib/controller/fake_models.rb2
-rw-r--r--actionpack/test/template/active_model_helper_i18n_test.rb12
-rw-r--r--actionpack/test/template/date_helper_test.rb22
-rw-r--r--actionpack/test/template/form_helper_test.rb103
-rw-r--r--actionpack/test/template/form_options_helper_i18n_test.rb6
-rw-r--r--actionpack/test/template/javascript_helper_test.rb5
-rw-r--r--actionpack/test/template/prototype_helper_test.rb5
-rw-r--r--actionpack/test/template/subscriber_test.rb102
-rw-r--r--actionpack/test/template/text_helper_test.rb14
96 files changed, 2976 insertions, 2019 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 782b4229fb..014a501080 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,13 @@
*Edge*
+* Fixed that PrototypeHelper#update_page should return html_safe [DHH]
+
+* Fixed that much of DateHelper wouldn't return html_safe? strings [DHH]
+
+* Fixed that fragment caching should return a cache hit as html_safe (or it would all just get escaped) [DHH]
+
+* Added that ActionController::Base now does helper :all instead of relying on the default ApplicationController in Rails to do it [DHH]
+
* Added ActionDispatch::Request#authorization to access the http authentication header regardless of its proxy hiding [DHH]
* Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection [DHH]. Examples:
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index 237ab577ba..efc35a7e56 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -15,4 +15,5 @@ module AbstractController
autoload :LocalizedCache
autoload :Logger
autoload :Rendering
+ autoload :UrlFor
end
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index a6889d5d01..48725ad82a 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -86,6 +86,11 @@ module AbstractController
abstract!
+ # Initialize controller with nil formats.
+ def initialize #:nodoc:
+ @_formats = nil
+ end
+
# Calls the action going through the entire action dispatch stack.
#
# The actual method that is called is determined by calling
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 332d86b089..d57136dbb8 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -17,12 +17,6 @@ module AbstractController
self._view_paths ||= ActionView::PathSet.new
end
- # Initialize controller with nil formats.
- def initialize(*) #:nodoc:
- @_formats = nil
- super
- end
-
# An instance of a view class. The default view class is ActionView::Base
#
# The view class must have the following methods:
diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb
new file mode 100644
index 0000000000..6b7d2b1f34
--- /dev/null
+++ b/actionpack/lib/abstract_controller/url_for.rb
@@ -0,0 +1,156 @@
+module AbstractController
+ # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
+ # is also possible: an URL can be generated from one of your routing definitions.
+ # URL generation functionality is centralized in this module.
+ #
+ # See AbstractController::Routing and AbstractController::Resources for general
+ # information about routing and routes.rb.
+ #
+ # <b>Tip:</b> If you need to generate URLs from your models or some other place,
+ # then AbstractController::UrlFor is what you're looking for. Read on for
+ # an introduction.
+ #
+ # == URL generation from parameters
+ #
+ # As you may know, some functions - such as AbstractController::Base#url_for
+ # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
+ # of parameters. For example, you've probably had the chance to write code
+ # like this in one of your views:
+ #
+ # <%= link_to('Click here', :controller => 'users',
+ # :action => 'new', :message => 'Welcome!') %>
+ #
+ # #=> Generates a link to: /users/new?message=Welcome%21
+ #
+ # link_to, and all other functions that require URL generation functionality,
+ # actually use AbstractController::UrlFor under the hood. And in particular,
+ # they use the AbstractController::UrlFor#url_for method. One can generate
+ # the same path as the above example by using the following code:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :only_path => true)
+ # # => "/users/new?message=Welcome%21"
+ #
+ # Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
+ # information about the website hostname that your Rails app is serving. So if you
+ # want to include the hostname as well, then you must also pass the <tt>:host</tt>
+ # argument:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :host => 'www.example.com') # Changed this.
+ # # => "http://www.example.com/users/new?message=Welcome%21"
+ #
+ # By default, all controllers and views have access to a special version of url_for,
+ # that already knows what the current hostname is. So if you use url_for in your
+ # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
+ # argument.
+ #
+ # For convenience reasons, mailers provide a shortcut for AbstractController::UrlFor#url_for.
+ # So within mailers, you only have to type 'url_for' instead of 'AbstractController::UrlFor#url_for'
+ # in full. However, mailers don't have hostname information, and what's why you'll still
+ # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
+ #
+ #
+ # == URL generation for named routes
+ #
+ # UrlFor also allows one to access methods that have been auto-generated from
+ # named routes. For example, suppose that you have a 'users' resource in your
+ # <b>routes.rb</b>:
+ #
+ # map.resources :users
+ #
+ # This generates, among other things, the method <tt>users_path</tt>. By default,
+ # this method is accessible from your controllers, views and mailers. If you need
+ # to access this auto-generated method from other places (such as a model), then
+ # you can do that by including AbstractController::UrlFor in your class:
+ #
+ # class User < ActiveRecord::Base
+ # include AbstractController::UrlFor
+ #
+ # def base_uri
+ # user_path(self)
+ # end
+ # end
+ #
+ # User.find(1).base_uri # => "/users/1"
+ #
+ module UrlFor
+ extend ActiveSupport::Concern
+
+ included do
+ ActionController::Routing::Routes.install_helpers(self)
+ extlib_inheritable_accessor :default_url_options,
+ :instance_writer => false, :instance_reader => false
+ self.default_url_options ||= {}
+ end
+
+ # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
+ # the form of a hash, just like the one you would use for url_for directly. Example:
+ #
+ # def default_url_options(options)
+ # { :project => @project.active? ? @project.url_name : "unknown" }
+ # end
+ #
+ # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
+ # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
+ # by this method.
+ def default_url_options(options = nil)
+ self.class.default_url_options
+ end
+
+ def rewrite_options(options) #:nodoc:
+ if options.delete(:use_defaults) != false && (defaults = default_url_options(options))
+ defaults.merge(options)
+ else
+ options
+ end
+ end
+
+ # Generate a url based on the options provided, default_url_options and the
+ # routes defined in routes.rb. The following options are supported:
+ #
+ # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
+ # * <tt>:host</tt> - Specifies the host the link should be targeted at.
+ # If <tt>:only_path</tt> is false, this option must be
+ # provided either explicitly, or via +default_url_options+.
+ # * <tt>:port</tt> - Optionally specify the port to connect to.
+ # * <tt>:anchor</tt> - An anchor name to be appended to the path.
+ # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
+ # +relative_url_root+ set in AbstractController::Base.relative_url_root.
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
+ #
+ # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
+ # +url_for+ is forwarded to the Routes module.
+ #
+ # Examples:
+ #
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
+ # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
+ def url_for(options = {})
+ options ||= {}
+ case options
+ when String
+ options
+ when Hash
+ _url_rewriter.rewrite(rewrite_options(options))
+ else
+ polymorphic_url(options)
+ end
+ end
+
+ protected
+
+ def _url_rewriter
+ ActionController::UrlRewriter
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index d66fc3fcc9..8bc2cc79d2 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -23,31 +23,32 @@ module ActionController
autoload :Helpers
autoload :HideActions
autoload :HttpAuthentication
- autoload :Logger
+ autoload :Instrumentation
autoload :MimeResponds
autoload :RackDelegation
autoload :Redirecting
- autoload :Rendering
autoload :Renderers
+ autoload :Rendering
autoload :RequestForgeryProtection
autoload :Rescue
autoload :Responder
autoload :SessionManagement
autoload :Streaming
+ autoload :Testing
autoload :UrlFor
autoload :Verification
end
- autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
- autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
- autoload :Routing, 'action_controller/deprecated'
+ autoload :Dispatcher, 'action_controller/deprecated/dispatcher'
autoload :Integration, 'action_controller/deprecated/integration_test'
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
+ autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
+ autoload :Routing, 'action_controller/deprecated'
+ autoload :TestCase, 'action_controller/test_case'
eager_autoload do
autoload :RecordIdentifier
autoload :UrlRewriter
- autoload :UrlWriter, 'action_controller/url_rewriter'
# TODO: Don't autoload exceptions, setup explicit
# requires for files that need them
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index b23be66910..260e5da336 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -6,6 +6,8 @@ module ActionController
include AbstractController::Layouts
include ActionController::Helpers
+ helper :all # By default, all helpers should be included
+
include ActionController::HideActions
include ActionController::UrlFor
include ActionController::Redirecting
@@ -13,7 +15,6 @@ module ActionController
include ActionController::Renderers::All
include ActionController::ConditionalGet
include ActionController::RackDelegation
- include ActionController::Logger
include ActionController::Configuration
# Legacy modules
@@ -31,9 +32,13 @@ module ActionController
include ActionController::Streaming
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Digest::ControllerMethods
- include ActionController::FilterParameterLogging
include ActionController::Translation
+ # Add instrumentations hooks at the bottom, to ensure they instrument
+ # all the methods properly.
+ include ActionController::Instrumentation
+ include ActionController::FilterParameterLogging
+
# TODO: Extract into its own module
# This should be moved together with other normalizing behavior
module ImplicitRender
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 69ed84da95..d784138ebe 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -60,17 +60,6 @@ module ActionController #:nodoc:
def cache_configured?
perform_caching && cache_store
end
-
- def log_event(name, before, after, instrumenter_id, payload)
- if name.to_s =~ /(read|write|cache|expire|exist)_(fragment|page)\??/
- key_or_path = payload[:key] || payload[:path]
- human_name = name.to_s.humanize
- duration = (after - before) * 1000
- logger.info("#{human_name} #{key_or_path.inspect} (%.1fms)" % duration)
- else
- super
- end
- end
end
def caching_allowed?
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index f569d0dd8b..00a7f034d3 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -36,8 +36,8 @@ module ActionController #:nodoc:
def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
if perform_caching
- if fragment_exist?(name,options)
- buffer.concat(read_fragment(name, options))
+ if fragment_exist?(name, options)
+ buffer.safe_concat(read_fragment(name, options))
else
pos = buffer.length
block.call
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
return content unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:write_fragment, :key => key) do
+ instrument_fragment_cache :write_fragment, key do
cache_store.write(key, content, options)
end
content
@@ -64,7 +64,7 @@ module ActionController #:nodoc:
return unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:read_fragment, :key => key) do
+ instrument_fragment_cache :read_fragment, key do
cache_store.read(key, options)
end
end
@@ -74,7 +74,7 @@ module ActionController #:nodoc:
return unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:exist_fragment?, :key => key) do
+ instrument_fragment_cache :exist_fragment?, key do
cache_store.exist?(key, options)
end
end
@@ -101,16 +101,18 @@ module ActionController #:nodoc:
key = fragment_cache_key(key) unless key.is_a?(Regexp)
message = nil
- ActiveSupport::Notifications.instrument(:expire_fragment, :key => key) do
+ instrument_fragment_cache :expire_fragment, key do
if key.is_a?(Regexp)
- message = "Expired fragments matching: #{key.source}"
cache_store.delete_matched(key, options)
else
- message = "Expired fragment: #{key}"
cache_store.delete(key, options)
end
end
end
+
+ def instrument_fragment_cache(name, key)
+ ActiveSupport::Notifications.instrument("action_controller.#{name}", :key => key){ yield }
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index d46f528c7e..5797eeebd6 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -64,7 +64,7 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- ActiveSupport::Notifications.instrument(:expire_page, :path => path) do
+ instrument_page_cache :expire_page, path do
File.delete(path) if File.exist?(path)
end
end
@@ -75,7 +75,7 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- ActiveSupport::Notifications.instrument(:cache_page, :path => path) do
+ instrument_page_cache :write_page, path do
FileUtils.makedirs(File.dirname(path))
File.open(path, "wb+") { |f| f.write(content) }
end
@@ -107,6 +107,10 @@ module ActionController #:nodoc:
def page_cache_path(path)
page_cache_directory + page_cache_file(path)
end
+
+ def instrument_page_cache(name, path)
+ ActiveSupport::Notifications.instrument("action_controller.#{name}", :path => path){ yield }
+ end
end
# Expires the page that was cached with the +options+ as a key. Example:
diff --git a/actionpack/lib/action_controller/deprecated/dispatcher.rb b/actionpack/lib/action_controller/deprecated/dispatcher.rb
new file mode 100644
index 0000000000..3da3c8ce7d
--- /dev/null
+++ b/actionpack/lib/action_controller/deprecated/dispatcher.rb
@@ -0,0 +1,31 @@
+module ActionController
+ class Dispatcher
+ cattr_accessor :prepare_each_request
+ self.prepare_each_request = false
+
+ class << self
+ def before_dispatch(*args, &block)
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.before_dispatch is deprecated. " <<
+ "Please use ActionDispatch::Callbacks.before instead.", caller
+ ActionDispatch::Callbacks.before(*args, &block)
+ end
+
+ def after_dispatch(*args, &block)
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.after_dispatch is deprecated. " <<
+ "Please use ActionDispatch::Callbacks.after instead.", caller
+ ActionDispatch::Callbacks.after(*args, &block)
+ end
+
+ def to_prepare(*args, &block)
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.to_prepare is deprecated. " <<
+ "Please use ActionDispatch::Callbacks.to_prepare instead.", caller
+ ActionDispatch::Callbacks.after(*args, &block)
+ end
+
+ def new
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.new is deprecated, use Rails.application instead."
+ Rails.application
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb
deleted file mode 100644
index cf02757cf6..0000000000
--- a/actionpack/lib/action_controller/dispatch/dispatcher.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require 'active_support/core_ext/module/delegation'
-
-module ActionController
- # Dispatches requests to the appropriate controller and takes care of
- # reloading the app after each request when Dependencies.load? is true.
- class Dispatcher
- cattr_accessor :prepare_each_request
- self.prepare_each_request = false
-
- class << self
- def define_dispatcher_callbacks(cache_classes)
- unless cache_classes
- # Run prepare callbacks before every request in development mode
- self.prepare_each_request = true
-
- ActionDispatch::Callbacks.after_dispatch do
- # Cleanup the application before processing the current request.
- ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
- ActiveSupport::Dependencies.clear
- ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
- end
-
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
- end
-
- if defined?(ActiveRecord)
- to_prepare(:activerecord_instantiate_observers) do
- ActiveRecord::Base.instantiate_observers
- end
- end
-
- if Base.logger && Base.logger.respond_to?(:flush)
- after_dispatch do
- Base.logger.flush
- end
- end
-
- to_prepare do
- I18n.reload!
- end
- end
-
- delegate :to_prepare, :before_dispatch, :around_dispatch, :after_dispatch,
- :to => ActionDispatch::Callbacks
-
- def new
- # DEPRECATE Rails application fallback
- Rails.application
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index a90f798cd5..0e869e4e87 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -21,6 +21,8 @@ module ActionController
class << self
delegate :default_charset=, :to => "ActionDispatch::Response"
+ delegate :resources_path_names, :to => "ActionController::Routing::Routes"
+ delegate :resources_path_names=, :to => "ActionController::Routing::Routes"
end
# cattr_reader :protected_instance_variables
@@ -29,15 +31,7 @@ module ActionController
@variables_added @request_origin @url
@parent_controller @action_name
@before_filter_chain_aborted @_headers @_params
- @_flash @_response)
-
- # Indicates whether or not optimise the generated named
- # route helper methods
- cattr_accessor :optimise_named_routes
- self.optimise_named_routes = true
-
- cattr_accessor :resources_path_names
- self.resources_path_names = { :new => 'new', :edit => 'edit' }
+ @_response)
# Controls the resource action separator
cattr_accessor :resource_action_separator
diff --git a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
index 59e200396a..0b1e1ee6ab 100644
--- a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
+++ b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
@@ -2,6 +2,8 @@ module ActionController
module FilterParameterLogging
extend ActiveSupport::Concern
+ INTERNAL_PARAMS = %w(controller action format _method only_path)
+
module ClassMethods
# Replace sensitive parameter data from the request log.
# Filters parameters that have any of the arguments as a substring.
@@ -48,27 +50,19 @@ module ActionController
filtered_params[key] = value
end
- filtered_params
+ filtered_params.except!(*INTERNAL_PARAMS)
end
protected :filter_parameters
end
-
- protected
-
- # Overwrite log_process_action to include parameters information.
- # If this method is invoked, it means logger is defined, so don't
- # worry with such scenario here.
- def log_process_action(controller) #:nodoc:
- params = controller.send(:filter_parameters, controller.request.params)
- logger.info " Parameters: #{params.inspect}" unless params.empty?
- super
- end
end
- INTERNAL_PARAMS = [:controller, :action, :format, :_method, :only_path]
-
protected
+ def append_info_to_payload(payload)
+ super
+ payload[:params] = filter_parameters(request.params)
+ end
+
def filter_parameters(params)
params.dup.except!(*INTERNAL_PARAMS)
end
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index 25e25940a7..bd768b634e 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -1,187 +1,14 @@
module ActionController #:nodoc:
- # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
- # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
- # action that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can
- # then expose the flash to its template. Actually, that exposure is automatically done. Example:
- #
- # class PostsController < ActionController::Base
- # def create
- # # save post
- # flash[:notice] = "Successfully created post"
- # redirect_to @post
- # end
- #
- # def show
- # # doesn't need to assign the flash notice to the template, that's done automatically
- # end
- # end
- #
- # show.html.erb
- # <% if flash[:notice] %>
- # <div class="notice"><%= flash[:notice] %></div>
- # <% end %>
- #
- # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
- # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
- #
- # See docs on the FlashHash class for more details about the flash.
module Flash
extend ActiveSupport::Concern
included do
+ delegate :flash, :to => :request
+ delegate :alert, :notice, :to => "request.flash"
helper_method :alert, :notice
end
- class FlashNow #:nodoc:
- def initialize(flash)
- @flash = flash
- end
-
- def []=(k, v)
- @flash[k] = v
- @flash.discard(k)
- v
- end
-
- def [](k)
- @flash[k]
- end
- end
-
- class FlashHash < Hash
- def initialize #:nodoc:
- super
- @used = Set.new
- end
-
- def []=(k, v) #:nodoc:
- keep(k)
- super
- end
-
- def update(h) #:nodoc:
- h.keys.each { |k| keep(k) }
- super
- end
-
- alias :merge! :update
-
- def replace(h) #:nodoc:
- @used = Set.new
- super
- end
-
- # Sets a flash that will not be available to the next action, only to the current.
- #
- # flash.now[:message] = "Hello current action"
- #
- # This method enables you to use the flash as a central messaging system in your app.
- # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
- # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
- # vanish when the current action is done.
- #
- # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
- def now
- FlashNow.new(self)
- end
-
- # Keeps either the entire current flash or a specific flash entry available for the next action:
- #
- # flash.keep # keeps the entire flash
- # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
- def keep(k = nil)
- use(k, false)
- end
-
- # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
- #
- # flash.discard # discard the entire flash at the end of the current action
- # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
- def discard(k = nil)
- use(k)
- end
-
- # Mark for removal entries that were kept, and delete unkept ones.
- #
- # This method is called automatically by filters, so you generally don't need to care about it.
- def sweep #:nodoc:
- keys.each do |k|
- unless @used.include?(k)
- @used << k
- else
- delete(k)
- @used.delete(k)
- end
- end
-
- # clean up after keys that could have been left over by calling reject! or shift on the flash
- (@used - keys).each{ |k| @used.delete(k) }
- end
-
- def store(session)
- return if self.empty?
- session["flash"] = self
- end
-
- private
- # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
- # use() # marks the entire flash as used
- # use('msg') # marks the "msg" entry as used
- # use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
- # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
- # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
- # if no key is passed.
- def use(key = nil, used = true)
- Array(key || keys).each { |k| used ? @used << k : @used.delete(k) }
- return key ? self[key] : self
- end
- end
-
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
- # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
- # to put a new one.
- def flash #:doc:
- unless @_flash
- @_flash = session["flash"] || FlashHash.new
- @_flash.sweep
- end
-
- @_flash
- end
-
- # Convenience accessor for flash[:alert]
- def alert
- flash[:alert]
- end
-
- # Convenience accessor for flash[:alert]=
- def alert=(message)
- flash[:alert] = message
- end
-
- # Convenience accessor for flash[:notice]
- def notice
- flash[:notice]
- end
-
- # Convenience accessor for flash[:notice]=
- def notice=(message)
- flash[:notice] = message
- end
-
protected
- def process_action(method_name)
- @_flash = nil
- super
- @_flash.store(session) if @_flash
- @_flash = nil
- end
-
- def reset_session
- super
- @_flash = nil
- end
-
def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
if alert = response_status_and_flash.delete(:alert)
flash[:alert] = alert
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index c82d9cf369..37be8b3999 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -1,6 +1,7 @@
module ActionController
module Head
- include UrlFor
+ 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.
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
new file mode 100644
index 0000000000..7222b7b2fa
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -0,0 +1,102 @@
+require 'abstract_controller/logger'
+
+module ActionController
+ # Adds instrumentation to several ends in ActionController::Base. It also provides
+ # some hooks related with process_action, this allows an ORM like ActiveRecord
+ # and/or DataMapper to plug in ActionController and show related information.
+ #
+ # Check ActiveRecord::Railties::ControllerRuntime for an example.
+ module Instrumentation
+ extend ActiveSupport::Concern
+
+ included do
+ include AbstractController::Logger
+ end
+
+ attr_internal :view_runtime
+
+ def process_action(action, *args)
+ ActiveSupport::Notifications.instrument("action_controller.process_action") do |payload|
+ result = super
+ payload[:controller] = self.class.name
+ payload[:action] = self.action_name
+ payload[:formats] = request.formats.map(&:to_s)
+ payload[:remote_ip] = request.remote_ip
+ payload[:method] = request.method
+ payload[:status] = response.status
+ payload[:request_uri] = request.request_uri rescue "unknown"
+ append_info_to_payload(payload)
+ result
+ end
+ end
+
+ def render(*args, &block)
+ if logger
+ render_output = nil
+
+ self.view_runtime = cleanup_view_runtime do
+ Benchmark.ms { render_output = super }
+ end
+
+ render_output
+ else
+ super
+ end
+ end
+
+ def send_file(path, options={})
+ ActiveSupport::Notifications.instrument("action_controller.send_file",
+ options.merge(:path => path)) do
+ super
+ end
+ end
+
+ def send_data(data, options = {})
+ ActiveSupport::Notifications.instrument("action_controller.send_data", options) do
+ super
+ end
+ end
+
+ def redirect_to(*args)
+ ActiveSupport::Notifications.instrument("action_controller.redirect_to") do |payload|
+ result = super
+ payload[:status] = self.status
+ payload[:location] = self.location
+ result
+ end
+ end
+
+ protected
+
+ # A hook which allows you to clean up any time taken into account in
+ # views wrongly, like database querying time.
+ #
+ # def cleanup_view_runtime
+ # super - time_taken_in_something_expensive
+ # end
+ #
+ # :api: plugin
+ def cleanup_view_runtime #:nodoc:
+ yield
+ end
+
+ # Everytime after an action is processed, this method is invoked
+ # with the payload, so you can add more information.
+ # :api: plugin
+ def append_info_to_payload(payload) #:nodoc:
+ payload[:view_runtime] = view_runtime
+ end
+
+ module ClassMethods
+ # A hook which allows other frameworks to log what happened during
+ # controller process action. This method should return an array
+ # with the messages to be added.
+ # :api: plugin
+ def log_process_action(payload) #:nodoc:
+ messages, view_runtime = [], payload[:view_runtime]
+ messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
+ messages
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/logger.rb b/actionpack/lib/action_controller/metal/logger.rb
deleted file mode 100644
index 4f4370e5f0..0000000000
--- a/actionpack/lib/action_controller/metal/logger.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require 'abstract_controller/logger'
-
-module ActionController
- # Adds instrumentation to <tt>process_action</tt> and a <tt>log_event</tt> method
- # responsible to log events from ActiveSupport::Notifications. This module handles
- # :process_action and :render_template events but allows any other module to hook
- # into log_event and provide its own logging facilities (as in ActionController::Caching).
- module Logger
- extend ActiveSupport::Concern
-
- included do
- include AbstractController::Logger
- end
-
- attr_internal :view_runtime
-
- def process_action(action)
- ActiveSupport::Notifications.instrument(:process_action, :controller => self, :action => action) do
- super
- end
- end
-
- def render(*args, &block)
- if logger
- render_output = nil
-
- self.view_runtime = cleanup_view_runtime do
- Benchmark.ms { render_output = super }
- end
-
- render_output
- else
- super
- end
- end
-
- # If you want to remove any time taken into account in :view_runtime
- # wrongly, you can do it here:
- #
- # def cleanup_view_runtime
- # super - time_taken_in_something_expensive
- # end
- #
- # :api: plugin
- def cleanup_view_runtime #:nodoc:
- yield
- end
-
- module ClassMethods
- # This is the hook invoked by ActiveSupport::Notifications.subscribe.
- # If you need to log any event, overwrite the method and do it here.
- def log_event(name, before, after, instrumenter_id, payload) #:nodoc:
- if name == :process_action
- duration = [(after - before) * 1000, 0.01].max
- controller = payload[:controller]
- request = controller.request
-
- logger.info "\n\nProcessed #{controller.class.name}##{payload[:action]} " \
- "to #{request.formats} (for #{request.remote_ip} at #{before.to_s(:db)}) " \
- "[#{request.method.to_s.upcase}]"
-
- log_process_action(controller)
-
- message = "Completed in %.0fms" % duration
- message << " | #{controller.response.status}"
- message << " [#{request.request_uri rescue "unknown"}]"
-
- logger.info(message)
- elsif name == :render_template
- # TODO Make render_template logging work if you are using just ActionView
- duration = (after - before) * 1000
- message = "Rendered #{payload[:identifier]}"
- message << " within #{payload[:layout]}" if payload[:layout]
- message << (" (%.1fms)" % duration)
- logger.info(message)
- end
- end
-
- protected
-
- # A hook which allows logging what happened during controller process action.
- # :api: plugin
- def log_process_action(controller) #:nodoc:
- view_runtime = controller.send :view_runtime
- logger.info(" View runtime: %.1fms" % view_runtime.to_f) if view_runtime
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 468c5f4fae..4c02677729 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -215,7 +215,10 @@ module ActionController #:nodoc:
# a proc to it.
#
def respond_with(*resources, &block)
- if response = retrieve_response_from_mimes([], &block)
+ raise "In order to use respond_with, first you need to declare the formats your " <<
+ "controller responds to in the class level" if mimes_for_respond_to.empty?
+
+ if response = retrieve_response_from_mimes(&block)
options = resources.extract_options!
options.merge!(:default_response => response)
(options.delete(:responder) || responder).call(self, resources, options)
@@ -246,9 +249,9 @@ module ActionController #:nodoc:
# Collects mimes and return the response for the negotiated format. Returns
# nil if :not_acceptable was sent to the client.
#
- def retrieve_response_from_mimes(mimes, &block)
+ def retrieve_response_from_mimes(mimes=nil, &block)
collector = Collector.new { default_render }
- mimes = collect_mimes_from_class_level if mimes.empty?
+ mimes ||= collect_mimes_from_class_level
mimes.each { |mime| collector.send(mime) }
block.call(collector) if block_given?
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 7a2f9a6fc5..faf0589fd2 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -9,7 +9,9 @@ module ActionController
module Redirecting
extend ActiveSupport::Concern
+
include AbstractController::Logger
+ include ActionController::UrlFor
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
#
@@ -55,8 +57,6 @@ module ActionController
self.status = _extract_redirect_to_status(options, response_status)
self.location = _compute_redirect_to_location(options)
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
-
- logger.info("Redirected to #{location}") if logger && logger.info?
end
private
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 288b5d7c99..8f03b8bb17 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -88,13 +88,11 @@ module ActionController #:nodoc:
@performed_render = false
if options[:x_sendfile]
- logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger
head options[:status], X_SENDFILE_HEADER => path
else
if options[:stream]
# TODO : Make render :text => proc {} work with the new base
render :status => options[:status], :text => Proc.new { |response, output|
- logger.info "Streaming file #{path}" unless logger.nil?
len = options[:buffer_size] || 4096
File.open(path, 'rb') do |file|
while buf = file.read(len)
@@ -103,7 +101,6 @@ module ActionController #:nodoc:
end
}
else
- logger.info "Sending file #{path}" unless logger.nil?
File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
end
end
@@ -141,7 +138,6 @@ module ActionController #:nodoc:
# 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:
- logger.info "Sending data #{options[:filename]}" if logger
send_file_headers! options.merge(:length => data.bytesize)
render :status => options[:status], :text => data
end
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 8c3810ebcb..73feacb872 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -2,40 +2,14 @@ module ActionController
module UrlFor
extend ActiveSupport::Concern
- include RackDelegation
+ include AbstractController::UrlFor
+ include ActionController::RackDelegation
- # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
- # the form of a hash, just like the one you would use for url_for directly. Example:
- #
- # def default_url_options(options)
- # { :project => @project.active? ? @project.url_name : "unknown" }
- # end
- #
- # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
- # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
- # by this method.
- def default_url_options(options = nil)
- end
-
- def rewrite_options(options) #:nodoc:
- if defaults = default_url_options(options)
- defaults.merge(options)
- else
- options
- end
- end
+ protected
- def url_for(options = {})
- options ||= {}
- case options
- when String
- options
- when Hash
- @url ||= UrlRewriter.new(request, params)
- @url.rewrite(rewrite_options(options))
- else
- polymorphic_url(options)
- end
+ def _url_rewriter
+ return ActionController::UrlRewriter unless request
+ @_url_rewriter ||= ActionController::UrlRewriter.new(request, params)
end
end
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index f861d12905..741101a210 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -5,6 +5,9 @@ module ActionController
class Railtie < Rails::Railtie
plugin_name :action_controller
+ require "action_controller/railties/subscriber"
+ subscriber ActionController::Railties::Subscriber.new
+
initializer "action_controller.set_configs" do |app|
app.config.action_controller.each do |k,v|
ActionController::Base.send "#{k}=", v
@@ -23,26 +26,6 @@ module ActionController
app.reload_routes!
end
- # Include middleware to serve up static assets
- initializer "action_controller.initialize_static_server" do |app|
- if app.config.serve_static_assets
- app.config.middleware.use(ActionDispatch::Static, Rails.public_path)
- end
- end
-
- initializer "action_controller.initialize_middleware_stack" do |app|
- middleware = app.config.middleware
- middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency })
- middleware.use(::Rack::Runtime)
- middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local })
- middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request })
- middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options })
- middleware.use(ActionDispatch::ParamsParser)
- middleware.use(::Rack::MethodOverride)
- middleware.use(::Rack::Head)
- middleware.use(ActionDispatch::StringCoercion)
- end
-
initializer "action_controller.initialize_framework_caches" do
ActionController::Base.cache_store ||= RAILS_CACHE
end
@@ -57,19 +40,41 @@ module ActionController
ActionController::Base.view_paths = view_path if ActionController::Base.view_paths.blank?
end
+ class MetalMiddlewareBuilder
+ def initialize(metals)
+ @metals = metals
+ end
+
+ def new(app)
+ ActionDispatch::Cascade.new(@metals, app)
+ end
+
+ def name
+ ActionDispatch::Cascade.name
+ end
+ alias_method :to_s, :name
+ end
+
initializer "action_controller.initialize_metal" do |app|
- Rails::Rack::Metal.requested_metals = app.config.metals
+ metal_root = "#{Rails.root}/app/metal"
+ load_list = app.config.metals || Dir["#{metal_root}/**/*.rb"]
- app.config.middleware.insert_before(:"ActionDispatch::ParamsParser",
- Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?)
+ metals = load_list.map { |metal|
+ metal = File.basename(metal.gsub("#{metal_root}/", ''), '.rb')
+ require_dependency metal
+ metal.camelize.constantize
+ }.compact
+
+ middleware = MetalMiddlewareBuilder.new(metals)
+ app.config.middleware.insert_before(:"ActionDispatch::ParamsParser", middleware)
end
- # # Prepare dispatcher callbacks and run 'prepare' callbacks
+ # Prepare dispatcher callbacks and run 'prepare' callbacks
initializer "action_controller.prepare_dispatcher" do |app|
# TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
+ # Notice that at this point, ActionDispatch::Callbacks were already loaded.
require 'rails/dispatcher'
-
- Dispatcher.define_dispatcher_callbacks(app.config.cache_classes)
+ ActionController::Dispatcher.prepare_each_request = true unless app.config.cache_classes
unless app.config.cache_classes
# Setup dev mode route reloading
@@ -80,15 +85,7 @@ module ActionController
app.reload_routes!
end
end
- ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes.call }
- end
- end
-
- initializer "action_controller.notifications" do |app|
- require 'active_support/notifications'
-
- ActiveSupport::Notifications.subscribe do |*args|
- ActionController::Base.log_event(*args) if ActionController::Base.logger
+ ActionDispatch::Callbacks.before { |callbacks| reload_routes.call }
end
end
diff --git a/actionpack/lib/action_controller/railties/subscriber.rb b/actionpack/lib/action_controller/railties/subscriber.rb
new file mode 100644
index 0000000000..a9f5d16c58
--- /dev/null
+++ b/actionpack/lib/action_controller/railties/subscriber.rb
@@ -0,0 +1,60 @@
+module ActionController
+ module Railties
+ class Subscriber < Rails::Subscriber
+ def process_action(event)
+ payload = event.payload
+
+ info "\nProcessed #{payload[:controller]}##{payload[:action]} " \
+ "to #{payload[:formats].join(', ')} (for #{payload[:remote_ip]} at #{event.time.to_s(:db)}) " \
+ "[#{payload[:method].to_s.upcase}]"
+
+ info " Parameters: #{payload[:params].inspect}" unless payload[:params].blank?
+
+ additions = ActionController::Base.log_process_action(payload)
+
+ message = "Completed in %.0fms" % event.duration
+ message << " (#{additions.join(" | ")})" unless additions.blank?
+ message << " | #{payload[:status]} [#{payload[:request_uri]}]\n\n"
+
+ info(message)
+ end
+
+ def send_file(event)
+ message = if event.payload[:x_sendfile]
+ header = ActionController::Streaming::X_SENDFILE_HEADER
+ "Sent #{header} header %s"
+ elsif event.payload[:stream]
+ "Streamed file %s"
+ else
+ "Sent file %s"
+ end
+
+ message << " (%.1fms)"
+ info(message % [event.payload[:path], event.duration])
+ end
+
+ def redirect_to(event)
+ info "Redirected to #{event.payload[:location]}"
+ end
+
+ def send_data(event)
+ info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration])
+ end
+
+ %w(write_fragment read_fragment exist_fragment?
+ expire_fragment expire_page write_page).each do |method|
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{method}(event)
+ key_or_path = event.payload[:key] || event.payload[:path]
+ human_name = #{method.to_s.humanize.inspect}
+ info("\#{human_name} \#{key_or_path} (%.1fms)" % event.duration)
+ end
+ METHOD
+ end
+
+ def logger
+ ActionController::Base.logger
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 398ea52495..14557ca782 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,6 +1,5 @@
-require 'active_support/test_case'
require 'rack/session/abstract/id'
-require 'action_controller/metal/testing'
+require 'action_view/test_case'
module ActionController
class TestRequest < ActionDispatch::TestRequest #:nodoc:
@@ -240,13 +239,15 @@ module ActionController
@request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
- @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
+ @request.session["flash"] = @request.flash.update(flash || {})
+ @request.session["flash"].sweep
@controller.request = @request
@controller.params.merge!(parameters)
build_request_uri(action, parameters)
Base.class_eval { include Testing }
@controller.process_with_new_base_test(@request, @response)
+ @request.session.delete('flash') if @request.session['flash'].blank?
@response
end
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
index 52b66c9303..933a1fa8f9 100644
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ b/actionpack/lib/action_controller/url_rewriter.rb
@@ -1,153 +1,21 @@
-module ActionController
- # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
- # is also possible: an URL can be generated from one of your routing definitions.
- # URL generation functionality is centralized in this module.
- #
- # See ActionController::Routing and ActionController::Resources for general
- # information about routing and routes.rb.
- #
- # <b>Tip:</b> If you need to generate URLs from your models or some other place,
- # then ActionController::UrlWriter is what you're looking for. Read on for
- # an introduction.
- #
- # == URL generation from parameters
- #
- # As you may know, some functions - such as ActionController::Base#url_for
- # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
- # of parameters. For example, you've probably had the chance to write code
- # like this in one of your views:
- #
- # <%= link_to('Click here', :controller => 'users',
- # :action => 'new', :message => 'Welcome!') %>
- #
- # #=> Generates a link to: /users/new?message=Welcome%21
- #
- # link_to, and all other functions that require URL generation functionality,
- # actually use ActionController::UrlWriter under the hood. And in particular,
- # they use the ActionController::UrlWriter#url_for method. One can generate
- # the same path as the above example by using the following code:
- #
- # include UrlWriter
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :only_path => true)
- # # => "/users/new?message=Welcome%21"
- #
- # Notice the <tt>:only_path => true</tt> part. This is because UrlWriter has no
- # information about the website hostname that your Rails app is serving. So if you
- # want to include the hostname as well, then you must also pass the <tt>:host</tt>
- # argument:
- #
- # include UrlWriter
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :host => 'www.example.com') # Changed this.
- # # => "http://www.example.com/users/new?message=Welcome%21"
- #
- # By default, all controllers and views have access to a special version of url_for,
- # that already knows what the current hostname is. So if you use url_for in your
- # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
- # argument.
- #
- # For convenience reasons, mailers provide a shortcut for ActionController::UrlWriter#url_for.
- # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlWriter#url_for'
- # in full. However, mailers don't have hostname information, and what's why you'll still
- # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
- #
- #
- # == URL generation for named routes
- #
- # UrlWriter also allows one to access methods that have been auto-generated from
- # named routes. For example, suppose that you have a 'users' resource in your
- # <b>routes.rb</b>:
- #
- # map.resources :users
- #
- # This generates, among other things, the method <tt>users_path</tt>. By default,
- # this method is accessible from your controllers, views and mailers. If you need
- # to access this auto-generated method from other places (such as a model), then
- # you can do that by including ActionController::UrlWriter in your class:
- #
- # class User < ActiveRecord::Base
- # include ActionController::UrlWriter
- #
- # def base_uri
- # user_path(self)
- # end
- # end
- #
- # User.find(1).base_uri # => "/users/1"
- module UrlWriter
- def self.included(base) #:nodoc:
- ActionController::Routing::Routes.install_helpers(base)
- base.mattr_accessor :default_url_options
-
- # The default options for urls written by this writer. Typically a <tt>:host</tt> pair is provided.
- base.default_url_options ||= {}
- end
-
- # Generate a url based on the options provided, default_url_options and the
- # routes defined in routes.rb. The following options are supported:
- #
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targeted at.
- # If <tt>:only_path</tt> is false, this option must be
- # provided either explicitly, or via +default_url_options+.
- # * <tt>:port</tt> - Optionally specify the port to connect to.
- # * <tt>:anchor</tt> - An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in ActionController::Base.relative_url_root.
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
- #
- # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
- # +url_for+ is forwarded to the Routes module.
- #
- # Examples:
- #
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
- # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
- def url_for(options)
- options = self.class.default_url_options.merge(options)
-
- url = ''
-
- unless options.delete(:only_path)
- url << (options.delete(:protocol) || 'http')
- url << '://' unless url.match("://")
-
- raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
-
- url << options.delete(:host)
- url << ":#{options.delete(:port)}" if options.key?(:port)
- else
- # Delete the unused options to prevent their appearance in the query string.
- [:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
- end
- trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
- url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
- generated = Routing::Routes.generate(options, {})
- url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
- url << anchor if anchor
-
- url
- end
- end
+require 'active_support/core_ext/hash/except'
+module ActionController
# Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
class UrlRewriter #:nodoc:
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
+
def initialize(request, parameters)
@request, @parameters = request, parameters
end
def rewrite(options = {})
- rewrite_url(options)
+ options[:host] ||= @request.host_with_port
+ options[:protocol] ||= @request.protocol
+
+ self.class.rewrite(options, @request.symbolized_path_parameters) do |options|
+ process_path_options(options)
+ end
end
def to_str
@@ -156,49 +24,53 @@ module ActionController
alias_method :to_s, :to_str
- private
- # Given a path and options, returns a rewritten URL string
- def rewrite_url(options)
- rewritten_url = ""
-
- unless options[:only_path]
- rewritten_url << (options[:protocol] || @request.protocol)
- rewritten_url << "://" unless rewritten_url.match("://")
- rewritten_url << rewrite_authentication(options)
- rewritten_url << (options[:host] || @request.host_with_port)
- rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
- end
-
- path = rewrite_path(options)
- rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
- rewritten_url << "##{CGI.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
-
- rewritten_url
+ def self.rewrite(options, path_segments=nil)
+ rewritten_url = ""
+
+ unless options[:only_path]
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << "://" unless rewritten_url.match("://")
+ rewritten_url << rewrite_authentication(options)
+
+ raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
+
+ rewritten_url << options[:host]
+ rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
end
- # Given a Hash of options, generates a route
- def rewrite_path(options)
- options = options.symbolize_keys
- options.update(options[:params].symbolize_keys) if options[:params]
+ path_options = options.except(*RESERVED_OPTIONS)
+ path_options = yield(path_options) if block_given?
+ path = Routing::Routes.generate(path_options, path_segments || {})
+
+ rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
+ rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+ rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
- if (overwrite = options.delete(:overwrite_params))
- options.update(@parameters.symbolize_keys)
- options.update(overwrite.symbolize_keys)
- end
+ rewritten_url
+ end
- RESERVED_OPTIONS.each { |k| options.delete(k) }
+ protected
- # Generates the query string, too
- Routing::Routes.generate(options, @request.symbolized_path_parameters)
+ def self.rewrite_authentication(options)
+ if options[:user] && options[:password]
+ "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
+ else
+ ""
end
+ end
+
+ # Given a Hash of options, generates a route
+ def process_path_options(options)
+ options = options.symbolize_keys
+ options.update(options[:params].symbolize_keys) if options[:params]
- def rewrite_authentication(options)
- if options[:user] && options[:password]
- "#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"
- else
- ""
- end
+ if (overwrite = options.delete(:overwrite_params))
+ options.update(@parameters.symbolize_keys)
+ options.update(overwrite.symbolize_keys)
end
+
+ options
+ end
+
end
end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 1e87a016f9..7b44212310 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -43,18 +43,27 @@ module ActionDispatch
autoload_under 'middleware' do
autoload :Callbacks
autoload :Cascade
+ autoload :Flash
+ autoload :Head
autoload :ParamsParser
autoload :Rescue
autoload :ShowExceptions
autoload :Static
- autoload :StringCoercion
end
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
autoload :Routing
module Http
- autoload :Headers, 'action_dispatch/http/headers'
+ extend ActiveSupport::Autoload
+
+ autoload :Cache
+ autoload :Headers
+ autoload :MimeNegotiation
+ autoload :Parameters
+ autoload :Upload
+ autoload :UploadedFile, 'action_dispatch/http/upload'
+ autoload :URL
end
module Session
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
new file mode 100644
index 0000000000..428e62dc6b
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -0,0 +1,123 @@
+module ActionDispatch
+ module Http
+ module Cache
+ module Request
+ def if_modified_since
+ if since = env['HTTP_IF_MODIFIED_SINCE']
+ Time.rfc2822(since) rescue nil
+ end
+ end
+
+ def if_none_match
+ env['HTTP_IF_NONE_MATCH']
+ end
+
+ def not_modified?(modified_at)
+ if_modified_since && modified_at && if_modified_since >= modified_at
+ end
+
+ def etag_matches?(etag)
+ if_none_match && if_none_match == etag
+ end
+
+ # Check response freshness (Last-Modified and ETag) against request
+ # If-Modified-Since and If-None-Match conditions. If both headers are
+ # supplied, both must match, or the request is not considered fresh.
+ def fresh?(response)
+ last_modified = if_modified_since
+ etag = if_none_match
+
+ return false unless last_modified || etag
+
+ success = true
+ success &&= not_modified?(response.last_modified) if last_modified
+ success &&= etag_matches?(response.etag) if etag
+ success
+ end
+ end
+
+ module Response
+ def cache_control
+ @cache_control ||= {}
+ end
+
+ def last_modified
+ if last = headers['Last-Modified']
+ Time.httpdate(last)
+ end
+ end
+
+ def last_modified?
+ headers.include?('Last-Modified')
+ end
+
+ def last_modified=(utc_time)
+ headers['Last-Modified'] = utc_time.httpdate
+ end
+
+ def etag
+ @etag
+ end
+
+ def etag?
+ @etag
+ end
+
+ def etag=(etag)
+ key = ActiveSupport::Cache.expand_cache_key(etag)
+ @etag = %("#{Digest::MD5.hexdigest(key)}")
+ end
+
+ private
+
+ 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!
+ control = @cache_control
+
+ if control.empty?
+ headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
+ elsif @cache_control[:no_cache]
+ headers["Cache-Control"] = "no-cache"
+ else
+ extras = control[:extras]
+ max_age = control[:max_age]
+
+ options = []
+ options << "max-age=#{max_age.to_i}" if max_age
+ options << (control[:public] ? "public" : "private")
+ options << "must-revalidate" if control[:must_revalidate]
+ options.concat(extras) if extras
+
+ headers["Cache-Control"] = options.join(", ")
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
new file mode 100644
index 0000000000..40617e239a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -0,0 +1,101 @@
+module ActionDispatch
+ module Http
+ module MimeNegotiation
+ # The MIME type of the HTTP request, such as Mime::XML.
+ #
+ # For backward compatibility, the post \format is extracted from the
+ # X-Post-Data-Format HTTP header if present.
+ def content_type
+ @env["action_dispatch.request.content_type"] ||= begin
+ if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
+ Mime::Type.lookup($1.strip.downcase)
+ else
+ nil
+ end
+ end
+ end
+
+ # Returns the accepted MIME type for the request.
+ def accepts
+ @env["action_dispatch.request.accepts"] ||= begin
+ header = @env['HTTP_ACCEPT'].to_s.strip
+
+ if header.empty?
+ [content_type]
+ else
+ Mime::Type.parse(header)
+ end
+ end
+ end
+
+ # Returns the Mime type for the \format used in the request.
+ #
+ # GET /posts/5.xml | request.format => Mime::XML
+ # GET /posts/5.xhtml | request.format => Mime::HTML
+ # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
+ #
+ def format(view_path = [])
+ formats.first
+ end
+
+ def formats
+ accept = @env['HTTP_ACCEPT']
+
+ @env["action_dispatch.request.formats"] ||=
+ if parameters[:format]
+ Array(Mime[parameters[:format]])
+ elsif xhr? || (accept && !accept.include?(?,))
+ accepts
+ else
+ [Mime::HTML]
+ end
+ end
+
+ # Sets the \format by string extension, which can be used to force custom formats
+ # that are not controlled by the extension.
+ #
+ # class ApplicationController < ActionController::Base
+ # before_filter :adjust_format_for_iphone
+ #
+ # private
+ # def adjust_format_for_iphone
+ # request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
+ # end
+ # end
+ def format=(extension)
+ parameters[:format] = extension.to_s
+ @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
+ end
+
+ # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
+ # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
+ # otherwise.
+ def template_format
+ parameter_format = parameters[:format]
+
+ if parameter_format
+ parameter_format
+ elsif xhr?
+ :js
+ else
+ :html
+ end
+ end
+
+ # Receives an array of mimes and return the first user sent mime that
+ # matches the order array.
+ #
+ def negotiate_mime(order)
+ formats.each do |priority|
+ if priority == Mime::ALL
+ return order.first
+ elsif order.include?(priority)
+ return priority
+ end
+ end
+
+ order.include?(Mime::ALL) ? formats.first : nil
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
new file mode 100644
index 0000000000..97546d5f93
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -0,0 +1,50 @@
+require 'active_support/core_ext/hash/keys'
+
+module ActionDispatch
+ module Http
+ module Parameters
+ # Returns both GET and POST \parameters in a single hash.
+ def parameters
+ @env["action_dispatch.request.parameters"] ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
+ end
+ alias :params :parameters
+
+ def path_parameters=(parameters) #:nodoc:
+ @env.delete("action_dispatch.request.symbolized_path_parameters")
+ @env.delete("action_dispatch.request.parameters")
+ @env["action_dispatch.request.path_parameters"] = parameters
+ end
+
+ # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
+ def symbolized_path_parameters
+ @env["action_dispatch.request.symbolized_path_parameters"] ||= path_parameters.symbolize_keys
+ end
+
+ # Returns a hash with the \parameters used to form the \path of the request.
+ # Returned hash keys are strings:
+ #
+ # {'action' => 'my_action', 'controller' => 'my_controller'}
+ #
+ # See <tt>symbolized_path_parameters</tt> for symbolized keys.
+ def path_parameters
+ @env["action_dispatch.request.path_parameters"] ||= {}
+ end
+
+ private
+
+ # Convert nested Hashs to HashWithIndifferentAccess
+ def normalize_parameters(value)
+ case value
+ when Hash
+ h = {}
+ value.each { |k, v| h[k] = normalize_parameters(v) }
+ h.with_indifferent_access
+ when Array
+ value.map { |e| normalize_parameters(e) }
+ else
+ value
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 6e8a5dcb8a..187ce7c15d 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -2,14 +2,17 @@ require 'tempfile'
require 'stringio'
require 'strscan'
-require 'active_support/memoizable'
-require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/string/access'
require 'action_dispatch/http/headers'
module ActionDispatch
class Request < Rack::Request
+ include ActionDispatch::Http::Cache::Request
+ include ActionDispatch::Http::MimeNegotiation
+ include ActionDispatch::Http::Parameters
+ include ActionDispatch::Http::Upload
+ include ActionDispatch::Http::URL
%w[ AUTH_TYPE GATEWAY_INTERFACE
PATH_TRANSLATED REMOTE_HOST
@@ -19,9 +22,11 @@ module ActionDispatch
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
HTTP_NEGOTIATE HTTP_PRAGMA ].each do |env|
- define_method(env.sub(/^HTTP_/n, '').downcase) do
- @env[env]
- end
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{env.sub(/^HTTP_/n, '').downcase}
+ @env["#{env}"]
+ end
+ METHOD
end
def key?(key)
@@ -35,7 +40,8 @@ module ActionDispatch
# <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS
# constant above, an UnknownHttpMethod exception is raised.
def request_method
- HTTP_METHOD_LOOKUP[super] || raise(ActionController::UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
+ 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)}")
end
# Returns the HTTP request \method used for action processing as a
@@ -43,7 +49,8 @@ module ActionDispatch
# method returns <tt>:get</tt> for a HEAD request because the two are
# functionally equivalent from the application's perspective.)
def method
- request_method == :head ? :get : request_method
+ method = env["REQUEST_METHOD"]
+ HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
end
# Is this a GET (or HEAD) request? Equivalent to <tt>request.method == :get</tt>.
@@ -53,17 +60,17 @@ module ActionDispatch
# Is this a POST request? Equivalent to <tt>request.method == :post</tt>.
def post?
- request_method == :post
+ method == :post
end
# Is this a PUT request? Equivalent to <tt>request.method == :put</tt>.
def put?
- request_method == :put
+ method == :put
end
# Is this a DELETE request? Equivalent to <tt>request.method == :delete</tt>.
def delete?
- request_method == :delete
+ method == :delete
end
# Is this a HEAD request? Since <tt>request.method</tt> sees HEAD as <tt>:get</tt>,
@@ -79,25 +86,6 @@ module ActionDispatch
Http::Headers.new(@env)
end
- # Returns the content length of the request as an integer.
- def content_length
- super.to_i
- end
-
- # The MIME type of the HTTP request, such as Mime::XML.
- #
- # For backward compatibility, the post \format is extracted from the
- # X-Post-Data-Format HTTP header if present.
- def content_type
- @env["action_dispatch.request.content_type"] ||= begin
- if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
- Mime::Type.lookup($1.strip.downcase)
- else
- nil
- end
- end
- end
-
def forgery_whitelisted?
method == :get || xhr? || content_type.nil? || !content_type.verify_request?
end
@@ -106,104 +94,9 @@ module ActionDispatch
content_type.to_s
end
- # Returns the accepted MIME type for the request.
- def accepts
- @env["action_dispatch.request.accepts"] ||= begin
- header = @env['HTTP_ACCEPT'].to_s.strip
-
- if header.empty?
- [content_type]
- else
- Mime::Type.parse(header)
- end
- end
- end
-
- def if_modified_since
- if since = env['HTTP_IF_MODIFIED_SINCE']
- Time.rfc2822(since) rescue nil
- end
- end
-
- def if_none_match
- env['HTTP_IF_NONE_MATCH']
- end
-
- def not_modified?(modified_at)
- if_modified_since && modified_at && if_modified_since >= modified_at
- end
-
- def etag_matches?(etag)
- if_none_match && if_none_match == etag
- end
-
- # Check response freshness (Last-Modified and ETag) against request
- # If-Modified-Since and If-None-Match conditions. If both headers are
- # supplied, both must match, or the request is not considered fresh.
- def fresh?(response)
- last_modified = if_modified_since
- etag = if_none_match
-
- return false unless last_modified || etag
-
- success = true
- success &&= not_modified?(response.last_modified) if last_modified
- success &&= etag_matches?(response.etag) if etag
- success
- end
-
- # Returns the Mime type for the \format used in the request.
- #
- # GET /posts/5.xml | request.format => Mime::XML
- # GET /posts/5.xhtml | request.format => Mime::HTML
- # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
- #
- def format(view_path = [])
- formats.first
- end
-
- def formats
- accept = @env['HTTP_ACCEPT']
-
- @env["action_dispatch.request.formats"] ||=
- if parameters[:format]
- Array.wrap(Mime[parameters[:format]])
- elsif xhr? || (accept && !accept.include?(?,))
- accepts
- else
- [Mime::HTML]
- end
- end
-
- # Sets the \format by string extension, which can be used to force custom formats
- # that are not controlled by the extension.
- #
- # class ApplicationController < ActionController::Base
- # before_filter :adjust_format_for_iphone
- #
- # private
- # def adjust_format_for_iphone
- # request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
- # end
- # end
- def format=(extension)
- parameters[:format] = extension.to_s
- @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
- end
-
- # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
- # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
- # otherwise.
- def template_format
- parameter_format = parameters[:format]
-
- if parameter_format
- parameter_format
- elsif xhr?
- :js
- else
- :html
- end
+ # Returns the content length of the request as an integer.
+ def content_length
+ super.to_i
end
# Returns true if the request's "X-Requested-With" header contains
@@ -236,7 +129,7 @@ module ActionDispatch
if @env.include? 'HTTP_CLIENT_IP'
if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
# We don't know which came from the proxy, and which from the user
- raise ActionController::ActionControllerError.new(<<EOM)
+ raise ActionController::ActionControllerError.new <<EOM
IP spoofing attack?!
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
@@ -262,124 +155,6 @@ EOM
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
end
- # Returns the complete URL used for this request.
- def url
- protocol + host_with_port + request_uri
- end
-
- # Returns 'https://' if this is an SSL request and 'http://' otherwise.
- def protocol
- ssl? ? 'https://' : 'http://'
- end
-
- # Is this an SSL request?
- def 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"]
- forwarded.split(/,\s?/).last
- else
- env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
- end
- end
-
- # Returns the host for this request, such as example.com.
- def host
- raw_host_with_port.sub(/:\d+$/, '')
- end
-
- # Returns a \host:\port string for this request, such as "example.com" or
- # "example.com:8080".
- def host_with_port
- "#{host}#{port_string}"
- end
-
- # Returns the port number of this request as an integer.
- def port
- if raw_host_with_port =~ /:(\d+)$/
- $1.to_i
- else
- standard_port
- end
- end
-
- # Returns the standard \port number for this request's protocol.
- def standard_port
- case protocol
- when 'https://' then 443
- else 80
- end
- end
-
- # Returns a \port suffix like ":8080" if the \port number of this request
- # is not the default HTTP \port 80 or HTTPS \port 443.
- def port_string
- port == standard_port ? '' : ":#{port}"
- end
-
- def server_port
- @env['SERVER_PORT'].to_i
- end
-
- # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
- # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
- def domain(tld_length = 1)
- return nil unless named_host?(host)
-
- host.split('.').last(1 + tld_length).join('.')
- end
-
- # Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
- # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
- # such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
- # in "www.rubyonrails.co.uk".
- def subdomains(tld_length = 1)
- return [] unless named_host?(host)
- parts = host.split('.')
- parts[0..-(tld_length+2)]
- end
-
- # Returns the query string, accounting for server idiosyncrasies.
- def query_string
- @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].to_s.split('?', 2)[1] || '')
- end
-
- # Returns the request URI, accounting for server idiosyncrasies.
- # WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
- def request_uri
- if uri = @env['REQUEST_URI']
- # Remove domain, which webrick puts into the request_uri.
- (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
- else
- # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
- uri = @env['PATH_INFO'].to_s
-
- if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
- uri = uri.sub(/#{script_filename}\//, '')
- end
-
- env_qs = @env['QUERY_STRING'].to_s
- uri += "?#{env_qs}" unless env_qs.empty?
-
- if uri.blank?
- @env.delete('REQUEST_URI')
- else
- @env['REQUEST_URI'] = uri
- end
- end
- end
-
- # Returns the interpreted \path to requested resource after all the installation
- # directory of this application was taken into account.
- def path
- path = request_uri.to_s[/\A[^\?]*/]
- path.sub!(/\A#{ActionController::Base.relative_url_root}/, '')
- path
- end
-
# Read the request \body. This is useful for web services that need to
# work with raw requests directly.
def raw_post
@@ -390,33 +165,6 @@ EOM
@env['RAW_POST_DATA']
end
- # Returns both GET and POST \parameters in a single hash.
- def parameters
- @env["action_dispatch.request.parameters"] ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
- end
- alias_method :params, :parameters
-
- def path_parameters=(parameters) #:nodoc:
- @env.delete("action_dispatch.request.symbolized_path_parameters")
- @env.delete("action_dispatch.request.parameters")
- @env["action_dispatch.request.path_parameters"] = parameters
- end
-
- # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
- def symbolized_path_parameters
- @env["action_dispatch.request.symbolized_path_parameters"] ||= path_parameters.symbolize_keys
- end
-
- # Returns a hash with the \parameters used to form the \path of the request.
- # Returned hash keys are strings:
- #
- # {'action' => 'my_action', 'controller' => 'my_controller'}
- #
- # See <tt>symbolized_path_parameters</tt> for symbolized keys.
- def path_parameters
- @env["action_dispatch.request.path_parameters"] ||= {}
- end
-
# The request body is an IO input stream. If the RAW_POST_DATA environment
# variable is already set, wrap it in a StringIO.
def body
@@ -432,18 +180,6 @@ EOM
FORM_DATA_MEDIA_TYPES.include?(content_type.to_s)
end
- # Override Rack's GET method to support indifferent access
- def GET
- @env["action_dispatch.request.query_parameters"] ||= normalize_parameters(super)
- end
- alias_method :query_parameters, :GET
-
- # Override Rack's POST method to support indifferent access
- def POST
- @env["action_dispatch.request.request_parameters"] ||= normalize_parameters(super)
- end
- alias_method :request_parameters, :POST
-
def body_stream #:nodoc:
@env['rack.input']
end
@@ -461,9 +197,18 @@ EOM
@env['rack.session.options'] = options
end
- def flash
- session['flash'] || {}
+ # Override Rack's GET method to support indifferent access
+ def GET
+ @env["action_dispatch.request.query_parameters"] ||= normalize_parameters(super)
+ end
+ alias :query_parameters :GET
+
+ # Override Rack's POST method to support indifferent access
+ def POST
+ @env["action_dispatch.request.request_parameters"] ||= normalize_parameters(super)
end
+ alias :request_parameters :POST
+
# Returns the authorization header regardless of whether it was specified directly or through one of the
# proxy alternatives.
@@ -473,77 +218,5 @@ EOM
@env['X_HTTP_AUTHORIZATION'] ||
@env['REDIRECT_X_HTTP_AUTHORIZATION']
end
-
- # Receives an array of mimes and return the first user sent mime that
- # matches the order array.
- #
- def negotiate_mime(order)
- formats.each do |priority|
- if priority == Mime::ALL
- return order.first
- elsif order.include?(priority)
- return priority
- end
- end
-
- order.include?(Mime::ALL) ? formats.first : nil
- end
-
- private
-
- def named_host?(host)
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
- end
-
- module UploadedFile
- def self.extended(object)
- object.class_eval do
- attr_accessor :original_path, :content_type
- alias_method :local_path, :path if method_defined?(:path)
- end
- end
-
- # Take the basename of the upload's original filename.
- # This handles the full Windows paths given by Internet Explorer
- # (and perhaps other broken user agents) without affecting
- # those which give the lone filename.
- # The Windows regexp is adapted from Perl's File::Basename.
- def original_filename
- unless defined? @original_filename
- @original_filename =
- unless original_path.blank?
- if original_path =~ /^(?:.*[:\\\/])?(.*)/m
- $1
- else
- File.basename original_path
- end
- end
- end
- @original_filename
- end
- end
-
- # Convert nested Hashs to HashWithIndifferentAccess and replace
- # file upload hashs with UploadedFile objects
- def normalize_parameters(value)
- case value
- when Hash
- if value.has_key?(:tempfile)
- upload = value[:tempfile]
- upload.extend(UploadedFile)
- upload.original_path = value[:filename]
- upload.content_type = value[:type]
- upload
- else
- h = {}
- value.each { |k, v| h[k] = normalize_parameters(v) }
- h.with_indifferent_access
- end
- when Array
- value.map { |e| normalize_parameters(e) }
- else
- value
- end
- end
end
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 8524bbd993..65df9b1f03 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -32,6 +32,8 @@ module ActionDispatch # :nodoc:
# end
# end
class Response < Rack::Response
+ include ActionDispatch::Http::Cache::Response
+
attr_accessor :request, :blank
attr_writer :header, :sending_file
@@ -55,10 +57,6 @@ module ActionDispatch # :nodoc:
yield self if block_given?
end
- def cache_control
- @cache_control ||= {}
- end
-
def status=(status)
@status = Rack::Utils.status_code(status)
end
@@ -114,33 +112,6 @@ module ActionDispatch # :nodoc:
# information.
attr_accessor :charset, :content_type
- def last_modified
- if last = headers['Last-Modified']
- Time.httpdate(last)
- end
- end
-
- def last_modified?
- headers.include?('Last-Modified')
- end
-
- def last_modified=(utc_time)
- headers['Last-Modified'] = utc_time.httpdate
- end
-
- def etag
- @etag
- end
-
- def etag?
- @etag
- end
-
- def etag=(etag)
- key = ActiveSupport::Cache.expand_cache_key(etag)
- @etag = %("#{Digest::MD5.hexdigest(key)}")
- end
-
CONTENT_TYPE = "Content-Type"
cattr_accessor(:default_charset) { "utf-8" }
@@ -222,31 +193,6 @@ module ActionDispatch # :nodoc:
end
private
- 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
-
def assign_default_content_type_and_charset!
return if headers[CONTENT_TYPE].present?
@@ -259,27 +205,5 @@ module ActionDispatch # :nodoc:
headers[CONTENT_TYPE] = type
end
- DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
-
- def set_conditional_cache_control!
- control = @cache_control
-
- if control.empty?
- headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
- elsif @cache_control[:no_cache]
- headers["Cache-Control"] = "no-cache"
- else
- extras = control[:extras]
- max_age = control[:max_age]
-
- options = []
- options << "max-age=#{max_age.to_i}" if max_age
- options << (control[:public] ? "public" : "private")
- options << "must-revalidate" if control[:must_revalidate]
- options.concat(extras) if extras
-
- headers["Cache-Control"] = options.join(", ")
- end
- end
end
end
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
new file mode 100644
index 0000000000..dc6121b911
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -0,0 +1,48 @@
+module ActionDispatch
+ module Http
+ module UploadedFile
+ def self.extended(object)
+ object.class_eval do
+ attr_accessor :original_path, :content_type
+ alias_method :local_path, :path if method_defined?(:path)
+ end
+ end
+
+ # Take the basename of the upload's original filename.
+ # This handles the full Windows paths given by Internet Explorer
+ # (and perhaps other broken user agents) without affecting
+ # those which give the lone filename.
+ # The Windows regexp is adapted from Perl's File::Basename.
+ def original_filename
+ unless defined? @original_filename
+ @original_filename =
+ unless original_path.blank?
+ if original_path =~ /^(?:.*[:\\\/])?(.*)/m
+ $1
+ else
+ File.basename original_path
+ end
+ end
+ end
+ @original_filename
+ end
+ end
+
+ module Upload
+ # Convert nested Hashs to HashWithIndifferentAccess and replace
+ # file upload hashs with UploadedFile objects
+ def normalize_parameters(value)
+ if Hash === value && value.has_key?(:tempfile)
+ upload = value[:tempfile]
+ upload.extend(UploadedFile)
+ upload.original_path = value[:filename]
+ upload.content_type = value[:type]
+ upload
+ else
+ super
+ end
+ end
+ private :normalize_parameters
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
new file mode 100644
index 0000000000..40ceb5a9b6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -0,0 +1,129 @@
+module ActionDispatch
+ module Http
+ module URL
+ # Returns the complete URL used for this request.
+ def url
+ protocol + host_with_port + request_uri
+ end
+
+ # Returns 'https://' if this is an SSL request and 'http://' otherwise.
+ def protocol
+ ssl? ? 'https://' : 'http://'
+ end
+
+ # Is this an SSL request?
+ def 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"]
+ forwarded.split(/,\s?/).last
+ else
+ env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
+ end
+ end
+
+ # Returns the host for this request, such as example.com.
+ def host
+ raw_host_with_port.sub(/:\d+$/, '')
+ end
+
+ # Returns a \host:\port string for this request, such as "example.com" or
+ # "example.com:8080".
+ def host_with_port
+ "#{host}#{port_string}"
+ end
+
+ # Returns the port number of this request as an integer.
+ def port
+ if raw_host_with_port =~ /:(\d+)$/
+ $1.to_i
+ else
+ standard_port
+ end
+ end
+
+ # Returns the standard \port number for this request's protocol.
+ def standard_port
+ case protocol
+ when 'https://' then 443
+ else 80
+ end
+ end
+
+ # Returns a \port suffix like ":8080" if the \port number of this request
+ # is not the default HTTP \port 80 or HTTPS \port 443.
+ def port_string
+ port == standard_port ? '' : ":#{port}"
+ end
+
+ def server_port
+ @env['SERVER_PORT'].to_i
+ end
+
+ # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
+ # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
+ def domain(tld_length = 1)
+ return nil unless named_host?(host)
+
+ host.split('.').last(1 + tld_length).join('.')
+ end
+
+ # Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
+ # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
+ # such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
+ # in "www.rubyonrails.co.uk".
+ def subdomains(tld_length = 1)
+ return [] unless named_host?(host)
+ parts = host.split('.')
+ parts[0..-(tld_length+2)]
+ end
+
+ # Returns the query string, accounting for server idiosyncrasies.
+ def query_string
+ @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].to_s.split('?', 2)[1] || '')
+ end
+
+ # Returns the request URI, accounting for server idiosyncrasies.
+ # WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
+ def request_uri
+ if uri = @env['REQUEST_URI']
+ # Remove domain, which webrick puts into the request_uri.
+ (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
+ else
+ # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
+ uri = @env['PATH_INFO'].to_s
+
+ if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
+ uri = uri.sub(/#{script_filename}\//, '')
+ end
+
+ env_qs = @env['QUERY_STRING'].to_s
+ uri += "?#{env_qs}" unless env_qs.empty?
+
+ if uri.blank?
+ @env.delete('REQUEST_URI')
+ else
+ @env['REQUEST_URI'] = uri
+ end
+ end
+ end
+
+ # Returns the interpreted \path to requested resource after all the installation
+ # directory of this application was taken into account.
+ def path
+ path = request_uri.to_s[/\A[^\?]*/]
+ path.sub!(/\A#{ActionController::Base.relative_url_root}/, '')
+ path
+ end
+
+ private
+
+ def named_host?(host)
+ !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 49bc20f11f..5ec406e134 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -1,4 +1,10 @@
module ActionDispatch
+ # Provide callbacks to be executed before and after the request dispatch.
+ #
+ # It also provides a to_prepare callback, which is performed in all requests
+ # in development by only once in production and notification callback for async
+ # operations.
+ #
class Callbacks
include ActiveSupport::Callbacks
@@ -29,12 +35,6 @@ module ActionDispatch
set_callback(:call, :after, *args, &block)
end
- class << self
- # DEPRECATED
- alias_method :before_dispatch, :before
- alias_method :after_dispatch, :after
- end
-
def initialize(app, prepare_each_request = false)
@app, @prepare_each_request = app, prepare_each_request
run_callbacks(:prepare)
@@ -45,6 +45,8 @@ module ActionDispatch
run_callbacks(:prepare) if @prepare_each_request
@app.call(env)
end
+ ensure
+ ActiveSupport::Notifications.instrument "action_dispatch.callback"
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
new file mode 100644
index 0000000000..99b36366d6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -0,0 +1,174 @@
+module ActionDispatch
+ class Request
+ # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
+ # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
+ # to put a new one.
+ def flash
+ session['flash'] ||= Flash::FlashHash.new
+ end
+ end
+
+ # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
+ # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
+ # action that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can
+ # then expose the flash to its template. Actually, that exposure is automatically done. Example:
+ #
+ # class PostsController < ActionController::Base
+ # def create
+ # # save post
+ # flash[:notice] = "Successfully created post"
+ # redirect_to posts_path(@post)
+ # end
+ #
+ # def show
+ # # doesn't need to assign the flash notice to the template, that's done automatically
+ # end
+ # end
+ #
+ # show.html.erb
+ # <% if flash[:notice] %>
+ # <div class="notice"><%= flash[:notice] %></div>
+ # <% end %>
+ #
+ # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
+ # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
+ #
+ # See docs on the FlashHash class for more details about the flash.
+ class Flash
+ class FlashNow #:nodoc:
+ def initialize(flash)
+ @flash = flash
+ end
+
+ def []=(k, v)
+ @flash[k] = v
+ @flash.discard(k)
+ v
+ end
+
+ def [](k)
+ @flash[k]
+ end
+ end
+
+ class FlashHash < Hash
+ def initialize #:nodoc:
+ super
+ @used = Set.new
+ end
+
+ def []=(k, v) #:nodoc:
+ keep(k)
+ super
+ end
+
+ def update(h) #:nodoc:
+ h.keys.each { |k| keep(k) }
+ super
+ end
+
+ alias :merge! :update
+
+ def replace(h) #:nodoc:
+ @used = Set.new
+ super
+ end
+
+ # Sets a flash that will not be available to the next action, only to the current.
+ #
+ # flash.now[:message] = "Hello current action"
+ #
+ # This method enables you to use the flash as a central messaging system in your app.
+ # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
+ # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
+ # vanish when the current action is done.
+ #
+ # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
+ def now
+ FlashNow.new(self)
+ end
+
+ # Keeps either the entire current flash or a specific flash entry available for the next action:
+ #
+ # flash.keep # keeps the entire flash
+ # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
+ def keep(k = nil)
+ use(k, false)
+ end
+
+ # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
+ #
+ # flash.discard # discard the entire flash at the end of the current action
+ # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
+ def discard(k = nil)
+ use(k)
+ end
+
+ # Mark for removal entries that were kept, and delete unkept ones.
+ #
+ # This method is called automatically by filters, so you generally don't need to care about it.
+ def sweep #:nodoc:
+ keys.each do |k|
+ unless @used.include?(k)
+ @used << k
+ else
+ delete(k)
+ @used.delete(k)
+ end
+ end
+
+ # clean up after keys that could have been left over by calling reject! or shift on the flash
+ (@used - keys).each{ |k| @used.delete(k) }
+ end
+
+ # Convenience accessor for flash[:alert]
+ def alert
+ self[:alert]
+ end
+
+ # Convenience accessor for flash[:alert]=
+ def alert=(message)
+ self[:alert] = message
+ end
+
+ # Convenience accessor for flash[:notice]
+ def notice
+ self[:notice]
+ end
+
+ # Convenience accessor for flash[:notice]=
+ def notice=(message)
+ self[:notice] = message
+ end
+
+ private
+ # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
+ # use() # marks the entire flash as used
+ # use('msg') # marks the "msg" entry as used
+ # use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
+ # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
+ # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
+ # if no key is passed.
+ def use(key = nil, used = true)
+ Array(key || keys).each { |k| used ? @used << k : @used.delete(k) }
+ return key ? self[key] : self
+ end
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ if (session = env['rack.session']) && (flash = session['flash'])
+ flash.sweep
+ end
+
+ @app.call(env)
+ ensure
+ if (session = env['rack.session']) && (flash = session['flash']) && flash.empty?
+ session.delete('flash')
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/head.rb b/actionpack/lib/action_dispatch/middleware/head.rb
new file mode 100644
index 0000000000..56e2d2f2a8
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/head.rb
@@ -0,0 +1,18 @@
+module ActionDispatch
+ class Head
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ if env["REQUEST_METHOD"] == "HEAD"
+ env["REQUEST_METHOD"] = "GET"
+ env["rack.methodoverride.original_method"] = "HEAD"
+ status, headers, body = @app.call(env)
+ [status, headers, []]
+ else
+ @app.call(env)
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 7d4f0998ce..311880cabc 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -102,7 +102,7 @@ module ActionDispatch
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
- raise ActionDispatch::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"
+ 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
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 4ebc8a2ab9..10f04dcdf6 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -1,7 +1,24 @@
require 'active_support/core_ext/exception'
+require 'active_support/notifications'
require 'action_dispatch/http/request'
module ActionDispatch
+ # This middleware rescues any exception returned by the application and renders
+ # nice exception pages if it's being rescued locally.
+ #
+ # Every time an exception is caught, a notification is published, becoming a good API
+ # to deal with exceptions. So, if you want send an e-mail through ActionMailer
+ # everytime this notification is published, you just need to do the following:
+ #
+ # ActiveSupport::Notifications.subscribe "action_dispatch.show_exception" do |name, start, end, instrumentation_id, payload|
+ # ExceptionNotifier.deliver_exception(start, payload)
+ # end
+ #
+ # The payload is a hash which has two pairs:
+ #
+ # * :env - Contains the rack env for the given request;
+ # * :exception - The exception raised;
+ #
class ShowExceptions
LOCALHOST = '127.0.0.1'.freeze
@@ -44,8 +61,11 @@ module ActionDispatch
def call(env)
@app.call(env)
rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
- render_exception(env, exception)
+ ActiveSupport::Notifications.instrument 'action_dispatch.show_exception',
+ :env => env, :exception => exception do
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ render_exception(env, exception)
+ end
end
private
diff --git a/actionpack/lib/action_dispatch/middleware/string_coercion.rb b/actionpack/lib/action_dispatch/middleware/string_coercion.rb
deleted file mode 100644
index 232e947835..0000000000
--- a/actionpack/lib/action_dispatch/middleware/string_coercion.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-module ActionDispatch
- class StringCoercion
- class UglyBody < ActiveSupport::BasicObject
- def initialize(body)
- @body = body
- end
-
- def each
- @body.each do |part|
- yield part.to_s
- end
- end
-
- private
- def method_missing(*args, &block)
- @body.__send__(*args, &block)
- end
- end
-
- def initialize(app)
- @app = app
- end
-
- def call(env)
- status, headers, body = @app.call(env)
- [status, headers, UglyBody.new(body)]
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 8f33346a4f..9aaa4355f2 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -68,16 +68,11 @@ module ActionDispatch
end
def normalize_path(path)
- path = nil if path == ""
- path = "#{@scope[:path]}#{path}" if @scope[:path]
- path = Rack::Mount::Utils.normalize_path(path) if path
-
- raise ArgumentError, "path is required" unless path
-
- path
+ path = "#{@scope[:path]}/#{path}"
+ raise ArgumentError, "path is required" if path.empty?
+ Mapper.normalize_path(path)
end
-
def app
Constraints.new(
to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults),
@@ -123,7 +118,6 @@ module ActionDispatch
end
end
-
def blocks
if @options[:constraints].present? && !@options[:constraints].is_a?(Hash)
block = @options[:constraints]
@@ -162,6 +156,14 @@ module ActionDispatch
end
end
+ # Invokes Rack::Mount::Utils.normalize path and ensure that
+ # (:locale) becomes (/:locale) instead of /(:locale).
+ def self.normalize_path(path)
+ path = Rack::Mount::Utils.normalize_path(path)
+ path.sub!(%r{/\(+/?:}, '(/:')
+ path
+ end
+
module Base
def initialize(set)
@set = set
@@ -200,13 +202,22 @@ module ActionDispatch
path = args.shift || block
path_proc = path.is_a?(Proc) ? path : proc { |params| path % params }
status = options[:status] || 301
+ body = 'Moved Permanently'
lambda do |env|
- req = Rack::Request.new(env)
- params = path_proc.call(env["action_dispatch.request.path_parameters"])
- url = req.scheme + '://' + req.host + params
+ req = Request.new(env)
+
+ uri = URI.parse(path_proc.call(req.params.symbolize_keys))
+ uri.scheme ||= req.scheme
+ uri.host ||= req.host
+ uri.port ||= req.port unless req.port == 80
- [ status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently'] ]
+ headers = {
+ 'Location' => uri.to_s,
+ 'Content-Type' => 'text/html',
+ 'Content-Length' => body.length.to_s
+ }
+ [ status, headers, [body] ]
end
end
@@ -236,46 +247,35 @@ module ActionDispatch
options[:controller] = args.first
end
- if path = options.delete(:path)
- path_set = true
- path, @scope[:path] = @scope[:path], Rack::Mount::Utils.normalize_path(@scope[:path].to_s + path.to_s)
- else
- path_set = false
- end
+ recover = {}
- if name_prefix = options.delete(:name_prefix)
- name_prefix_set = true
- name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{name_prefix}" : name_prefix)
- else
- name_prefix_set = false
+ options[:constraints] ||= {}
+ unless options[:constraints].is_a?(Hash)
+ block, options[:constraints] = options[:constraints], {}
end
- if controller = options.delete(:controller)
- controller_set = true
- controller, @scope[:controller] = @scope[:controller], controller
- else
- controller_set = false
+ scope_options.each do |option|
+ if value = options.delete(option)
+ recover[option] = @scope[option]
+ @scope[option] = send("merge_#{option}_scope", @scope[option], value)
+ end
end
- constraints = options.delete(:constraints) || {}
- unless constraints.is_a?(Hash)
- block, constraints = constraints, {}
- end
- constraints, @scope[:constraints] = @scope[:constraints], (@scope[:constraints] || {}).merge(constraints)
- blocks, @scope[:blocks] = @scope[:blocks], (@scope[:blocks] || []) + [block]
+ recover[:block] = @scope[:blocks]
+ @scope[:blocks] = merge_blocks_scope(@scope[:blocks], block)
- options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options)
+ recover[:options] = @scope[:options]
+ @scope[:options] = merge_options_scope(@scope[:options], options)
yield
-
self
ensure
- @scope[:path] = path if path_set
- @scope[:name_prefix] = name_prefix if name_prefix_set
- @scope[:controller] = controller if controller_set
- @scope[:options] = options
- @scope[:blocks] = blocks
- @scope[:constraints] = constraints
+ scope_options.each do |option|
+ @scope[option] = recover[option] if recover.has_key?(option)
+ end
+
+ @scope[:options] = recover[:options]
+ @scope[:blocks] = recover[:block]
end
def controller(controller)
@@ -283,7 +283,7 @@ module ActionDispatch
end
def namespace(path)
- scope("/#{path}") { yield }
+ scope(path.to_s, :name_prefix => path.to_s, :namespace => path.to_s) { yield }
end
def constraints(constraints = {})
@@ -304,25 +304,83 @@ module ActionDispatch
args.push(options)
super(*args)
end
+
+ private
+ def scope_options
+ @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
+ end
+
+ def merge_path_scope(parent, child)
+ Mapper.normalize_path("#{parent}/#{child}")
+ end
+
+ def merge_name_prefix_scope(parent, child)
+ parent ? "#{parent}_#{child}" : child
+ end
+
+ def merge_namespace_scope(parent, child)
+ parent ? "#{parent}/#{child}" : child
+ end
+
+ def merge_controller_scope(parent, child)
+ @scope[:namespace] ? "#{@scope[:namespace]}/#{child}" : child
+ end
+
+ def merge_resources_path_names_scope(parent, child)
+ merge_options_scope(parent, child)
+ end
+
+ def merge_constraints_scope(parent, child)
+ merge_options_scope(parent, child)
+ end
+
+ def merge_blocks_scope(parent, child)
+ (parent || []) + [child]
+ end
+
+ def merge_options_scope(parent, child)
+ (parent || {}).merge(child)
+ end
end
module Resources
+ CRUD_ACTIONS = [:index, :show, :create, :update, :destroy]
+
class Resource #:nodoc:
- attr_reader :plural, :singular
+ def self.default_actions
+ [:index, :create, :new, :show, :update, :destroy, :edit]
+ end
+
+ attr_reader :plural, :singular, :options
def initialize(entities, options = {})
entities = entities.to_s
+ @options = options
@plural = entities.pluralize
@singular = entities.singularize
end
+ def default_actions
+ self.class.default_actions
+ end
+
+ def actions
+ if only = options[:only]
+ only.map(&:to_sym)
+ elsif except = options[:except]
+ default_actions - except.map(&:to_sym)
+ else
+ default_actions
+ end
+ end
+
def name
- plural
+ options[:as] || plural
end
def controller
- plural
+ options[:controller] || plural
end
def member_name
@@ -339,15 +397,24 @@ module ActionDispatch
end
class SingletonResource < Resource #:nodoc:
+ def self.default_actions
+ [:show, :create, :update, :destroy, :new, :edit]
+ end
+
def initialize(entity, options = {})
super
end
def name
- singular
+ options[:as] || singular
end
end
+ def initialize(*args)
+ super
+ @scope[:resources_path_names] = @set.resources_path_names
+ end
+
def resource(*resources, &block)
options = resources.extract_options!
@@ -357,7 +424,14 @@ module ActionDispatch
return self
end
- resource = SingletonResource.new(resources.pop)
+ if path_names = options.delete(:path_names)
+ scope(:resources_path_names => path_names) do
+ resource(resources, options)
+ end
+ return self
+ end
+
+ resource = SingletonResource.new(resources.pop, options)
if @scope[:scope_level] == :resources
nested do
@@ -366,16 +440,16 @@ module ActionDispatch
return self
end
- scope(:path => "/#{resource.name}", :controller => resource.controller) do
+ scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resource, resource) do
yield if block_given?
- get "(.:format)", :to => :show, :as => resource.member_name
- post "(.:format)", :to => :create
- put "(.:format)", :to => :update
- delete "(.:format)", :to => :destroy
- get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}"
- get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}"
+ get :show, :as => resource.member_name if resource.actions.include?(:show)
+ post :create if resource.actions.include?(:create)
+ put :update if resource.actions.include?(:update)
+ delete :destroy if resource.actions.include?(:destroy)
+ get :new, :as => resource.singular if resource.actions.include?(:new)
+ get :edit, :as => resource.singular if resource.actions.include?(:edit)
end
end
@@ -391,7 +465,14 @@ module ActionDispatch
return self
end
- resource = Resource.new(resources.pop)
+ if path_names = options.delete(:path_names)
+ scope(:resources_path_names => path_names) do
+ resources(resources, options)
+ end
+ return self
+ end
+
+ resource = Resource.new(resources.pop, options)
if @scope[:scope_level] == :resources
nested do
@@ -400,28 +481,22 @@ module ActionDispatch
return self
end
- scope(:path => "/#{resource.name}", :controller => resource.controller) do
+ scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resources, resource) do
yield if block_given?
with_scope_level(:collection) do
- get "(.:format)", :to => :index, :as => resource.collection_name
- post "(.:format)", :to => :create
-
- with_exclusive_name_prefix :new do
- get "/new(.:format)", :to => :new, :as => resource.singular
- end
+ get :index, :as => resource.collection_name if resource.actions.include?(:index)
+ post :create if resource.actions.include?(:create)
+ get :new, :as => resource.singular if resource.actions.include?(:new)
end
with_scope_level(:member) do
- scope("/:id") do
- get "(.:format)", :to => :show, :as => resource.member_name
- put "(.:format)", :to => :update
- delete "(.:format)", :to => :destroy
-
- with_exclusive_name_prefix :edit do
- get "/edit(.:format)", :to => :edit, :as => resource.singular
- end
+ scope(':id') do
+ get :show, :as => resource.member_name if resource.actions.include?(:show)
+ put :update if resource.actions.include?(:update)
+ delete :destroy if resource.actions.include?(:destroy)
+ get :edit, :as => resource.singular if resource.actions.include?(:edit)
end
end
end
@@ -448,7 +523,7 @@ module ActionDispatch
end
with_scope_level(:member) do
- scope("/:id", :name_prefix => parent_resource.member_name, :as => "") do
+ scope(':id', :name_prefix => parent_resource.member_name, :as => "") do
yield
end
end
@@ -460,7 +535,7 @@ module ActionDispatch
end
with_scope_level(:nested) do
- scope("/#{parent_resource.id_segment}", :name_prefix => parent_resource.member_name) do
+ scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do
yield
end
end
@@ -474,9 +549,22 @@ module ActionDispatch
return self
end
+ resources_path_names = options.delete(:path_names)
+
if args.first.is_a?(Symbol)
- with_exclusive_name_prefix(args.first) do
- return match("/#{args.first}(.:format)", options.merge(:to => args.first.to_sym))
+ action = args.first
+ if CRUD_ACTIONS.include?(action)
+ begin
+ old_path = @scope[:path]
+ @scope[:path] = "#{@scope[:path]}(.:format)"
+ return match(options.reverse_merge(:to => action))
+ ensure
+ @scope[:path] = old_path
+ end
+ else
+ with_exclusive_name_prefix(action) do
+ return match("#{action_path(action, resources_path_names)}(.:format)", options.reverse_merge(:to => action))
+ end
end
end
@@ -502,6 +590,11 @@ module ActionDispatch
end
private
+ def action_path(name, path_names = nil)
+ path_names ||= @scope[:resources_path_names]
+ path_names[name.to_sym] || name.to_s
+ end
+
def with_exclusive_name_prefix(prefix)
begin
old_name_prefix = @scope[:name_prefix]
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index bd397432ce..660d28dbec 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -74,9 +74,8 @@ module ActionDispatch
@routes = {}
@helpers = []
- @module ||= Module.new
- @module.instance_methods.each do |selector|
- @module.class_eval { remove_method selector }
+ @module ||= Module.new do
+ instance_methods.each { |selector| remove_method(selector) }
end
end
@@ -138,67 +137,87 @@ module ActionDispatch
end
end
- def named_helper_module_eval(code, *args)
- @module.module_eval(code, *args)
- end
-
def define_hash_access(route, name, kind, options)
selector = hash_access_name(name, kind)
- named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
+
+ # We use module_eval to avoid leaks
+ @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
def #{selector}(options = nil) # def hash_for_users_url(options = nil)
options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false}
end # end
protected :#{selector} # protected :hash_for_users_url
- end_eval
+ END_EVAL
helpers << selector
end
+ # Create a url helper allowing ordered parameters to be associated
+ # with corresponding dynamic segments, so you can do:
+ #
+ # foo_url(bar, baz, bang)
+ #
+ # Instead of:
+ #
+ # foo_url(:bar => bar, :baz => baz, :bang => bang)
+ #
+ # Also allow options hash, so you can do:
+ #
+ # foo_url(bar, baz, bang, :sort_by => 'baz')
+ #
def define_url_helper(route, name, kind, options)
selector = url_helper_name(name, kind)
- # The segment keys used for positional parameters
-
hash_access_method = hash_access_name(name, kind)
- # allow ordered parameters to be associated with corresponding
- # dynamic segments, so you can do
+ # We use module_eval to avoid leaks.
#
- # foo_url(bar, baz, bang)
+ # def users_url(*args)
+ # if args.empty? || Hash === args.first
+ # options = hash_for_users_url(args.first || {})
+ # else
+ # options = hash_for_users_url(args.extract_options!)
+ # default = default_url_options(options) if self.respond_to?(:default_url_options, true)
+ # options = (default ||= {}).merge(options)
#
- # instead of
+ # keys = []
+ # keys -= options.keys if args.size < keys.size - 1
#
- # foo_url(:bar => bar, :baz => baz, :bang => bang)
+ # args = args.zip(keys).inject({}) do |h, (v, k)|
+ # h[k] = v
+ # h
+ # end
#
- # Also allow options hash, so you can do
+ # # Tell url_for to skip default_url_options
+ # options[:use_defaults] = false
+ # options.merge!(args)
+ # end
#
- # foo_url(bar, baz, bang, :sort_by => 'baz')
- #
- named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
- def #{selector}(*args) # def users_url(*args)
- #
- opts = if args.empty? || Hash === args.first # opts = if args.empty? || Hash === args.first
- args.first || {} # args.first || {}
- else # else
- options = args.extract_options! # options = args.extract_options!
- args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| # args = args.zip([]).inject({}) do |h, (v, k)|
- h[k] = v # h[k] = v
- h # h
- end # end
- options.merge(args) # options.merge(args)
- end # end
- #
- url_for(#{hash_access_method}(opts)) # url_for(hash_for_users_url(opts))
- #
- end # end
- #Add an alias to support the now deprecated formatted_* URL. # #Add an alias to support the now deprecated formatted_* URL.
- def formatted_#{selector}(*args) # def formatted_users_url(*args)
- ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn(
- "formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " +
- "Please pass format to the standard " + # "Please pass format to the standard " +
- "#{selector} method instead.", caller) # "users_url method instead.", caller)
- #{selector}(*args) # users_url(*args)
- end # end
- protected :#{selector} # protected :users_url
- end_eval
+ # url_for(options)
+ # end
+ @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
+ def #{selector}(*args)
+ if args.empty? || Hash === args.first
+ options = #{hash_access_method}(args.first || {})
+ else
+ options = #{hash_access_method}(args.extract_options!)
+ default = default_url_options(options) if self.respond_to?(:default_url_options, true)
+ options = (default ||= {}).merge(options)
+
+ keys = #{route.segment_keys.inspect}
+ keys -= options.keys if args.size < keys.size - 1 # take format into account
+
+ args = args.zip(keys).inject({}) do |h, (v, k)|
+ h[k] = v
+ h
+ end
+
+ # Tell url_for to skip default_url_options
+ options[:use_defaults] = false
+ options.merge!(args)
+ end
+
+ url_for(options)
+ end
+ protected :#{selector}
+ END_EVAL
helpers << selector
end
end
@@ -206,9 +225,16 @@ module ActionDispatch
attr_accessor :routes, :named_routes
attr_accessor :disable_clear_and_finalize
+ def self.default_resources_path_names
+ { :new => 'new', :edit => 'edit' }
+ end
+
+ attr_accessor :resources_path_names
+
def initialize
self.routes = []
self.named_routes = NamedRouteCollection.new
+ self.resources_path_names = self.class.default_resources_path_names
@disable_clear_and_finalize = false
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 5686bbdbde..c2486d3730 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -2,6 +2,15 @@ module ActionDispatch
module Assertions
# A small suite of assertions that test responses from Rails applications.
module ResponseAssertions
+ extend ActiveSupport::Concern
+
+ included do
+ # TODO: Need to pull in AV::Template monkey patches that track which
+ # templates are rendered. assert_template should probably be part
+ # of AV instead of AD.
+ require 'action_view/test_case'
+ end
+
# Asserts that the response is one of the following types:
#
# * <tt>:success</tt> - Status code was 200
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index c2dc591ff7..a6b1126e2b 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -524,7 +524,7 @@ module ActionDispatch
fix_content = lambda do |node|
# Gets around a bug in the Rails 1.1 HTML parser.
- node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { CGI.escapeHTML($1) }
+ node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
end
selected = elements.map do |element|
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 2a5f5dcd5c..4ec47d146c 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -1,6 +1,5 @@
require 'stringio'
require 'uri'
-require 'active_support/test_case'
require 'active_support/core_ext/object/metaclass'
require 'rack/test'
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 8ce6e82524..93aa69c060 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -52,6 +52,8 @@ module ActionView
autoload :TemplateHandler, 'action_view/template'
autoload :TemplateHandlers, 'action_view/template'
end
+
+ autoload :TestCase, 'action_view/test_case'
end
require 'action_view/erb/util'
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index c70f29f098..87b7adf6c4 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -216,7 +216,7 @@ module ActionView
end
options[:object_name] ||= params.first
- I18n.with_options :locale => options[:locale], :scope => [:activemodel, :errors, :template] do |locale|
+ I18n.with_options :locale => options[:locale], :scope => [:errors, :template] do |locale|
header_message = if options.include?(:header_message)
options[:header_message]
else
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 4b51dc7856..34f38b0a8a 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -616,7 +616,7 @@ module ActionView
build_selects_from_types(order)
else
- "#{select_date}#{@options[:datetime_separator]}#{select_time}"
+ "#{select_date}#{@options[:datetime_separator]}#{select_time}".html_safe!
end
end
@@ -835,7 +835,7 @@ module ActionView
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
select_html << select_options_as_html.to_s
- content_tag(:select, select_html, select_options) + "\n"
+ (content_tag(:select, select_html, select_options) + "\n").html_safe!
end
# Builds a prompt option tag with supplied options or from default options
@@ -860,12 +860,12 @@ module ActionView
# build_hidden(:year, 2008)
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
def build_hidden(type, value)
- tag(:input, {
+ (tag(:input, {
:type => "hidden",
:id => input_id_from_type(type),
:name => input_name_from_type(type),
:value => value
- }) + "\n"
+ }) + "\n").html_safe!
end
# Returns the name attribute for the input tag
@@ -896,7 +896,7 @@ module ActionView
separator = separator(type) unless type == order.first # don't add on last field
select.insert(0, separator.to_s + send("select_#{type}").to_s)
end
- select
+ select.html_safe!
end
# Returns the separator for a given datetime component
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 81c9c88820..20e9916d62 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -505,7 +505,7 @@ module ActionView
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
- # is found in the current I18n locale (through views.labels.<modelname>.<attribute>) or you specify it explicitly.
+ # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
# Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
# target labels for radio_button tags (where the value is used in the ID of the input tag).
@@ -517,8 +517,8 @@ module ActionView
# You can localize your labels based on model and attribute names.
# For example you can define the following in your locale (e.g. en.yml)
#
- # views:
- # labels:
+ # helpers:
+ # label:
# post:
# body: "Write your entire text here"
#
@@ -777,7 +777,7 @@ module ActionView
options["for"] ||= name_and_id["id"]
content = if text.blank?
- I18n.t("views.labels.#{object_name}.#{method_name}", :default => "").presence
+ I18n.t("helpers.label.#{object_name}.#{method_name}", :default => "").presence
else
text.to_s
end
@@ -798,7 +798,7 @@ module ActionView
if field_type == "hidden"
options.delete("size")
end
- options["type"] = field_type
+ options["type"] ||= field_type
options["value"] ||= value_before_type_cast(object) unless field_type == "file"
options["value"] &&= html_escape(options["value"])
add_default_name_and_id(options)
@@ -842,7 +842,12 @@ module ActionView
checked = self.class.check_box_checked?(value(object), checked_value)
end
options["checked"] = "checked" if checked
- add_default_name_and_id(options)
+ if options["multiple"]
+ add_default_name_and_id_for_value(checked_value, options)
+ options.delete("multiple")
+ else
+ add_default_name_and_id(options)
+ end
hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
checkbox = tag("input", options)
(hidden + checkbox).html_safe!
@@ -1058,7 +1063,7 @@ module ActionView
def radio_button(method, tag_value, options = {})
@template.radio_button(@object_name, method, tag_value, objectify_options(options))
end
-
+
def hidden_field(method, options = {})
@emitted_hidden_id = true if method == :id
@template.hidden_field(@object_name, method, objectify_options(options))
@@ -1072,7 +1077,36 @@ module ActionView
@template.error_messages_for(@object_name, objectify_options(options))
end
- def submit(value = "Save changes", options = {})
+ # Add the submit button for the given form. When no value is given, it checks
+ # if the object is a new resource or not to create the proper label:
+ #
+ # <% form_for @post do |f| %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # In the example above, if @post is a new record, it will use "Create Post" as
+ # submit button label, otherwise, it uses "Update Post".
+ #
+ # Those labels can be customized using I18n, under the helpers.submit key and accept
+ # the {{model}} as translation interpolation:
+ #
+ # en:
+ # helpers:
+ # submit:
+ # create: "Create a {{model}}"
+ # update: "Confirm changes to {{model}}"
+ #
+ # It also searches for a key specific for the given object:
+ #
+ # en:
+ # helpers:
+ # submit:
+ # post:
+ # create: "Add {{model}}"
+ #
+ def submit(value=nil, options={})
+ value, options = nil, value if value.is_a?(Hash)
+ value ||= submit_default_value
@template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
end
@@ -1085,6 +1119,24 @@ module ActionView
@default_options.merge(options.merge(:object => @object))
end
+ def submit_default_value
+ object = @object.respond_to?(:to_model) ? @object.to_model : @object
+ key = object ? (object.new_record? ? :create : :update) : :submit
+
+ model = if object.class.respond_to?(:model_name)
+ object.class.model_name.human
+ else
+ @object_name.to_s.humanize
+ end
+
+ defaults = []
+ defaults << :"helpers.submit.#{object_name}.#{key}"
+ defaults << :"helpers.submit.#{key}"
+ defaults << "#{key.to_s.humanize} #{model}"
+
+ I18n.t(defaults.shift, :model => model, :default => defaults)
+ end
+
def nested_attributes_association?(association_name)
@object.respond_to?("#{association_name}_attributes=")
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 935ab5f3e8..02ad637509 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -571,7 +571,7 @@ module ActionView
option_tags = "<option value=\"\">#{options[:include_blank] if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
end
if value.blank? && options[:prompt]
- prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select')
+ prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
"<option value=\"\">#{prompt}</option>\n" + option_tags
else
option_tags
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 397871b85e..64b71663c3 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -92,7 +92,7 @@ module ActionView
:precision => precision,
:delimiter => delimiter,
:separator => separator)
- ).gsub(/%u/, unit)
+ ).gsub(/%u/, unit).html_safe!
rescue
number
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 8c1f0ad81f..ff7bc3b34e 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -1030,7 +1030,7 @@ module ActionView
# page.hide 'spinner'
# end
def update_page(&block)
- JavaScriptGenerator.new(@template, &block).to_s
+ JavaScriptGenerator.new(@template, &block).to_s.html_safe!
end
# Works like update_page but wraps the generated JavaScript in a <script>
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index a3bee3e8c2..814d86812d 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -565,7 +565,7 @@ module ActionView
end
link_text = block_given?? yield(href) : href
- href = 'http://' + href unless href.index('http') == 0
+ href = 'http://' + href unless href =~ %r{^[a-z]+://}i
content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('')
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 5b136d4f54..14628c5404 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -11,6 +11,11 @@ module ActionView
module UrlHelper
include JavaScriptHelper
+ # Need to map default url options to controller one.
+ def default_url_options(*args) #:nodoc:
+ @controller.send(:default_url_options, *args)
+ end
+
# Returns the URL for the set of +options+ provided. This takes the
# same options as +url_for+ in Action Controller (see the
# documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
@@ -461,10 +466,10 @@ module ActionView
string = ''
extras = ''
- extras << "cc=#{CGI.escape(cc).gsub("+", "%20")}&" unless cc.nil?
- extras << "bcc=#{CGI.escape(bcc).gsub("+", "%20")}&" unless bcc.nil?
- extras << "body=#{CGI.escape(body).gsub("+", "%20")}&" unless body.nil?
- extras << "subject=#{CGI.escape(subject).gsub("+", "%20")}&" unless subject.nil?
+ 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 = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
email_address = email_address.to_s
diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml
index 5e2a92b89a..a3548051c1 100644
--- a/actionpack/lib/action_view/locale/en.yml
+++ b/actionpack/lib/action_view/locale/en.yml
@@ -9,7 +9,7 @@
delimiter: ","
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3
-
+
# Used in number_to_currency()
currency:
format:
@@ -20,15 +20,15 @@
separator: "."
delimiter: ","
precision: 2
-
+
# Used in number_to_percentage()
percentage:
format:
# These three are to override number.format and are optional
- # separator:
+ # separator:
delimiter: ""
- # precision:
-
+ # precision:
+
# Used in number_to_precision()
precision:
format:
@@ -36,12 +36,12 @@
# separator:
delimiter: ""
# precision:
-
+
# Used in number_to_human_size()
human:
format:
# These three are to override number.format and are optional
- # separator:
+ # separator:
delimiter: ""
precision: 1
storage_units:
@@ -102,16 +102,22 @@
minute: "Minute"
second: "Seconds"
- activemodel:
- errors:
- template:
- header:
- one: "1 error prohibited this {{model}} from being saved"
- other: "{{count}} errors prohibited this {{model}} from being saved"
- # The variable :count is also available
- body: "There were problems with the following fields:"
+ errors:
+ template:
+ header:
+ one: "1 error prohibited this {{model}} from being saved"
+ other: "{{count}} errors prohibited this {{model}} from being saved"
+ # The variable :count is also available
+ body: "There were problems with the following fields:"
- support:
+ helpers:
select:
- # default value for :prompt => true in FormOptionsHelper
+ # Default value for :prompt => true in FormOptionsHelper
prompt: "Please select"
+
+ # Default translation keys for submit FormHelper
+ submit:
+ create: 'Create {{model}}'
+ update: 'Update {{model}}'
+ submit: 'Save {{model}}'
+
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index a90e0636b9..968dc7b25e 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -1,2 +1,17 @@
require "action_view"
-require "rails" \ No newline at end of file
+require "rails"
+
+module ActionView
+ class Railtie < Rails::Railtie
+ plugin_name :action_view
+
+ require "action_view/railties/subscriber"
+ subscriber ActionView::Railties::Subscriber.new
+
+ initializer "action_view.cache_asset_timestamps" do |app|
+ unless app.config.cache_classes
+ ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/railties/subscriber.rb b/actionpack/lib/action_view/railties/subscriber.rb
new file mode 100644
index 0000000000..803f19379c
--- /dev/null
+++ b/actionpack/lib/action_view/railties/subscriber.rb
@@ -0,0 +1,24 @@
+module ActionView
+ module Railties
+ class Subscriber < Rails::Subscriber
+ def render_template(event)
+ message = "Rendered #{from_rails_root(event.payload[:identifier])}"
+ message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
+ message << (" (%.1fms)" % event.duration)
+ info(message)
+ end
+ alias :render_partial :render_template
+ alias :render_collection :render_template
+
+ def logger
+ ActionController::Base.logger
+ end
+
+ protected
+
+ def from_rails_root(string)
+ string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "")
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 5158415c20..8c936ae09e 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -212,34 +212,34 @@ module ActionView
end
def render
- options = @options
+ identifier = ((@template = find_template) ? @template.identifier : @path)
if @collection
- ActiveSupport::Notifications.instrument(:render_collection, :path => @path,
- :count => @collection.size) do
+ ActiveSupport::Notifications.instrument("action_view.render_collection",
+ :identifier => identifier || "collection", :count => @collection.size) do
render_collection
end
else
- content = ActiveSupport::Notifications.instrument(:render_partial, :path => @path) do
+ content = ActiveSupport::Notifications.instrument("action_view.render_partial",
+ :identifier => identifier) do
render_partial
end
- if !@block && options[:layout]
- content = @view._render_layout(find_template(options[:layout]), @locals){ content }
+ if !@block && (layout = @options[:layout])
+ content = @view._render_layout(find_template(layout), @locals){ content }
end
content
end
end
def render_collection
- @template = template = find_template
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 = @template ? collection_with_template : collection_without_template
result.join(spacer).html_safe!
end
@@ -277,7 +277,6 @@ module ActionView
end
def render_partial(object = @object)
- @template = template = find_template
locals, view = @locals, @view
object ||= locals[template.variable_name]
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 48316cac53..ec278ca783 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -93,25 +93,23 @@ module ActionView
def _render_template(template, layout = nil, options = {})
locals = options[:locals] || {}
- content = ActiveSupport::Notifications.instrument(:render_template,
- :identifier => template.identifier, :layout => (layout ? layout.identifier : nil)) do
- template.render(self, locals)
- end
+ ActiveSupport::Notifications.instrument("action_view.render_template",
+ :identifier => template.identifier, :layout => layout.try(:identifier)) do
- @_content_for[:layout] = content
+ content = template.render(self, locals)
+ @_content_for[:layout] = content
- if layout
- @_layout = layout.identifier
- content = _render_layout(layout, locals)
- end
+ if layout
+ @_layout = layout.identifier
+ content = _render_layout(layout, locals)
+ end
- content
+ content
+ end
end
def _render_layout(layout, locals, &block)
- ActiveSupport::Notifications.instrument(:render_layout, :identifier => layout.identifier) do
- layout.render(self, locals){ |*name| _layout_for(*name, &block) }
- end
+ layout.render(self, locals){ |*name| _layout_for(*name, &block) }
end
end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 67e086d8bd..2abb352d4e 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -13,7 +13,7 @@ module ActionView #:nodoc:
end
def identifier
- self
+ 'text template'
end
def inspect
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index be9a2ed50d..16d66b6eca 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,6 +1,3 @@
-require 'active_support/test_case'
-require 'action_controller/test_case'
-
module ActionView
class Base
alias_method :initialize_without_template_tracking, :initialize
diff --git a/actionpack/test/abstract/url_for_test.rb b/actionpack/test/abstract/url_for_test.rb
new file mode 100644
index 0000000000..e5570349b8
--- /dev/null
+++ b/actionpack/test/abstract/url_for_test.rb
@@ -0,0 +1,272 @@
+require 'abstract_unit'
+
+module AbstractController
+ module Testing
+
+ class UrlForTests < ActionController::TestCase
+ class W
+ include AbstractController::UrlFor
+ end
+
+ def teardown
+ W.default_url_options.clear
+ end
+
+ def add_host!
+ W.default_url_options[:host] = 'www.basecamphq.com'
+ end
+
+ def test_exception_is_thrown_without_host
+ assert_raise RuntimeError do
+ W.new.url_for :controller => 'c', :action => 'a', :id => 'i'
+ end
+ end
+
+ def test_anchor
+ assert_equal('/c/a#anchor',
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => 'anchor')
+ )
+ end
+
+ def test_anchor_should_call_to_param
+ assert_equal('/c/a#anchor',
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor'))
+ )
+ end
+
+ def test_anchor_should_be_cgi_escaped
+ assert_equal('/c/a#anc%2Fhor',
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor'))
+ )
+ end
+
+ def test_default_host
+ add_host!
+ assert_equal('http://www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i')
+ )
+ end
+
+ def test_host_may_be_overridden
+ add_host!
+ assert_equal('http://37signals.basecamphq.com/c/a/i',
+ W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i')
+ )
+ end
+
+ def test_port
+ add_host!
+ assert_equal('http://www.basecamphq.com:3000/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000)
+ )
+ end
+
+ def test_protocol
+ add_host!
+ assert_equal('https://www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+ )
+ end
+
+ def test_protocol_with_and_without_separator
+ add_host!
+ assert_equal('https://www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+ )
+ assert_equal('https://www.basecamphq.com/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https://')
+ )
+ end
+
+ def test_trailing_slash
+ add_host!
+ options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'}
+ assert_equal('http://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) )
+ end
+
+ def test_trailing_slash_with_protocol
+ add_host!
+ options = { :trailing_slash => true,:protocol => 'https', :controller => 'foo', :action => 'bar', :id => '33'}
+ assert_equal('https://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) )
+ assert_equal 'https://www.basecamphq.com/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string'}))
+ end
+
+ def test_trailing_slash_with_only_path
+ options = {:controller => 'foo', :trailing_slash => true}
+ assert_equal '/foo/', W.new.url_for(options.merge({:only_path => true}))
+ options.update({:action => 'bar', :id => '33'})
+ assert_equal '/foo/bar/33/', W.new.url_for(options.merge({:only_path => true}))
+ assert_equal '/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string',:only_path => true}))
+ end
+
+ def test_trailing_slash_with_anchor
+ options = {:trailing_slash => true, :controller => 'foo', :action => 'bar', :id => '33', :only_path => true, :anchor=> 'chapter7'}
+ assert_equal '/foo/bar/33/#chapter7', W.new.url_for(options)
+ assert_equal '/foo/bar/33/?query=string#chapter7', W.new.url_for(options.merge({:query => 'string'}))
+ end
+
+ def test_trailing_slash_with_params
+ url = W.new.url_for(:trailing_slash => true, :only_path => true, :controller => 'cont', :action => 'act', :p1 => 'cafe', :p2 => 'link')
+ params = extract_params(url)
+ assert_equal params[0], { :p1 => 'cafe' }.to_query
+ assert_equal params[1], { :p2 => 'link' }.to_query
+ end
+
+ def test_relative_url_root_is_respected
+ orig_relative_url_root = ActionController::Base.relative_url_root
+ ActionController::Base.relative_url_root = '/subdir'
+
+ add_host!
+ assert_equal('https://www.basecamphq.com/subdir/c/a/i',
+ W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
+ )
+ ensure
+ ActionController::Base.relative_url_root = orig_relative_url_root
+ end
+
+ def test_named_routes
+ with_routing do |set|
+ set.draw do |map|
+ match 'this/is/verbose', :to => 'home#index', :as => :no_args
+ match 'home/sweet/home/:user', :to => 'home#index', :as => :home
+ end
+
+ # We need to create a new class in order to install the new named route.
+ kls = Class.new { include AbstractController::UrlFor }
+ controller = kls.new
+ assert controller.respond_to?(:home_url)
+ assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
+ controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
+
+ assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused'))
+ assert_equal("http://www.basecamphq.com/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'www.basecamphq.com'))
+ assert_equal("http://www.basecamphq.com/this/is/verbose", controller.send(:no_args_url, :host=>'www.basecamphq.com'))
+ end
+ end
+
+ def test_relative_url_root_is_respected_for_named_routes
+ orig_relative_url_root = ActionController::Base.relative_url_root
+ ActionController::Base.relative_url_root = '/subdir'
+
+ with_routing do |set|
+ set.draw do |map|
+ match '/home/sweet/home/:user', :to => 'home#index', :as => :home
+ end
+
+ kls = Class.new { include AbstractController::UrlFor }
+ controller = kls.new
+
+ assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again',
+ controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
+ end
+ ensure
+ ActionController::Base.relative_url_root = orig_relative_url_root
+ end
+
+ def test_only_path
+ with_routing do |set|
+ set.draw do |map|
+ match 'home/sweet/home/:user', :to => 'home#index', :as => :home
+ match ':controller/:action/:id'
+ end
+
+ # We need to create a new class in order to install the new named route.
+ kls = Class.new { include AbstractController::UrlFor }
+ controller = kls.new
+ assert controller.respond_to?(:home_url)
+ assert_equal '/brave/new/world',
+ controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true)
+
+ assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true))
+ assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama'))
+ end
+ end
+
+ def test_one_parameter
+ assert_equal('/c/a?param=val',
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :param => 'val')
+ )
+ end
+
+ def test_two_parameters
+ url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :p1 => 'X1', :p2 => 'Y2')
+ params = extract_params(url)
+ assert_equal params[0], { :p1 => 'X1' }.to_query
+ assert_equal params[1], { :p2 => 'Y2' }.to_query
+ end
+
+ def test_hash_parameter
+ url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:name => 'Bob', :category => 'prof'})
+ params = extract_params(url)
+ assert_equal params[0], { 'query[category]' => 'prof' }.to_query
+ assert_equal params[1], { 'query[name]' => 'Bob' }.to_query
+ end
+
+ def test_array_parameter
+ url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => ['Bob', 'prof'])
+ params = extract_params(url)
+ assert_equal params[0], { 'query[]' => 'Bob' }.to_query
+ assert_equal params[1], { 'query[]' => 'prof' }.to_query
+ end
+
+ def test_hash_recursive_parameters
+ url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:person => {:name => 'Bob', :position => 'prof'}, :hobby => 'piercing'})
+ params = extract_params(url)
+ assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query
+ assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query
+ assert_equal params[2], { 'query[person][position]' => 'prof' }.to_query
+ end
+
+ def test_hash_recursive_and_array_parameters
+ url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :id => 101, :query => {:person => {:name => 'Bob', :position => ['prof', 'art director']}, :hobby => 'piercing'})
+ assert_match %r(^/c/a/101), url
+ params = extract_params(url)
+ assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query
+ assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query
+ assert_equal params[2], { 'query[person][position][]' => 'art director' }.to_query
+ assert_equal params[3], { 'query[person][position][]' => 'prof' }.to_query
+ end
+
+ def test_path_generation_for_symbol_parameter_keys
+ assert_generates("/image", :controller=> :image)
+ end
+
+ def test_named_routes_with_nil_keys
+ with_routing do |set|
+ set.draw do |map|
+ match 'posts.:format', :to => 'posts#index', :as => :posts
+ match '/', :to => 'posts#index', :as => :main
+ end
+
+ # We need to create a new class in order to install the new named route.
+ kls = Class.new { include AbstractController::UrlFor }
+ kls.default_url_options[:host] = 'www.basecamphq.com'
+
+ controller = kls.new
+ params = {:action => :index, :controller => :posts, :format => :xml}
+ assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params))
+ params[:format] = nil
+ assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params))
+ end
+ end
+
+ def test_multiple_includes_maintain_distinct_options
+ first_class = Class.new { include AbstractController::UrlFor }
+ second_class = Class.new { include AbstractController::UrlFor }
+
+ first_host, second_host = 'firsthost.com', 'secondhost.com'
+
+ first_class.default_url_options[:host] = first_host
+ second_class.default_url_options[:host] = second_host
+
+ assert_equal first_class.default_url_options[:host], first_host
+ assert_equal second_class.default_url_options[:host], second_host
+ end
+
+ private
+ def extract_params(url)
+ url.split('?', 2).last.split('&').sort
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 8c65087898..10913c0fdb 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -19,8 +19,6 @@ require 'action_view'
require 'action_view/base'
require 'action_dispatch'
require 'fixture_template'
-require 'active_support/test_case'
-require 'action_view/test_case'
require 'active_support/dependencies'
activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
@@ -50,14 +48,6 @@ ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
FIXTURES = Pathname.new(FIXTURE_LOAD_PATH)
-# Turn on notifications
-require 'active_support/notifications'
-Thread.abort_on_exception = true
-
-ActiveSupport::Notifications.subscribe do |*args|
- ActionController::Base.log_event(*args) if ActionController::Base.logger
-end
-
module SetupOnce
extend ActiveSupport::Concern
@@ -95,29 +85,15 @@ class ActiveSupport::TestCase
end
end
-class MockLogger
- attr_reader :logged
- attr_accessor :level
-
- def initialize
- @level = Logger::DEBUG
- @logged = []
- end
-
- def method_missing(method, *args, &blk)
- @logged << args.first
- @logged << blk.call if block_given?
- end
-end
-
class ActionController::IntegrationTest < ActiveSupport::TestCase
def self.build_app(routes = nil)
+ ActionDispatch::Flash
ActionDispatch::MiddlewareStack.new { |middleware|
- middleware.use "ActionDispatch::StringCoercion"
middleware.use "ActionDispatch::ShowExceptions"
middleware.use "ActionDispatch::Callbacks"
middleware.use "ActionDispatch::ParamsParser"
- middleware.use "Rack::Head"
+ middleware.use "ActionDispatch::Flash"
+ middleware.use "ActionDispatch::Head"
}.build(routes || ActionController::Routing::Routes)
end
diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/active_record_unit.rb
index 9a094cf66b..4f2b052720 100644
--- a/actionpack/test/active_record_unit.rb
+++ b/actionpack/test/active_record_unit.rb
@@ -17,7 +17,6 @@ unless defined?(ActiveRecord) && defined?(Fixtures)
raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR)
$LOAD_PATH.unshift PATH_TO_AR
require 'active_record'
- require 'active_record/fixtures'
rescue LoadError => e
$stderr.print "Failed to load Active Record. Skipping Active Record assertion tests: #{e}"
ActiveRecordTestConnector.able_to_connect = false
diff --git a/actionpack/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb
index 0f534da14b..37c7738301 100644
--- a/actionpack/test/activerecord/controller_runtime_test.rb
+++ b/actionpack/test/activerecord/controller_runtime_test.rb
@@ -1,39 +1,53 @@
require 'active_record_unit'
require 'active_record/railties/controller_runtime'
require 'fixtures/project'
+require 'rails/subscriber/test_helper'
+require 'action_controller/railties/subscriber'
ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime
-class ARLoggingController < ActionController::Base
- def show
- render :inline => "<%= Project.all %>"
+module ControllerRuntimeSubscriberTest
+ class SubscriberController < ActionController::Base
+ def show
+ render :inline => "<%= Project.all %>"
+ end
end
-end
-class ARLoggingTest < ActionController::TestCase
- tests ARLoggingController
+ def self.included(base)
+ base.tests SubscriberController
+ end
def setup
+ @old_logger = ActionController::Base.logger
+ Rails::Subscriber.add(:action_controller, ActionController::Railties::Subscriber.new)
super
- set_logger
end
- def wait
- ActiveSupport::Notifications.notifier.wait
+ def teardown
+ super
+ Rails::Subscriber.subscribers.clear
+ ActionController::Base.logger = @old_logger
end
+ def set_logger(logger)
+ ActionController::Base.logger = logger
+ end
+
def test_log_with_active_record
get :show
wait
- assert_match /ActiveRecord runtime/, logs[3]
+
+ assert_equal 2, @logger.logged(:info).size
+ assert_match /\(Views: [\d\.]+ms | ActiveRecord: [\d\.]+ms\)/, @logger.logged(:info)[1]
end
- private
- def set_logger
- @controller.logger = MockLogger.new
- end
+ class SyncSubscriberTest < ActionController::TestCase
+ include Rails::Subscriber::SyncTestHelper
+ include ControllerRuntimeSubscriberTest
+ end
- def logs
- @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
- end
-end
+ class AsyncSubscriberTest < ActionController::TestCase
+ include Rails::Subscriber::AsyncTestHelper
+ include ControllerRuntimeSubscriberTest
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb
index ad744421db..ea82758cf5 100644
--- a/actionpack/test/activerecord/polymorphic_routes_test.rb
+++ b/actionpack/test/activerecord/polymorphic_routes_test.rb
@@ -26,7 +26,7 @@ class Series < ActiveRecord::Base
end
class PolymorphicRoutesTest < ActionController::TestCase
- include ActionController::UrlWriter
+ include ActionController::UrlFor
self.default_url_options[:host] = 'example.com'
def setup
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 65118f9bc9..1510a6a7e0 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -6,6 +6,7 @@ require 'pp' # require 'pp' early to prevent hidden_methods from not picking up
module Submodule
class ContainedEmptyController < ActionController::Base
end
+
class ContainedNonEmptyController < ActionController::Base
def public_action
render :nothing => true
@@ -20,12 +21,15 @@ module Submodule
end
hide_action :another_hidden_action
end
+
class SubclassedController < ContainedNonEmptyController
hide_action :public_action # Hiding it here should not affect the superclass.
end
end
+
class EmptyController < ActionController::Base
end
+
class NonEmptyController < ActionController::Base
def public_action
render :nothing => true
@@ -37,7 +41,6 @@ class NonEmptyController < ActionController::Base
end
class MethodMissingController < ActionController::Base
-
hide_action :shouldnt_be_called
def shouldnt_be_called
raise "NO WAY!"
@@ -48,16 +51,15 @@ protected
def method_missing(selector)
render :text => selector.to_s
end
-
end
class DefaultUrlOptionsController < ActionController::Base
- def default_url_options_action
- render :nothing => true
+ def from_view
+ render :inline => "<%= #{params[:route]} %>"
end
def default_url_options(options = nil)
- { :host => 'www.override.com', :action => 'new', :bacon => 'chunky' }
+ { :host => 'www.override.com', :action => 'new', :locale => 'en' }
end
end
@@ -68,6 +70,7 @@ class ControllerClassTests < Test::Unit::TestCase
assert_equal 'submodule/contained_empty', Submodule::ContainedEmptyController.controller_path
assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path
end
+
def test_controller_name
assert_equal 'empty', EmptyController.controller_name
assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name
@@ -86,41 +89,16 @@ class ControllerInstanceTests < Test::Unit::TestCase
def test_action_methods
@empty_controllers.each do |c|
- hide_mocha_methods_from_controller(c)
assert_equal Set.new, c.class.__send__(:action_methods), "#{c.controller_path} should be empty!"
end
+
@non_empty_controllers.each do |c|
- hide_mocha_methods_from_controller(c)
assert_equal Set.new(%w(public_action)), c.class.__send__(:action_methods), "#{c.controller_path} should not be empty!"
end
end
-
- protected
- # Mocha adds some public instance methods to Object that would be
- # considered actions, so explicitly hide_action them.
- def hide_mocha_methods_from_controller(controller)
- mocha_methods = [
- :expects, :mocha, :mocha_inspect, :reset_mocha, :stubba_object,
- :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__, :to_matcher,
- ]
- controller.class.__send__(:hide_action, *mocha_methods)
- end
end
-
class PerformActionTest < ActionController::TestCase
- class MockLogger
- attr_reader :logged
-
- def initialize
- @logged = []
- end
-
- def method_missing(method, *args)
- @logged << args.first.to_s
- end
- end
-
def use_controller(controller_class)
@controller = controller_class.new
@@ -128,9 +106,8 @@ class PerformActionTest < ActionController::TestCase
# a more accurate simulation of what happens in "real life".
@controller.logger = Logger.new(nil)
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
-
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
@request.host = "www.nextangle.com"
rescue_action_in_public!
@@ -145,8 +122,7 @@ class PerformActionTest < ActionController::TestCase
def test_method_missing_is_not_an_action_name
use_controller MethodMissingController
-
- assert ! @controller.__send__(:action_method?, 'method_missing')
+ assert !@controller.__send__(:action_method?, 'method_missing')
get :method_missing
assert_response :success
@@ -172,16 +148,43 @@ class DefaultUrlOptionsTest < ActionController::TestCase
def test_default_url_options_are_used_if_set
with_routing do |set|
set.draw do |map|
- match 'default_url_options', :to => 'default_url_options#default_url_options_action', :as => :default_url_options
+ match 'from_view', :to => 'default_url_options#from_view', :as => :from_view
match ':controller/:action'
end
- get :default_url_options_action # Make a dummy request so that the controller is initialized properly.
+ get :from_view, :route => "from_view_url"
- assert_equal 'http://www.override.com/default_url_options/new?bacon=chunky', @controller.url_for(:controller => 'default_url_options')
- assert_equal 'http://www.override.com/default_url_options?bacon=chunky', @controller.send(:default_url_options_url)
+ assert_equal 'http://www.override.com/from_view?locale=en', @response.body
+ assert_equal 'http://www.override.com/from_view?locale=en', @controller.send(:from_view_url)
+ assert_equal 'http://www.override.com/default_url_options/new?locale=en', @controller.url_for(:controller => 'default_url_options')
end
end
+
+ def test_default_url_options_are_used_in_non_positional_parameters
+ with_routing do |set|
+ set.draw do |map|
+ scope("/:locale") do
+ resources :descriptions
+ end
+ match ':controller/:action'
+ end
+
+ get :from_view, :route => "description_path(1)"
+
+ assert_equal '/en/descriptions/1', @response.body
+ assert_equal '/en/descriptions', @controller.send(:descriptions_path)
+ assert_equal '/pl/descriptions', @controller.send(:descriptions_path, "pl")
+ assert_equal '/pl/descriptions', @controller.send(:descriptions_path, :locale => "pl")
+ assert_equal '/pl/descriptions.xml', @controller.send(:descriptions_path, "pl", "xml")
+ assert_equal '/en/descriptions.xml', @controller.send(:descriptions_path, :format => "xml")
+ assert_equal '/en/descriptions/1', @controller.send(:description_path, 1)
+ assert_equal '/pl/descriptions/1', @controller.send(:description_path, "pl", 1)
+ assert_equal '/pl/descriptions/1', @controller.send(:description_path, 1, :locale => "pl")
+ assert_equal '/pl/descriptions/1.xml', @controller.send(:description_path, "pl", 1, "xml")
+ assert_equal '/en/descriptions/1.xml', @controller.send(:description_path, 1, :format => "xml")
+ end
+ end
+
end
class EmptyUrlOptionsTest < ActionController::TestCase
@@ -197,15 +200,12 @@ class EmptyUrlOptionsTest < ActionController::TestCase
get :public_action
assert_equal "http://www.example.com/non_empty/public_action", @controller.url_for
end
-end
-class EnsureNamedRoutesWorksTicket22BugTest < ActionController::TestCase
- def test_named_routes_still_work
+ def test_named_routes_with_path_without_doing_a_request_first
with_routing do |set|
set.draw do |map|
resources :things
end
- EmptyController.send :include, ActionController::UrlWriter
assert_equal '/things', EmptyController.new.send(:things_path)
end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 679eaf7b38..8a13d1e5f1 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -629,20 +629,6 @@ class FragmentCachingTest < ActionController::TestCase
assert_equal 'generated till now -> fragment content', buffer
end
- def test_fragment_for_logging
- fragment_computed = false
- events = []
- ActiveSupport::Notifications.subscribe { |*args| events << args }
-
- buffer = 'generated till now -> '
- @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
-
- assert fragment_computed
- assert_equal 'generated till now -> ', buffer
- ActiveSupport::Notifications.notifier.wait
- assert_equal [:exist_fragment?, :write_fragment], events.map(&:first)
- end
-
end
class FunctionalCachingController < ActionController::Base
diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb
index 64f1ad7610..7e19bce3b7 100644
--- a/actionpack/test/controller/dispatcher_test.rb
+++ b/actionpack/test/controller/dispatcher_test.rb
@@ -1,73 +1,59 @@
require 'abstract_unit'
-class DispatcherTest < Test::Unit::TestCase
- Dispatcher = ActionController::Dispatcher
-
- class Foo
- cattr_accessor :a, :b
- end
+# Ensure deprecated dispatcher works
+class DeprecatedDispatcherTest < ActiveSupport::TestCase
+ class DummyApp
+ def call(env)
+ [200, {}, 'response']
+ end
+ end
def setup
- ENV['REQUEST_METHOD'] = 'GET'
-
- # Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
ActionDispatch::Callbacks.reset_callbacks(:prepare)
ActionDispatch::Callbacks.reset_callbacks(:call)
-
- ActionController::Routing::Routes.stubs(:call).returns([200, {}, 'response'])
- Dispatcher.stubs(:require_dependency)
end
- def teardown
- ENV.delete 'REQUEST_METHOD'
- end
+ def test_assert_deprecated_to_prepare
+ a = nil
+
+ assert_deprecated do
+ ActionController::Dispatcher.to_prepare { a = 1 }
+ end
- def test_clears_dependencies_after_dispatch_if_in_loading_mode
- ActiveSupport::Dependencies.expects(:clear).once
- dispatch(false)
+ assert_nil a
+ dispatch
+ assert_equal 1, a
end
- def test_prepare_callbacks
- a = b = c = nil
- ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 }
- ActionDispatch::Callbacks.to_prepare { |*args| b = c = 2 }
- ActionDispatch::Callbacks.to_prepare { |*args| c = 3 }
+ def test_assert_deprecated_before_dispatch
+ a = nil
- # Ensure to_prepare callbacks are not run when defined
- assert_nil a || b || c
+ assert_deprecated do
+ ActionController::Dispatcher.before_dispatch { a = 1 }
+ end
- # Run callbacks
+ assert_nil a
dispatch
-
assert_equal 1, a
- assert_equal 2, b
- assert_equal 3, c
-
- # Make sure they are only run once
- a = b = c = nil
- dispatch
- assert_nil a || b || c
end
- def test_to_prepare_with_identifier_replaces
- ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a, Foo.b = 1, 1 }
- ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a = 2 }
+ def test_assert_deprecated_after_dispatch
+ a = nil
+
+ assert_deprecated do
+ ActionController::Dispatcher.after_dispatch { a = 1 }
+ end
+ assert_nil a
dispatch
- assert_equal 2, Foo.a
- assert_equal nil, Foo.b
+ assert_equal 1, a
end
private
- def dispatch(cache_classes = true)
- ActionController::Dispatcher.prepare_each_request = false
- Dispatcher.define_dispatcher_callbacks(cache_classes)
- @dispatcher ||= ActionDispatch::Callbacks.new(ActionController::Routing::Routes)
- @dispatcher.call({'rack.input' => StringIO.new(''), 'action_dispatch.show_exceptions' => false})
+ def dispatch(cache_classes = true)
+ @dispatcher ||= ActionDispatch::Callbacks.new(DummyApp.new, !cache_classes)
+ @dispatcher.call({'rack.input' => StringIO.new('')})
end
- def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)
- assert_equal howmany, klass.subclasses.size, message
- end
end
diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb
index 420ebeacf4..d0635669c2 100644
--- a/actionpack/test/controller/filter_params_test.rb
+++ b/actionpack/test/controller/filter_params_test.rb
@@ -66,18 +66,6 @@ class FilterParamTest < ActionController::TestCase
assert_raise(NoMethodError) { @controller.filter_parameters([{'password' => '[FILTERED]'}]) }
end
- def test_filter_parameters_inside_logs
- FilterParamController.filter_parameter_logging(:lifo, :amount)
-
- get :payment, :lifo => 'Pratik', :amount => '420', :step => '1'
- ActiveSupport::Notifications.notifier.wait
-
- filtered_params_logs = logs.detect {|l| l =~ /\AParameters/ }
- assert filtered_params_logs.index('"amount"=>"[FILTERED]"')
- assert filtered_params_logs.index('"lifo"=>"[FILTERED]"')
- assert filtered_params_logs.index('"step"=>"1"')
- end
-
private
def set_logger
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index a9b60386f1..85a2e7f44b 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -159,7 +159,7 @@ class FlashTest < ActionController::TestCase
end
def test_keep_and_discard_return_values
- flash = ActionController::Flash::FlashHash.new
+ flash = ActionDispatch::Flash::FlashHash.new
flash.update(:foo => :foo_indeed, :bar => :bar_indeed)
assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed
@@ -187,4 +187,42 @@ class FlashTest < ActionController::TestCase
get :redirect_with_other_flashes
assert_equal "Horses!", @controller.send(:flash)[:joyride]
end
-end \ No newline at end of file
+end
+
+class FlashIntegrationTest < ActionController::IntegrationTest
+ SessionKey = '_myapp_session'
+ SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
+
+ class TestController < ActionController::Base
+ def set_flash
+ flash["that"] = "hello"
+ head :ok
+ end
+
+ def use_flash
+ render :inline => "flash: #{flash["that"]}"
+ end
+ end
+
+ def test_flash
+ with_test_route_set do
+ get '/set_flash'
+ assert_response :success
+ assert_equal "hello", @request.flash["that"]
+
+ get '/use_flash'
+ assert_response :success
+ assert_equal "flash: hello", @response.body
+ end
+ end
+
+ private
+ def with_test_route_set
+ with_routing do |set|
+ set.draw do |map|
+ match ':action', :to => ActionDispatch::Session::CookieStore.new(TestController, :key => SessionKey, :secret => SessionSecret)
+ end
+ yield
+ end
+ end
+end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 624b14e69b..683ab5236c 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -254,6 +254,10 @@ class IntegrationProcessTest < ActionController::IntegrationTest
render :text => "Created", :status => 201
end
+ def method
+ render :text => "method: #{request.method}"
+ end
+
def cookie_monster
cookies["cookie_1"] = nil
cookies["cookie_3"] = "chocolate"
@@ -379,6 +383,14 @@ class IntegrationProcessTest < ActionController::IntegrationTest
head '/post'
assert_equal 201, status
assert_equal "", body
+
+ get '/get/method'
+ assert_equal 200, status
+ assert_equal "method: get", body
+
+ head '/get/method'
+ assert_equal 200, status
+ assert_equal "", body
end
end
@@ -391,6 +403,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest
with_routing do |set|
set.draw do |map|
match ':action', :to => ::IntegrationProcessTest::IntegrationController
+ get 'get/:action', :to => ::IntegrationProcessTest::IntegrationController
end
yield
end
diff --git a/actionpack/test/controller/logging_test.rb b/actionpack/test/controller/logging_test.rb
deleted file mode 100644
index 4206dffa7e..0000000000
--- a/actionpack/test/controller/logging_test.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-require 'abstract_unit'
-
-module Another
- class LoggingController < ActionController::Base
- layout "layouts/standard"
-
- def show
- render :nothing => true
- end
-
- def with_layout
- render :template => "test/hello_world", :layout => true
- end
- end
-end
-
-class LoggingTest < ActionController::TestCase
- tests Another::LoggingController
-
- def setup
- super
- set_logger
- end
-
- def get(*args)
- super
- wait
- end
-
- def wait
- ActiveSupport::Notifications.notifier.wait
- end
-
- def test_logging_without_parameters
- get :show
- assert_equal 4, logs.size
- assert_nil logs.detect {|l| l =~ /Parameters/ }
- end
-
- def test_logging_with_parameters
- get :show, :id => '10'
- assert_equal 5, logs.size
-
- params = logs.detect {|l| l =~ /Parameters/ }
- assert_equal 'Parameters: {"id"=>"10"}', params
- end
-
- def test_log_controller_with_namespace_and_action
- get :show
- assert_match /Processed\sAnother::LoggingController#show/, logs[1]
- end
-
- def test_log_view_runtime
- get :show
- assert_match /View runtime/, logs[2]
- end
-
- def test_log_completed_status_and_request_uri
- get :show
- last = logs.last
- assert_match /Completed/, last
- assert_match /200/, last
- assert_match /another\/logging\/show/, last
- end
-
- def test_logger_prints_layout_and_template_rendering_info
- get :with_layout
- logged = logs.find {|l| l =~ /render/i }
- assert_match /Rendered (.*)test\/hello_world.erb within (.*)layouts\/standard.html.erb/, logged
- end
-
- private
- def set_logger
- @controller.logger = MockLogger.new
- end
-
- def logs
- @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
- end
-end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 6b9cace9cd..ba2347e4e2 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -461,31 +461,27 @@ end
class RespondWithController < ActionController::Base
respond_to :html, :json
- respond_to :xml, :except => :using_defaults
- respond_to :js, :only => [ :using_defaults, :using_resource ]
+ respond_to :xml, :except => :using_resource_with_block
+ respond_to :js, :only => [ :using_resource_with_block, :using_resource ]
- def using_defaults
- respond_to do |format|
- format.csv { render :text => "CSV" }
- end
+ def using_resource
+ respond_with(resource)
end
- def using_defaults_with_type_list
- respond_to(:js, :xml)
+ def using_resource_with_block
+ respond_with(resource) do |format|
+ format.csv { render :text => "CSV" }
+ end
end
- def default_overwritten
- respond_to do |format|
+ def using_resource_with_overwrite_block
+ respond_with(resource) do |format|
format.html { render :text => "HTML" }
end
end
- def using_resource
- respond_with(Customer.new("david", 13))
- end
-
def using_resource_with_collection
- respond_with([Customer.new("david", 13), Customer.new("jamis", 9)])
+ respond_with([resource, Customer.new("jamis", 9)])
end
def using_resource_with_parent
@@ -493,16 +489,16 @@ class RespondWithController < ActionController::Base
end
def using_resource_with_status_and_location
- respond_with(Customer.new("david", 13), :location => "http://test.host/", :status => :created)
+ respond_with(resource, :location => "http://test.host/", :status => :created)
end
def using_resource_with_responder
responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" }
- respond_with(Customer.new("david", 13), :responder => responder)
+ respond_with(resource, :responder => responder)
end
def using_resource_with_action
- respond_with(Customer.new("david", 13), :action => :foo) do |format|
+ respond_with(resource, :action => :foo) do |format|
format.html { raise ActionView::MissingTemplate.new([], "method") }
end
end
@@ -511,11 +507,15 @@ class RespondWithController < ActionController::Base
responder = Class.new(ActionController::Responder) do
def respond; @controller.render :text => "respond #{format}"; end
end
- respond_with(Customer.new("david", 13), :responder => responder)
+ respond_with(resource, :responder => responder)
end
protected
+ def resource
+ Customer.new("david", 13)
+ end
+
def _render_js(js, options)
self.content_type ||= Mime::JS
self.response_body = js.respond_to?(:to_js) ? js.to_js : js
@@ -527,12 +527,18 @@ class InheritedRespondWithController < RespondWithController
respond_to :xml, :json
def index
- respond_with(Customer.new("david", 13)) do |format|
+ respond_with(resource) do |format|
format.json { render :text => "JSON" }
end
end
end
+class EmptyRespondWithController < ActionController::Base
+ def index
+ respond_with(Customer.new("david", 13))
+ end
+end
+
class RespondWithControllerTest < ActionController::TestCase
tests RespondWithController
@@ -547,56 +553,54 @@ class RespondWithControllerTest < ActionController::TestCase
ActionController::Base.use_accept_header = false
end
- def test_using_defaults
+ def test_using_resource
+ @request.accept = "text/javascript"
+ get :using_resource
+ assert_equal "text/javascript", @response.content_type
+ assert_equal '$("body").visualEffect("highlight");', @response.body
+
+ @request.accept = "application/xml"
+ get :using_resource
+ assert_equal "application/xml", @response.content_type
+ assert_equal "<name>david</name>", @response.body
+
+ @request.accept = "application/json"
+ assert_raise ActionView::MissingTemplate do
+ get :using_resource
+ end
+ end
+
+ def test_using_resource_with_block
@request.accept = "*/*"
- get :using_defaults
+ get :using_resource_with_block
assert_equal "text/html", @response.content_type
assert_equal 'Hello world!', @response.body
@request.accept = "text/csv"
- get :using_defaults
+ get :using_resource_with_block
assert_equal "text/csv", @response.content_type
assert_equal "CSV", @response.body
- @request.accept = "text/javascript"
- get :using_defaults
- assert_equal "text/javascript", @response.content_type
- assert_equal '$("body").visualEffect("highlight");', @response.body
- end
-
- def test_using_defaults_with_type_list
- @request.accept = "*/*"
- get :using_defaults_with_type_list
- assert_equal "text/javascript", @response.content_type
- assert_equal '$("body").visualEffect("highlight");', @response.body
-
@request.accept = "application/xml"
- get :using_defaults_with_type_list
+ get :using_resource
assert_equal "application/xml", @response.content_type
- assert_equal "<p>Hello world!</p>\n", @response.body
+ assert_equal "<name>david</name>", @response.body
end
- def test_default_overwritten
- get :default_overwritten
+ def test_using_resource_with_overwrite_block
+ get :using_resource_with_overwrite_block
assert_equal "text/html", @response.content_type
assert_equal "HTML", @response.body
end
- def test_using_resource
- @request.accept = "text/javascript"
- get :using_resource
- assert_equal "text/javascript", @response.content_type
- assert_equal '$("body").visualEffect("highlight");', @response.body
-
+ def test_not_acceptable
@request.accept = "application/xml"
- get :using_resource
- assert_equal "application/xml", @response.content_type
- assert_equal "<name>david</name>", @response.body
+ get :using_resource_with_block
+ assert_equal 406, @response.status
- @request.accept = "application/json"
- assert_raise ActionView::MissingTemplate do
- get :using_resource
- end
+ @request.accept = "text/javascript"
+ get :using_resource_with_overwrite_block
+ assert_equal 406, @response.status
end
def test_using_resource_for_post_with_html_redirects_on_success
@@ -831,22 +835,12 @@ class RespondWithControllerTest < ActionController::TestCase
RespondWithController.responder = ActionController::Responder
end
- def test_not_acceptable
- @request.accept = "application/xml"
- get :using_defaults
- assert_equal 406, @response.status
-
- @request.accept = "text/html"
- get :using_defaults_with_type_list
- assert_equal 406, @response.status
-
- @request.accept = "application/json"
- get :using_defaults_with_type_list
- assert_equal 406, @response.status
-
- @request.accept = "text/javascript"
- get :default_overwritten
- assert_equal 406, @response.status
+ def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called
+ @controller = EmptyRespondWithController.new
+ @request.accept = "*/*"
+ assert_raise RuntimeError do
+ get :index
+ end
end
private
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 1a03396ae9..01ed491732 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -30,16 +30,6 @@ module Backoffice
end
class ResourcesTest < ActionController::TestCase
- # The assertions in these tests are incompatible with the hash method
- # optimisation. This could indicate user level problems
- def setup
- ActionController::Base.optimise_named_routes = false
- end
-
- def teardown
- ActionController::Base.optimise_named_routes = true
- end
-
def test_should_arrange_actions
resource = ActionDispatch::Routing::DeprecatedMapper::Resource.new(:messages,
:collection => { :rss => :get, :reorder => :post, :csv => :post },
@@ -304,27 +294,27 @@ class ResourcesTest < ActionController::TestCase
end
end
- def test_member_when_changed_default_restful_actions_and_path_names_not_specified
- default_path_names = ActionController::Base.resources_path_names
- ActionController::Base.resources_path_names = {:new => 'nuevo', :edit => 'editar'}
-
- with_restful_routing :messages do
- new_options = { :action => 'new', :controller => 'messages' }
- new_path = "/messages/nuevo"
- edit_options = { :action => 'edit', :id => '1', :controller => 'messages' }
- edit_path = "/messages/1/editar"
-
- assert_restful_routes_for :messages do |options|
- assert_recognizes(options.merge(new_options), :path => new_path, :method => :get)
- end
-
- assert_restful_routes_for :messages do |options|
- assert_recognizes(options.merge(edit_options), :path => edit_path, :method => :get)
- end
- end
- ensure
- ActionController::Base.resources_path_names = default_path_names
- end
+ # def test_member_when_changed_default_restful_actions_and_path_names_not_specified
+ # default_path_names = ActionController::Base.resources_path_names
+ # ActionController::Base.resources_path_names = {:new => 'nuevo', :edit => 'editar'}
+ #
+ # with_restful_routing :messages do
+ # new_options = { :action => 'new', :controller => 'messages' }
+ # new_path = "/messages/nuevo"
+ # edit_options = { :action => 'edit', :id => '1', :controller => 'messages' }
+ # edit_path = "/messages/1/editar"
+ #
+ # assert_restful_routes_for :messages do |options|
+ # assert_recognizes(options.merge(new_options), :path => new_path, :method => :get)
+ # end
+ #
+ # assert_restful_routes_for :messages do |options|
+ # assert_recognizes(options.merge(edit_options), :path => edit_path, :method => :get)
+ # end
+ # end
+ # ensure
+ # ActionController::Base.resources_path_names = default_path_names
+ # end
def test_with_two_member_actions_with_same_method
[:put, :post].each do |method|
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index c15eaade58..f390bbdc89 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -82,9 +82,6 @@ class LegacyRouteSetTests < Test::Unit::TestCase
attr_reader :rs
def setup
- # These tests assume optimisation is on, so re-enable it.
- ActionController::Base.optimise_named_routes = true
-
@rs = ::ActionController::Routing::RouteSet.new
end
@@ -632,7 +629,6 @@ class LegacyRouteSetTests < Test::Unit::TestCase
end
def test_routes_changed_correctly_after_clear
- ActionController::Base.optimise_named_routes = true
rs = ::ActionController::Routing::RouteSet.new
rs.draw do |r|
r.connect 'ca', :controller => 'ca', :action => "aa"
diff --git a/actionpack/test/controller/subscriber_test.rb b/actionpack/test/controller/subscriber_test.rb
new file mode 100644
index 0000000000..ef1a325799
--- /dev/null
+++ b/actionpack/test/controller/subscriber_test.rb
@@ -0,0 +1,192 @@
+require "abstract_unit"
+require "rails/subscriber/test_helper"
+require "action_controller/railties/subscriber"
+
+module Another
+ class SubscribersController < ActionController::Base
+ def show
+ render :nothing => true
+ end
+
+ def redirector
+ redirect_to "http://foo.bar/"
+ end
+
+ def data_sender
+ send_data "cool data", :filename => "omg.txt"
+ end
+
+ def xfile_sender
+ send_file File.expand_path("company.rb", FIXTURE_LOAD_PATH), :x_sendfile => true
+ end
+
+ def file_sender
+ send_file File.expand_path("company.rb", FIXTURE_LOAD_PATH)
+ end
+
+ def with_fragment_cache
+ render :inline => "<%= cache('foo'){ 'bar' } %>"
+ end
+
+ def with_page_cache
+ cache_page("Super soaker", "/index.html")
+ render :nothing => true
+ end
+ end
+end
+
+module ActionControllerSubscriberTest
+
+ def self.included(base)
+ base.tests Another::SubscribersController
+ end
+
+ def setup
+ @old_logger = ActionController::Base.logger
+
+ @cache_path = File.expand_path('../temp/test_cache', File.dirname(__FILE__))
+ ActionController::Base.page_cache_directory = @cache_path
+ ActionController::Base.cache_store = :file_store, @cache_path
+
+ Rails::Subscriber.add(:action_controller, ActionController::Railties::Subscriber.new)
+ super
+ end
+
+ def teardown
+ super
+ Rails::Subscriber.subscribers.clear
+ FileUtils.rm_rf(@cache_path)
+ ActionController::Base.logger = @old_logger
+ end
+
+ def set_logger(logger)
+ ActionController::Base.logger = logger
+ end
+
+ def test_process_action
+ get :show
+ wait
+ assert_equal 2, logs.size
+ assert_match /Processed\sAnother::SubscribersController#show/, logs[0]
+ end
+
+ def test_process_action_formats
+ get :show
+ wait
+ assert_equal 2, logs.size
+ assert_match /text\/html/, logs[0]
+ end
+
+ def test_process_action_without_parameters
+ get :show
+ wait
+ assert_nil logs.detect {|l| l =~ /Parameters/ }
+ end
+
+ def test_process_action_with_parameters
+ get :show, :id => '10'
+ wait
+
+ assert_equal 3, logs.size
+ assert_equal 'Parameters: {"id"=>"10"}', logs[1]
+ end
+
+ def test_process_action_with_view_runtime
+ get :show
+ wait
+ assert_match /\(Views: [\d\.]+ms\)/, logs[1]
+ end
+
+ def test_process_action_with_status_and_request_uri
+ get :show
+ wait
+ last = logs.last
+ assert_match /Completed/, last
+ assert_match /200/, last
+ assert_match /another\/subscribers\/show/, last
+ end
+
+ def test_process_action_with_filter_parameters
+ Another::SubscribersController.filter_parameter_logging(:lifo, :amount)
+
+ get :show, :lifo => 'Pratik', :amount => '420', :step => '1'
+ wait
+
+ params = logs[1]
+ assert_match /"amount"=>"\[FILTERED\]"/, params
+ assert_match /"lifo"=>"\[FILTERED\]"/, params
+ assert_match /"step"=>"1"/, params
+ end
+
+ def test_redirect_to
+ get :redirector
+ wait
+
+ assert_equal 3, logs.size
+ assert_equal "Redirected to http://foo.bar/", logs[0]
+ end
+
+ def test_send_data
+ get :data_sender
+ wait
+
+ assert_equal 3, logs.size
+ assert_match /Sent data omg\.txt/, logs[0]
+ end
+
+ def test_send_file
+ get :file_sender
+ wait
+
+ assert_equal 3, logs.size
+ assert_match /Sent file/, logs[0]
+ assert_match /test\/fixtures\/company\.rb/, logs[0]
+ end
+
+ def test_send_xfile
+ get :xfile_sender
+ wait
+
+ assert_equal 3, logs.size
+ assert_match /Sent X\-Sendfile header/, logs[0]
+ assert_match /test\/fixtures\/company\.rb/, logs[0]
+ end
+
+ def test_with_fragment_cache
+ ActionController::Base.perform_caching = true
+ get :with_fragment_cache
+ wait
+
+ assert_equal 4, logs.size
+ assert_match /Exist fragment\? views\/foo/, logs[0]
+ assert_match /Write fragment views\/foo/, logs[1]
+ ensure
+ ActionController::Base.perform_caching = true
+ end
+
+ def test_with_page_cache
+ ActionController::Base.perform_caching = true
+ get :with_page_cache
+ wait
+
+ assert_equal 3, logs.size
+ assert_match /Write page/, logs[0]
+ assert_match /\/index\.html/, logs[0]
+ ensure
+ ActionController::Base.perform_caching = true
+ end
+
+ def logs
+ @logs ||= @logger.logged(:info)
+ end
+
+ class SyncSubscriberTest < ActionController::TestCase
+ include Rails::Subscriber::SyncTestHelper
+ include ActionControllerSubscriberTest
+ end
+
+ class AsyncSubscriberTest < ActionController::TestCase
+ include Rails::Subscriber::AsyncTestHelper
+ include ActionControllerSubscriberTest
+ end
+end
diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb
index 428f40b9f8..c2b8cd85d8 100644
--- a/actionpack/test/controller/url_rewriter_test.rb
+++ b/actionpack/test/controller/url_rewriter_test.rb
@@ -100,286 +100,3 @@ class UrlRewriterTests < ActionController::TestCase
end
end
-class UrlWriterTests < ActionController::TestCase
- class W
- include ActionController::UrlWriter
- end
-
- def teardown
- W.default_url_options.clear
- end
-
- def add_host!
- W.default_url_options[:host] = 'www.basecamphq.com'
- end
-
- def test_exception_is_thrown_without_host
- assert_raise RuntimeError do
- W.new.url_for :controller => 'c', :action => 'a', :id => 'i'
- end
- end
-
- def test_anchor
- assert_equal('/c/a#anchor',
- W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => 'anchor')
- )
- end
-
- def test_anchor_should_call_to_param
- assert_equal('/c/a#anchor',
- W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor'))
- )
- end
-
- def test_anchor_should_be_cgi_escaped
- assert_equal('/c/a#anc%2Fhor',
- W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor'))
- )
- end
-
- def test_default_host
- add_host!
- assert_equal('http://www.basecamphq.com/c/a/i',
- W.new.url_for(:controller => 'c', :action => 'a', :id => 'i')
- )
- end
-
- def test_host_may_be_overridden
- add_host!
- assert_equal('http://37signals.basecamphq.com/c/a/i',
- W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i')
- )
- end
-
- def test_port
- add_host!
- assert_equal('http://www.basecamphq.com:3000/c/a/i',
- W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000)
- )
- end
-
- def test_protocol
- add_host!
- assert_equal('https://www.basecamphq.com/c/a/i',
- W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
- )
- end
-
- def test_protocol_with_and_without_separator
- add_host!
- assert_equal('https://www.basecamphq.com/c/a/i',
- W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
- )
- assert_equal('https://www.basecamphq.com/c/a/i',
- W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https://')
- )
- end
-
- def test_trailing_slash
- add_host!
- options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'}
- assert_equal('http://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) )
- end
-
- def test_trailing_slash_with_protocol
- add_host!
- options = { :trailing_slash => true,:protocol => 'https', :controller => 'foo', :action => 'bar', :id => '33'}
- assert_equal('https://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) )
- assert_equal 'https://www.basecamphq.com/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string'}))
- end
-
- def test_trailing_slash_with_only_path
- options = {:controller => 'foo', :trailing_slash => true}
- assert_equal '/foo/', W.new.url_for(options.merge({:only_path => true}))
- options.update({:action => 'bar', :id => '33'})
- assert_equal '/foo/bar/33/', W.new.url_for(options.merge({:only_path => true}))
- assert_equal '/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string',:only_path => true}))
- end
-
- def test_trailing_slash_with_anchor
- options = {:trailing_slash => true, :controller => 'foo', :action => 'bar', :id => '33', :only_path => true, :anchor=> 'chapter7'}
- assert_equal '/foo/bar/33/#chapter7', W.new.url_for(options)
- assert_equal '/foo/bar/33/?query=string#chapter7', W.new.url_for(options.merge({:query => 'string'}))
- end
-
- def test_trailing_slash_with_params
- url = W.new.url_for(:trailing_slash => true, :only_path => true, :controller => 'cont', :action => 'act', :p1 => 'cafe', :p2 => 'link')
- params = extract_params(url)
- assert_equal params[0], { :p1 => 'cafe' }.to_query
- assert_equal params[1], { :p2 => 'link' }.to_query
- end
-
- def test_relative_url_root_is_respected
- orig_relative_url_root = ActionController::Base.relative_url_root
- ActionController::Base.relative_url_root = '/subdir'
-
- add_host!
- assert_equal('https://www.basecamphq.com/subdir/c/a/i',
- W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
- )
- ensure
- ActionController::Base.relative_url_root = orig_relative_url_root
- end
-
- def test_named_routes
- with_routing do |set|
- set.draw do |map|
- match 'this/is/verbose', :to => 'home#index', :as => :no_args
- match 'home/sweet/home/:user', :to => 'home#index', :as => :home
- end
-
- # We need to create a new class in order to install the new named route.
- kls = Class.new { include ActionController::UrlWriter }
- controller = kls.new
- assert controller.respond_to?(:home_url)
- assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
- controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
-
- assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused'))
- assert_equal("http://www.basecamphq.com/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'www.basecamphq.com'))
- assert_equal("http://www.basecamphq.com/this/is/verbose", controller.send(:no_args_url, :host=>'www.basecamphq.com'))
- end
- end
-
- def test_relative_url_root_is_respected_for_named_routes
- orig_relative_url_root = ActionController::Base.relative_url_root
- ActionController::Base.relative_url_root = '/subdir'
-
- with_routing do |set|
- set.draw do |map|
- match '/home/sweet/home/:user', :to => 'home#index', :as => :home
- end
-
- kls = Class.new { include ActionController::UrlWriter }
- controller = kls.new
-
- assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again',
- controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
- end
- ensure
- ActionController::Base.relative_url_root = orig_relative_url_root
- end
-
- def test_only_path
- with_routing do |set|
- set.draw do |map|
- match 'home/sweet/home/:user', :to => 'home#index', :as => :home
- match ':controller/:action/:id'
- end
-
- # We need to create a new class in order to install the new named route.
- kls = Class.new { include ActionController::UrlWriter }
- controller = kls.new
- assert controller.respond_to?(:home_url)
- assert_equal '/brave/new/world',
- controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true)
-
- assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true))
- assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama'))
- end
- end
-
- def test_one_parameter
- assert_equal('/c/a?param=val',
- W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :param => 'val')
- )
- end
-
- def test_two_parameters
- url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :p1 => 'X1', :p2 => 'Y2')
- params = extract_params(url)
- assert_equal params[0], { :p1 => 'X1' }.to_query
- assert_equal params[1], { :p2 => 'Y2' }.to_query
- end
-
- def test_hash_parameter
- url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:name => 'Bob', :category => 'prof'})
- params = extract_params(url)
- assert_equal params[0], { 'query[category]' => 'prof' }.to_query
- assert_equal params[1], { 'query[name]' => 'Bob' }.to_query
- end
-
- def test_array_parameter
- url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => ['Bob', 'prof'])
- params = extract_params(url)
- assert_equal params[0], { 'query[]' => 'Bob' }.to_query
- assert_equal params[1], { 'query[]' => 'prof' }.to_query
- end
-
- def test_hash_recursive_parameters
- url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:person => {:name => 'Bob', :position => 'prof'}, :hobby => 'piercing'})
- params = extract_params(url)
- assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query
- assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query
- assert_equal params[2], { 'query[person][position]' => 'prof' }.to_query
- end
-
- def test_hash_recursive_and_array_parameters
- url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :id => 101, :query => {:person => {:name => 'Bob', :position => ['prof', 'art director']}, :hobby => 'piercing'})
- assert_match %r(^/c/a/101), url
- params = extract_params(url)
- assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query
- assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query
- assert_equal params[2], { 'query[person][position][]' => 'art director' }.to_query
- assert_equal params[3], { 'query[person][position][]' => 'prof' }.to_query
- end
-
- def test_path_generation_for_symbol_parameter_keys
- assert_generates("/image", :controller=> :image)
- end
-
- def test_named_routes_with_nil_keys
- with_routing do |set|
- set.draw do |map|
- match 'posts.:format', :to => 'posts#index', :as => :posts
- match '/', :to => 'posts#index', :as => :main
- end
-
- # We need to create a new class in order to install the new named route.
- kls = Class.new { include ActionController::UrlWriter }
- kls.default_url_options[:host] = 'www.basecamphq.com'
-
- controller = kls.new
- params = {:action => :index, :controller => :posts, :format => :xml}
- assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params))
- params[:format] = nil
- assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params))
- end
- end
-
- def test_formatted_url_methods_are_deprecated
- with_routing do |set|
- set.draw do |map|
- resources :posts
- end
- # We need to create a new class in order to install the new named route.
- kls = Class.new { include ActionController::UrlWriter }
- controller = kls.new
- params = {:id => 1, :format => :xml}
- assert_deprecated do
- assert_equal("/posts/1.xml", controller.send(:formatted_post_path, params))
- end
- assert_deprecated do
- assert_equal("/posts/1.xml", controller.send(:formatted_post_path, 1, :xml))
- end
- end
- end
-
- def test_multiple_includes_maintain_distinct_options
- first_class = Class.new { include ActionController::UrlWriter }
- second_class = Class.new { include ActionController::UrlWriter }
-
- first_host, second_host = 'firsthost.com', 'secondhost.com'
-
- first_class.default_url_options[:host] = first_host
- second_class.default_url_options[:host] = second_host
-
- assert_equal first_class.default_url_options[:host], first_host
- assert_equal second_class.default_url_options[:host], second_host
- end
-
- private
- def extract_params(url)
- url.split('?', 2).last.split('&').sort
- end
-end
diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb
new file mode 100644
index 0000000000..f3ea5209f4
--- /dev/null
+++ b/actionpack/test/dispatch/callbacks_test.rb
@@ -0,0 +1,107 @@
+require 'abstract_unit'
+
+class DispatcherTest < Test::Unit::TestCase
+ class Foo
+ cattr_accessor :a, :b
+ end
+
+ class DummyApp
+ def call(env)
+ [200, {}, 'response']
+ end
+ end
+
+ def setup
+ Foo.a, Foo.b = 0, 0
+ ActionDispatch::Callbacks.reset_callbacks(:prepare)
+ ActionDispatch::Callbacks.reset_callbacks(:call)
+ end
+
+ def test_prepare_callbacks_with_cache_classes
+ a = b = c = nil
+ ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 }
+ ActionDispatch::Callbacks.to_prepare { |*args| b = c = 2 }
+ ActionDispatch::Callbacks.to_prepare { |*args| c = 3 }
+
+ # Ensure to_prepare callbacks are not run when defined
+ assert_nil a || b || c
+
+ # Run callbacks
+ dispatch
+
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+
+ # Make sure they are only run once
+ a = b = c = nil
+ dispatch
+ assert_nil a || b || c
+ end
+
+ def test_prepare_callbacks_without_cache_classes
+ a = b = c = nil
+ ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 }
+ ActionDispatch::Callbacks.to_prepare { |*args| b = c = 2 }
+ ActionDispatch::Callbacks.to_prepare { |*args| c = 3 }
+
+ # Ensure to_prepare callbacks are not run when defined
+ assert_nil a || b || c
+
+ # Run callbacks
+ dispatch(false)
+
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+
+ # Make sure they are run again
+ a = b = c = nil
+ dispatch(false)
+ assert_equal 1, a
+ assert_equal 2, b
+ assert_equal 3, c
+ end
+
+ def test_to_prepare_with_identifier_replaces
+ ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a, Foo.b = 1, 1 }
+ ActionDispatch::Callbacks.to_prepare(:unique_id) { |*args| Foo.a = 2 }
+
+ dispatch
+ assert_equal 2, Foo.a
+ assert_equal 0, Foo.b
+ end
+
+ def test_before_and_after_callbacks
+ ActionDispatch::Callbacks.before { |*args| Foo.a += 1; Foo.b += 1 }
+ ActionDispatch::Callbacks.after { |*args| Foo.a += 1; Foo.b += 1 }
+
+ dispatch
+ assert_equal 2, Foo.a
+ assert_equal 2, Foo.b
+
+ dispatch
+ assert_equal 4, Foo.a
+ assert_equal 4, Foo.b
+ end
+
+ def test_should_send_an_instrumentation_callback_for_async_processing
+ ActiveSupport::Notifications.expects(:instrument).with("action_dispatch.callback")
+ dispatch
+ end
+
+ def test_should_send_an_instrumentation_callback_for_async_processing_even_on_failure
+ ActiveSupport::Notifications.notifier.expects(:publish)
+ assert_raise RuntimeError do
+ dispatch { |env| raise "OMG" }
+ end
+ end
+
+ private
+
+ def dispatch(cache_classes = true, &block)
+ @dispatcher ||= ActionDispatch::Callbacks.new(block || DummyApp.new, !cache_classes)
+ @dispatcher.call({'rack.input' => StringIO.new('')})
+ end
+
+end
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index b62df9a6b2..cb95ecea50 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -319,7 +319,7 @@ class RequestTest < ActiveSupport::TestCase
end
test "allow method hacking on post" do
- [:get, :head, :options, :put, :post, :delete].each do |method|
+ [:get, :options, :put, :post, :delete].each do |method|
request = stub_request "REQUEST_METHOD" => method.to_s.upcase
assert_equal(method == :head ? :get : method, request.method)
end
@@ -341,7 +341,7 @@ class RequestTest < ActiveSupport::TestCase
end
test "head masquerading as get" do
- request = stub_request 'REQUEST_METHOD' => 'HEAD'
+ request = stub_request 'REQUEST_METHOD' => 'GET', "rack.methodoverride.original_method" => "HEAD"
assert_equal :get, request.method
assert request.get?
assert request.head?
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index c4b0b9cdbf..6dccabdb3f 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -22,6 +22,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
delete 'logout' => :destroy, :as => :logout
end
+ resource :session do
+ get :create
+ end
+
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
match 'account/login', :to => redirect("/login")
@@ -29,6 +33,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'account/modulo/:name', :to => redirect("/%{name}s")
match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" }
+ match 'account/google' => redirect('http://www.google.com/')
match 'openid/login', :via => [:get, :post], :to => "openid#login"
@@ -47,6 +52,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get 'admin/accounts' => "queenbee#accounts"
end
+ scope 'es' do
+ resources :projects, :path_names => { :edit => 'cambiar' }, :as => 'projeto'
+ end
+
resources :projects, :controller => :project do
resources :involvements, :attachments
@@ -56,7 +65,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :companies do
resources :people
- resource :avatar
+ resource :avatar, :controller => :avatar
end
resources :images do
@@ -65,7 +74,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :people do
nested do
- namespace ":access_token" do
+ scope "/:access_token" do
resource :avatar
end
end
@@ -88,6 +97,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ resources :replies do
+ member do
+ put :answer, :to => :mark_as_answer
+ delete :answer, :to => :unmark_as_answer
+ end
+ end
+
+ resources :posts, :only => [:index, :show]
+
match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp
match 'people/:id/update', :to => 'people#update', :as => :update_person
@@ -97,7 +115,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'articles/:year/:month/:day/:title', :to => "articles#show", :as => :article
namespace :account do
+ match 'description', :to => "account#description", :as => "description"
resource :subscription, :credit, :credit_card
+
+ namespace :admin do
+ resource :subscription
+ end
+ end
+
+ namespace :forum do
+ resources :products, :as => '' do
+ resources :questions
+ end
end
controller :articles do
@@ -112,6 +141,16 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :rooms
end
+ scope '(:locale)', :locale => /en|pl/ do
+ resources :descriptions
+ end
+
+ namespace :admin do
+ scope '(/:locale)', :locale => /en|pl/ do
+ resources :descriptions
+ end
+ end
+
match '/info' => 'projects#info', :as => 'info'
root :to => 'projects#index'
@@ -165,6 +204,31 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_session_singleton_resource
+ with_test_routes do
+ get '/session'
+ assert_equal 'sessions#create', @response.body
+ assert_equal '/session', session_path
+
+ post '/session'
+ assert_equal 'sessions#create', @response.body
+
+ put '/session'
+ assert_equal 'sessions#update', @response.body
+
+ delete '/session'
+ assert_equal 'sessions#destroy', @response.body
+
+ get '/session/new'
+ assert_equal 'sessions#new', @response.body
+ assert_equal '/session/new', new_session_path
+
+ get '/session/edit'
+ assert_equal 'sessions#edit', @response.body
+ assert_equal '/session/edit', edit_session_path
+ end
+ end
+
def test_redirect_modulo
with_test_routes do
get '/account/modulo/name'
@@ -193,20 +257,19 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
- # TODO: rackmount is broken
- # def test_admin
- # with_test_routes do
- # get '/admin', {}, {'REMOTE_ADDR' => '192.168.1.100'}
- # assert_equal 'queenbee#index', @response.body
- #
- # assert_raise(ActionController::RoutingError) { get '/admin', {}, {'REMOTE_ADDR' => '10.0.0.100'} }
- #
- # get '/admin/accounts', {}, {'REMOTE_ADDR' => '192.168.1.100'}
- # assert_equal 'queenbee#accounts', @response.body
- #
- # assert_raise(ActionController::RoutingError) { get '/admin/accounts', {}, {'REMOTE_ADDR' => '10.0.0.100'} }
- # end
- # end
+ def test_admin
+ with_test_routes do
+ get '/admin', {}, {'REMOTE_ADDR' => '192.168.1.100'}
+ assert_equal 'queenbee#index', @response.body
+
+ assert_raise(ActionController::RoutingError) { get '/admin', {}, {'REMOTE_ADDR' => '10.0.0.100'} }
+
+ get '/admin/accounts', {}, {'REMOTE_ADDR' => '192.168.1.100'}
+ assert_equal 'queenbee#accounts', @response.body
+
+ assert_raise(ActionController::RoutingError) { get '/admin/accounts', {}, {'REMOTE_ADDR' => '10.0.0.100'} }
+ end
+ end
def test_global
with_test_routes do
@@ -231,31 +294,34 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_projects
with_test_routes do
get '/projects'
- assert_equal 'projects#index', @response.body
+ assert_equal 'project#index', @response.body
assert_equal '/projects', projects_path
+ post '/projects'
+ assert_equal 'project#create', @response.body
+
get '/projects.xml'
- assert_equal 'projects#index', @response.body
+ assert_equal 'project#index', @response.body
assert_equal '/projects.xml', projects_path(:format => 'xml')
get '/projects/new'
- assert_equal 'projects#new', @response.body
+ assert_equal 'project#new', @response.body
assert_equal '/projects/new', new_project_path
get '/projects/new.xml'
- assert_equal 'projects#new', @response.body
+ assert_equal 'project#new', @response.body
assert_equal '/projects/new.xml', new_project_path(:format => 'xml')
get '/projects/1'
- assert_equal 'projects#show', @response.body
+ assert_equal 'project#show', @response.body
assert_equal '/projects/1', project_path(:id => '1')
get '/projects/1.xml'
- assert_equal 'projects#show', @response.body
+ assert_equal 'project#show', @response.body
assert_equal '/projects/1.xml', project_path(:id => '1', :format => 'xml')
get '/projects/1/edit'
- assert_equal 'projects#edit', @response.body
+ assert_equal 'project#edit', @response.body
assert_equal '/projects/1/edit', edit_project_path(:id => '1')
end
end
@@ -317,7 +383,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1')
get '/projects/1/companies/1/avatar'
- assert_equal 'avatars#show', @response.body
+ assert_equal 'avatar#show', @response.body
assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1')
end
end
@@ -394,6 +460,42 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_replies
+ with_test_routes do
+ put '/replies/1/answer'
+ assert_equal 'replies#mark_as_answer', @response.body
+
+ delete '/replies/1/answer'
+ assert_equal 'replies#unmark_as_answer', @response.body
+ end
+ end
+
+ def test_posts
+ with_test_routes do
+ get '/posts'
+ assert_equal 'posts#index', @response.body
+ assert_equal '/posts', posts_path
+
+ get '/posts/1'
+ assert_equal 'posts#show', @response.body
+ assert_equal '/posts/1', post_path(:id => 1)
+
+ assert_raise(ActionController::RoutingError) { post '/posts' }
+ assert_raise(ActionController::RoutingError) { put '/posts/1' }
+ assert_raise(ActionController::RoutingError) { delete '/posts/1' }
+ end
+ end
+
+ def test_path_names
+ with_test_routes do
+ get '/es/projeto'
+ assert_equal 'projects#index', @response.body
+
+ get '/es/projeto/1/cambiar'
+ assert_equal 'projects#edit', @response.body
+ end
+ end
+
def test_sprockets
with_test_routes do
get '/sprockets.js'
@@ -419,6 +521,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_forum_products
+ with_test_routes do
+ get '/forum'
+ assert_equal 'forum/products#index', @response.body
+ assert_equal '/forum', forum_products_path
+
+ get '/forum/basecamp'
+ assert_equal 'forum/products#show', @response.body
+ assert_equal '/forum/basecamp', forum_product_path(:id => 'basecamp')
+
+ get '/forum/basecamp/questions'
+ assert_equal 'forum/questions#index', @response.body
+ assert_equal '/forum/basecamp/questions', forum_product_questions_path(:product_id => 'basecamp')
+
+ get '/forum/basecamp/questions/1'
+ assert_equal 'forum/questions#show', @response.body
+ assert_equal '/forum/basecamp/questions/1', forum_product_question_path(:product_id => 'basecamp', :id => 1)
+ end
+ end
+
def test_articles_perma
with_test_routes do
get '/articles/2009/08/18/rails-3'
@@ -431,13 +553,24 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_account_namespace
with_test_routes do
get '/account/subscription'
- assert_equal 'subscriptions#show', @response.body
+ assert_equal 'account/subscriptions#show', @response.body
+ assert_equal '/account/subscription', account_subscription_path
get '/account/credit'
- assert_equal 'credits#show', @response.body
+ assert_equal 'account/credits#show', @response.body
+ assert_equal '/account/credit', account_credit_path
get '/account/credit_card'
- assert_equal 'credit_cards#show', @response.body
+ assert_equal 'account/credit_cards#show', @response.body
+ assert_equal '/account/credit_card', account_credit_card_path
+ end
+ end
+
+ def test_nested_namespace
+ with_test_routes do
+ get '/account/admin/subscription'
+ assert_equal 'account/admin/subscriptions#show', @response.body
+ assert_equal '/account/admin/subscription', account_admin_subscription_path
end
end
@@ -472,7 +605,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'projects#index', @response.body
end
end
-
+
def test_index
with_test_routes do
assert_equal '/info', info_path
@@ -488,7 +621,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'projects#info', @response.body
end
end
-
+
def test_convention_match_with_no_scope
with_test_routes do
assert_equal '/account/overview', account_overview_path
@@ -497,6 +630,78 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_redirect_with_complete_url
+ with_test_routes do
+ get '/account/google'
+ assert_equal 301, @response.status
+ assert_equal 'http://www.google.com/', @response.headers['Location']
+ assert_equal 'Moved Permanently', @response.body
+ end
+ end
+
+ def test_redirect_with_port
+ previous_host, self.host = self.host, 'www.example.com:3000'
+ with_test_routes do
+ get '/account/login'
+ assert_equal 301, @response.status
+ assert_equal 'http://www.example.com:3000/login', @response.headers['Location']
+ assert_equal 'Moved Permanently', @response.body
+ end
+ ensure
+ self.host = previous_host
+ end
+
+ def test_normalize_namespaced_matches
+ with_test_routes do
+ assert_equal '/account/description', account_description_path
+
+ get '/account/description'
+ assert_equal 'account#description', @response.body
+ end
+ end
+
+ def test_optional_scoped_path
+ with_test_routes do
+ assert_equal '/en/descriptions', descriptions_path("en")
+ assert_equal '/descriptions', descriptions_path(nil)
+ assert_equal '/en/descriptions/1', description_path("en", 1)
+ assert_equal '/descriptions/1', description_path(nil, 1)
+
+ get '/en/descriptions'
+ assert_equal 'descriptions#index', @response.body
+
+ get '/descriptions'
+ assert_equal 'descriptions#index', @response.body
+
+ get '/en/descriptions/1'
+ assert_equal 'descriptions#show', @response.body
+
+ get '/descriptions/1'
+ assert_equal 'descriptions#show', @response.body
+ end
+ end
+
+ def test_nested_optional_scoped_path
+ with_test_routes do
+ assert_equal '/admin/en/descriptions', admin_descriptions_path("en")
+ assert_equal '/admin/descriptions', admin_descriptions_path(nil)
+ assert_equal '/admin/en/descriptions/1', admin_description_path("en", 1)
+ assert_equal '/admin/descriptions/1', admin_description_path(nil, 1)
+
+ get '/admin/en/descriptions'
+ assert_equal 'admin/descriptions#index', @response.body
+
+ get '/admin/descriptions'
+ assert_equal 'admin/descriptions#index', @response.body
+
+ get '/admin/en/descriptions/1'
+ assert_equal 'admin/descriptions#show', @response.body
+
+ get '/admin/descriptions/1'
+ assert_equal 'admin/descriptions#show', @response.body
+ end
+ end
+
private
def with_test_routes
real_routes, temp_routes = ActionController::Routing::Routes, Routes
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 9f6a93756c..951fb4a22e 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -104,4 +104,27 @@ class ShowExceptionsTest < ActionController::IntegrationTest
assert_response 405
assert_match /ActionController::MethodNotAllowed/, body
end
+
+ test "publishes notifications" do
+ # Wait pending notifications to be published
+ ActiveSupport::Notifications.notifier.wait
+
+ @app, event = ProductionApp, nil
+ self.remote_addr = '127.0.0.1'
+
+ ActiveSupport::Notifications.subscribe('action_dispatch.show_exception') do |*args|
+ event = args
+ end
+
+ get "/"
+ assert_response 500
+ assert_match /puke/, body
+
+ ActiveSupport::Notifications.notifier.wait
+
+ assert_equal 'action_dispatch.show_exception', event.first
+ assert_kind_of Hash, event.last[:env]
+ assert_equal 'GET', event.last[:env]["REQUEST_METHOD"]
+ assert_kind_of RuntimeError, event.last[:exception]
+ end
end
diff --git a/actionpack/test/dispatch/string_coercion_test.rb b/actionpack/test/dispatch/string_coercion_test.rb
deleted file mode 100644
index d79b17b932..0000000000
--- a/actionpack/test/dispatch/string_coercion_test.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'abstract_unit'
-
-class StringCoercionTest < ActiveSupport::TestCase
- test "body responds to each" do
- original_body = []
- body = ActionDispatch::StringCoercion::UglyBody.new(original_body)
-
- assert original_body.respond_to?(:each)
- assert body.respond_to?(:each)
- end
-
- test "body responds to to_path" do
- original_body = []
- def original_body.to_path; end
- body = ActionDispatch::StringCoercion::UglyBody.new(original_body)
-
- assert original_body.respond_to?(:to_path)
- assert body.respond_to?(:to_path)
- end
-
- test "body does not responds to to_path" do
- original_body = []
- body = ActionDispatch::StringCoercion::UglyBody.new(original_body)
-
- assert !original_body.respond_to?(:to_path)
- assert !body.respond_to?(:to_path)
- end
-
- test "calls to_s on body parts" do
- app = lambda { |env|
- [200, {'Content-Type' => 'html'}, [1, 2, 3]]
- }
- app = ActionDispatch::StringCoercion.new(app)
- parts = []
- status, headers, body = app.call({})
- body.each { |part| parts << part }
-
- assert_equal %w( 1 2 3 ), parts
- end
-end
diff --git a/actionpack/test/fixtures/respond_with/using_defaults.js.rjs b/actionpack/test/fixtures/respond_with/using_defaults.js.rjs
deleted file mode 100644
index 469fcd8e15..0000000000
--- a/actionpack/test/fixtures/respond_with/using_defaults.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page[:body].visual_effect :highlight \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs b/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs
deleted file mode 100644
index 469fcd8e15..0000000000
--- a/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page[:body].visual_effect :highlight \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder b/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder
deleted file mode 100644
index 598d62e2fc..0000000000
--- a/actionpack/test/fixtures/respond_with/using_defaults_with_type_list.xml.builder
+++ /dev/null
@@ -1 +0,0 @@
-xml.p "Hello world!" \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_defaults.html.erb b/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb
index 6769dd60bd..6769dd60bd 100644
--- a/actionpack/test/fixtures/respond_with/using_defaults.html.erb
+++ b/actionpack/test/fixtures/respond_with/using_resource_with_block.html.erb
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index b0e5d7a94c..7346cb22bc 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -69,7 +69,7 @@ class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost
attr_accessor :author
def author_attributes=(attributes); end
- attr_accessor :comments
+ attr_accessor :comments, :comment_ids
def comments_attributes=(attributes); end
attr_accessor :tags
diff --git a/actionpack/test/template/active_model_helper_i18n_test.rb b/actionpack/test/template/active_model_helper_i18n_test.rb
index 2465444fc5..4eb2f262bd 100644
--- a/actionpack/test/template/active_model_helper_i18n_test.rb
+++ b/actionpack/test/template/active_model_helper_i18n_test.rb
@@ -16,27 +16,27 @@ class ActiveModelHelperI18nTest < Test::Unit::TestCase
stubs(:content_tag).returns 'content_tag'
- I18n.stubs(:t).with(:'header', :locale => 'en', :scope => [:activemodel, :errors, :template], :count => 1, :model => '').returns "1 error prohibited this from being saved"
- I18n.stubs(:t).with(:'body', :locale => 'en', :scope => [:activemodel, :errors, :template]).returns 'There were problems with the following fields:'
+ I18n.stubs(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns "1 error prohibited this from being saved"
+ I18n.stubs(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
end
def test_error_messages_for_given_a_header_option_it_does_not_translate_header_message
- I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:activemodel, :errors, :template], :count => 1, :model => '').never
+ I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').never
error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en')
end
def test_error_messages_for_given_no_header_option_it_translates_header_message
- I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:activemodel, :errors, :template], :count => 1, :model => '').returns 'header message'
+ I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns 'header message'
error_messages_for(:object => @object, :locale => 'en')
end
def test_error_messages_for_given_a_message_option_it_does_not_translate_message
- I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:activemodel, :errors, :template]).never
+ I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).never
error_messages_for(:object => @object, :message => 'message', :locale => 'en')
end
def test_error_messages_for_given_no_message_option_it_translates_message
- I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:activemodel, :errors, :template]).returns 'There were problems with the following fields:'
+ I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
error_messages_for(:object => @object, :locale => 'en')
end
end
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index 9fb2080f77..fb51b67185 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -2475,6 +2475,28 @@ class DateHelperTest < ActionView::TestCase
}, options)
end
+ def test_select_html_safety
+ assert select_day(16).html_safe?
+ assert select_month(8).html_safe?
+ assert select_year(Time.mktime(2003, 8, 16, 8, 4, 18)).html_safe?
+ assert select_minute(Time.mktime(2003, 8, 16, 8, 4, 18)).html_safe?
+ assert select_second(Time.mktime(2003, 8, 16, 8, 4, 18)).html_safe?
+
+ assert select_minute(8, :use_hidden => true).html_safe?
+ assert select_month(8, :prompt => 'Choose month').html_safe?
+
+ assert select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector').html_safe?
+ assert select_date(Time.mktime(2003, 8, 16), :date_separator => " / ", :start_year => 2003, :end_year => 2005, :prefix => "date[first]").html_safe?
+ end
+
+ def test_object_select_html_safety
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ assert date_select("post", "written_on", :default => Time.local(2006, 9, 19, 15, 16, 35), :include_blank => true).html_safe?
+ assert time_select("post", "written_on", :ignore_date => true).html_safe?
+ end
+
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index b1e9fe99a2..0c5c5d17ee 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -16,8 +16,8 @@ class FormHelperTest < ActionView::TestCase
}
}
},
- :views => {
- :labels => {
+ :helpers => {
+ :label => {
:post => {
:body => "Write entire text here"
}
@@ -25,6 +25,20 @@ class FormHelperTest < ActionView::TestCase
}
}
+ # Create "submit" locale for testing I18n submit helpers
+ I18n.backend.store_translations 'submit', {
+ :helpers => {
+ :submit => {
+ :create => 'Create {{model}}',
+ :update => 'Confirm {{model}} changes',
+ :submit => 'Save changes',
+ :another_post => {
+ :update => 'Update your {{model}}'
+ }
+ }
+ }
+ }
+
@post = Post.new
@comment = Comment.new
def @post.errors()
@@ -190,6 +204,11 @@ class FormHelperTest < ActionView::TestCase
hidden_field("post", "title", :value => "Something Else")
end
+ def test_text_field_with_custom_type
+ assert_dom_equal '<input id="user_email" size="30" name="user[email]" type="email" />',
+ text_field("user", "email", :type => "email")
+ end
+
def test_check_box
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
@@ -234,6 +253,19 @@ class FormHelperTest < ActionView::TestCase
)
end
+ def test_check_box_with_multiple_behavior
+ @post.comment_ids = [2,3]
+ assert_dom_equal(
+ '<input name="post[comment_ids][]" type="hidden" value="0" /><input id="post_comment_ids_1" name="post[comment_ids][]" type="checkbox" value="1" />',
+ check_box("post", "comment_ids", { :multiple => true }, 1)
+ )
+ assert_dom_equal(
+ '<input name="post[comment_ids][]" type="hidden" value="0" /><input checked="checked" id="post_comment_ids_3" name="post[comment_ids][]" type="checkbox" value="3" />',
+ check_box("post", "comment_ids", { :multiple => true }, 3)
+ )
+ end
+
+
def test_checkbox_disabled_still_submits_checked_value
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="1" /><input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
@@ -475,6 +507,67 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_submit_with_object_as_new_record_and_locale_strings
+ old_locale, I18n.locale = I18n.locale, :submit
+
+ def @post.new_record?() true; end
+ form_for(:post, @post) do |f|
+ concat f.submit
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='commit' id='post_submit' type='submit' value='Create Post' />" +
+ "</form>"
+ assert_dom_equal expected, output_buffer
+ ensure
+ I18n.locale = old_locale
+ end
+
+ def test_submit_with_object_as_existing_record_and_locale_strings
+ old_locale, I18n.locale = I18n.locale, :submit
+
+ form_for(:post, @post) do |f|
+ concat f.submit
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='commit' id='post_submit' type='submit' value='Confirm Post changes' />" +
+ "</form>"
+ assert_dom_equal expected, output_buffer
+ ensure
+ I18n.locale = old_locale
+ end
+
+ def test_submit_without_object_and_locale_strings
+ old_locale, I18n.locale = I18n.locale, :submit
+
+ form_for(:post) do |f|
+ concat f.submit :class => "extra"
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='commit' class='extra' id='post_submit' type='submit' value='Save changes' />" +
+ "</form>"
+ assert_dom_equal expected, output_buffer
+ ensure
+ I18n.locale = old_locale
+ end
+
+ def test_submit_with_object_and_nested_lookup
+ old_locale, I18n.locale = I18n.locale, :submit
+
+ form_for(:another_post, @post) do |f|
+ concat f.submit
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='commit' id='another_post_submit' type='submit' value='Update your Post' />" +
+ "</form>"
+ assert_dom_equal expected, output_buffer
+ ensure
+ I18n.locale = old_locale
+ end
+
def test_nested_fields_for
form_for(:post, @post) do |f|
f.fields_for(:comment, @post) do |c|
@@ -659,7 +752,7 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
-
+
def test_nested_fields_for_with_existing_records_on_a_nested_attributes_one_to_one_association_with_explicit_hidden_field_placement
@post.author = Author.new(321)
@@ -670,7 +763,7 @@ class FormHelperTest < ActionView::TestCase
concat af.text_field(:name)
end
end
-
+
expected = '<form action="http://www.example.com" method="post">' +
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
@@ -715,7 +808,7 @@ class FormHelperTest < ActionView::TestCase
end
end
end
-
+
expected = '<form action="http://www.example.com" method="post">' +
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
diff --git a/actionpack/test/template/form_options_helper_i18n_test.rb b/actionpack/test/template/form_options_helper_i18n_test.rb
index 91e370efa7..4972ea6511 100644
--- a/actionpack/test/template/form_options_helper_i18n_test.rb
+++ b/actionpack/test/template/form_options_helper_i18n_test.rb
@@ -6,7 +6,7 @@ class FormOptionsHelperI18nTests < ActionView::TestCase
def setup
@prompt_message = 'Select!'
I18n.backend.send(:init_translations)
- I18n.backend.store_translations :en, :support => { :select => { :prompt => @prompt_message } }
+ I18n.backend.store_translations :en, :helpers => { :select => { :prompt => @prompt_message } }
end
def teardown
@@ -14,7 +14,7 @@ class FormOptionsHelperI18nTests < ActionView::TestCase
end
def test_select_with_prompt_true_translates_prompt_message
- I18n.expects(:translate).with('support.select.prompt', { :default => 'Please select' })
+ I18n.expects(:translate).with('helpers.select.prompt', { :default => 'Please select' })
select('post', 'category', [], :prompt => true)
end
@@ -24,4 +24,4 @@ class FormOptionsHelperI18nTests < ActionView::TestCase
select('post', 'category', [], :prompt => true)
)
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index f0f686f6e2..03caad3d46 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -13,8 +13,13 @@ class JavaScriptHelperTest < ActionView::TestCase
def setup
super
+ ActiveSupport.escape_html_entities_in_json = true
@template = self
end
+
+ def teardown
+ ActiveSupport.escape_html_entities_in_json = false
+ end
def _evaluate_assigns_and_ivars() end
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index 313a769088..9225153798 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -317,6 +317,11 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
def setup
super
@generator = create_generator
+ ActiveSupport.escape_html_entities_in_json = true
+ end
+
+ def teardown
+ ActiveSupport.escape_html_entities_in_json = false
end
def _evaluate_assigns_and_ivars() end
diff --git a/actionpack/test/template/subscriber_test.rb b/actionpack/test/template/subscriber_test.rb
new file mode 100644
index 0000000000..af0b3102cf
--- /dev/null
+++ b/actionpack/test/template/subscriber_test.rb
@@ -0,0 +1,102 @@
+require "abstract_unit"
+require "rails/subscriber/test_helper"
+require "action_view/railties/subscriber"
+require "controller/fake_models"
+
+module ActionViewSubscriberTest
+
+ def setup
+ @old_logger = ActionController::Base.logger
+ @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ Rails.stubs(:root).returns(File.expand_path(FIXTURE_LOAD_PATH))
+ Rails::Subscriber.add(:action_view, ActionView::Railties::Subscriber.new)
+ super
+ end
+
+ def teardown
+ super
+ Rails::Subscriber.subscribers.clear
+ ActionController::Base.logger = @old_logger
+ end
+
+ def set_logger(logger)
+ ActionController::Base.logger = logger
+ end
+
+ def test_render_file_template
+ @view.render(:file => "test/hello_world.erb")
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered test\/hello_world\.erb/, @logger.logged(:info).last
+ end
+
+ def test_render_text_template
+ @view.render(:text => "OMG")
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered text template/, @logger.logged(:info).last
+ end
+
+ def test_render_inline_template
+ @view.render(:inline => "<%= 'OMG' %>")
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered inline template/, @logger.logged(:info).last
+ end
+
+ def test_render_partial_template
+ @view.render(:partial => "test/customer")
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered test\/_customer.erb/, @logger.logged(:info).last
+ end
+
+ def test_render_partial_with_implicit_path
+ @view.stubs(:controller_path).returns("test")
+ @view.render(Customer.new("david"), :greeting => "hi")
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered customers\/_customer\.html\.erb/, @logger.logged(:info).last
+ end
+
+ def test_render_collection_template
+ @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ])
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered test\/_customer.erb/, @logger.logged(:info).last
+ end
+
+ def test_render_collection_with_implicit_path
+ @view.stubs(:controller_path).returns("test")
+ @view.render([ Customer.new("david"), Customer.new("mary") ], :greeting => "hi")
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered customers\/_customer\.html\.erb/, @logger.logged(:info).last
+ end
+
+ def test_render_collection_template_without_path
+ @view.stubs(:controller_path).returns("test")
+ @view.render([ GoodCustomer.new("david"), Customer.new("mary") ], :greeting => "hi")
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match /Rendered collection/, @logger.logged(:info).last
+ end
+
+ class SyncSubscriberTest < ActiveSupport::TestCase
+ include Rails::Subscriber::SyncTestHelper
+ include ActionViewSubscriberTest
+ end
+
+ class AsyncSubscriberTest < ActiveSupport::TestCase
+ include Rails::Subscriber::AsyncTestHelper
+ include ActionViewSubscriberTest
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 08143ba680..088c07b8bb 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -360,6 +360,20 @@ class TextHelperTest < ActionView::TestCase
assert_equal %(<p>#{link10_result} Link</p>), auto_link("<p>#{link10_raw} Link</p>")
end
+ def test_auto_link_other_protocols
+ silence_warnings do
+ begin
+ old_re_value = ActionView::Helpers::TextHelper::AUTO_LINK_RE
+ ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, %r{(ftp://)[^\s<]+}
+ link_raw = 'ftp://example.com/file.txt'
+ link_result = generate_result(link_raw)
+ assert_equal %(Download #{link_result}), auto_link("Download #{link_raw}")
+ ensure
+ ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, old_re_value
+ end
+ end
+ end
+
def test_auto_link_already_linked
linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com')
linked2 = generate_result('www.rubyonrails.com', 'http://www.rubyonrails.com')