From d0c90331f19004f9f297989785f1516631ea0fc3 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 26 Nov 2004 02:04:35 +0000 Subject: Added a new container for cookies that makes them more intuative to use. The old methods of cookie and @cookies have been deprecated. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@20 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/CHANGELOG | 17 ++++++-- actionpack/install.rb | 1 + actionpack/lib/action_controller.rb | 2 + actionpack/lib/action_controller/base.rb | 32 +++------------ actionpack/lib/action_controller/cookies.rb | 61 +++++++++++++++++++++++++++++ actionpack/test/controller/cookie_test.rb | 35 ++++++++++++++++- 6 files changed, 116 insertions(+), 32 deletions(-) create mode 100644 actionpack/lib/action_controller/cookies.rb diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 87280683fc..eedef92916 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,9 +1,20 @@ *SVN* -* Added ActionController#cookies[]= as an alias for cookie "name", "value" -- you'll still need to use the latter if you have options to set. +* Added a new container for cookies that makes them more intuative to use. The old methods of cookie and @cookies have been deprecated. -* Added ActionController#cookies[] as a reader for @cookies that'll return the value of the cookie instead of the cookie object itself. - NOTE: If you were using the old accessor, this could potentially break your code -- if you expect a full cookie object! + Examples for writing: + + cookies["user_name"] = "david" # => Will set a simple session cookie + cookies["login"] = { "value" => "XJ-122", "expires" => Time.now + 360} # => Will set a cookie that expires in 1 hour + + Examples for reading: + + cookies["user_name"] # => "david" + cookies.size # => 2 + + Read more in ActionController::Cookies + + NOTE: If you were using the old accessor (cookies instead of @cookies), this could potentially break your code -- if you expect a full cookie object! * Added the opportunity to defined method_missing on a controller which will handle all requests for actions not otherwise defined #223 [timb] diff --git a/actionpack/install.rb b/actionpack/install.rb index 758c476a70..0f7b91e0d7 100644 --- a/actionpack/install.rb +++ b/actionpack/install.rb @@ -41,6 +41,7 @@ files = %w- action_controller/cgi_ext/cgi_ext.rb action_controller/cgi_ext/cgi_methods.rb action_controller/cgi_process.rb + action_controller/cookies.rb action_controller/filters.rb action_controller/flash.rb action_controller/helpers.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 6445940d78..9cc31cd90c 100755 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -34,6 +34,7 @@ require 'action_controller/flash' require 'action_controller/scaffolding' require 'action_controller/helpers' require 'action_controller/dependencies' +require 'action_controller/cookies' require 'action_controller/cgi_process' ActionController::Base.class_eval do @@ -45,6 +46,7 @@ ActionController::Base.class_eval do include ActionController::Scaffolding include ActionController::Helpers include ActionController::Dependencies + include ActionController::Cookies end require 'action_view' diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 5c386ac05a..a794272a9a 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -539,33 +539,6 @@ module ActionController #:nodoc: @performed_redirect = true end - # Creates a new cookie that is sent along-side the next render or redirect command. API is the same as for CGI::Cookie. - # Examples: - # - # cookie("name", "value1", "value2", ...) - # cookie("name" => "name", "value" => "value") - # cookie('name' => 'name', - # 'value' => ['value1', 'value2', ...], - # 'path' => 'path', # optional - # 'domain' => 'domain', # optional - # 'expires' => Time.now, # optional - # 'secure' => true # optional - # ) - def cookie(*options) #:doc: - @response.headers["cookie"] << CGI::Cookie.new(*options) - end - - # Alias for cookie "name", "value" - def cookies[]=(name, value) - cookie(name, value) - end - - # Returns the value of the cookie by +name+ -- or nil if no such cookie exist. You set new cookies using either the cookie method - # or cookies[]= (for simple name/value cookies without options). - def cookies[](name) - @cookies[name].value if @cookies[name] - end - # Resets the session by clearsing out all the objects stored within and initializing a new session object. def reset_session #:doc: @request.reset_session @@ -573,6 +546,11 @@ module ActionController #:nodoc: @response.session = @session end + # Deprecated cookie writer method + def cookie(*options) + @response.headers["cookie"] << CGI::Cookie.new(*options) + end + private def initialize_template_class(response) begin diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/cookies.rb new file mode 100644 index 0000000000..78815b7bc4 --- /dev/null +++ b/actionpack/lib/action_controller/cookies.rb @@ -0,0 +1,61 @@ +module ActionController #:nodoc: + # Cookies are read and written through ActionController#cookies. The cookies being read is what was received along with the request, + # the cookies being written is what will be sent out will the response. Cookies are read by value (so you won't get the cookie object + # itself back -- just the value it holds). Examples for writting: + # + # cookies["user_name"] = "david" # => Will set a simple session cookie + # cookies["login"] = { "value" => "XJ-122", "expires" => Time.now + 360} # => Will set a cookie that expires in 1 hour + # + # Examples for reading: + # + # cookies["user_name"] # => "david" + # cookies.size # => 2 + # + # All the options for setting cookies are: + # + # value:: the cookie's value or list of values (as an array). + # path:: the path for which this cookie applies. Defaults to the root of the application. + # domain:: the domain for which this cookie applies. + # expires:: the time at which this cookie expires, as a +Time+ object. + # secure:: whether this cookie is a secure cookie or not (default to false). + # Secure cookies are only transmitted to HTTPS servers. + module Cookies + # Returns the cookie container, which operates as described above. + def cookies + CookieContainer.new(self) + end + end + + class CookieContainer < Hash #:nodoc: + def initialize(controller) + @controller, @cookies = controller, controller.instance_variable_get("@cookies") + super() + update(@cookies) + end + + # Returns the value of the cookie by +name+ -- or nil if no such cookie exist. You set new cookies using either the cookie method + # or cookies[]= (for simple name/value cookies without options). + def [](name) + @cookies[name].value if @cookies[name] + end + + def []=(name, options) + if options.is_a?(Hash) + options["name"] = name + else + options = [ name, options ] + end + + set_cookie(name, options) + end + + private + def set_cookie(name, options) #:doc: + if options.is_a?(Array) + @controller.response.headers["cookie"] << CGI::Cookie.new(*options) + else + @controller.response.headers["cookie"] << CGI::Cookie.new(options) + end + end + end +end diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb index d3099bcd99..1bd17c5c2f 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/controller/cookie_test.rb @@ -2,11 +2,27 @@ require File.dirname(__FILE__) + '/../abstract_unit' class CookieTest < Test::Unit::TestCase class TestController < ActionController::Base - def authenticate + def authenticate_with_deprecated_writer cookie "name" => "user_name", "value" => "david" render_text "hello world" end + def authenticate + cookies["user_name"] = "david" + render_text "hello world" + end + + def authenticate_for_fourten_days + cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) } + render_text "hello world" + end + + def set_multiple_cookies + cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) } + cookies["login"] = "XJ-122" + render_text "hello world" + end + def access_frozen_cookies @cookies["wont"] = "work" end @@ -21,12 +37,27 @@ class CookieTest < Test::Unit::TestCase @request.host = "www.nextangle.com" end + def test_setting_cookie_with_deprecated_writer + @request.action = "authenticate_with_deprecated_writer" + assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], process_request.headers["cookie"] + end + def test_setting_cookie @request.action = "authenticate" assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], process_request.headers["cookie"] end - def test_setting_cookie + def test_setting_cookie_for_fourteen_days + @request.action = "authenticate_for_fourten_days" + assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], process_request.headers["cookie"] + end + + def test_multiple_cookies + @request.action = "set_multiple_cookies" + assert_equal 2, process_request.headers["cookie"].size + end + + def test_setting_cookie_on_frozen_instance_variable @request.action = "access_frozen_cookies" assert_raises(TypeError) { process_request } end -- cgit v1.2.3