aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG3
-rw-r--r--actionpack/MIT-LICENSE2
-rw-r--r--actionpack/Rakefile2
-rwxr-xr-xactionpack/lib/action_controller.rb2
-rwxr-xr-xactionpack/lib/action_controller/base.rb51
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb37
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb6
-rw-r--r--actionpack/lib/action_controller/cgi_ext/cookie.rb2
-rw-r--r--actionpack/lib/action_controller/mime_type.rb19
-rw-r--r--actionpack/lib/action_controller/mime_types.rb2
-rw-r--r--actionpack/lib/action_controller/request_forgery_protection.rb2
-rw-r--r--actionpack/lib/action_controller/rescue.rb6
-rw-r--r--actionpack/lib/action_controller/session/cookie_store.rb9
-rw-r--r--actionpack/lib/action_pack.rb2
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view.rb2
-rw-r--r--actionpack/lib/action_view/helpers/javascripts/controls.js2
-rw-r--r--actionpack/lib/action_view/helpers/javascripts/dragdrop.js2
-rw-r--r--actionpack/lib/action_view/helpers/javascripts/effects.js2
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb127
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb63
-rw-r--r--actionpack/test/controller/caching_test.rb80
-rw-r--r--actionpack/test/controller/cookie_test.rb6
-rw-r--r--actionpack/test/controller/filter_params_test.rb20
-rw-r--r--actionpack/test/controller/mime_type_test.rb25
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb71
-rwxr-xr-xactionpack/test/controller/session/cookie_store_test.rb16
-rw-r--r--actionpack/test/template/prototype_helper_test.rb5
-rw-r--r--actionpack/test/template/text_helper_test.rb41
30 files changed, 414 insertions, 197 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 2caaa40bf6..4a24d2f8b9 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,9 +1,10 @@
-*SVN*
+*2.1.0 RC1 (May 11th, 2008)*
* Fixed that forgery protection can be used without session tracking (Peter Jones) [#139]
* Added session(:on) to turn session management back on in a controller subclass if the superclass turned it off (Peter Jones) [#136]
+* Change the request forgery protection to go by Content-Type instead of request.format so that you can't bypass it by POSTing to "#{request.uri}.xml" [rick]
* InstanceTag#default_time_from_options with hash args uses Time.current as default; respects hash settings when time falls in system local spring DST gap [Geoff Buesing]
* select_date defaults to Time.zone.today when config.time_zone is set [Geoff Buesing]
diff --git a/actionpack/MIT-LICENSE b/actionpack/MIT-LICENSE
index 007cc942e1..13c90d46e9 100644
--- a/actionpack/MIT-LICENSE
+++ b/actionpack/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2007 David Heinemeier Hansson
+Copyright (c) 2004-2008 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index e460d820ba..0147a5c1e8 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -76,7 +76,7 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.requirements << 'none'
- s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD)
+ s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD)
s.require_path = 'lib'
s.autorequire = 'action_controller'
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 919fbc6c6a..810a5fb9b5 100755
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2007 David Heinemeier Hansson
+# Copyright (c) 2004-2008 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index e1bf005f39..ea55fe42ce 100755
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -259,12 +259,12 @@ module ActionController #:nodoc:
DEFAULT_RENDER_STATUS_CODE = "200 OK"
include StatusCodes
-
+
# Controller specific instance variables which will not be accessible inside views.
@@protected_view_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
@action_name @before_filter_chain_aborted @action_cache_path @_session @_cookies @_headers @_params
@_flash @_response)
-
+
# Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
# and images to a dedicated asset server away from the main web server. Example:
# ActionController::Base.asset_host = "http://assets.example.com"
@@ -325,7 +325,7 @@ module ActionController #:nodoc:
# Controls the default charset for all renders.
@@default_charset = "utf-8"
cattr_accessor :default_charset
-
+
# The logger is used for generating information on the action run-time (including benchmarking) if available.
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
cattr_accessor :logger
@@ -333,7 +333,7 @@ module ActionController #:nodoc:
# Controls the resource action separator
@@resource_action_separator = "/"
cattr_accessor :resource_action_separator
-
+
# Allow to override path names for default resources' actions
@@resources_path_names = { :new => 'new', :edit => 'edit' }
cattr_accessor :resources_path_names
@@ -433,7 +433,7 @@ module ActionController #:nodoc:
end
# Adds a view_path to the front of the view_paths array.
- # If the current class has no view paths, copy them from
+ # If the current class has no view paths, copy them from
# the superclass. This change will be visible for all future requests.
#
# ArticleController.prepend_view_path("views/default")
@@ -444,9 +444,9 @@ module ActionController #:nodoc:
view_paths.unshift(*path)
ActionView::TemplateFinder.process_view_paths(path)
end
-
+
# Adds a view_path to the end of the view_paths array.
- # If the current class has no view paths, copy them from
+ # If the current class has no view paths, copy them from
# the superclass. This change will be visible for all future requests.
#
# ArticleController.append_view_path("views/default")
@@ -457,7 +457,7 @@ module ActionController #:nodoc:
view_paths.push(*path)
ActionView::TemplateFinder.process_view_paths(path)
end
-
+
# Replace sensitive parameter data from the request log.
# Filters parameters that have any of the arguments as a substring.
# Looks in all subhashes of the param hash for keys to filter.
@@ -504,6 +504,7 @@ module ActionController #:nodoc:
filtered_parameters
end
+ protected :filter_parameters
end
# Don't render layouts for templates with the given extensions.
@@ -643,12 +644,12 @@ module ActionController #:nodoc:
end
self.view_paths = []
-
+
# View load paths for controller.
def view_paths
@template.finder.view_paths
end
-
+
def view_paths=(value)
@template.finder.view_paths = value # Mutex needed
end
@@ -662,7 +663,7 @@ module ActionController #:nodoc:
def prepend_view_path(path)
@template.finder.prepend_view_path(path) # Mutex needed
end
-
+
# Adds a view_path to the end of the view_paths array.
# This change affects the current request only.
#
@@ -874,10 +875,10 @@ module ActionController #:nodoc:
elsif action_name = options[:action]
template = default_template_name(action_name.to_s)
if options[:layout] && !template_exempt_from_layout?(template)
- render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
+ render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
else
render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
- end
+ end
elsif xml = options[:xml]
response.content_type ||= Mime::XML
@@ -895,12 +896,12 @@ module ActionController #:nodoc:
if collection = options[:collection]
render_for_text(
- @template.send!(:render_partial_collection, partial, collection,
+ @template.send!(:render_partial_collection, partial, collection,
options[:spacer_template], options[:locals]), options[:status]
)
else
render_for_text(
- @template.send!(:render_partial, partial,
+ @template.send!(:render_partial, partial,
ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]), options[:status]
)
end
@@ -1024,7 +1025,7 @@ module ActionController #:nodoc:
# redirect_to articles_url
# redirect_to :back
#
- # The redirection happens as a "302 Moved" header unless otherwise specified.
+ # The redirection happens as a "302 Moved" header unless otherwise specified.
#
# Examples:
# redirect_to post_url(@post), :status=>:found
@@ -1035,17 +1036,17 @@ module ActionController #:nodoc:
# When using <tt>redirect_to :back</tt>, if there is no referrer,
# RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
- def redirect_to(options = {}, response_status = {}) #:doc:
+ def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
- if options.is_a?(Hash) && options[:status]
- status = options.delete(:status)
- elsif response_status[:status]
- status = response_status[:status]
- else
- status = 302
+ if options.is_a?(Hash) && options[:status]
+ status = options.delete(:status)
+ elsif response_status[:status]
+ status = response_status[:status]
+ else
+ status = 302
end
-
+
case options
when %r{^\w+://.*}
raise DoubleRenderError if performed?
@@ -1119,7 +1120,7 @@ module ActionController #:nodoc:
response.body = text.is_a?(Proc) ? text : text.to_s
end
end
-
+
def initialize_template_class(response)
response.template = ActionView::Base.new(self.class.view_paths, {}, self)
response.template.extend self.class.master_helper_module
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index 7b0551c664..1ef9e60a21 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -9,7 +9,7 @@ module ActionController #:nodoc:
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
- # caches_action :show, :feed
+ # caches_action :index, :show, :feed
# end
#
# In this example, the public action doesn't require authentication, so it's possible to use the faster page caching method. But both the
@@ -27,15 +27,19 @@ module ActionController #:nodoc:
# You can set modify the default action cache path by passing a :cache_path option. This will be passed directly to ActionCachePath.path_for. This is handy
# for actions with multiple possible routes that should be cached differently. If a block is given, it is called with the current controller instance.
#
+ # And you can also use :if to pass a Proc that specifies when the action should be cached.
+ #
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
+ # caches_action :index, :if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request
# caches_action :show, :cache_path => { :project => 1 }
- # caches_action :show, :cache_path => Proc.new { |controller|
- # controller.params[:user_id] ?
+ # caches_action :feed, :cache_path => Proc.new { |controller|
+ # controller.params[:user_id] ?
# controller.send(:user_list_url, c.params[:user_id], c.params[:id]) :
# controller.send(:list_url, c.params[:id]) }
# end
+ #
module Actions
def self.included(base) #:nodoc:
base.extend(ClassMethods)
@@ -49,7 +53,8 @@ module ActionController #:nodoc:
# See ActionController::Caching::Actions for details.
def caches_action(*actions)
return unless cache_configured?
- around_filter(ActionCacheFilter.new(*actions))
+ options = actions.extract_options!
+ around_filter(ActionCacheFilter.new(:cache_path => options.delete(:cache_path)), {:only => actions}.merge(options))
end
end
@@ -67,16 +72,12 @@ module ActionController #:nodoc:
end
class ActionCacheFilter #:nodoc:
- def initialize(*actions, &block)
- @options = actions.extract_options!
- @actions = Set.new(actions)
+ def initialize(options, &block)
+ @options = options
end
def before(controller)
- return unless @actions.include?(controller.action_name.intern)
-
cache_path = ActionCachePath.new(controller, path_options_for(controller, @options))
-
if cache = controller.read_fragment(cache_path.path)
controller.rendered_action_cache = true
set_content_type!(controller, cache_path.extension)
@@ -88,7 +89,7 @@ module ActionController #:nodoc:
end
def after(controller)
- return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache || !caching_allowed(controller)
+ return if controller.rendered_action_cache || !caching_allowed(controller)
controller.write_fragment(controller.action_cache_path.path, controller.response.body)
end
@@ -105,16 +106,16 @@ module ActionController #:nodoc:
controller.request.get? && controller.response.headers['Status'].to_i == 200
end
end
-
+
class ActionCachePath
attr_reader :path, :extension
-
+
class << self
def path_for(controller, options)
new(controller, options).path
end
end
-
+
def initialize(controller, options = {})
@extension = extract_extension(controller.request.path)
path = controller.url_for(options).split('://').last
@@ -122,16 +123,16 @@ module ActionController #:nodoc:
add_extension!(path, @extension)
@path = URI.unescape(path)
end
-
+
private
def normalize!(path)
path << 'index' if path[-1] == ?/
end
-
+
def add_extension!(path, extension)
path << ".#{extension}" if extension
end
-
+
def extract_extension(file_path)
# Don't want just what comes after the last '.' to accommodate multi part extensions
# such as tar.gz.
@@ -140,4 +141,4 @@ module ActionController #:nodoc:
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index 3164e14f6f..61559e9ec7 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -28,7 +28,7 @@ module ActionController #:nodoc:
# class ListsController < ApplicationController
# caches_action :index, :show, :public, :feed
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
- # end
+ # end
module Sweeping
def self.included(base) #:nodoc:
base.extend(ClassMethods)
@@ -40,7 +40,7 @@ module ActionController #:nodoc:
sweepers.each do |sweeper|
ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
- sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(Inflector.classify(sweeper)) : sweeper).instance
+ sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
if sweeper_instance.is_a?(Sweeper)
around_filter(sweeper_instance, :only => configuration[:only])
@@ -94,4 +94,4 @@ module ActionController #:nodoc:
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/cgi_ext/cookie.rb b/actionpack/lib/action_controller/cgi_ext/cookie.rb
index 3dd374f126..a244e2a39a 100644
--- a/actionpack/lib/action_controller/cgi_ext/cookie.rb
+++ b/actionpack/lib/action_controller/cgi_ext/cookie.rb
@@ -37,7 +37,7 @@ class CGI #:nodoc:
@path = nil
else
@name = name['name']
- @value = Array(name['value'])
+ @value = (name['value'].kind_of?(String) ? [name['value']] : Array(name['value'])).delete_if(&:blank?)
@domain = name['domain']
@expires = name['expires']
@secure = name['secure'] || false
diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb
index 8c02f20521..f43e2ba06d 100644
--- a/actionpack/lib/action_controller/mime_type.rb
+++ b/actionpack/lib/action_controller/mime_type.rb
@@ -17,6 +17,10 @@ module Mime
# end
# end
class Type
+ @@html_types = Set.new [:html, :all]
+ @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
+ cattr_reader :html_types, :unverifiable_types
+
# A simple helper class used in parsing the accept header
class AcceptItem #:nodoc:
attr_accessor :order, :name, :q
@@ -153,12 +157,21 @@ module Mime
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
end
end
-
+
+ # Returns true if ActionPack should check requests using this Mime Type for possible request forgery. See
+ # ActionController::RequestForgerProtection.
+ def verify_request?
+ !@@unverifiable_types.include?(to_sym)
+ end
+
+ def html?
+ @@html_types.include?(to_sym) || @string =~ /html/
+ end
+
private
def method_missing(method, *args)
if method.to_s =~ /(\w+)\?$/
- mime_type = $1.downcase.to_sym
- mime_type == @symbol || (mime_type == :html && @symbol == :all)
+ $1.downcase.to_sym == to_sym
else
super
end
diff --git a/actionpack/lib/action_controller/mime_types.rb b/actionpack/lib/action_controller/mime_types.rb
index 71706b4c41..01a266d3fe 100644
--- a/actionpack/lib/action_controller/mime_types.rb
+++ b/actionpack/lib/action_controller/mime_types.rb
@@ -17,4 +17,4 @@ Mime::Type.register "multipart/form-data", :multipart_form
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
# http://www.ietf.org/rfc/rfc4627.txt
-Mime::Type.register "application/json", :json, %w( text/x-json )
+Mime::Type.register "application/json", :json, %w( text/x-json ) \ No newline at end of file
diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb
index 139e91ecf9..02c9d59d07 100644
--- a/actionpack/lib/action_controller/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/request_forgery_protection.rb
@@ -99,7 +99,7 @@ module ActionController #:nodoc:
end
def verifiable_request_format?
- request.format.html? || request.format.js?
+ request.content_type.nil? || request.content_type.verify_request?
end
# Sets the token value for the current session. Pass a <tt>:secret</tt> option
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index 5022c9a815..40ef4ea044 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -199,10 +199,8 @@ module ActionController #:nodoc:
private
def perform_action_with_rescue #:nodoc:
perform_action_without_rescue
- rescue Exception => exception # errors from action performed
- return if rescue_action_with_handler(exception)
-
- rescue_action(exception)
+ rescue Exception => exception
+ rescue_action_with_handler(exception) || rescue_action(exception)
end
def rescues_path(template_name)
diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb
index 560491f996..ada1862c3e 100644
--- a/actionpack/lib/action_controller/session/cookie_store.rb
+++ b/actionpack/lib/action_controller/session/cookie_store.rb
@@ -130,17 +130,20 @@ class CGI::Session::CookieStore
# Marshal a session hash into safe cookie data. Include an integrity hash.
def marshal(session)
data = ActiveSupport::Base64.encode64(Marshal.dump(session)).chop
- CGI.escape "#{data}--#{generate_digest(data)}"
+ "#{data}--#{generate_digest(data)}"
end
# Unmarshal cookie data to a hash and verify its integrity.
def unmarshal(cookie)
if cookie
- data, digest = CGI.unescape(cookie).split('--')
- unless digest == generate_digest(data)
+ data, digest = cookie.split('--')
+
+ # Do two checks to transparently support old double-escaped data.
+ unless digest == generate_digest(data) || digest == generate_digest(data = CGI.unescape(data))
delete
raise TamperedWithCookie
end
+
Marshal.load(ActiveSupport::Base64.decode64(data))
end
end
diff --git a/actionpack/lib/action_pack.rb b/actionpack/lib/action_pack.rb
index 006c83dbc8..c7fd3092e7 100644
--- a/actionpack/lib/action_pack.rb
+++ b/actionpack/lib/action_pack.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2007 David Heinemeier Hansson
+# Copyright (c) 2004-2008 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 7aa6a5db96..70fc1ced8c 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 0
- TINY = 2
+ TINY = 991
STRING = [MAJOR, MINOR, TINY].join('.')
end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 609334d52d..5f4126e4e9 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2007 David Heinemeier Hansson
+# Copyright (c) 2004-2008 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
diff --git a/actionpack/lib/action_view/helpers/javascripts/controls.js b/actionpack/lib/action_view/helpers/javascripts/controls.js
index fbc4418b83..5aaf0bb2b7 100644
--- a/actionpack/lib/action_view/helpers/javascripts/controls.js
+++ b/actionpack/lib/action_view/helpers/javascripts/controls.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
// Contributors:
diff --git a/actionpack/lib/action_view/helpers/javascripts/dragdrop.js b/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
index ccf4a1e45c..bf5cfea66c 100644
--- a/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
+++ b/actionpack/lib/action_view/helpers/javascripts/dragdrop.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
diff --git a/actionpack/lib/action_view/helpers/javascripts/effects.js b/actionpack/lib/action_view/helpers/javascripts/effects.js
index 65aed23957..f030b5dbe9 100644
--- a/actionpack/lib/action_view/helpers/javascripts/effects.js
+++ b/actionpack/lib/action_view/helpers/javascripts/effects.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
// Justin Palmer (http://encytemedia.com/)
// Mark Pilgrim (http://diveintomark.org/)
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 1b12aa8058..1a0e660d52 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -458,7 +458,7 @@ module ActionView
url_options = options[:url]
url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
- function << "'#{url_for(url_options)}'"
+ function << "'#{escape_javascript(url_for(url_options))}'"
function << ", #{javascript_options})"
function = "#{options[:before]}; #{function}" if options[:before]
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index f8c3b67474..9d220c546a 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -3,19 +3,19 @@ require 'html/document'
module ActionView
module Helpers #:nodoc:
- # The TextHelper module provides a set of methods for filtering, formatting
- # and transforming strings, which can reduce the amount of inline Ruby code in
- # your views. These helper methods extend ActionView making them callable
+ # The TextHelper module provides a set of methods for filtering, formatting
+ # and transforming strings, which can reduce the amount of inline Ruby code in
+ # your views. These helper methods extend ActionView making them callable
# within your template files.
- module TextHelper
- # The preferred method of outputting text in your views is to use the
- # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
- # do not operate as expected in an eRuby code block. If you absolutely must
+ module TextHelper
+ # The preferred method of outputting text in your views is to use the
+ # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
+ # do not operate as expected in an eRuby code block. If you absolutely must
# output text within a non-output code block (i.e., <% %>), you can use the concat method.
#
# ==== Examples
- # <%
- # concat "hello", binding
+ # <%
+ # concat "hello", binding
# # is the equivalent of <%= "hello" %>
#
# if (logged_in == true):
@@ -30,15 +30,15 @@ module ActionView
end
if RUBY_VERSION < '1.9'
- # If +text+ is longer than +length+, +text+ will be truncated to the length of
+ # If +text+ is longer than +length+, +text+ will be truncated to the length of
# +length+ (defaults to 30) and the last characters will be replaced with the +truncate_string+
# (defaults to "...").
#
# ==== Examples
- # truncate("Once upon a time in a world far far away", 14)
+ # truncate("Once upon a time in a world far far away", 14)
# # => Once upon a...
#
- # truncate("Once upon a time in a world far far away")
+ # truncate("Once upon a time in a world far far away")
# # => Once upon a time in a world f...
#
# truncate("And they found that many people were sleeping better.", 25, "(clipped)")
@@ -63,20 +63,20 @@ module ActionView
end
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
- # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+
+ # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+
# as a single-quoted string with \1 where the phrase is to be inserted (defaults to
# '<strong class="highlight">\1</strong>')
#
# ==== Examples
- # highlight('You searched for: rails', 'rails')
+ # highlight('You searched for: rails', 'rails')
# # => You searched for: <strong class="highlight">rails</strong>
#
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
- # # => You searched for: ruby, rails, dhh
+ # # => You searched for: ruby, rails, dhh
#
- # highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
+ # highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
# # => You searched <em>for</em>: <em>rails</em>
- #
+ #
# highlight('You searched for: rails', 'rails', "<a href='search?q=\1'>\1</a>")
# # => You searched for: <a href='search?q=rails>rails</a>
def highlight(text, phrases, highlighter = '<strong class="highlight">\1</strong>')
@@ -89,23 +89,23 @@ module ActionView
end
if RUBY_VERSION < '1.9'
- # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
+ # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
# The +radius+ expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
# defined in +radius+ (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
# then the +excerpt_string+ will be prepended/appended accordingly. The resulting string will be stripped in any case.
# If the +phrase+ isn't found, nil is returned.
#
# ==== Examples
- # excerpt('This is an example', 'an', 5)
+ # excerpt('This is an example', 'an', 5)
# # => "...s is an exam..."
#
- # excerpt('This is an example', 'is', 5)
+ # excerpt('This is an example', 'is', 5)
# # => "This is a..."
#
- # excerpt('This is an example', 'is')
+ # excerpt('This is an example', 'is')
# # => "This is an example"
#
- # excerpt('This next thing is an example', 'ex', 2)
+ # excerpt('This next thing is an example', 'ex', 2)
# # => "...next..."
#
# excerpt('This is also an example', 'an', 8, '<chop> ')
@@ -147,33 +147,24 @@ module ActionView
end
end
- # Attempts to pluralize the +singular+ word unless +count+ is 1. If +plural+
- # is supplied, it will use that when count is > 1, if the ActiveSupport Inflector
- # is loaded, it will use the Inflector to determine the plural form, otherwise
- # it will just add an 's' to the +singular+ word.
+ # Attempts to pluralize the +singular+ word unless +count+ is 1. If
+ # +plural+ is supplied, it will use that when count is > 1, otherwise
+ # it will use the Inflector to determine the plural form
#
# ==== Examples
- # pluralize(1, 'person')
+ # pluralize(1, 'person')
# # => 1 person
#
- # pluralize(2, 'person')
+ # pluralize(2, 'person')
# # => 2 people
#
- # pluralize(3, 'person', 'users')
+ # pluralize(3, 'person', 'users')
# # => 3 users
#
# pluralize(0, 'person')
# # => 0 people
def pluralize(count, singular, plural = nil)
- "#{count || 0} " + if count == 1 || count == '1'
- singular
- elsif plural
- plural
- elsif Object.const_defined?("Inflector")
- Inflector.pluralize(singular)
- else
- singular + "s"
- end
+ "#{count || 0} " + ((count == 1 || count == '1') ? singular : (plural || singular.pluralize))
end
# Wraps the +text+ into lines no longer than +line_width+ width. This method
@@ -229,7 +220,7 @@ module ActionView
end
end
- # Returns the text with all the Textile codes turned into HTML tags,
+ # Returns the text with all the Textile codes turned into HTML tags,
# but without the bounding <p> tag that RedCloth adds.
#
# You can learn more about Textile's syntax at its website[http://www.textism.com/tools/textile].
@@ -273,25 +264,25 @@ module ActionView
# # => "<p>We like to <em>write</em> <code>code</code>, not just <em>read</em> it!</p>"
#
# markdown("The [Markdown website](http://daringfireball.net/projects/markdown/) has more information.")
- # # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
+ # # => "<p>The <a href="http://daringfireball.net/projects/markdown/">Markdown website</a>
# # has more information.</p>"
#
# markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")')
- # # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
+ # # => '<p><img src="http://rubyonrails.com/images/rails.png" alt="The ROR logo" title="Ruby on Rails" /></p>'
def markdown(text)
text.blank? ? "" : BlueCloth.new(text).to_html
end
rescue LoadError
# We can't really help what's not there
end
-
+
# Returns +text+ transformed into HTML using simple formatting rules.
- # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
+ # Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
# paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
# considered as a linebreak and a <tt><br /></tt> tag is appended. This
- # method does not remove the newlines from the +text+.
+ # method does not remove the newlines from the +text+.
#
- # You can pass any HTML attributes into <tt>html_options</tt>. These
+ # You can pass any HTML attributes into <tt>html_options</tt>. These
# will be added to all created paragraphs.
# ==== Examples
# my_text = "Here is some basic text...\n...with a line break."
@@ -316,19 +307,19 @@ module ActionView
text << "</p>"
end
- # Turns all URLs and e-mail addresses into clickable links. The +link+ parameter
+ # Turns all URLs and e-mail addresses into clickable links. The +link+ parameter
# will limit what should be linked. You can add HTML attributes to the links using
- # +href_options+. Options for +link+ are <tt>:all</tt> (default),
- # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
+ # +href_options+. Options for +link+ are <tt>:all</tt> (default),
+ # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
# e-mail address is yielded and the result is used as the link text.
#
# ==== Examples
- # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
+ # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
# # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
# # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
#
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :urls)
- # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
+ # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
# # or e-mail david@loudthinking.com"
#
# auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :email_addresses)
@@ -338,9 +329,9 @@ module ActionView
# auto_link(post_body, :all, :target => '_blank') do |text|
# truncate(text, 15)
# end
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
+ # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
- #
+ #
def auto_link(text, link = :all, href_options = {}, &block)
return '' if text.blank?
case link
@@ -349,15 +340,15 @@ module ActionView
when :urls then auto_link_urls(text, href_options, &block)
end
end
-
+
# Creates a Cycle object whose _to_s_ method cycles through elements of an
- # array every time it is called. This can be used for example, to alternate
- # classes for table rows. You can use named cycles to allow nesting in loops.
- # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
- # named cycle. You can manually reset a cycle by calling reset_cycle and passing the
+ # array every time it is called. This can be used for example, to alternate
+ # classes for table rows. You can use named cycles to allow nesting in loops.
+ # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
+ # named cycle. You can manually reset a cycle by calling reset_cycle and passing the
# name of the cycle.
#
- # ==== Examples
+ # ==== Examples
# # Alternate CSS classes for even and odd numbers...
# @items = [1,2,3,4]
# <table>
@@ -370,8 +361,8 @@ module ActionView
#
#
# # Cycle CSS classes for rows, and text colors for values within each row
- # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
- # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
+ # @items = x = [{:first => 'Robert', :middle => 'Daniel', :last => 'James'},
+ # {:first => 'Emily', :middle => 'Shannon', :maiden => 'Pike', :last => 'Hicks'},
# {:first => 'June', :middle => 'Dae', :last => 'Jones'}]
# <% @items.each do |item| %>
# <tr class="<%= cycle("even", "odd", :name => "row_class") -%>">
@@ -401,8 +392,8 @@ module ActionView
end
return cycle.to_s
end
-
- # Resets a cycle so that it starts from the first element the next time
+
+ # Resets a cycle so that it starts from the first element the next time
# it is called. Pass in +name+ to reset a named cycle.
#
# ==== Example
@@ -428,12 +419,12 @@ module ActionView
class Cycle #:nodoc:
attr_reader :values
-
+
def initialize(first_value, *values)
@values = values.unshift(first_value)
reset
end
-
+
def reset
@index = 0
end
@@ -453,7 +444,7 @@ module ActionView
@_cycles = Hash.new unless defined?(@_cycles)
return @_cycles[name]
end
-
+
def set_cycle(name, cycle_object)
@_cycles = Hash.new unless defined?(@_cycles)
@_cycles[name] = cycle_object
@@ -462,13 +453,13 @@ module ActionView
AUTO_LINK_RE = %r{
( # leading text
<\w+.*?>| # leading HTML tag, or
- [^=!:'"/]| # leading punctuation, or
+ [^=!:'"/]| # leading punctuation, or
^ # beginning of line
)
(
(?:https?://)| # protocol spec, or
(?:www\.) # www.*
- )
+ )
(
[-\w]+ # subdomain or domain
(?:\.[-\w]+)* # remaining subdomains or domain
@@ -502,7 +493,7 @@ module ActionView
body = text.dup
text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
text = $1
-
+
if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/)
text
else
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 375ebfcdc5..38c8d18cb0 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -120,17 +120,72 @@ module ActionView
# exception.
#
# ==== Examples
+ # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
+ # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
+ # your application on resources and use
+ #
+ # link_to "Profile", profile_path(@profile)
+ # # => <a href="/profiles/1">Profile</a>
+ #
+ # or the even pithier
+ #
+ # link_to "Profile", @profile
+ # # => <a href="/profiles/1">Profile</a>
+ #
+ # in place of the older more verbose, non-resource-oriented
+ #
+ # link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
+ # # => <a href="/profiles/show/1">Profile</a>
+ #
+ # Similarly,
+ #
+ # link_to "Profiles", profiles_path
+ # # => <a href="/profiles">Profiles</a>
+ #
+ # is better than
+ #
+ # link_to "Profiles", :controller => "profiles"
+ # # => <a href="/profiles">Profiles</a>
+ #
+ # Classes and ids for CSS are easy to produce:
+ #
+ # link_to "Articles", articles_path, :id => "news", :class => "article"
+ # # => <a href="/articles" class="article" id="news">Articles</a>
+ #
+ # Be careful when using the older argument style, as an extra literal hash is needed:
+ #
+ # link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
+ # # => <a href="/articles" class="article" id="news">Articles</a>
+ #
+ # Leaving the hash off gives the wrong link:
+ #
+ # link_to "WRONG!", :controller => "articles", :id => "news", :class => "article"
+ # # => <a href="/articles/index/news?class=article">WRONG!</a>
+ #
+ # +link_to+ can also produce links with anchors or query strings:
+ #
+ # link_to "Comment wall", profile_path(@profile, :anchor => "wall")
+ # # => <a href="/profiles/1#wall">Comment wall</a>
+ #
+ # link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails"
+ # # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
+ #
+ # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
+ # # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
+ #
+ # The three options specfic to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
+ #
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
# # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
#
# link_to "Help", { :action => "help" }, :popup => true
# # => <a href="/testing/help/" onclick="window.open(this.href);return false;">Help</a>
#
- # link_to "View Image", { :action => "view" }, :popup => ['new_window_name', 'height=300,width=600']
- # # => <a href="/testing/view/" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
+ # link_to "View Image", @image, :popup => ['new_window_name', 'height=300,width=600']
+ # # => <a href="/images/9" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
#
- # link_to "Delete Image", { :action => "delete", :id => @image.id }, :confirm => "Are you sure?", :method => :delete
- # # => <a href="/testing/delete/9/" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
+ # link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete
+ # # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
# f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
# var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
# m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;">Delete Image</a>
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index ddc1c68383..4aacb4a78a 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -6,6 +6,7 @@ CACHE_DIR = 'test_cache'
FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
ActionController::Base.page_cache_directory = FILE_STORE_PATH
ActionController::Base.cache_store = :file_store, FILE_STORE_PATH
+ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/' ]
class PageCachingTestController < ActionController::Base
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
@@ -128,7 +129,7 @@ class PageCachingTest < Test::Unit::TestCase
end
end
end
-
+
def test_page_caching_conditional_options
@request.env['HTTP_ACCEPT'] = 'application/json'
get :ok
@@ -151,12 +152,15 @@ end
class ActionCachingTestController < ActionController::Base
- caches_action :index, :redirected, :forbidden
+ caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }
caches_action :show, :cache_path => 'http://test.host/custom/show'
caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
+ caches_action :with_layout
+
+ layout 'talk_from_action.erb'
def index
- @cache_this = Time.now.to_f.to_s
+ @cache_this = MockTime.now.to_f.to_s
render :text => @cache_this
end
@@ -169,14 +173,26 @@ class ActionCachingTestController < ActionController::Base
headers["Status"] = "403 Forbidden"
end
+ def with_layout
+ @cache_this = MockTime.now.to_f.to_s
+ render :text => @cache_this, :layout => true
+ end
+
alias_method :show, :index
alias_method :edit, :index
+ alias_method :destroy, :index
def expire
expire_action :controller => 'action_caching_test', :action => 'index'
render :nothing => true
end
+end
+class MockTime < Time
+ # Let Time spicy to assure that Time.now != Time.now
+ def to_f
+ super+rand
+ end
end
class ActionCachingMockController
@@ -223,6 +239,36 @@ class ActionCacheTest < Test::Unit::TestCase
assert_equal cached_time, @response.body
end
+ def test_simple_action_not_cached
+ get :destroy
+ cached_time = content_to_cache
+ assert_equal cached_time, @response.body
+ assert_cache_does_not_exist 'hostname.com/action_caching_test/destroy'
+ reset!
+
+ get :destroy
+ assert_not_equal cached_time, @response.body
+ end
+
+ def test_action_cache_with_layout
+ get :with_layout
+ cached_time = content_to_cache
+ assert_not_equal cached_time, @response.body
+ assert_cache_exists 'hostname.com/action_caching_test/with_layout'
+ reset!
+
+ get :with_layout
+ assert_not_equal cached_time, @response.body
+
+ assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout')
+ end
+
+ def test_action_cache_conditional_options
+ @request.env['HTTP_ACCEPT'] = 'application/json'
+ get :index
+ assert_cache_does_not_exist 'hostname.com/action_caching_test'
+ end
+
def test_action_cache_with_custom_cache_path
get :show
cached_time = content_to_cache
@@ -350,9 +396,22 @@ class ActionCacheTest < Test::Unit::TestCase
end
def assert_cache_exists(path)
- full_path = File.join(FILE_STORE_PATH, "views", path + '.cache')
+ full_path = cache_path(path)
assert File.exist?(full_path), "#{full_path.inspect} does not exist."
end
+
+ def assert_cache_does_not_exist(path)
+ full_path = cache_path(path)
+ assert !File.exist?(full_path), "#{full_path.inspect} should not exist."
+ end
+
+ def cache_path(path)
+ File.join(FILE_STORE_PATH, 'views', path + '.cache')
+ end
+
+ def read_fragment(path)
+ @controller.read_fragment(path)
+ end
end
class FragmentCachingTestController < ActionController::Base
@@ -516,7 +575,7 @@ class FunctionalFragmentCachingTest < Test::Unit::TestCase
def setup
ActionController::Base.perform_caching = true
@store = ActiveSupport::Cache::MemoryStore.new
- ActionController::Base.cache_store = @store
+ ActionController::Base.cache_store = @store
@controller = FunctionalCachingController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@@ -529,17 +588,17 @@ Hello
This bit's fragment cached
CACHED
assert_equal expected_body, @response.body
-
+
assert_equal "This bit's fragment cached", @store.read('views/test.host/functional_caching/fragment_cached')
end
-
+
def test_fragment_caching_in_partials
get :html_fragment_cached_with_partial
assert_response :success
assert_match /Fragment caching in a partial/, @response.body
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial')
end
-
+
def test_fragment_caching_in_rjs_partials
xhr :get, :js_fragment_cached_with_partial
assert_response :success
@@ -547,8 +606,3 @@ CACHED
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
end
end
-
-
-
-
-
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index 42f3bd26a4..b45fbb17d3 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -82,6 +82,7 @@ class CookieTest < Test::Unit::TestCase
def test_expiring_cookie
get :logout
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "", "expires" => Time.at(0)) ], @response.headers["cookie"]
+ assert_equal CGI::Cookie::new("name" => "user_name", "value" => "", "expires" => Time.at(0)).value, []
end
def test_cookiejar_accessor
@@ -137,4 +138,9 @@ class CookieTest < Test::Unit::TestCase
cookies = CGI::Cookie.parse('return_to=http://rubyonrails.org/search?term=api&scope=all&global=true')
assert_equal({"return_to" => ["http://rubyonrails.org/search?term=api&scope=all&global=true"]}, cookies)
end
+
+ def test_cookies_should_not_be_split_on_values_with_newlines
+ cookies = CGI::Cookie.new("name" => "val", "value" => "this\nis\na\ntest")
+ assert cookies.size == 1
+ end
end
diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb
index 11adacb5e3..c4de10181d 100644
--- a/actionpack/test/controller/filter_params_test.rb
+++ b/actionpack/test/controller/filter_params_test.rb
@@ -7,14 +7,14 @@ class FilterParamTest < Test::Unit::TestCase
def setup
@controller = FilterParamController.new
end
-
+
def test_filter_parameters
assert FilterParamController.respond_to?(:filter_parameter_logging)
assert !@controller.respond_to?(:filter_parameters)
-
+
FilterParamController.filter_parameter_logging
assert @controller.respond_to?(:filter_parameters)
-
+
test_hashes = [[{},{},[]],
[{'foo'=>nil},{'foo'=>nil},[]],
[{'foo'=>'bar'},{'foo'=>'bar'},[]],
@@ -24,11 +24,11 @@ class FilterParamTest < Test::Unit::TestCase
[{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
[{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
[{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana']]
-
+
test_hashes.each do |before_filter, after_filter, filter_words|
FilterParamController.filter_parameter_logging(*filter_words)
- assert_equal after_filter, @controller.filter_parameters(before_filter)
-
+ assert_equal after_filter, @controller.send!(:filter_parameters, before_filter)
+
filter_words.push('blah')
FilterParamController.filter_parameter_logging(*filter_words) do |key, value|
value.reverse! if key =~ /bargain/
@@ -37,7 +37,13 @@ class FilterParamTest < Test::Unit::TestCase
before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}}
after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}}
- assert_equal after_filter, @controller.filter_parameters(before_filter)
+ assert_equal after_filter, @controller.send!(:filter_parameters, before_filter)
end
end
+
+ def test_filter_parameters_is_protected
+ FilterParamController.filter_parameter_logging(:foo)
+ assert !FilterParamController.action_methods.include?('filter_parameters')
+ assert_raise(NoMethodError) { @controller.filter_parameters([{'password' => '[FILTERED]'}]) }
+ end
end
diff --git a/actionpack/test/controller/mime_type_test.rb b/actionpack/test/controller/mime_type_test.rb
index 03b0f8bad2..f16a3c68b4 100644
--- a/actionpack/test/controller/mime_type_test.rb
+++ b/actionpack/test/controller/mime_type_test.rb
@@ -52,16 +52,33 @@ class MimeTypeTest < Test::Unit::TestCase
end
def test_type_convenience_methods
- types = [:html, :xml, :png, :pdf, :yaml, :url_encoded_form]
+ # Don't test Mime::ALL, since it Mime::ALL#html? == true
+ types = Mime::SET.to_a.map(&:to_sym).uniq - [:all]
+
+ # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE
+ types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) }
+
types.each do |type|
mime = Mime.const_get(type.to_s.upcase)
- assert mime.send("#{type}?"), "Mime::#{type.to_s.upcase} is not #{type}?"
- (types - [type]).each { |t| assert !mime.send("#{t}?"), "Mime::#{t.to_s.upcase} is #{t}?" }
+ assert mime.send("#{type}?"), "#{mime.inspect} is not #{type}?"
+ (types - [type]).each { |other_type| assert !mime.send("#{other_type}?"), "#{mime.inspect} is #{other_type}?" }
end
end
-
+
def test_mime_all_is_html
assert Mime::ALL.all?, "Mime::ALL is not all?"
assert Mime::ALL.html?, "Mime::ALL is not html?"
end
+
+ def test_verifiable_mime_types
+ unverified_types = Mime::Type.unverifiable_types
+ all_types = Mime::SET.to_a.map(&:to_sym)
+ all_types.uniq!
+ # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE
+ all_types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) }
+
+ unverified, verified = all_types.partition { |type| Mime::Type.unverifiable_types.include? type }
+ assert verified.all? { |type| Mime.const_get(type.to_s.upcase).verify_request? }, "Not all Mime Types are verified: #{verified.inspect}"
+ assert unverified.all? { |type| !Mime.const_get(type.to_s.upcase).verify_request? }, "Some Mime Types are verified: #{unverified.inspect}"
+ end
end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 7022713e30..f7adaa7d4e 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -101,19 +101,79 @@ module RequestForgeryProtectionTests
post :unsafe
assert_response :success
end
-
+
def test_should_not_allow_post_without_token
assert_raises(ActionController::InvalidAuthenticityToken) { post :index }
end
-
+
def test_should_not_allow_put_without_token
assert_raises(ActionController::InvalidAuthenticityToken) { put :index }
end
-
+
def test_should_not_allow_delete_without_token
assert_raises(ActionController::InvalidAuthenticityToken) { delete :index }
end
-
+
+ def test_should_not_allow_api_formatted_post_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ post :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_put_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ put :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_delete_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ delete :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_post_sent_as_url_encoded_form_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ post :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_put_sent_as_url_encoded_form_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ put :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_delete_sent_as_url_encoded_form_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ delete :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_post_sent_as_multipart_form_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
+ post :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_put_sent_as_multipart_form_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
+ put :index, :format => 'xml'
+ end
+ end
+
+ def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_token
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
+ delete :index, :format => 'xml'
+ end
+ end
+
def test_should_not_allow_xhr_post_without_token
assert_raises(ActionController::InvalidAuthenticityToken) { xhr :post, :index }
end
@@ -142,16 +202,19 @@ module RequestForgeryProtectionTests
end
def test_should_allow_post_with_xml
+ @request.env['CONTENT_TYPE'] = Mime::XML.to_s
post :index, :format => 'xml'
assert_response :success
end
def test_should_allow_put_with_xml
+ @request.env['CONTENT_TYPE'] = Mime::XML.to_s
put :index, :format => 'xml'
assert_response :success
end
def test_should_allow_delete_with_xml
+ @request.env['CONTENT_TYPE'] = Mime::XML.to_s
delete :index, :format => 'xml'
assert_response :success
end
diff --git a/actionpack/test/controller/session/cookie_store_test.rb b/actionpack/test/controller/session/cookie_store_test.rb
index d308f2a31b..5adaeaf5c5 100755
--- a/actionpack/test/controller/session/cookie_store_test.rb
+++ b/actionpack/test/controller/session/cookie_store_test.rb
@@ -43,7 +43,9 @@ class CookieStoreTest < Test::Unit::TestCase
{ :empty => ['BAgw--0686dcaccc01040f4bd4f35fe160afe9bc04c330', {}],
:a_one => ['BAh7BiIGYWkG--5689059497d7f122a7119f171aef81dcfd807fec', { 'a' => 1 }],
:typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--9d20154623b9eeea05c62ab819be0e2483238759', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
- :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--bf9785a666d3c4ac09f7fe3353496b437546cfbf', { 'user_id' => 123, 'flash' => {} }] }
+ :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--bf9785a666d3c4ac09f7fe3353496b437546cfbf', { 'user_id' => 123, 'flash' => {} }],
+ :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--bf9785a666d3c4ac09f7fe3353496b437546cfbf'), { 'user_id' => 123, 'flash' => {} }] }
+
end
def setup
@@ -101,6 +103,15 @@ class CookieStoreTest < Test::Unit::TestCase
end
end
+ def test_restores_double_encoded_cookies
+ set_cookie! cookie_value(:double_escaped)
+ new_session do |session|
+ session.dbman.restore
+ assert_equal session["user_id"], 123
+ assert_equal session["flash"], {}
+ end
+ end
+
def test_close_doesnt_write_cookie_if_data_is_blank
new_session do |session|
assert_no_cookies session
@@ -241,6 +252,7 @@ class CookieStoreWithMD5DigestTest < CookieStoreTest
{ :empty => ['BAgw--0415cc0be9579b14afc22ee2d341aa21', {}],
:a_one => ['BAh7BiIGYWkG--5a0ed962089cc6600ff44168a5d59bc8', { 'a' => 1 }],
:typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--f426763f6ef435b3738b493600db8d64', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
- :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--0af9156650dab044a53a91a4ddec2c51', { 'user_id' => 123, 'flash' => {} }] }
+ :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--0af9156650dab044a53a91a4ddec2c51', { 'user_id' => 123, 'flash' => {} }],
+ :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--0af9156650dab044a53a91a4ddec2c51'), { 'user_id' => 123, 'flash' => {} }] }
end
end
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index a84d4e72af..9a1079b297 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -86,6 +86,11 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } })
end
+ def test_link_to_remote_url_quote_escaping
+ assert_dom_equal %(<a href="#" onclick="new Ajax.Request('http://www.example.com/whatnot\\\'s', {asynchronous:true, evalScripts:true}); return false;">Remote</a>),
+ link_to_remote("Remote", { :url => { :action => "whatnot's" } })
+ end
+
def test_periodically_call_remote
assert_dom_equal %(<script type="text/javascript">\n//<![CDATA[\nnew PeriodicalExecuter(function() {new Ajax.Updater('schremser_bier', 'http://www.example.com/mehr_bier', {asynchronous:true, evalScripts:true})}, 10)\n//]]>\n</script>),
periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" })
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 25ecda687f..06e1fd1929 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -23,9 +23,9 @@ class TextHelperTest < ActionView::TestCase
text = "A\r\n \nB\n\n\r\n\t\nC\nD".freeze
assert_equal "<p>A\n<br /> \n<br />B</p>\n\n<p>\t\n<br />C\n<br />D</p>", simple_format(text)
-
+
assert_equal %q(<p class="test">This is a classy test</p>), simple_format("This is a classy test", :class => 'test')
- assert_equal %Q(<p class="test">para 1</p>\n\n<p class="test">para 2</p>), simple_format("para 1\n\npara 2", :class => 'test')
+ assert_equal %Q(<p class="test">para 1</p>\n\n<p class="test">para 2</p>), simple_format("para 1\n\npara 2", :class => 'test')
end
def test_truncate
@@ -41,7 +41,7 @@ class TextHelperTest < ActionView::TestCase
if RUBY_VERSION < '1.9.0'
def test_truncate_multibyte
with_kcode 'none' do
- assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", 10)
+ assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", 10)
end
with_kcode 'u' do
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...",
@@ -73,7 +73,7 @@ class TextHelperTest < ActionView::TestCase
"This is a <b>beautiful</b> morning, but also a <b>beautiful</b> day",
highlight("This is a beautiful morning, but also a beautiful day", "beautiful", '<b>\1</b>')
)
-
+
assert_equal(
"This text is not changed because we supplied an empty phrase",
highlight("This text is not changed because we supplied an empty phrase", nil)
@@ -166,18 +166,9 @@ class TextHelperTest < ActionView::TestCase
assert_equal("2 counters", pluralize(2, "count", "counters"))
assert_equal("0 counters", pluralize(nil, "count", "counters"))
assert_equal("2 people", pluralize(2, "person"))
- assert_equal("10 buffaloes", pluralize(10, "buffalo"))
- end
-
- uses_mocha("should_just_add_s_for_pluralize_without_inflector_loaded") do
- def test_should_just_add_s_for_pluralize_without_inflector_loaded
- Object.expects(:const_defined?).with("Inflector").times(4).returns(false)
- assert_equal("1 count", pluralize(1, "count"))
- assert_equal("2 persons", pluralize(2, "person"))
- assert_equal("2 personss", pluralize("2", "persons"))
- assert_equal("2 counts", pluralize(2, "count"))
- assert_equal("10 buffalos", pluralize(10, "buffalo"))
- end
+ assert_equal("10 buffaloes", pluralize(10, "buffalo"))
+ assert_equal("1 berry", pluralize(1, "berry"))
+ assert_equal("12 berries", pluralize(12, "berry"))
end
def test_auto_link_parsing
@@ -298,7 +289,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal("2", value.to_s)
assert_equal("3", value.to_s)
end
-
+
def test_cycle_class_with_no_arguments
assert_raise(ArgumentError) { value = Cycle.new() }
end
@@ -311,11 +302,11 @@ class TextHelperTest < ActionView::TestCase
assert_equal("2", cycle("one", 2, "3"))
assert_equal("3", cycle("one", 2, "3"))
end
-
+
def test_cycle_with_no_arguments
assert_raise(ArgumentError) { value = cycle() }
end
-
+
def test_cycle_resets_with_new_values
assert_equal("even", cycle("even", "odd"))
assert_equal("odd", cycle("even", "odd"))
@@ -325,7 +316,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal("3", cycle(1, 2, 3))
assert_equal("1", cycle(1, 2, 3))
end
-
+
def test_named_cycles
assert_equal("1", cycle(1, 2, 3, :name => "numbers"))
assert_equal("red", cycle("red", "blue", :name => "colors"))
@@ -334,24 +325,24 @@ class TextHelperTest < ActionView::TestCase
assert_equal("3", cycle(1, 2, 3, :name => "numbers"))
assert_equal("red", cycle("red", "blue", :name => "colors"))
end
-
+
def test_default_named_cycle
assert_equal("1", cycle(1, 2, 3))
assert_equal("2", cycle(1, 2, 3, :name => "default"))
assert_equal("3", cycle(1, 2, 3))
end
-
+
def test_reset_cycle
assert_equal("1", cycle(1, 2, 3))
assert_equal("2", cycle(1, 2, 3))
reset_cycle
assert_equal("1", cycle(1, 2, 3))
end
-
+
def test_reset_unknown_cycle
reset_cycle("colors")
end
-
+
def test_recet_named_cycle
assert_equal("1", cycle(1, 2, 3, :name => "numbers"))
assert_equal("red", cycle("red", "blue", :name => "colors"))
@@ -361,7 +352,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal("2", cycle(1, 2, 3, :name => "numbers"))
assert_equal("red", cycle("red", "blue", :name => "colors"))
end
-
+
def test_cycle_no_instance_variable_clashes
@cycles = %w{Specialized Fuji Giant}
assert_equal("red", cycle("red", "blue"))