aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
authorMikel Lindsaar <raasdnil@gmail.com>2010-01-19 19:41:15 +1100
committerMikel Lindsaar <raasdnil@gmail.com>2010-01-19 19:41:15 +1100
commiteaae58ce0c79aa5b4d6de16e2e67034b7fd971bb (patch)
treed2d3fc90f977020d4e389f526f7ce494a23c3e69 /actionpack/lib
parentc5acbcbb0f72b9968decd702041dcb5b72574a28 (diff)
parentc71120e29caddda295c133adfb279870733a3f81 (diff)
downloadrails-eaae58ce0c79aa5b4d6de16e2e67034b7fd971bb.tar.gz
rails-eaae58ce0c79aa5b4d6de16e2e67034b7fd971bb.tar.bz2
rails-eaae58ce0c79aa5b4d6de16e2e67034b7fd971bb.zip
Merge branch 'master' of github.com:lifo/docrails
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller/base.rb140
-rw-r--r--actionpack/lib/action_controller/deprecated/dispatcher.rb5
-rw-r--r--actionpack/lib/action_controller/metal/cookies.rb186
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb4
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb3
-rw-r--r--actionpack/lib/action_controller/railtie.rb50
-rw-r--r--actionpack/lib/action_controller/railties/subscriber.rb7
-rw-r--r--actionpack/lib/action_dispatch.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb216
-rw-r--r--actionpack/lib/action_dispatch/middleware/notifications.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb32
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb9
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb2
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb29
-rw-r--r--actionpack/lib/action_dispatch/railties/subscriber.rb17
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb59
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb4
20 files changed, 432 insertions, 367 deletions
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 48725ad82a..3119ee498b 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -62,15 +62,19 @@ module AbstractController
# Array[String]:: A list of all methods that should be considered
# actions.
def action_methods
- @action_methods ||=
+ @action_methods ||= begin
# All public instance methods of this class, including ancestors
- public_instance_methods(true).map { |m| m.to_s }.to_set -
- # Except for public instance methods of Base and its ancestors
- internal_methods.map { |m| m.to_s } +
- # Be sure to include shadowed public instance methods of this class
- public_instance_methods(false).map { |m| m.to_s } -
- # And always exclude explicitly hidden actions
- hidden_actions
+ methods = public_instance_methods(true).map { |m| m.to_s }.to_set -
+ # Except for public instance methods of Base and its ancestors
+ internal_methods.map { |m| m.to_s } +
+ # Be sure to include shadowed public instance methods of this class
+ public_instance_methods(false).map { |m| m.to_s } -
+ # And always exclude explicitly hidden actions
+ hidden_actions
+
+ # Clear out AS callback method pollution
+ methods.reject { |method| method =~ /_one_time_conditions/ }
+ end
end
# Returns the full controller name, underscored, without the ending Controller.
@@ -116,68 +120,72 @@ module AbstractController
self.class.controller_path
end
- private
- # Returns true if the name can be considered an action. This can
- # be overridden in subclasses to modify the semantics of what
- # can be considered an action.
- #
- # ==== Parameters
- # name<String>:: The name of an action to be tested
- #
- # ==== Returns
- # TrueClass, FalseClass
- def action_method?(name)
- self.class.action_methods.include?(name)
+ def action_methods
+ self.class.action_methods
end
- # Call the action. Override this in a subclass to modify the
- # behavior around processing an action. This, and not #process,
- # is the intended way to override action dispatching.
- def process_action(method_name, *args)
- send_action(method_name, *args)
- end
+ private
+ # Returns true if the name can be considered an action. This can
+ # be overridden in subclasses to modify the semantics of what
+ # can be considered an action.
+ #
+ # ==== Parameters
+ # name<String>:: The name of an action to be tested
+ #
+ # ==== Returns
+ # TrueClass, FalseClass
+ def action_method?(name)
+ self.class.action_methods.include?(name)
+ end
- # Actually call the method associated with the action. Override
- # this method if you wish to change how action methods are called,
- # not to add additional behavior around it. For example, you would
- # override #send_action if you want to inject arguments into the
- # method.
- alias send_action send
-
- # If the action name was not found, but a method called "action_missing"
- # was found, #method_for_action will return "_handle_action_missing".
- # This method calls #action_missing with the current action name.
- def _handle_action_missing
- action_missing(@_action_name)
- end
+ # Call the action. Override this in a subclass to modify the
+ # behavior around processing an action. This, and not #process,
+ # is the intended way to override action dispatching.
+ def process_action(method_name, *args)
+ send_action(method_name, *args)
+ end
- # Takes an action name and returns the name of the method that will
- # handle the action. In normal cases, this method returns the same
- # name as it receives. By default, if #method_for_action receives
- # a name that is not an action, it will look for an #action_missing
- # method and return "_handle_action_missing" if one is found.
- #
- # Subclasses may override this method to add additional conditions
- # that should be considered an action. For instance, an HTTP controller
- # with a template matching the action name is considered to exist.
- #
- # If you override this method to handle additional cases, you may
- # also provide a method (like _handle_method_missing) to handle
- # the case.
- #
- # If none of these conditions are true, and method_for_action
- # returns nil, an ActionNotFound exception will be raised.
- #
- # ==== Parameters
- # action_name<String>:: An action name to find a method name for
- #
- # ==== Returns
- # String:: The name of the method that handles the action
- # nil:: No method name could be found. Raise ActionNotFound.
- def method_for_action(action_name)
- if action_method?(action_name) then action_name
- elsif respond_to?(:action_missing, true) then "_handle_action_missing"
+ # Actually call the method associated with the action. Override
+ # this method if you wish to change how action methods are called,
+ # not to add additional behavior around it. For example, you would
+ # override #send_action if you want to inject arguments into the
+ # method.
+ alias send_action send
+
+ # If the action name was not found, but a method called "action_missing"
+ # was found, #method_for_action will return "_handle_action_missing".
+ # This method calls #action_missing with the current action name.
+ def _handle_action_missing
+ action_missing(@_action_name)
+ end
+
+ # Takes an action name and returns the name of the method that will
+ # handle the action. In normal cases, this method returns the same
+ # name as it receives. By default, if #method_for_action receives
+ # a name that is not an action, it will look for an #action_missing
+ # method and return "_handle_action_missing" if one is found.
+ #
+ # Subclasses may override this method to add additional conditions
+ # that should be considered an action. For instance, an HTTP controller
+ # with a template matching the action name is considered to exist.
+ #
+ # If you override this method to handle additional cases, you may
+ # also provide a method (like _handle_method_missing) to handle
+ # the case.
+ #
+ # If none of these conditions are true, and method_for_action
+ # returns nil, an ActionNotFound exception will be raised.
+ #
+ # ==== Parameters
+ # action_name<String>:: An action name to find a method name for
+ #
+ # ==== Returns
+ # String:: The name of the method that handles the action
+ # nil:: No method name could be found. Raise ActionNotFound.
+ def method_for_action(action_name)
+ if action_method?(action_name) then action_name
+ elsif respond_to?(:action_missing, true) then "_handle_action_missing"
+ end
end
- end
end
end
diff --git a/actionpack/lib/action_controller/deprecated/dispatcher.rb b/actionpack/lib/action_controller/deprecated/dispatcher.rb
index 3da3c8ce7d..8c21e375dd 100644
--- a/actionpack/lib/action_controller/deprecated/dispatcher.rb
+++ b/actionpack/lib/action_controller/deprecated/dispatcher.rb
@@ -1,8 +1,5 @@
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. " <<
@@ -18,7 +15,7 @@ module ActionController
def to_prepare(*args, &block)
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.to_prepare is deprecated. " <<
- "Please use ActionDispatch::Callbacks.to_prepare instead.", caller
+ "Please use config.to_prepare instead", caller
ActionDispatch::Callbacks.after(*args, &block)
end
diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb
index 5b51bd21d0..7aa687b52c 100644
--- a/actionpack/lib/action_controller/metal/cookies.rb
+++ b/actionpack/lib/action_controller/metal/cookies.rb
@@ -1,48 +1,4 @@
module ActionController #:nodoc:
- # Cookies are read and written through ActionController#cookies.
- #
- # The cookies being read are the ones received along with the request, the cookies
- # being written will be sent out with the response. Reading a cookie does not get
- # the cookie object itself back, just the value it holds.
- #
- # Examples for writing:
- #
- # # Sets a simple session cookie.
- # cookies[:user_name] = "david"
- #
- # # Sets a cookie that expires in 1 hour.
- # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
- #
- # Examples for reading:
- #
- # cookies[:user_name] # => "david"
- # cookies.size # => 2
- #
- # Example for deleting:
- #
- # cookies.delete :user_name
- #
- # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
- #
- # cookies[:key] = {
- # :value => 'a yummy cookie',
- # :expires => 1.year.from_now,
- # :domain => 'domain.com'
- # }
- #
- # cookies.delete(:key, :domain => 'domain.com')
- #
- # The option symbols for setting cookies are:
- #
- # * <tt>:value</tt> - The cookie's value or list of values (as an array).
- # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
- # of the application.
- # * <tt>:domain</tt> - The domain for which this cookie applies.
- # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
- # * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
- # Default is +false+.
- # * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
- # only HTTP. Defaults to +false+.
module Cookies
extend ActiveSupport::Concern
@@ -52,146 +8,10 @@ module ActionController #:nodoc:
helper_method :cookies
cattr_accessor :cookie_verifier_secret
end
-
- protected
- # Returns the cookie container, which operates as described above.
+
+ private
def cookies
- @cookies ||= CookieJar.build(request, response)
- end
- end
-
- class CookieJar < Hash #:nodoc:
- def self.build(request, response)
- new.tap do |hash|
- hash.update(request.cookies)
- hash.response = response
- end
- end
-
- attr_accessor :response
-
- # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
- def [](name)
- super(name.to_s)
- end
-
- # Sets the cookie named +name+. The second argument may be the very cookie
- # value, or a hash of options as documented above.
- def []=(key, options)
- if options.is_a?(Hash)
- options.symbolize_keys!
- value = options[:value]
- else
- value = options
- options = { :value => value }
+ request.cookie_jar
end
-
- super(key.to_s, value)
-
- options[:path] ||= "/"
- response.set_cookie(key, options)
- end
-
- # Removes the cookie on the client machine by setting the value to an empty string
- # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
- # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
- def delete(key, options = {})
- options.symbolize_keys!
- options[:path] ||= "/"
- value = super(key.to_s)
- response.delete_cookie(key, options)
- value
- end
-
- # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
- #
- # cookies.permanent[:prefers_open_id] = true
- # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
- #
- # This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
- #
- # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
- #
- # cookies.permanent.signed[:remember_me] = current_user.id
- # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
- def permanent
- @permanent ||= PermanentCookieJar.new(self)
- end
-
- # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
- # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
- # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
- # be raised.
- #
- # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret.
- #
- # Example:
- #
- # cookies.signed[:discount] = 45
- # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
- #
- # cookies.signed[:discount] # => 45
- def signed
- @signed ||= SignedCookieJar.new(self)
- end
- end
-
- class PermanentCookieJar < CookieJar #:nodoc:
- def initialize(parent_jar)
- @parent_jar = parent_jar
- end
-
- def []=(key, options)
- if options.is_a?(Hash)
- options.symbolize_keys!
- else
- options = { :value => options }
- end
-
- options[:expires] = 20.years.from_now
- @parent_jar[key] = options
- end
-
- def signed
- @signed ||= SignedCookieJar.new(self)
- end
-
- def controller
- @parent_jar.controller
- end
-
- def method_missing(method, *arguments, &block)
- @parent_jar.send(method, *arguments, &block)
- end
- end
-
- class SignedCookieJar < CookieJar #:nodoc:
- def initialize(parent_jar)
- unless ActionController::Base.cookie_verifier_secret
- raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies"
- end
-
- @parent_jar = parent_jar
- @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret)
- end
-
- def [](name)
- @verifier.verify(@parent_jar[name])
- end
-
- def []=(key, options)
- if options.is_a?(Hash)
- options.symbolize_keys!
- options[:value] = @verifier.generate(options[:value])
- else
- options = { :value => @verifier.generate(options) }
- end
-
- @parent_jar[key] = options
- end
-
- def method_missing(method, *arguments, &block)
- @parent_jar.send(method, *arguments, &block)
- end
end
end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 7222b7b2fa..876f778751 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -20,11 +20,7 @@ module ActionController
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
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index c193a5eff4..d62269b9af 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -10,6 +10,9 @@ module ActionController
@_response = response
@_response.request = request
ret = process(request.parameters[:action])
+ if cookies = @_request.env['action_dispatch.cookies']
+ cookies.write(@_response)
+ end
@_response.body ||= self.response_body
@_response.prepare!
set_test_assigns
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 741101a210..7ea64c1923 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -23,7 +23,6 @@ module ActionController
initializer "action_controller.initialize_routing" do |app|
app.route_configuration_files << app.config.routes_configuration_file
app.route_configuration_files << app.config.builtin_routes_configuration_file
- app.reload_routes!
end
initializer "action_controller.initialize_framework_caches" do
@@ -40,54 +39,5 @@ 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|
- metal_root = "#{Rails.root}/app/metal"
- load_list = app.config.metals || Dir["#{metal_root}/**/*.rb"]
-
- 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
- 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'
- ActionController::Dispatcher.prepare_each_request = true unless app.config.cache_classes
-
- unless app.config.cache_classes
- # Setup dev mode route reloading
- routes_last_modified = app.routes_changed_at
- reload_routes = lambda do
- unless app.routes_changed_at == routes_last_modified
- routes_last_modified = app.routes_changed_at
- app.reload_routes!
- end
- end
- ActionDispatch::Callbacks.before { |callbacks| reload_routes.call }
- end
- end
-
end
end
diff --git a/actionpack/lib/action_controller/railties/subscriber.rb b/actionpack/lib/action_controller/railties/subscriber.rb
index a9f5d16c58..6659e5df47 100644
--- a/actionpack/lib/action_controller/railties/subscriber.rb
+++ b/actionpack/lib/action_controller/railties/subscriber.rb
@@ -3,18 +3,13 @@ module ActionController
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"
+ message << " by #{payload[:controller]}##{payload[:action]} [#{payload[:status]}]"
info(message)
end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 7b44212310..082562d921 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -43,8 +43,10 @@ module ActionDispatch
autoload_under 'middleware' do
autoload :Callbacks
autoload :Cascade
+ autoload :Cookies
autoload :Flash
autoload :Head
+ autoload :Notifications
autoload :ParamsParser
autoload :Rescue
autoload :ShowExceptions
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 65df9b1f03..f299306ff4 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -119,7 +119,7 @@ module ActionDispatch # :nodoc:
def to_a
assign_default_content_type_and_charset!
handle_conditional_get!
- self["Set-Cookie"] = @cookie.join("\n")
+ self["Set-Cookie"] = @cookie.join("\n") unless @cookie.blank?
self["ETag"] = @etag if @etag
super
end
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 5ec406e134..d07841218a 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -45,8 +45,6 @@ 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/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
new file mode 100644
index 0000000000..0dc03a1a7e
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -0,0 +1,216 @@
+module ActionDispatch
+ class Request
+ def cookie_jar
+ env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
+ end
+ end
+
+ # Cookies are read and written through ActionController#cookies.
+ #
+ # The cookies being read are the ones received along with the request, the cookies
+ # being written will be sent out with the response. Reading a cookie does not get
+ # the cookie object itself back, just the value it holds.
+ #
+ # Examples for writing:
+ #
+ # # Sets a simple session cookie.
+ # cookies[:user_name] = "david"
+ #
+ # # Sets a cookie that expires in 1 hour.
+ # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
+ #
+ # Examples for reading:
+ #
+ # cookies[:user_name] # => "david"
+ # cookies.size # => 2
+ #
+ # Example for deleting:
+ #
+ # cookies.delete :user_name
+ #
+ # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
+ #
+ # cookies[:key] = {
+ # :value => 'a yummy cookie',
+ # :expires => 1.year.from_now,
+ # :domain => 'domain.com'
+ # }
+ #
+ # cookies.delete(:key, :domain => 'domain.com')
+ #
+ # The option symbols for setting cookies are:
+ #
+ # * <tt>:value</tt> - The cookie's value or list of values (as an array).
+ # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
+ # of the application.
+ # * <tt>:domain</tt> - The domain for which this cookie applies.
+ # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
+ # * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
+ # Default is +false+.
+ # * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
+ # only HTTP. Defaults to +false+.
+ class Cookies
+ class CookieJar < Hash #:nodoc:
+ def self.build(request)
+ new.tap do |hash|
+ hash.update(request.cookies)
+ end
+ end
+
+ def initialize
+ @set_cookies = {}
+ @delete_cookies = {}
+
+ super
+ end
+
+ # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
+ def [](name)
+ super(name.to_s)
+ end
+
+ # Sets the cookie named +name+. The second argument may be the very cookie
+ # value, or a hash of options as documented above.
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ value = options[:value]
+ else
+ value = options
+ options = { :value => value }
+ end
+
+ value = super(key.to_s, value)
+
+ options[:path] ||= "/"
+ @set_cookies[key] = options
+ value
+ end
+
+ # Removes the cookie on the client machine by setting the value to an empty string
+ # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
+ # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
+ def delete(key, options = {})
+ options.symbolize_keys!
+ options[:path] ||= "/"
+ value = super(key.to_s)
+ @delete_cookies[key] = options
+ value
+ end
+
+ # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
+ #
+ # cookies.permanent[:prefers_open_id] = true
+ # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+ #
+ # This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
+ #
+ # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
+ #
+ # cookies.permanent.signed[:remember_me] = current_user.id
+ # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+ def permanent
+ @permanent ||= PermanentCookieJar.new(self)
+ end
+
+ # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
+ # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
+ # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
+ # be raised.
+ #
+ # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret.
+ #
+ # Example:
+ #
+ # cookies.signed[:discount] = 45
+ # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
+ #
+ # cookies.signed[:discount] # => 45
+ def signed
+ @signed ||= SignedCookieJar.new(self)
+ end
+
+ def write(response)
+ @set_cookies.each { |k, v| response.set_cookie(k, v) }
+ @delete_cookies.each { |k, v| response.delete_cookie(k, v) }
+ end
+ end
+
+ class PermanentCookieJar < CookieJar #:nodoc:
+ def initialize(parent_jar)
+ @parent_jar = parent_jar
+ end
+
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ else
+ options = { :value => options }
+ end
+
+ options[:expires] = 20.years.from_now
+ @parent_jar[key] = options
+ end
+
+ def signed
+ @signed ||= SignedCookieJar.new(self)
+ end
+
+ def controller
+ @parent_jar.controller
+ end
+
+ def method_missing(method, *arguments, &block)
+ @parent_jar.send(method, *arguments, &block)
+ end
+ end
+
+ class SignedCookieJar < CookieJar #:nodoc:
+ def initialize(parent_jar)
+ unless ActionController::Base.cookie_verifier_secret
+ raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies"
+ end
+
+ @parent_jar = parent_jar
+ @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret)
+ end
+
+ def [](name)
+ if value = @parent_jar[name]
+ @verifier.verify(value)
+ end
+ end
+
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ options[:value] = @verifier.generate(options[:value])
+ else
+ options = { :value => @verifier.generate(options) }
+ end
+
+ @parent_jar[key] = options
+ end
+
+ def method_missing(method, *arguments, &block)
+ @parent_jar.send(method, *arguments, &block)
+ end
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, body = @app.call(env)
+
+ if cookie_jar = env['action_dispatch.cookies']
+ response = Rack::Response.new(body, status, headers)
+ cookie_jar.write(response)
+ response.to_a
+ else
+ [status, headers, body]
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/notifications.rb b/actionpack/lib/action_dispatch/middleware/notifications.rb
new file mode 100644
index 0000000000..01d2cbb435
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/notifications.rb
@@ -0,0 +1,24 @@
+module ActionDispatch
+ # Provide notifications in the middleware stack. Notice that for the before_dispatch
+ # and after_dispatch notifications, we just send the original env, so we don't pile
+ # up large env hashes in the queue. However, in exception cases, the whole env hash
+ # is actually useful, so we send it all.
+ class Notifications
+ def initialize(app)
+ @app = app
+ end
+
+ def call(stack_env)
+ env = stack_env.dup
+ ActiveSupport::Notifications.instrument("action_dispatch.before_dispatch", :env => env)
+
+ ActiveSupport::Notifications.instrument!("action_dispatch.after_dispatch", :env => env) do
+ @app.call(stack_env)
+ end
+ rescue Exception => exception
+ ActiveSupport::Notifications.instrument('action_dispatch.exception',
+ :env => stack_env, :exception => exception)
+ raise exception
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 10f04dcdf6..3bcd004e12 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -20,7 +20,7 @@ module ActionDispatch
# * :exception - The exception raised;
#
class ShowExceptions
- LOCALHOST = '127.0.0.1'.freeze
+ LOCALHOST = ['127.0.0.1', '::1'].freeze
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
@@ -61,11 +61,8 @@ module ActionDispatch
def call(env)
@app.call(env)
rescue Exception => 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
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ render_exception(env, exception)
end
private
@@ -88,7 +85,10 @@ module ActionDispatch
def rescue_action_locally(request, exception)
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
:request => request,
- :exception => exception
+ :exception => exception,
+ :application_trace => application_trace(exception),
+ :framework_trace => framework_trace(exception),
+ :full_trace => full_trace(exception)
)
file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
body = template.render(:file => file, :layout => 'rescues/layout.erb')
@@ -118,7 +118,7 @@ module ActionDispatch
# True if the request came from localhost, 127.0.0.1.
def local_request?(request)
- request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
+ LOCALHOST.any?{ |local_ip| request.remote_addr == local_ip && request.remote_ip == local_ip }
end
def status_code(exception)
@@ -148,9 +148,21 @@ module ActionDispatch
end
end
- def clean_backtrace(exception)
+ def application_trace(exception)
+ clean_backtrace(exception, :silent)
+ end
+
+ def framework_trace(exception)
+ clean_backtrace(exception, :noise)
+ end
+
+ def full_trace(exception)
+ clean_backtrace(exception, :all)
+ end
+
+ def clean_backtrace(exception, *args)
defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
- Rails.backtrace_cleaner.clean(exception.backtrace) :
+ Rails.backtrace_cleaner.clean(exception.backtrace, *args) :
exception.backtrace
end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index 5224403dab..839df50999 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -11,13 +11,20 @@
clean_params.delete("controller")
request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
+
+ def debug_hash(hash)
+ hash.sort_by { |k, v| k.to_s }.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
+ end
%>
<h2 style="margin-top: 30px">Request</h2>
<p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
<p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
-<div id="session_dump" style="display:none"><%= debug(@request.session.instance_variable_get("@data")) %></div>
+<div id="session_dump" style="display:none"><pre><%= debug_hash @request.session %></pre></div>
+
+<p><a href="#" onclick="document.getElementById('env_dump').style.display='block'; return false;">Show env dump</a></p>
+<div id="env_dump" style="display:none"><pre><%= debug_hash @request.env %></pre></div>
<h2 style="margin-top: 30px">Response</h2>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
index 07b4919934..d18b162a93 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
@@ -1,8 +1,8 @@
<%
traces = [
- ["Application Trace", @exception.application_backtrace],
- ["Framework Trace", @exception.framework_backtrace],
- ["Full Trace", @exception.clean_backtrace]
+ ["Application Trace", @application_trace],
+ ["Framework Trace", @framework_trace],
+ ["Full Trace", @full_trace]
]
names = traces.collect {|name, trace| name}
%>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
index 693e56270a..bd6ffbab5d 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
@@ -4,7 +4,7 @@
in <%=h @request.parameters['controller'].humanize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
<% end %>
</h1>
-<pre><%=h @exception.clean_message %></pre>
+<pre><%=h @exception.message %></pre>
<%= render :file => "rescues/_trace.erb" %>
<%= render :file => "rescues/_request_and_response.erb" %>
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
new file mode 100644
index 0000000000..18978bfb39
--- /dev/null
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -0,0 +1,29 @@
+require "action_dispatch"
+require "rails"
+
+module ActionDispatch
+ class Railtie < Rails::Railtie
+ plugin_name :action_dispatch
+
+ require "action_dispatch/railties/subscriber"
+ subscriber ActionDispatch::Railties::Subscriber.new
+
+ # Prepare dispatcher callbacks and run 'prepare' callbacks
+ initializer "action_dispatch.prepare_dispatcher" do |app|
+ # TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
+ require 'rails/dispatcher'
+
+ unless app.config.cache_classes
+ # Setup dev mode route reloading
+ routes_last_modified = app.routes_changed_at
+ reload_routes = lambda do
+ unless app.routes_changed_at == routes_last_modified
+ routes_last_modified = app.routes_changed_at
+ app.reload_routes!
+ end
+ end
+ ActionDispatch::Callbacks.before { |callbacks| reload_routes.call }
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/railties/subscriber.rb b/actionpack/lib/action_dispatch/railties/subscriber.rb
new file mode 100644
index 0000000000..c08a844c6a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/railties/subscriber.rb
@@ -0,0 +1,17 @@
+module ActionDispatch
+ module Railties
+ class Subscriber < Rails::Subscriber
+ def before_dispatch(event)
+ request = Request.new(event.payload[:env])
+ path = request.request_uri.inspect rescue "unknown"
+
+ info "\n\nProcessing #{path} to #{request.formats.join(', ')} " <<
+ "(for #{request.remote_ip} at #{event.time.to_s(:db)}) [#{request.method.to_s.upcase}]"
+ end
+
+ def logger
+ ActionController::Base.logger
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 9aaa4355f2..811c287355 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -418,28 +418,12 @@ module ActionDispatch
def resource(*resources, &block)
options = resources.extract_options!
- if resources.length > 1
- raise ArgumentError if block_given?
- resources.each { |r| resource(r, options) }
- return self
- end
-
- if path_names = options.delete(:path_names)
- scope(:resources_path_names => path_names) do
- resource(resources, options)
- end
+ if verify_common_behavior_for(:resource, resources, options, &block)
return self
end
resource = SingletonResource.new(resources.pop, options)
- if @scope[:scope_level] == :resources
- nested do
- resource(resource.name, options, &block)
- end
- return self
- end
-
scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resource, resource) do
yield if block_given?
@@ -459,28 +443,12 @@ module ActionDispatch
def resources(*resources, &block)
options = resources.extract_options!
- if resources.length > 1
- raise ArgumentError if block_given?
- resources.each { |r| resources(r, options) }
- return self
- end
-
- if path_names = options.delete(:path_names)
- scope(:resources_path_names => path_names) do
- resources(resources, options)
- end
+ if verify_common_behavior_for(:resources, resources, options, &block)
return self
end
resource = Resource.new(resources.pop, options)
- if @scope[:scope_level] == :resources
- nested do
- resources(resource.name, options, &block)
- end
- return self
- end
-
scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resources, resource) do
yield if block_given?
@@ -595,6 +563,29 @@ module ActionDispatch
path_names[name.to_sym] || name.to_s
end
+ def verify_common_behavior_for(method, resources, options, &block)
+ if resources.length > 1
+ resources.each { |r| send(method, r, options, &block) }
+ return true
+ end
+
+ if path_names = options.delete(:path_names)
+ scope(:resources_path_names => path_names) do
+ send(method, resources.pop, options, &block)
+ end
+ return true
+ end
+
+ if @scope[:scope_level] == :resources
+ nested do
+ send(method, resources.pop, options, &block)
+ end
+ return true
+ end
+
+ false
+ end
+
def with_exclusive_name_prefix(prefix)
begin
old_name_prefix = @scope[:name_prefix]
diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb
index 90863fca08..885945fde3 100644
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -27,10 +27,10 @@ module ActionView
def debug(object)
begin
Marshal::dump(object)
- "<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", "&nbsp; ")}</pre>"
+ "<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", "&nbsp; ")}</pre>".html_safe!
rescue Exception => e # errors from Marshal or YAML
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
- "<code class='debug_dump'>#{h(object.inspect)}</code>"
+ "<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe!
end
end
end