aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb9
-rw-r--r--actionpack/lib/action_controller/metal/cookies.rb168
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb4
-rw-r--r--actionpack/lib/action_view/render/rendering.rb22
-rw-r--r--actionpack/test/controller/cookie_test.rb35
-rw-r--r--actionpack/test/controller/flash_test.rb24
-rw-r--r--actionpack/test/dispatch/routing_test.rb9
8 files changed, 206 insertions, 71 deletions
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index f4e1580977..64a8a5f241 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -125,8 +125,8 @@ module AbstractController
if options.key?(:text)
options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text)
elsif options.key?(:inline)
- handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
- template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
+ handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
+ template = ActionView::Template.new(options[:inline], "inline template", handler, {})
options[:_template] = template
elsif options.key?(:template)
options[:_template_name] = options[:template]
@@ -194,9 +194,8 @@ module AbstractController
# otherwise, process the parameter into a ViewPathSet.
def view_paths=(paths)
clear_template_caches!
- self._view_paths = paths.is_a?(ActionView::PathSet) ?
- paths : ActionView::Base.process_view_paths(paths)
+ self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb
index e27374e4c4..8d5f0d7199 100644
--- a/actionpack/lib/action_controller/metal/cookies.rb
+++ b/actionpack/lib/action_controller/metal/cookies.rb
@@ -50,56 +50,148 @@ module ActionController #:nodoc:
included do
helper_method :cookies
+ cattr_accessor :cookie_verifier_secret
end
- protected
- # Returns the cookie container, which operates as described above.
- def cookies
- @cookies ||= CookieJar.build(request, response)
+ protected
+ # Returns the cookie container, which operates as described above.
+ def cookies
+ @cookies ||= CookieJar.build(request, response)
+ end
end
- end
- class CookieJar < Hash #:nodoc:
- def self.build(request, response)
- new.tap do |hash|
- hash.update(request.cookies)
- hash.response = response
+ class CookieJar < Hash #:nodoc:
+ def self.build(request, response)
+ new.tap do |hash|
+ hash.update(request.cookies)
+ hash.response = response
+ end
end
- end
- attr_accessor :response
+ 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
+ # 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
- # 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)
+ 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!
- value = options[:value]
- else
- value = options
- options = { :value => value }
+ options[:path] ||= "/"
+ value = super(key.to_s)
+ response.delete_cookie(key, options)
+ value
end
- super(key.to_s, value)
-
- options[:path] ||= "/"
- response.set_cookie(key, options)
+ # 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
-
- # 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
+
+ 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/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
index 5141918499..833475cff7 100644
--- a/actionpack/lib/action_controller/metal/rack_delegation.rb
+++ b/actionpack/lib/action_controller/metal/rack_delegation.rb
@@ -3,7 +3,7 @@ module ActionController
extend ActiveSupport::Concern
included do
- delegate :session, :reset_session, :to => "@_request"
+ delegate :session, :to => "@_request"
delegate :headers, :status=, :location=, :content_type=,
:status, :location, :content_type, :to => "@_response"
attr_internal :request
@@ -24,5 +24,9 @@ module ActionController
response.body = body if response
super
end
+
+ def reset_session
+ @_request.reset_session
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index bf2443c1be..a4dc5e0956 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -5,7 +5,7 @@ module ActionDispatch
module Routing
class RouteSet #:nodoc:
NotFound = lambda { |env|
- raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect} with #{env.inspect}"
+ raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
}
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
@@ -426,7 +426,7 @@ module ActionDispatch
end
end
- raise ActionController::RoutingError, "No route matches #{path.inspect} with #{environment.inspect}"
+ raise ActionController::RoutingError, "No route matches #{path.inspect}"
end
end
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 7006a5b968..d4d16b4d98 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -78,12 +78,12 @@ module ActionView
end
def _render_inline(inline, layout, options)
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- template = Template.new(options[:inline],
- "inline #{options[:inline].inspect}", handler, {})
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ template = Template.new(options[:inline], "inline template", handler, {})
- locals = options[:locals]
+ locals = options[:locals]
content = template.render(self, locals)
+
_render_text(content, layout, locals)
end
@@ -91,6 +91,7 @@ module ActionView
content = layout.render(self, locals) do |*name|
_layout_for(*name) { content }
end if layout
+
content
end
@@ -113,21 +114,16 @@ module ActionView
msg
end
- locals = options[:locals] || {}
-
- content = if partial
- _render_partial_object(template, options)
- else
- template.render(self, locals)
- end
-
+ locals = options[:locals] || {}
+ content = partial ? _render_partial_object(template, options) : template.render(self, locals)
@_content_for[:layout] = content
if layout
@_layout = layout.identifier
logger.info("Rendering template within #{layout.inspect}") if logger
- content = layout.render(self, locals) {|*name| _layout_for(*name) }
+ content = layout.render(self, locals) { |*name| _layout_for(*name) }
end
+
content
end
end
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index 53d4364576..84d5ce6ad4 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -1,5 +1,7 @@
require 'abstract_unit'
+ActionController::Base.cookie_verifier_secret = "thisISverySECRET123"
+
class CookieTest < ActionController::TestCase
class TestController < ActionController::Base
def authenticate
@@ -47,6 +49,21 @@ class CookieTest < ActionController::TestCase
cookies["user_name"] = { :value => "david", :httponly => true }
head :ok
end
+
+ def set_permanent_cookie
+ cookies.permanent[:user_name] = "Jamie"
+ head :ok
+ end
+
+ def set_signed_cookie
+ cookies.signed[:user_id] = 45
+ head :ok
+ end
+
+ def set_permanent_signed_cookie
+ cookies.permanent.signed[:remember_me] = 100
+ head :ok
+ end
end
tests TestController
@@ -134,6 +151,24 @@ class CookieTest < ActionController::TestCase
response = get :authenticate
assert response.headers["Set-Cookie"] =~ /user_name=david/
end
+
+ def test_permanent_cookie
+ get :set_permanent_cookie
+ assert_match /Jamie/, @response.headers["Set-Cookie"]
+ assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"]
+ end
+
+ def test_signed_cookie
+ get :set_signed_cookie
+ assert_equal 45, @controller.send(:cookies).signed[:user_id]
+ end
+
+ def test_permanent_signed_cookie
+ get :set_permanent_signed_cookie
+ assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"]
+ assert_equal 100, @controller.send(:cookies).signed[:remember_me]
+ end
+
private
def assert_cookie_header(expected)
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 1f5be431ac..a9b60386f1 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -34,7 +34,7 @@ class FlashTest < ActionController::TestCase
flash.keep
render :inline => "hello"
end
-
+
def use_flash_and_update_it
flash.update("this" => "hello again")
@flash_copy = {}.update flash
@@ -76,11 +76,11 @@ class FlashTest < ActionController::TestCase
def redirect_with_alert
redirect_to '/nowhere', :alert => "Beware the nowheres!"
end
-
+
def redirect_with_notice
redirect_to '/somewhere', :notice => "Good luck in the somewheres!"
end
-
+
def redirect_with_other_flashes
redirect_to '/wonderland', :flash => { :joyride => "Horses!" }
end
@@ -101,7 +101,7 @@ class FlashTest < ActionController::TestCase
def test_keep_flash
get :set_flash
-
+
get :use_flash_and_keep_it
assert_equal "hello", assigns["flash_copy"]["that"]
assert_equal "hello", assigns["flashy"]
@@ -112,7 +112,7 @@ class FlashTest < ActionController::TestCase
get :use_flash
assert_nil assigns["flash_copy"]["that"], "On third flash"
end
-
+
def test_flash_now
get :set_flash_now
assert_equal "hello", assigns["flash_copy"]["that"]
@@ -123,8 +123,8 @@ class FlashTest < ActionController::TestCase
assert_nil assigns["flash_copy"]["that"]
assert_nil assigns["flash_copy"]["foo"]
assert_nil assigns["flashy"]
- end
-
+ end
+
def test_update_flash
get :set_flash
get :use_flash_and_update_it
@@ -140,7 +140,7 @@ class FlashTest < ActionController::TestCase
assert_equal "hello", assigns["flashy_that"]
assert_equal "good-bye", assigns["flashy_this"]
assert_nil assigns["flashy_that_reset"]
- end
+ end
def test_does_not_set_the_session_if_the_flash_is_empty
get :std_action
@@ -165,24 +165,24 @@ class FlashTest < ActionController::TestCase
assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed
assert_nil flash.discard(:unknown) # non existant key passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard()) # nothing passed
- assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil)) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil)) # nothing passed
assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed
assert_nil flash.keep(:unknown) # non existant key passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep()) # nothing passed
- assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed
end
def test_redirect_to_with_alert
get :redirect_with_alert
assert_equal "Beware the nowheres!", @controller.send(:flash)[:alert]
end
-
+
def test_redirect_to_with_notice
get :redirect_with_notice
assert_equal "Good luck in the somewheres!", @controller.send(:flash)[:notice]
end
-
+
def test_redirect_to_with_other_flashes
get :redirect_with_other_flashes
assert_equal "Horses!", @controller.send(:flash)[:joyride]
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 7058bc2ea0..1c7822358d 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -109,6 +109,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
scope ':access_token', :constraints => { :access_token => /\w{5,5}/ } do
resources :rooms
end
+
+ root :to => 'projects#index'
end
end
@@ -458,6 +460,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_root
+ with_test_routes do
+ get '/'
+ assert_equal 'projects#index', @response.body
+ end
+ end
+
private
def with_test_routes
real_routes, temp_routes = ActionController::Routing::Routes, Routes