Browse files

Merge commit 'mainstream/master'

Conflicts:

	actionmailer/lib/action_mailer/base.rb
  • Loading branch information...
2 parents d6ecce6 + fc02eab commit 879493c35fd8d9e12e5cf3e56cd67ff07c3345c5 @lifo lifo committed May 15, 2008
Showing with 733 additions and 483 deletions.
  1. +1 −1 actionmailer/CHANGELOG
  2. +1 −1 actionmailer/MIT-LICENSE
  3. +1 −1 actionmailer/Rakefile
  4. +1 −1 actionmailer/lib/action_mailer.rb
  5. +48 −48 actionmailer/lib/action_mailer/base.rb
  6. +1 −1 actionmailer/lib/action_mailer/version.rb
  7. +2 −1 actionpack/CHANGELOG
  8. +1 −1 actionpack/MIT-LICENSE
  9. +1 −1 actionpack/Rakefile
  10. +1 −1 actionpack/lib/action_controller.rb
  11. +26 −25 actionpack/lib/action_controller/base.rb
  12. +19 −18 actionpack/lib/action_controller/caching/actions.rb
  13. +3 −3 actionpack/lib/action_controller/caching/sweeping.rb
  14. +1 −1 actionpack/lib/action_controller/cgi_ext/cookie.rb
  15. +16 −3 actionpack/lib/action_controller/mime_type.rb
  16. +1 −1 actionpack/lib/action_controller/mime_types.rb
  17. +1 −1 actionpack/lib/action_controller/request_forgery_protection.rb
  18. +2 −4 actionpack/lib/action_controller/rescue.rb
  19. +6 −3 actionpack/lib/action_controller/session/cookie_store.rb
  20. +1 −1 actionpack/lib/action_pack.rb
  21. +1 −1 actionpack/lib/action_pack/version.rb
  22. +1 −1 actionpack/lib/action_view.rb
  23. +1 −1 actionpack/lib/action_view/helpers/javascripts/controls.js
  24. +1 −1 actionpack/lib/action_view/helpers/javascripts/dragdrop.js
  25. +1 −1 actionpack/lib/action_view/helpers/javascripts/effects.js
  26. +1 −1 actionpack/lib/action_view/helpers/prototype_helper.rb
  27. +59 −68 actionpack/lib/action_view/helpers/text_helper.rb
  28. +67 −13 actionpack/test/controller/caching_test.rb
  29. +6 −0 actionpack/test/controller/cookie_test.rb
  30. +13 −7 actionpack/test/controller/filter_params_test.rb
  31. +21 −4 actionpack/test/controller/mime_type_test.rb
  32. +67 −4 actionpack/test/controller/request_forgery_protection_test.rb
  33. +14 −2 actionpack/test/controller/session/cookie_store_test.rb
  34. +5 −0 actionpack/test/template/prototype_helper_test.rb
  35. +16 −25 actionpack/test/template/text_helper_test.rb
  36. +1 −1 activerecord/CHANGELOG
  37. +1 −1 activerecord/MIT-LICENSE
  38. +1 −1 activerecord/Rakefile
  39. +1 −1 activerecord/lib/active_record.rb
  40. +15 −16 activerecord/lib/active_record/associations.rb
  41. +28 −24 activerecord/lib/active_record/base.rb
  42. +8 −8 activerecord/lib/active_record/dirty.rb
  43. +7 −7 activerecord/lib/active_record/fixtures.rb
  44. +1 −1 activerecord/lib/active_record/version.rb
  45. +20 −1 activerecord/test/cases/dirty_test.rb
  46. +9 −3 activerecord/test/cases/finder_test.rb
  47. +11 −9 activerecord/test/cases/fixtures_test.rb
  48. +28 −0 activerecord/test/cases/inheritance_test.rb
  49. +4 −0 activerecord/test/models/company.rb
  50. +2 −0 activerecord/test/models/pirate.rb
  51. +1 −1 activeresource/CHANGELOG
  52. +1 −1 activeresource/Rakefile
  53. +1 −1 activeresource/lib/active_resource/version.rb
  54. +1 −1 activesupport/CHANGELOG
  55. +1 −1 activesupport/MIT-LICENSE
  56. +1 −0 activesupport/lib/active_support.rb
  57. +1 −1 activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
  58. +0 −13 activesupport/lib/active_support/deprecation.rb
  59. +43 −0 activesupport/lib/active_support/ordered_hash.rb
  60. +0 −44 activesupport/lib/active_support/ordered_options.rb
  61. +1 −1 activesupport/lib/active_support/time_with_zone.rb
  62. +4 −5 activesupport/lib/active_support/values/time_zone.rb
  63. +1 −1 activesupport/lib/active_support/version.rb
  64. +1 −1 activesupport/test/core_ext/time_with_zone_test.rb
  65. +0 −10 activesupport/test/deprecation_test.rb
  66. +45 −0 activesupport/test/ordered_hash_test.rb
  67. +0 −44 activesupport/test/ordered_options_test.rb
  68. +1 −1 pushgems.rb
  69. +1 −1 railties/CHANGELOG
  70. +1 −1 railties/MIT-LICENSE
  71. +6 −6 railties/Rakefile
  72. +3 −0 railties/configs/initializers/new_rails_defaults.rb
  73. +1 −1 railties/html/javascripts/controls.js
  74. +1 −1 railties/html/javascripts/dragdrop.js
  75. +1 −1 railties/html/javascripts/effects.js
  76. +2 −1 railties/lib/commands/dbconsole.rb
  77. +1 −1 railties/lib/dispatcher.rb
  78. +3 −2 railties/lib/rails/plugin/locator.rb
  79. +1 −1 railties/lib/rails/version.rb
  80. +16 −9 railties/lib/rails_generator/commands.rb
  81. +2 −1 railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
  82. +2 −2 railties/lib/tasks/databases.rake
  83. +24 −10 railties/test/generators/generator_test_helper.rb
  84. +20 −0 railties/test/generators/rails_controller_generator_test.rb
