aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch')
-rw-r--r--actionpack/lib/action_dispatch/middleware/closed_error.rb7
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb28
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb68
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb5
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb3
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb12
7 files changed, 103 insertions, 24 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/closed_error.rb b/actionpack/lib/action_dispatch/middleware/closed_error.rb
new file mode 100644
index 0000000000..0a4db47f4b
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/closed_error.rb
@@ -0,0 +1,7 @@
+module ActionDispatch
+ class ClosedError < StandardError #:nodoc:
+ def initialize(kind)
+ super "Cannot modify #{kind} because it was closed. This means it was already streamed back to the client or converted to HTTP headers."
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 7ac608f0a8..24ebb8fed7 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -83,7 +83,7 @@ module ActionDispatch
# Raised when storing more than 4K of session data.
class CookieOverflow < StandardError; end
- class CookieJar < Hash #:nodoc:
+ class CookieJar #:nodoc:
# This regular expression is used to split the levels of a domain.
# The top level domain can be any string without a period or
@@ -115,13 +115,22 @@ module ActionDispatch
@delete_cookies = {}
@host = host
@secure = secure
-
- super()
+ @closed = false
+ @cookies = {}
end
+ attr_reader :closed
+ alias :closed? :closed
+ def close!; @closed = true end
+
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
def [](name)
- super(name.to_s)
+ @cookies[name.to_s]
+ end
+
+ def update(other_hash)
+ @cookies.update other_hash
+ self
end
def handle_options(options) #:nodoc:
@@ -145,6 +154,7 @@ module ActionDispatch
# 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)
+ raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
value = options[:value]
@@ -153,7 +163,7 @@ module ActionDispatch
options = { :value => value }
end
- value = super(key.to_s, value)
+ value = @cookies[key.to_s] = value
handle_options(options)
@@ -170,7 +180,7 @@ module ActionDispatch
handle_options(options)
- value = super(key.to_s)
+ value = @cookies.delete(key.to_s)
@delete_cookies[key] = options
value
end
@@ -225,6 +235,7 @@ module ActionDispatch
end
def []=(key, options)
+ raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
else
@@ -263,6 +274,7 @@ module ActionDispatch
end
def []=(key, options)
+ raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
options[:value] = @verifier.generate(options[:value])
@@ -305,6 +317,7 @@ module ActionDispatch
end
def call(env)
+ cookie_jar = nil
status, headers, body = @app.call(env)
if cookie_jar = env['action_dispatch.cookies']
@@ -315,6 +328,9 @@ module ActionDispatch
end
[status, headers, body]
+ ensure
+ cookie_jar = ActionDispatch::Request.new(env).cookie_jar unless cookie_jar
+ cookie_jar.close!
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 21aeeb217a..027ff7f8ac 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -43,9 +43,15 @@ module ActionDispatch
class FlashNow #:nodoc:
def initialize(flash)
@flash = flash
+ @closed = false
end
+ attr_reader :closed
+ alias :closed? :closed
+ def close!; @closed = true end
+
def []=(k, v)
+ raise ClosedError, :flash if closed?
@flash[k] = v
@flash.discard(k)
v
@@ -66,27 +72,70 @@ module ActionDispatch
end
end
- class FlashHash < Hash
+ class FlashHash
+ include Enumerable
+
def initialize #:nodoc:
- super
- @used = Set.new
+ @used = Set.new
+ @closed = false
+ @flashes = {}
end
+ attr_reader :closed
+ alias :closed? :closed
+ def close!; @closed = true end
+
def []=(k, v) #:nodoc:
+ raise ClosedError, :flash if closed?
keep(k)
- super
+ @flashes[k] = v
+ end
+
+ def [](k)
+ @flashes[k]
end
def update(h) #:nodoc:
h.keys.each { |k| keep(k) }
- super
+ @flashes.update h
+ self
+ end
+
+ def keys
+ @flashes.keys
+ end
+
+ def key?(name)
+ @flashes.key? name
+ end
+
+ def delete(key)
+ @flashes.delete key
+ self
+ end
+
+ def to_hash
+ @flashes.dup
+ end
+
+ def empty?
+ @flashes.empty?
+ end
+
+ def clear
+ @flashes.clear
+ end
+
+ def each(&block)
+ @flashes.each(&block)
end
alias :merge! :update
def replace(h) #:nodoc:
@used = Set.new
- super
+ @flashes.replace h
+ self
end
# Sets a flash that will not be available to the next action, only to the current.
@@ -100,7 +149,7 @@ module ActionDispatch
#
# Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
def now
- FlashNow.new(self)
+ @now ||= FlashNow.new(self)
end
# Keeps either the entire current flash or a specific flash entry available for the next action:
@@ -184,8 +233,11 @@ module ActionDispatch
session = env['rack.session'] || {}
flash_hash = env['action_dispatch.request.flash_hash']
- if flash_hash && (!flash_hash.empty? || session.key?('flash'))
+ if flash_hash
+ if !flash_hash.empty? || session.key?('flash')
session["flash"] = flash_hash
+ end
+ flash_hash.close!
end
if session.key?('flash') && session['flash'].empty?
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 35be0b3a27..13aef18ee1 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,6 +1,7 @@
require 'erb'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/object/inclusion'
require 'active_support/inflector'
require 'action_dispatch/routing/redirection'
@@ -1345,11 +1346,11 @@ module ActionDispatch
end
def resource_scope? #:nodoc:
- [:resource, :resources].include?(@scope[:scope_level])
+ @scope[:scope_level].among?(:resource, :resources)
end
def resource_method_scope? #:nodoc:
- [:collection, :member, :new].include?(@scope[:scope_level])
+ @scope[:scope_level].among?(:collection, :member, :new)
end
def with_exclusive_scope
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 77a15f3e97..16a6b93ce8 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/inclusion'
+
module ActionDispatch
module Assertions
# A small suite of assertions that test responses from \Rails applications.
@@ -33,7 +35,7 @@ module ActionDispatch
def assert_response(type, message = nil)
validate_request!
- if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
+ if type.among?(:success, :missing, :redirect, :error) && @response.send("#{type}?")
assert_block("") { true } # to count the assertion
elsif type.is_a?(Fixnum) && @response.response_code == type
assert_block("") { true } # to count the assertion
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index 2b862fb7d6..f41d3e5ddb 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -1,4 +1,5 @@
require 'action_controller/vendor/html-scanner'
+require 'active_support/core_ext/object/inclusion'
#--
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
@@ -441,7 +442,7 @@ module ActionDispatch
if matches
assert_block("") { true } # to count the assertion
- if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
+ if block_given? && !rjs_type.among?(:remove, :show, :hide, :toggle)
begin
@selected ||= nil
in_scope, @selected = @selected, matches
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 4706112a06..174babb9aa 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -1,6 +1,7 @@
require 'stringio'
require 'uri'
require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/object/inclusion'
require 'active_support/core_ext/object/try'
require 'rack/test'
require 'test/unit/assertions'
@@ -243,7 +244,8 @@ module ActionDispatch
end
# Performs the actual request.
- def process(method, path, parameters = nil, rack_environment = nil)
+ def process(method, path, parameters = nil, env = nil)
+ env ||= {}
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
@@ -259,7 +261,7 @@ module ActionDispatch
hostname, port = host.split(':')
- env = {
+ default_env = {
:method => method,
:params => parameters,
@@ -277,9 +279,7 @@ module ActionDispatch
session = Rack::Test::Session.new(_mock_session)
- (rack_environment || {}).each do |key, value|
- env[key] = value
- end
+ env.reverse_merge!(default_env)
# NOTE: rack-test v0.5 doesn't build a default uri correctly
# Make sure requested path is always a full uri
@@ -321,7 +321,7 @@ module ActionDispatch
define_method(method) do |*args|
reset! unless integration_session
# reset the html_document variable, but only for new get/post calls
- @html_document = nil unless %w(cookies assigns).include?(method)
+ @html_document = nil unless method.among?("cookies", "assigns")
integration_session.__send__(method, *args).tap do
copy_session_variables!
end