View
2 actionmailer/CHANGELOG
@@ -1,4 +1,4 @@
-*SVN*
+*2.1.0 RC1 (May 11th, 2008)*
* Fixed that a return-path header would be ignored #7572 [joost]
View
2 actionmailer/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
View
2 actionmailer/Rakefile
@@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "actionmailer"
s.homepage = "http://www.rubyonrails.org"
- s.add_dependency('actionpack', '= 2.0.2' + PKG_BUILD)
+ s.add_dependency('actionpack', '= 2.0.991' + PKG_BUILD)
s.has_rdoc = true
s.requirements << 'none'
View
2 actionmailer/lib/action_mailer.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
View
96 actionmailer/lib/action_mailer/base.rb
@@ -11,11 +11,11 @@ module ActionMailer #:nodoc:
# = Mailer Models
#
# To use ActionMailer, you need to create a mailer model.
- #
+ #
# $ script/generate mailer Notifier
#
- # The generated model inherits from ActionMailer::Base. Emails are defined by creating methods within the model which are then
- # used to set variables to be used in the mail template, to change options on the mail, or
+ # The generated model inherits from ActionMailer::Base. Emails are defined by creating methods within the model which are then
+ # used to set variables to be used in the mail template, to change options on the mail, or
# to add attachments.
#
# Examples:
@@ -48,7 +48,7 @@ module ActionMailer #:nodoc:
# named after each key in the hash containing the value that that key points to.
#
# So, for example, <tt>body :account => recipient</tt> would result
- # in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
+ # in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
# view.
#
#
@@ -57,7 +57,7 @@ module ActionMailer #:nodoc:
# Like ActionController, each mailer class has a corresponding view directory
# in which each method of the class looks for a template with its name.
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same name as the method
- # in your mailer model. For example, in the mailer defined above, the template at
+ # in your mailer model. For example, in the mailer defined above, the template at
# <tt>app/views/notifier/signup_notification.erb</tt> would be used to generate the email.
#
# Variables defined in the model are accessible as instance variables in the view.
@@ -71,48 +71,48 @@ module ActionMailer #:nodoc:
#
# You got a new note!
# <%= truncate(note.body, 25) %>
- #
+ #
#
# = Generating URLs
- #
+ #
# URLs can be generated in mailer views using <tt>url_for</tt> or named routes.
- # Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request,
- # so you'll need to provide all of the details needed to generate a URL.
+ # Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request,
+ # so you'll need to provide all of the details needed to generate a URL.
#
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
- #
+ #
# <%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
#
# When using named routes you only need to supply the <tt>:host</tt>:
- #
+ #
# <%= users_url(:host => "example.com") %>
#
# You will want to avoid using the <tt>name_of_route_path</tt> form of named routes because it doesn't make sense to
# generate relative URLs in email messages.
#
- # It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt> option in
+ # It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt> option in
# the <tt>ActionMailer::Base.default_url_options</tt> hash as follows:
#
# ActionMailer::Base.default_url_options[:host] = "example.com"
- #
+ #
# This can also be set as a configuration option in <tt>config/environment.rb</tt>:
#
# config.action_mailer.default_url_options = { :host => "example.com" }
#
# If you do decide to set a default <tt>:host</tt> for your mailers you will want to use the
# <tt>:only_path => false</tt> option when using <tt>url_for</tt>. This will ensure that absolute URLs are generated because
- # the <tt>url_for</tt> view helper will, by default, generate relative URLs when a <tt>:host</tt> option isn't
+ # the <tt>url_for</tt> view helper will, by default, generate relative URLs when a <tt>:host</tt> option isn't
# explicitly provided.
#
# = Sending mail
#
- # Once a mailer action and template are defined, you can deliver your message or create it and save it
+ # Once a mailer action and template are defined, you can deliver your message or create it and save it
# for delivery later:
#
# Notifier.deliver_signup_notification(david) # sends the email
# mail = Notifier.create_signup_notification(david) # => a tmail object
# Notifier.deliver(mail)
- #
+ #
# You never instantiate your mailer class. Rather, your delivery instance
# methods are automatically wrapped in class methods that start with the word
# <tt>deliver_</tt> followed by the name of the mailer method that you would
@@ -133,7 +133,7 @@ module ActionMailer #:nodoc:
# body :account => recipient
# content_type "text/html"
# end
- # end
+ # end
#
#
# = Multipart email
@@ -156,17 +156,17 @@ module ActionMailer #:nodoc:
# end
# end
# end
- #
+ #
# Multipart messages can also be used implicitly because ActionMailer will automatically
# detect and use multipart templates, where each template is named after the name of the action, followed
# by the content type. Each such detected template will be added as separate part to the message.
- #
+ #
# For example, if the following templates existed:
# * signup_notification.text.plain.erb
# * signup_notification.text.html.erb
# * signup_notification.text.xml.builder
# * signup_notification.text.x-yaml.erb
- #
+ #
# Each would be rendered and added as a separate part to the message,
# with the corresponding content type. The content type for the entire
# message is automatically set to <tt>multipart/alternative</tt>, which indicates
@@ -197,7 +197,7 @@ module ActionMailer #:nodoc:
# a.body = generate_your_pdf_here()
# end
# end
- # end
+ # end
#
#
# = Configuration options
@@ -255,16 +255,16 @@ class Base
cattr_accessor :template_extensions
@@template_extensions = ['erb', 'builder', 'rhtml', 'rxml']
- @@smtp_settings = {
- :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
+ @@smtp_settings = {
+ :address => "localhost",
+ :port => 25,
+ :domain => 'localhost.localdomain',
+ :user_name => nil,
+ :password => nil,
:authentication => nil
}
cattr_accessor :smtp_settings
-
+
@@sendmail_settings = {
:location => '/usr/sbin/sendmail',
:arguments => '-i -t'
@@ -276,10 +276,10 @@ class Base
superclass_delegating_accessor :delivery_method
self.delivery_method = :smtp
-
+
@@perform_deliveries = true
cattr_accessor :perform_deliveries
-
+
@@deliveries = []
cattr_accessor :deliveries
@@ -288,7 +288,7 @@ class Base
@@default_content_type = "text/plain"
cattr_accessor :default_content_type
-
+
@@default_mime_version = "1.0"
cattr_accessor :default_mime_version
@@ -297,47 +297,47 @@ class Base
# Specify the BCC addresses for the message
adv_attr_accessor :bcc
-
+
# Define the body of the message. This is either a Hash (in which case it
# specifies the variables to pass to the template when it is rendered),
# or a string, in which case it specifies the actual text of the message.
adv_attr_accessor :body
-
+
# Specify the CC addresses for the message.
adv_attr_accessor :cc
-
+
# Specify the charset to use for the message. This defaults to the
# +default_charset+ specified for ActionMailer::Base.
adv_attr_accessor :charset
-
+
# Specify the content type for the message. This defaults to <tt>text/plain</tt>
# in most cases, but can be automatically set in some situations.
adv_attr_accessor :content_type
-
+
# Specify the from address for the message.
adv_attr_accessor :from
-
+
# Specify additional headers to be added to the message.
adv_attr_accessor :headers
-
+
# Specify the order in which parts should be sorted, based on content-type.
# This defaults to the value for the +default_implicit_parts_order+.
adv_attr_accessor :implicit_parts_order
-
+
# Defaults to "1.0", but may be explicitly given if needed.
adv_attr_accessor :mime_version
-
+
# The recipient addresses for the message, either as a string (for a single
# address) or an array (for multiple addresses).
adv_attr_accessor :recipients
-
+
# The date on which the message was sent. If not set (the default), the
# header will be set by the delivery agent.
adv_attr_accessor :sent_on
-
+
# Specify the subject of the message.
adv_attr_accessor :subject
-
+
# Specify the template name to use for current message. This is the "base"
# template name, without the extension or directory, and may be used to
# have multiple mailer methods share the same template.
@@ -353,7 +353,7 @@ def mailer_name(value = nil)
self.class.mailer_name
end
end
-
+
def mailer_name=(value)
self.class.mailer_name = value
end
@@ -431,7 +431,7 @@ def template_root=(root)
# remain uninitialized (useful when you only need to invoke the "receive"
# method, for instance).
def initialize(method_name=nil, *parameters) #:nodoc:
- create!(method_name, *parameters) if method_name
+ create!(method_name, *parameters) if method_name
end
# Initialize the mailer via the given +method_name+. The body will be
@@ -517,7 +517,7 @@ def initialize_defaults(method_name)
@content_type ||= @@default_content_type.dup
@implicit_parts_order ||= @@default_implicit_parts_order.dup
@template ||= method_name
- @mailer_name ||= Inflector.underscore(self.class.name)
+ @mailer_name ||= self.class.name.underscore
@parts ||= []
@headers ||= {}
@body ||= {}
@@ -603,7 +603,7 @@ def create_mail
part = (TMail::Mail === p ? p : p.to_mail(self))
m.parts << part
end
-
+
if real_content_type =~ /multipart/
ctype_attrs.delete "charset"
m.set_content_type(real_content_type, nil, ctype_attrs)
@@ -618,7 +618,7 @@ def perform_delivery_smtp(mail)
mail.ready_to_send
sender = mail['return-path'] || mail.from
- Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain],
+ Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain],
smtp_settings[:user_name], smtp_settings[:password], smtp_settings[:authentication]) do |smtp|
smtp.sendmail(mail.encoded, sender, destinations)
end
View
2 actionmailer/lib/action_mailer/version.rb
@@ -2,7 +2,7 @@ module ActionMailer
module VERSION #:nodoc:
MAJOR = 2
MINOR = 0
- TINY = 2
+ TINY = 991
STRING = [MAJOR, MINOR, TINY].join('.')
end
View
3 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]
View
2 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
View
2 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'
View
2 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
View
51 actionpack/lib/action_controller/base.rb
@@ -259,12 +259,12 @@ class Base
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,15 +325,15 @@ class Base
# 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
# 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 @@ def view_paths=(value)
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 @@ def prepend_view_path(path)
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 @@ def append_view_path(path)
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 @@ def filter_parameter_logging(*filter_words, &block)
filtered_parameters
end
+ protected :filter_parameters
end
# Don't render layouts for templates with the given extensions.
@@ -643,12 +644,12 @@ def session_enabled?
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 @@ def view_paths=(value)
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 @@ def render(options = nil, extra_options = {}, &block) #:doc:
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 @@ def render(options = nil, extra_options = {}, &block) #:doc:
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 @@ def default_url_options(options = nil)
# 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 @@ def default_url_options(options = nil)
# 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 @@ def render_for_text(text = nil, status = nil, append_response = false) #: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
View
37 actionpack/lib/action_controller/caching/actions.rb
@@ -9,7 +9,7 @@ module Caching
# 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 Caching
# 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 ClassMethods
# 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 @@ def expire_action(options = {})
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 @@ def before(controller)
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,33 +106,33 @@ def caching_allowed(controller)
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
normalize!(path)
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 @@ def extract_extension(file_path)
end
end
end
-end
+end
View
6 actionpack/lib/action_controller/caching/sweeping.rb
@@ -28,7 +28,7 @@ module Caching
# 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 @@ def cache_sweeper(*sweepers)
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 @@ def method_missing(method, *arguments)
end
end
end
-end
+end
View
2 actionpack/lib/action_controller/cgi_ext/cookie.rb
@@ -37,7 +37,7 @@ def initialize(name = '', *value)
@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
View
19 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 @@ def ==(mime_type)
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
View
2 actionpack/lib/action_controller/mime_types.rb
@@ -17,4 +17,4 @@
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 )
View
2 actionpack/lib/action_controller/request_forgery_protection.rb
@@ -99,7 +99,7 @@ def verified_request?
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
View
6 actionpack/lib/action_controller/rescue.rb
@@ -199,10 +199,8 @@ def rescue_action_with_handler(exception)
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)
View
9 actionpack/lib/action_controller/session/cookie_store.rb
@@ -130,17 +130,20 @@ def generate_digest(data)
# 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
View
2 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
View
2 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
View
2 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
View
2 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:
View
2 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.
View
2 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/)
View
2 actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -458,7 +458,7 @@ def remote_function(options)
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]
View
127 actionpack/lib/action_view/helpers/text_helper.rb
@@ -3,19 +3,19 @@
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 @@ def concat(string, binding)
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 @@ def truncate(text, length = 30, truncate_string = "...") #:nodoc:
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 @@ def highlight(text, phrases, highlighter = '<strong class="highlight">\1</strong
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 @@ def excerpt(text, phrase, radius = 100, excerpt_string = "...") #:nodoc:
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 @@ def textilize(text)
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 @@ def textilize_without_paragraph(text)
# # => "<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 @@ def simple_format(text, html_options={})
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 @@ def simple_format(text, html_options={})
# 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 @@ def auto_link(text, link = :all, href_options = {}, &block)
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 @@ def auto_link(text, link = :all, href_options = {}, &block)
#
#
# # 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 @@ def cycle(first_value, *values)
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 @@ def reset_cycle(name = "default")
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 @@ def get_cycle(name)
@_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 @@ def set_cycle(name, cycle_object)
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 @@ def auto_link_email_addresses(text)
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
View
80 actionpack/test/controller/caching_test.rb
@@ -6,6 +6,7 @@
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 @@ def test_should_cache_ok_at_custom_path
end
end
end
-
+
def test_page_caching_conditional_options
@request.env['HTTP_ACCEPT'] = 'application/json'
get :ok
@@ -151,12 +152,15 @@ def page_cached?(action)
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 @@ def forbidden
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 @@ def test_simple_action_cache
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 @@ def reset!
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,26 +588,21 @@ def test_fragment_caching
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
assert_match /Fragment caching in a partial/, @response.body
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
end
end
-
-
-
-
-
View
6 actionpack/test/controller/cookie_test.rb
@@ -82,6 +82,7 @@ def test_setting_test_cookie
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 @@ def test_cookies_should_not_be_split_on_ampersand_values
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
View
20 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 @@ def test_filter_parameters
[{'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 @@ def test_filter_parameters
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
View
25 actionpack/test/controller/mime_type_test.rb
@@ -52,16 +52,33 @@ def test_type_should_be_equal_to_symbol
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
View
71 actionpack/test/controller/request_forgery_protection_test.rb
@@ -101,19 +101,79 @@ def test_should_allow_post_without_token_on_unsafe_action
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 @@ def test_should_allow_delete_with_token
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
View
16 actionpack/test/controller/session/cookie_store_test.rb
@@ -43,7 +43,9 @@ def self.cookies
{ :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 @@ def test_restore_deletes_tampered_cookies
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 @@ def self.cookies
{ :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
View
5 actionpack/test/template/prototype_helper_test.rb
@@ -86,6 +86,11 @@ def test_link_to_remote_html_options
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" })
View
41 actionpack/test/template/text_helper_test.rb
@@ -23,9 +23,9 @@ def test_simple_format
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 @@ def test_truncate_should_use_default_length_of_30
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 @@ def test_highlighter
"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 @@ def test_pluralization
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 @@ def test_cycle_class
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 @@ def test_cycle
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 @@ def test_cycle_resets_with_new_values
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 @@ def test_named_cycles
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
-
+