Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of git@github.com:lifo/docrails

  • Loading branch information...
commit 4812e350b9e96641a29c8db6ea153426dad1c9a2 2 parents 78af01f + d18bfa2
Colin Curtin authored
Showing with 7,232 additions and 3,313 deletions.
  1. +5 −0 actionmailer/CHANGELOG
  2. +82 −52 actionmailer/lib/action_mailer/base.rb
  3. +1 −1  actionmailer/lib/action_mailer/version.rb
  4. +6 −2 actionmailer/test/abstract_unit.rb
  5. +26 −0 actionmailer/test/mail_service_test.rb
  6. +7 −1 actionpack/CHANGELOG
  7. +52 −12 actionpack/lib/action_controller/base.rb
  8. +12 −14 actionpack/lib/action_controller/caching/pages.rb
  9. +0 −1  actionpack/lib/action_controller/dispatcher.rb
  10. +20 −2 actionpack/lib/action_controller/mime_type.rb
  11. +1 −1  actionpack/lib/action_controller/polymorphic_routes.rb
  12. +1 −1  actionpack/lib/action_controller/request.rb
  13. +1 −1  actionpack/lib/action_controller/request_forgery_protection.rb
  14. +87 −38 actionpack/lib/action_controller/resources.rb
  15. +4 −2 actionpack/lib/action_controller/routing.rb
  16. +23 −30 actionpack/lib/action_controller/routing/builder.rb
  17. +1 −1  actionpack/lib/action_controller/routing/recognition_optimisation.rb
  18. +1 −1  actionpack/lib/action_controller/routing/route.rb
  19. +4 −0 actionpack/lib/action_controller/routing/routing_ext.rb
  20. +25 −3 actionpack/lib/action_controller/routing/segments.rb
  21. +10 −0 actionpack/lib/action_controller/session/active_record_store.rb
  22. +1 −0  actionpack/lib/action_controller/test_process.rb
  23. +1 −1  actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
  24. +1 −1  actionpack/lib/action_pack/version.rb
  25. +6 −2 actionpack/lib/action_view/base.rb
  26. +9 −4 actionpack/lib/action_view/helpers/atom_feed_helper.rb
  27. +7 −5 actionpack/lib/action_view/helpers/tag_helper.rb
  28. +2 −2 actionpack/lib/action_view/helpers/text_helper.rb
  29. +2 −0  actionpack/lib/action_view/template_handlers/erb.rb
  30. +4 −0 actionpack/test/controller/html-scanner/sanitizer_test.rb
  31. +46 −0 actionpack/test/controller/logging_test.rb
  32. +6 −6 actionpack/test/controller/mime_type_test.rb
  33. +11 −0 actionpack/test/controller/polymorphic_routes_test.rb
  34. +66 −52 actionpack/test/controller/request_forgery_protection_test.rb
  35. +266 −0 actionpack/test/controller/resources_test.rb
  36. +1 −0  actionpack/test/controller/session/cookie_store_test.rb
  37. +5 −1 actionpack/test/template/asset_tag_helper_test.rb
  38. +2 −1  actionpack/test/template/atom_feed_helper_test.rb
  39. +1 −1  actionpack/test/template/form_tag_helper_test.rb
  40. +4 −0 actionpack/test/template/tag_helper_test.rb
  41. +1 −0  actionpack/test/template/text_helper_test.rb
  42. +2 −0  activemodel/lib/active_model/errors.rb
  43. +1 −1  activerecord/CHANGELOG
  44. +1 −1  activerecord/lib/active_record/association_preload.rb
  45. +1 −1  activerecord/lib/active_record/associations.rb
  46. +22 −3 activerecord/lib/active_record/base.rb
  47. +1 −1  activerecord/lib/active_record/calculations.rb
  48. +14 −8 activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
  49. +2 −0  activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
  50. +1 −2  activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
  51. +9 −6 activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
  52. +3 −1 activerecord/lib/active_record/migration.rb
  53. +2 −0  activerecord/lib/active_record/schema_dumper.rb
  54. +9 −7 activerecord/lib/active_record/validations.rb
  55. +1 −1  activerecord/lib/active_record/version.rb
  56. +23 −0 activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
  57. +5 −0 activerecord/test/cases/calculations_test.rb
  58. +12 −4 activerecord/test/cases/pooled_connections_test.rb
  59. +1 −1  activeresource/CHANGELOG
  60. +2 −0  activeresource/lib/active_resource/base.rb
  61. +1 −1  activeresource/lib/active_resource/version.rb
  62. +1 −3 activesupport/CHANGELOG
  63. +2 −1  activesupport/Rakefile
  64. +2 −0  activesupport/lib/active_support/buffered_logger.rb
  65. +2 −0  activesupport/lib/active_support/core_ext/logger.rb
  66. +10 −2 activesupport/lib/active_support/dependencies.rb
  67. +2 −1  activesupport/lib/active_support/inflector.rb
  68. +4 −4 activesupport/lib/active_support/rescuable.rb
  69. +4 −3 activesupport/lib/active_support/values/time_zone.rb
  70. +2 −2 activesupport/lib/active_support/vendor.rb
  71. +3 −3 activesupport/lib/active_support/vendor/{memcache-client-1.5.0 → memcache-client-1.5.1}/memcache.rb
  72. +1 −1  activesupport/lib/active_support/version.rb
  73. +6 −0 activesupport/test/caching_test.rb
  74. +1 −1  activesupport/test/core_ext/time_ext_test.rb
  75. +5 −5 activesupport/test/dependencies_test.rb
  76. +1 −1  activesupport/test/time_zone_test.rb
  77. +1 −1  railties/CHANGELOG
  78. +2 −1  railties/Rakefile
  79. +26 −0 railties/doc/guides/asciidoc.conf
  80. +21 −5 railties/doc/guides/html/2_2_release_notes.html
  81. +10 −10 railties/doc/guides/html/actioncontroller_basics.html
  82. +401 −4 railties/doc/guides/html/activerecord_validations_callbacks.html
  83. +9 −6 railties/doc/guides/html/association_basics.html
  84. +5 −0 railties/doc/guides/html/authors.html
  85. +434 −0 railties/doc/guides/html/command_line.html
  86. +155 −147 railties/doc/guides/html/configuring.html
  87. +1,088 −578 railties/doc/guides/html/creating_plugins.html
  88. +5 −5 railties/doc/guides/html/getting_started_with_rails.html
  89. +1,079 −0 railties/doc/guides/html/i18n.html
  90. +13 −0 railties/doc/guides/html/index.html
  91. +2 −1  railties/doc/guides/html/migrations.html
  92. +2 −2 railties/doc/guides/html/routing_outside_in.html
  93. +335 −206 railties/doc/guides/html/testing_rails_applications.html
  94. +10 −5 railties/doc/guides/source/2_2_release_notes.txt
  95. +1 −1  railties/doc/guides/source/actioncontroller_basics/cookies.txt
  96. +2 −2 railties/doc/guides/source/actioncontroller_basics/filters.txt
  97. +2 −2 railties/doc/guides/source/actioncontroller_basics/parameter_filtering.txt
  98. +3 −3 railties/doc/guides/source/actioncontroller_basics/request_response_objects.txt
  99. +1 −1  railties/doc/guides/source/actioncontroller_basics/streaming.txt
  100. +1 −1  railties/doc/guides/source/actioncontroller_basics/verification.txt
  101. +238 −2 railties/doc/guides/source/activerecord_validations_callbacks.txt
  102. +12 −6 railties/doc/guides/source/association_basics.txt
  103. +6 −0 railties/doc/guides/source/authors.txt
  104. +99 −20 railties/doc/guides/source/caching_with_rails.txt
  105. +127 −158 railties/doc/guides/source/configuring.txt
  106. +69 −71 railties/doc/guides/source/creating_plugins/acts_as_yaffle.txt
  107. +88 −30 railties/doc/guides/source/creating_plugins/appendix.txt
  108. +0 −861 railties/doc/guides/source/creating_plugins/basics.markdown
  109. +63 −0 railties/doc/guides/source/creating_plugins/controllers.txt
  110. +98 −0 railties/doc/guides/source/creating_plugins/core_ext.txt
  111. +0 −69 railties/doc/guides/source/creating_plugins/custom_generator.txt
  112. +50 −0 railties/doc/guides/source/creating_plugins/gems.txt
  113. +144 −0 railties/doc/guides/source/creating_plugins/generator_commands.txt
  114. +98 −0 railties/doc/guides/source/creating_plugins/generators.txt
  115. +49 −0 railties/doc/guides/source/creating_plugins/helpers.txt
  116. +48 −72 railties/doc/guides/source/creating_plugins/index.txt
  117. +0 −89 railties/doc/guides/source/creating_plugins/migration_generator.txt
  118. +213 −0 railties/doc/guides/source/creating_plugins/migrations.txt
  119. +74 −0 railties/doc/guides/source/creating_plugins/models.txt
  120. +0 −122 railties/doc/guides/source/creating_plugins/odds_and_ends.txt
  121. +0 −169 railties/doc/guides/source/creating_plugins/preparation.txt
  122. +18 −0 railties/doc/guides/source/creating_plugins/rdoc.txt
  123. +17 −17 railties/doc/guides/source/creating_plugins/{custom_route.txt → routes.txt}
  124. +84 −0 railties/doc/guides/source/creating_plugins/setup.txt
  125. +0 −103 railties/doc/guides/source/creating_plugins/string_to_squawk.txt
  126. +27 −0 railties/doc/guides/source/creating_plugins/tasks.txt
  127. +165 −0 railties/doc/guides/source/creating_plugins/tests.txt
  128. +0 −61 railties/doc/guides/source/creating_plugins/view_helper.txt
  129. +48 −25 railties/doc/guides/source/finders.txt
  130. +5 −5 railties/doc/guides/source/getting_started_with_rails.txt
  131. +542 −0 railties/doc/guides/source/i18n.txt
  132. +10 −0 railties/doc/guides/source/index.txt
  133. +1 −1  railties/doc/guides/source/migrations/index.txt
  134. +2 −0  railties/doc/guides/source/migrations/rakeing_around.txt
  135. +1 −1  railties/doc/guides/source/routing_outside_in.txt
  136. +4 −0 railties/doc/guides/source/stylesheets/base.css
  137. +249 −141 railties/doc/guides/source/testing_rails_applications.txt
  138. +1 −1  railties/environments/boot.rb
  139. +2 −2 railties/lib/rails/vendor_gem_source_index.rb
  140. +1 −1  railties/lib/rails/version.rb
  141. +1 −1  railties/lib/tasks/databases.rake
View
5 actionmailer/CHANGELOG
@@ -1,3 +1,8 @@
+*2.2.1 [RC2] (November 14th, 2008)*
+
+* Turn on STARTTLS if it is available in Net::SMTP (added in Ruby 1.8.7) and the SMTP server supports it (This is required for Gmail's SMTP server) #1336 [Grant Hollingworth]
+
+
*2.2.0 [RC1] (October 24th, 2008)*
* Add layout functionality to mailers [Pratik]
View
134 actionmailer/lib/action_mailer/base.rb
@@ -201,49 +201,7 @@ module ActionMailer #:nodoc:
# end
#
#
- # = Configuration options
- #
- # These options are specified on the class level, like <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
- #
- # * <tt>template_root</tt> - Determines the base from which template references will be made.
- #
- # * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
- # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
- #
- # * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
- # * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
- # * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
- # * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
- # * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
- # * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
- # * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here.
- # This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
- #
- # * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
- # * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
- # * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt>.
- #
- # * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
- #
- # * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, and <tt>:test</tt>.
- #
- # * <tt>perform_deliveries</tt> - Determines whether <tt>deliver_*</tt> methods are actually carried out. By default they are,
- # but this can be turned off to help functional testing.
- #
- # * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with <tt>delivery_method :test</tt>. Most useful
- # for unit and functional testing.
- #
- # * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
- # pick a different charset from inside a method with +charset+.
- # * <tt>default_content_type</tt> - The default content type used for the main part of the message. Defaults to "text/plain". You
- # can also pick a different content type from inside a method with +content_type+.
- # * <tt>default_mime_version</tt> - The default mime version used for the message. Defaults to <tt>1.0</tt>. You
- # can also pick a different value from inside a method with +mime_version+.
- # * <tt>default_implicit_parts_order</tt> - When a message is built implicitly (i.e. multiple parts are assembled from templates
- # which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
- # <tt>["text/html", "text/enriched", "text/plain"]</tt>. Items that appear first in the array have higher priority in the mail client
- # and appear last in the mime encoded message. You can also pick a different order from inside a method with
- # +implicit_parts_order+.
+ # Configuration options are specified on the class level, like <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
class Base
include AdvAttrAccessor, PartContainer
if Object.const_defined?(:ActionController)
@@ -254,6 +212,10 @@ class Base
private_class_method :new #:nodoc:
class_inheritable_accessor :view_paths
+ ##
+ # :singleton-method:
+ # The logger is used for generating information on the mailing run if available.
+ # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
cattr_accessor :logger
@@smtp_settings = {
@@ -264,88 +226,150 @@ class Base
:password => nil,
:authentication => nil
}
+ ##
+ # :singleton-method:
+ # Allows detailed configuration for <tt>:smtp</tt> delivery method:
+ # * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default "localhost" setting.
+ # * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
+ # * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
+ # * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
+ # * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
+ # * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the authentication type here.
+ # This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
cattr_accessor :smtp_settings
@@sendmail_settings = {
:location => '/usr/sbin/sendmail',
:arguments => '-i -t'
}
+ ##
+ # :singleton-method:
+ # Allows you to override options for the <tt>:sendmail</tt> delivery method.
+ # * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
+ # * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt>.
cattr_accessor :sendmail_settings
@@raise_delivery_errors = true
+ ##
+ # :singleton-method:
+ # Whether or not errors should be raised if the email fails to be delivered.
cattr_accessor :raise_delivery_errors
+ ##
+ # :singleton-method:
+ # Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, and <tt>:test</tt>.
superclass_delegating_accessor :delivery_method
self.delivery_method = :smtp
@@perform_deliveries = true
+ ##
+ # :singleton-method:
+ # Determines whether <tt>deliver_*</tt> methods are actually carried out. By default they are,
+ # but this can be turned off to help functional testing.
cattr_accessor :perform_deliveries
@@deliveries = []
+ ##
+ # :singleton-method:
+ # Keeps an array of all the emails sent out through the Action Mailer with <tt>delivery_method :test</tt>. Most useful
+ # for unit and functional testing.
cattr_accessor :deliveries
@@default_charset = "utf-8"
+ ##
+ # :singleton-method:
+ # The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
+ # pick a different charset from inside a method with +charset+.
cattr_accessor :default_charset
@@default_content_type = "text/plain"
+ ##
+ # :singleton-method:
+ # The default content type used for the main part of the message. Defaults to "text/plain". You
+ # can also pick a different content type from inside a method with +content_type+.
cattr_accessor :default_content_type
@@default_mime_version = "1.0"
+ ##
+ # :singleton-method:
+ # The default mime version used for the message. Defaults to <tt>1.0</tt>. You
+ # can also pick a different value from inside a method with +mime_version+.
cattr_accessor :default_mime_version
@@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ]
+ ##
+ # :singleton-method:
+ # When a message is built implicitly (i.e. multiple parts are assembled from templates
+ # which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
+ # <tt>["text/html", "text/enriched", "text/plain"]</tt>. Items that appear first in the array have higher priority in the mail client
+ # and appear last in the mime encoded message. You can also pick a different order from inside a method with
+ # +implicit_parts_order+.
cattr_accessor :default_implicit_parts_order
cattr_reader :protected_instance_variables
@@protected_instance_variables = %w(@body)
+ ##
# 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 the address (if different than the "from" address) to direct
# replies to this message.
adv_attr_accessor :reply_to
+ ##
# 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.
@@ -386,12 +410,15 @@ def respond_to?(method_symbol, include_private = false) #:nodoc:
end
def method_missing(method_symbol, *parameters) #:nodoc:
- match = matches_dynamic_method?(method_symbol)
- case match[1]
- when 'create' then new(match[2], *parameters).mail
- when 'deliver' then new(match[2], *parameters).deliver!
- when 'new' then nil
- else super
+ if match = matches_dynamic_method?(method_symbol)
+ case match[1]
+ when 'create' then new(match[2], *parameters).mail
+ when 'deliver' then new(match[2], *parameters).deliver!
+ when 'new' then nil
+ else super
+ end
+ else
+ super
end
end
@@ -429,6 +456,7 @@ def register_template_extension(extension)
"Use ActionView::Base.register_template_extension instead", caller)
end
+ # Determines the base from which template references will be made.
def template_root
self.view_paths && self.view_paths.first
end
@@ -440,7 +468,7 @@ def template_root=(root)
private
def matches_dynamic_method?(method_name) #:nodoc:
method_name = method_name.to_s
- /(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
+ /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
end
end
@@ -663,8 +691,10 @@ 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],
- smtp_settings[:user_name], smtp_settings[:password], smtp_settings[:authentication]) do |smtp|
+ smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
+ smtp.enable_starttls_auto if smtp.respond_to?(:enable_starttls_auto)
+ smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password],
+ smtp_settings[:authentication]) do |smtp|
smtp.sendmail(mail.encoded, sender, destinations)
end
end
View
2  actionmailer/lib/action_mailer/version.rb
@@ -2,7 +2,7 @@ module ActionMailer
module VERSION #:nodoc:
MAJOR = 2
MINOR = 2
- TINY = 0
+ TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
View
8 actionmailer/test/abstract_unit.rb
@@ -24,11 +24,15 @@ def initialize
def sendmail(mail, from, to)
@@deliveries << [mail, from, to]
end
+
+ def start(*args)
+ yield self
+ end
end
class Net::SMTP
- def self.start(*args)
- yield MockSMTP.new
+ def self.new(*args)
+ MockSMTP.new
end
end
View
26 actionmailer/test/mail_service_test.rb
@@ -938,6 +938,20 @@ def test_body_is_stored_as_an_ivar
mail = TestMailer.create_body_ivar(@recipient)
assert_equal "body: foo\nbar: baz", mail.body
end
+
+ def test_starttls_is_enabled_if_supported
+ MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(true)
+ MockSMTP.any_instance.expects(:enable_starttls_auto)
+ ActionMailer::Base.delivery_method = :smtp
+ TestMailer.deliver_signed_up(@recipient)
+ end
+
+ def test_starttls_is_disabled_if_not_supported
+ MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(false)
+ MockSMTP.any_instance.expects(:enable_starttls_auto).never
+ ActionMailer::Base.delivery_method = :smtp
+ TestMailer.deliver_signed_up(@recipient)
+ end
end
end # uses_mocha
@@ -1031,4 +1045,16 @@ def test_should_not_respond_to_create_with_template_suffix_if_it_begins_with_a_d
def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_digit
assert !RespondToMailer.respond_to?(:deliver_1_template)
end
+
+ def test_should_not_respond_to_method_where_deliver_is_not_a_suffix
+ assert !RespondToMailer.respond_to?(:foo_deliver_template)
+ end
+
+ def test_should_still_raise_exception_with_expected_message_when_calling_an_undefined_method
+ error = assert_raises NoMethodError do
+ RespondToMailer.not_a_method
+ end
+
+ assert_match /undefined method.*not_a_method/, error.message
+ end
end
View
8 actionpack/CHANGELOG
@@ -1,4 +1,10 @@
-*2.2.1 [RC2 or 2.2 final]*
+*2.2.1 [RC2] (November 14th, 2008)*
+
+* Added render :js for people who want to render inline JavaScript replies without using RJS [DHH]
+
+* Fixed that polymorphic_url should compact given array #1317 [hiroshi]
+
+* Fixed the sanitize helper to avoid double escaping already properly escaped entities #683 [antonmos/Ryan McGeary]
* Fixed that FormTagHelper generated illegal html if name contained square brackets #1238 [Vladimir Dobriakov]
View
64 actionpack/lib/action_controller/base.rb
@@ -260,30 +260,44 @@ class Base
include StatusCodes
- cattr_reader :protected_instance_variables
+ ##
+ # :singleton-method:
# Controller specific instance variables which will not be accessible inside views.
+ cattr_reader :protected_instance_variables
@@protected_instance_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)
+ @@asset_host = ""
+ ##
+ # :singleton-method:
# 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"
- @@asset_host = ""
cattr_accessor :asset_host
+ @@consider_all_requests_local = true
+ ##
+ # :singleton-method:
# All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
# When the application is ready to go public, this should be set to false, and the protected method <tt>local_request?</tt>
# should instead be implemented in the controller to determine when debugging screens should be shown.
- @@consider_all_requests_local = true
cattr_accessor :consider_all_requests_local
+ @@allow_concurrency = false
+ ##
+ # :singleton-method:
# Indicates whether to allow concurrent action processing. Your
# controller actions and any other code they call must also behave well
# when called from concurrent threads. Turned off by default.
- @@allow_concurrency = false
cattr_accessor :allow_concurrency
+ @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
+ Mime::URL_ENCODED_FORM => :url_encoded_form,
+ Mime::XML => :xml_simple,
+ Mime::JSON => :json }
+ ##
+ # :singleton-method:
# Modern REST web services often need to submit complex data to the web application.
# The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the
# <tt>params</tt> hash. These handlers are invoked for POST and PUT requests.
@@ -310,37 +324,47 @@ class Base
# A YAML parser is also available and can be turned on with:
#
# ActionController::Base.param_parsers[Mime::YAML] = :yaml
- @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
- Mime::URL_ENCODED_FORM => :url_encoded_form,
- Mime::XML => :xml_simple,
- Mime::JSON => :json }
cattr_accessor :param_parsers
- # Controls the default charset for all renders.
@@default_charset = "utf-8"
+ ##
+ # :singleton-method:
+ # Controls the default charset for all renders.
cattr_accessor :default_charset
+ ##
+ # :singleton-method:
# 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 = "/"
+ ##
+ # :singleton-method:
+ # Controls the resource action separator
cattr_accessor :resource_action_separator
- # Allow to override path names for default resources' actions
@@resources_path_names = { :new => 'new', :edit => 'edit' }
+ ##
+ # :singleton-method:
+ # Allow to override path names for default resources' actions
cattr_accessor :resources_path_names
+ ##
+ # :singleton-method:
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
# sets it to <tt>:authenticity_token</tt> by default.
cattr_accessor :request_forgery_protection_token
+ ##
+ # :singleton-method:
# Indicates whether or not optimise the generated named
# route helper methods
cattr_accessor :optimise_named_routes
self.optimise_named_routes = true
+ ##
+ # :singleton-method:
# Indicates whether the response format should be determined by examining the Accept HTTP header,
# or by using the simpler params + ajax rules.
#
@@ -351,38 +375,54 @@ class Base
cattr_accessor :use_accept_header
self.use_accept_header = true
+ ##
+ # :singleton-method:
# Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
class_inheritable_accessor :allow_forgery_protection
self.allow_forgery_protection = true
+ ##
+ # :singleton-method:
# If you are deploying to a subdirectory, you will need to set
# <tt>config.action_controller.relative_url_root</tt>
# This defaults to ENV['RAILS_RELATIVE_URL_ROOT']
cattr_accessor :relative_url_root
self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
+ ##
+ # :singleton-method:
# Holds the request object that's primarily used to get environment variables through access like
# <tt>request.env["REQUEST_URI"]</tt>.
attr_internal :request
+ ##
+ # :singleton-method:
# Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like <tt>params["post_id"]</tt>
# to get the post_id. No type casts are made, so all values are returned as strings.
attr_internal :params
+ ##
+ # :singleton-method:
# Holds the response object that's primarily used to set additional HTTP headers through access like
# <tt>response.headers["Cache-Control"] = "no-cache"</tt>. Can also be used to access the final body HTML after a template
# has been rendered through response.body -- useful for <tt>after_filter</tt>s that wants to manipulate the output,
# such as a OutputCompressionFilter.
attr_internal :response
+ ##
+ # :singleton-method:
# Holds a hash of objects in the session. Accessed like <tt>session[:person]</tt> to get the object tied to the "person"
# key. The session will hold any type of object as values, but the key should be a string or symbol.
attr_internal :session
+ ##
+ # :singleton-method:
# Holds a hash of header names and values. Accessed like <tt>headers["Cache-Control"]</tt> to get the value of the Cache-Control
# directive. Values should always be specified as strings.
attr_internal :headers
+ ##
+ # :singleton-method:
# Returns the name of the action this controller is processing.
attr_accessor :action_name
@@ -1241,7 +1281,7 @@ def log_processing_for_parameters
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
parameters = parameters.except!(:controller, :action, :format, :_method)
- logger.info " Parameters: #{parameters.inspect}"
+ logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
end
def default_render #:nodoc:
View
26 actionpack/lib/action_controller/caching/pages.rb
@@ -33,28 +33,26 @@ module Caching
#
# Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
# expired.
- #
- # == Setting the cache directory
- #
- # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
- # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
- # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
- # web server to look in the new location for cached files.
- #
- # == Setting the cache extension
- #
- # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
- # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
- # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
- # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
module Pages
def self.included(base) #:nodoc:
base.extend(ClassMethods)
base.class_eval do
@@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
+ ##
+ # :singleton-method:
+ # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
+ # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
+ # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
+ # web server to look in the new location for cached files.
cattr_accessor :page_cache_directory
@@page_cache_extension = '.html'
+ ##
+ # :singleton-method:
+ # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
+ # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
+ # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
+ # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
cattr_accessor :page_cache_extension
end
end
View
1  actionpack/lib/action_controller/dispatcher.rb
@@ -23,7 +23,6 @@ def define_dispatcher_callbacks(cache_classes)
if defined?(ActiveRecord)
after_dispatch :checkin_connections
- before_dispatch { ActiveRecord::Base.verify_active_connections! }
to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
end
View
22 actionpack/lib/action_controller/mime_type.rb
@@ -20,8 +20,22 @@ module Mime
# end
class Type
@@html_types = Set.new [:html, :all]
+ cattr_reader :html_types
+
+ @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form]
+ ##
+ # :singleton-method:
+ # These are the content types which browsers can generate without using ajax, flash, etc
+ # i.e. following a link, getting an image or posting a form. CSRF protection
+ # only needs to protect against these types.
+ cattr_reader :browser_generated_types
+
+
@@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
- cattr_reader :html_types, :unverifiable_types
+ def self.unverifiable_types
+ ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller)
+ @@unverifiable_types
+ end
# A simple helper class used in parsing the accept header
class AcceptItem #:nodoc:
@@ -167,13 +181,17 @@ def ==(mime_type)
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
# ActionController::RequestForgerProtection.
def verify_request?
- !@@unverifiable_types.include?(to_sym)
+ browser_generated?
end
def html?
@@html_types.include?(to_sym) || @string =~ /html/
end
+ def browser_generated?
+ @@browser_generated_types.include?(to_sym)
+ end
+
private
def method_missing(method, *args)
if method.to_s =~ /(\w+)\?$/
View
2  actionpack/lib/action_controller/polymorphic_routes.rb
@@ -73,7 +73,7 @@ module PolymorphicRoutes
#
def polymorphic_url(record_or_hash_or_array, options = {})
if record_or_hash_or_array.kind_of?(Array)
- record_or_hash_or_array = record_or_hash_or_array.dup
+ record_or_hash_or_array = record_or_hash_or_array.compact
end
record = extract_record(record_or_hash_or_array)
View
2  actionpack/lib/action_controller/request.rb
@@ -13,7 +13,7 @@ def self.relative_url_root=(relative_url_root)
ActiveSupport::Deprecation.warn(
"ActionController::AbstractRequest.relative_url_root= has been renamed." +
"You can now set it with config.action_controller.relative_url_root=", caller)
- ActionController::base.relative_url_root=relative_url_root
+ ActionController::Base.relative_url_root=relative_url_root
end
HTTP_METHODS = %w(get head put post delete options)
View
2  actionpack/lib/action_controller/request_forgery_protection.rb
@@ -99,7 +99,7 @@ def verified_request?
end
def verifiable_request_format?
- request.content_type.nil? || request.content_type.verify_request?
+ !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
125 actionpack/lib/action_controller/resources.rb
@@ -42,7 +42,11 @@ module ActionController
#
# Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
module Resources
+ INHERITABLE_OPTIONS = :namespace, :shallow, :actions
+
class Resource #:nodoc:
+ DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy
+
attr_reader :collection_methods, :member_methods, :new_methods
attr_reader :path_prefix, :name_prefix, :path_segment
attr_reader :plural, :singular
@@ -57,6 +61,7 @@ def initialize(entities, options)
arrange_actions
add_default_actions
+ set_allowed_actions
set_prefixes
end
@@ -113,6 +118,10 @@ def uncountable?
@singular.to_s == @plural.to_s
end
+ def has_action?(action)
+ !DEFAULT_ACTIONS.include?(action) || @options[:actions].nil? || @options[:actions].include?(action)
+ end
+
protected
def arrange_actions
@collection_methods = arrange_actions_by_methods(options.delete(:collection))
@@ -125,6 +134,25 @@ def add_default_actions
add_default_action(new_methods, :get, :new)
end
+ def set_allowed_actions
+ only = @options.delete(:only)
+ except = @options.delete(:except)
+
+ if only && except
+ raise ArgumentError, 'Please supply either :only or :except, not both.'
+ elsif only == :all || except == :none
+ options[:actions] = DEFAULT_ACTIONS
+ elsif only == :none || except == :all
+ options[:actions] = []
+ elsif only
+ options[:actions] = DEFAULT_ACTIONS & Array(only).map(&:to_sym)
+ elsif except
+ options[:actions] = DEFAULT_ACTIONS - Array(except).map(&:to_sym)
+ else
+ # leave options[:actions] alone
+ end
+ end
+
def set_prefixes
@path_prefix = options.delete(:path_prefix)
@name_prefix = options.delete(:name_prefix)
@@ -255,7 +283,12 @@ def initialize(entity, options)
# * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action.
# * <tt>:controller</tt> - Specify the controller name for the routes.
# * <tt>:singular</tt> - Specify the singular name used in the member routes.
- # * <tt>:requirements</tt> - Set custom routing parameter requirements.
+ # * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either
+ # regular expressions (which must match for the route to match) or extra parameters. For example:
+ #
+ # map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' }
+ #
+ # will only match if the first part is alphabetic, and will pass the parameter :extra to the controller.
# * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes.
# * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example:
# # products_path == '/productos'
@@ -353,6 +386,25 @@ def initialize(entity, options)
#
# map.resources :users, :has_many => { :posts => :comments }, :shallow => true
#
+ # * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
+ #
+ # <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
+ # list of action names. By default, routes are generated for all seven actions.
+ #
+ # For example:
+ #
+ # map.resources :posts, :only => [:index, :show] do |post|
+ # post.resources :comments, :except => [:update, :destroy]
+ # end
+ # # --> GET /posts (maps to the PostsController#index action)
+ # # --> POST /posts (fails)
+ # # --> GET /posts/1 (maps to the PostsController#show action)
+ # # --> DELETE /posts/1 (fails)
+ # # --> POST /posts/1/comments (maps to the CommentsController#create action)
+ # # --> PUT /posts/1/comments/1 (fails)
+ #
+ # The <tt>:only</tt> and <tt>:except</tt> options are inherited by any nested resource(s).
+ #
# If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
#
# Examples:
@@ -478,7 +530,7 @@ def map_resource(entities, options = {}, &block)
map_associations(resource, options)
if block_given?
- with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block)
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
end
end
end
@@ -495,7 +547,7 @@ def map_singleton_resource(entities, options = {}, &block)
map_associations(resource, options)
if block_given?
- with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block)
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
end
end
end
@@ -507,7 +559,7 @@ def map_associations(resource, options)
name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
Array(options[:has_one]).each do |association|
- resource(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace], :shallow => options[:shallow])
+ resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
end
end
@@ -522,7 +574,7 @@ def map_has_many_associations(resource, associations, options)
map_has_many_associations(resource, association, options)
end
when Symbol, String
- resources(associations, :path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], :has_many => options[:has_many])
+ resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
else
end
end
@@ -531,41 +583,39 @@ def map_collection_actions(map, resource)
resource.collection_methods.each do |method, actions|
actions.each do |action|
[method].flatten.each do |m|
- action_options = action_options_for(action, resource, m)
- map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
+ map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
end
end
end
end
def map_default_collection_actions(map, resource)
- index_action_options = action_options_for("index", resource)
index_route_name = "#{resource.name_prefix}#{resource.plural}"
if resource.uncountable?
index_route_name << "_index"
end
- map_named_routes(map, index_route_name, resource.path, index_action_options)
-
- create_action_options = action_options_for("create", resource)
- map_unnamed_routes(map, resource.path, create_action_options)
+ map_resource_routes(map, resource, :index, resource.path, index_route_name)
+ map_resource_routes(map, resource, :create, resource.path, index_route_name)
end
def map_default_singleton_actions(map, resource)
- create_action_options = action_options_for("create", resource)
- map_unnamed_routes(map, resource.path, create_action_options)
+ map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}")
end
def map_new_actions(map, resource)
resource.new_methods.each do |method, actions|
actions.each do |action|
- action_options = action_options_for(action, resource, method)
- if action == :new
- map_named_routes(map, "new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
- else
- map_named_routes(map, "#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options)
+ route_path = resource.new_path
+ route_name = "new_#{resource.name_prefix}#{resource.singular}"
+
+ unless action == :new
+ route_path = "#{route_path}#{resource.action_separator}#{action}"
+ route_name = "#{action}_#{route_name}"
end
+
+ map_resource_routes(map, resource, action, route_path, route_name, method)
end
end
end
@@ -574,34 +624,33 @@ def map_member_actions(map, resource)
resource.member_methods.each do |method, actions|
actions.each do |action|
[method].flatten.each do |m|
- action_options = action_options_for(action, resource, m)
-
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
action_path ||= Base.resources_path_names[action] || action
- map_named_routes(map, "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options)
+ map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m)
end
end
end
- show_action_options = action_options_for("show", resource)
- map_named_routes(map, "#{resource.shallow_name_prefix}#{resource.singular}", resource.member_path, show_action_options)
-
- update_action_options = action_options_for("update", resource)
- map_unnamed_routes(map, resource.member_path, update_action_options)
-
- destroy_action_options = action_options_for("destroy", resource)
- map_unnamed_routes(map, resource.member_path, destroy_action_options)
- end
-
- def map_unnamed_routes(map, path_without_format, options)
- map.connect(path_without_format, options)
- map.connect("#{path_without_format}.:format", options)
+ route_path = "#{resource.shallow_name_prefix}#{resource.singular}"
+ map_resource_routes(map, resource, :show, resource.member_path, route_path)
+ map_resource_routes(map, resource, :update, resource.member_path, route_path)
+ map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
end
- def map_named_routes(map, name, path_without_format, options)
- map.named_route(name, path_without_format, options)
- map.named_route("formatted_#{name}", "#{path_without_format}.:format", options)
+ def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil)
+ if resource.has_action?(action)
+ action_options = action_options_for(action, resource, method)
+ formatted_route_path = "#{route_path}.:format"
+
+ if route_name && @set.named_routes[route_name.to_sym].nil?
+ map.named_route(route_name, route_path, action_options)
+ map.named_route("formatted_#{route_name}", formatted_route_path, action_options)
+ else
+ map.connect(route_path, action_options)
+ map.connect(formatted_route_path, action_options)
+ end
+ end
end
def add_conditions_for(conditions, method)
View
6 actionpack/lib/action_controller/routing.rb
@@ -84,9 +84,11 @@ module ActionController
# This sets up +blog+ as the default controller if no other is specified.
# This means visiting '/' would invoke the blog controller.
#
- # More formally, you can define defaults in a route with the <tt>:defaults</tt> key.
+ # More formally, you can include arbitrary parameters in the route, thus:
#
- # map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' }
+ # map.connect ':controller/:action/:id', :action => 'show', :page => 'Dashboard'
+ #
+ # This will pass the :page parameter to all incoming requests that match this route.
#
# Note: The default routes, as provided by the Rails generator, make all actions in every
# controller accessible via GET requests. You should consider removing them or commenting
View
53 actionpack/lib/action_controller/routing/builder.rb
@@ -1,23 +1,16 @@
module ActionController
module Routing
class RouteBuilder #:nodoc:
- attr_accessor :separators, :optional_separators
+ attr_reader :separators, :optional_separators
+ attr_reader :separator_regexp, :nonseparator_regexp, :interval_regexp
def initialize
- self.separators = Routing::SEPARATORS
- self.optional_separators = %w( / )
- end
-
- def separator_pattern(inverted = false)
- "[#{'^' if inverted}#{Regexp.escape(separators.join)}]"
- end
-
- def interval_regexp
- Regexp.new "(.*?)(#{separators.source}|$)"
- end
+ @separators = Routing::SEPARATORS
+ @optional_separators = %w( / )
- def multiline_regexp?(expression)
- expression.options & Regexp::MULTILINE == Regexp::MULTILINE
+ @separator_regexp = /[#{Regexp.escape(separators.join)}]/
+ @nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/
+ @interval_regexp = /(.*?)(#{separator_regexp}|$)/
end
# Accepts a "route path" (a string defining a route), and returns the array
@@ -30,7 +23,7 @@ def segments_for_route_path(path)
rest, segments = path, []
until rest.empty?
- segment, rest = segment_for rest
+ segment, rest = segment_for(rest)
segments << segment
end
segments
@@ -39,20 +32,20 @@ def segments_for_route_path(path)
# A factory method that returns a new segment instance appropriate for the
# format of the given string.
def segment_for(string)
- segment = case string
- when /\A:(\w+)/
- key = $1.to_sym
- case key
- when :controller then ControllerSegment.new(key)
- else DynamicSegment.new key
- end
- when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true)
- when /\A\?(.*?)\?/
- StaticSegment.new($1, :optional => true)
- when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1)
- when Regexp.new(separator_pattern) then
- DividerSegment.new($&, :optional => (optional_separators.include? $&))
- end
+ segment =
+ case string
+ when /\A:(\w+)/
+ key = $1.to_sym
+ key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key)
+ when /\A\*(\w+)/
+ PathSegment.new($1.to_sym, :optional => true)
+ when /\A\?(.*?)\?/
+ StaticSegment.new($1, :optional => true)
+ when nonseparator_regexp
+ StaticSegment.new($1)
+ when separator_regexp
+ DividerSegment.new($&, :optional => optional_separators.include?($&))
+ end
[segment, $~.post_match]
end
@@ -98,7 +91,7 @@ def assign_route_options(segments, defaults, requirements)
if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
end
- if multiline_regexp?(requirement)
+ if requirement.multiline?
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
end
segment.regexp = requirement
View
2  actionpack/lib/action_controller/routing/recognition_optimisation.rb
@@ -148,7 +148,7 @@ def recognize_optimized(path, env)
end
nil
end
- }, __FILE__, __LINE__
+ }, '(recognize_optimized)', 1
end
def clear_recognize_optimized!
View
2  actionpack/lib/action_controller/routing/route.rb
@@ -219,7 +219,7 @@ def recognition_extraction
next_capture = 1
extraction = segments.collect do |segment|
x = segment.match_extraction(next_capture)
- next_capture += Regexp.new(segment.regexp_chunk).number_of_captures
+ next_capture += segment.number_of_captures
x
end
extraction.compact
View
4 actionpack/lib/action_controller/routing/routing_ext.rb
@@ -27,6 +27,10 @@ def number_of_captures
Regexp.new("|#{source}").match('').captures.length
end
+ def multiline?
+ options & MULTILINE == MULTILINE
+ end
+
class << self
def optionalize(pattern)
case unoptionalize(pattern)
View
28 actionpack/lib/action_controller/routing/segments.rb
@@ -13,6 +13,10 @@ def initialize
@is_optional = false
end
+ def number_of_captures
+ Regexp.new(regexp_chunk).number_of_captures
+ end
+
def extraction_code
nil
end
@@ -84,6 +88,10 @@ def regexp_chunk
optional? ? Regexp.optionalize(chunk) : chunk
end
+ def number_of_captures
+ 0
+ end
+
def build_pattern(pattern)
escaped = Regexp.escape(value)
if optional? && ! pattern.empty?
@@ -194,10 +202,16 @@ def regexp_chunk
end
end
+ def number_of_captures
+ if regexp
+ regexp.number_of_captures + 1
+ else
+ 1
+ end
+ end
+
def build_pattern(pattern)
- chunk = regexp_chunk
- chunk = "(#{chunk})" if Regexp.new(chunk).number_of_captures == 0
- pattern = "#{chunk}#{pattern}"
+ pattern = "#{regexp_chunk}#{pattern}"
optional? ? Regexp.optionalize(pattern) : pattern
end
@@ -230,6 +244,10 @@ def regexp_chunk
"(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))"
end
+ def number_of_captures
+ 1
+ end
+
# Don't URI.escape the controller name since it may contain slashes.
def interpolation_chunk(value_code = local_name)
"\#{#{value_code}.to_s}"
@@ -275,6 +293,10 @@ def regexp_chunk
regexp || "(.*)"
end
+ def number_of_captures
+ regexp ? regexp.number_of_captures : 1
+ end
+
def optionality_implied?
true
end
View
10 actionpack/lib/action_controller/session/active_record_store.rb
@@ -56,6 +56,8 @@ def model
class ActiveRecordStore
# The default Active Record class.
class Session < ActiveRecord::Base
+ ##
+ # :singleton-method:
# Customizable data column name. Defaults to 'data'.
cattr_accessor :data_column_name
self.data_column_name = 'data'
@@ -166,17 +168,25 @@ def raise_on_session_data_overflow!
# binary session data in a +text+ column. For higher performance,
# store in a +blob+ column instead and forgo the Base64 encoding.
class SqlBypass
+ ##
+ # :singleton-method:
# Use the ActiveRecord::Base.connection by default.
cattr_accessor :connection
+ ##
+ # :singleton-method:
# The table name defaults to 'sessions'.
cattr_accessor :table_name
@@table_name = 'sessions'
+ ##
+ # :singleton-method:
# The session id field defaults to 'session_id'.
cattr_accessor :session_id_column
@@session_id_column = 'session_id'
+ ##
+ # :singleton-method:
# The data field defaults to 'data'.
cattr_accessor :data_column
@@data_column = 'data'
View
1  actionpack/lib/action_controller/test_process.rb
@@ -395,6 +395,7 @@ def process(action, parameters = nil, session = nil, flash = nil)
@html_document = nil
@request.env['REQUEST_METHOD'] ||= "GET"
+
@request.action = action.to_s
parameters ||= {}
View
2  actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -160,7 +160,7 @@ def process_attributes_for(node, options)
if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
node.attributes.delete(attr_name)
else
- node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(value)
+ node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value))
end
end
end
View
2  actionpack/lib/action_pack/version.rb
@@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 2
- TINY = 0
+ TINY = 1
STRING = [MAJOR, MINOR, TINY].join('.')
end
View
8 actionpack/lib/action_view/base.rb
@@ -183,13 +183,17 @@ def self.exempt_from_layout(*extensions)
@@exempt_from_layout.merge(regexps)
end
+ @@debug_rjs = false
+ ##
+ # :singleton-method:
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
- @@debug_rjs = false
cattr_accessor :debug_rjs
- # A warning will be displayed whenever an action results in a cache miss on your view paths.
@@warn_cache_misses = false
+ ##
+ # :singleton-method:
+ # A warning will be displayed whenever an action results in a cache miss on your view paths.
cattr_accessor :warn_cache_misses
attr_internal :request
View
13 actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -1,3 +1,5 @@
+require 'set'
+
# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other
# template languages).
module ActionView
@@ -121,6 +123,8 @@ def atom_feed(options = {}, &block)
end
class AtomBuilder
+ XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
+
def initialize(xml)
@xml = xml
end
@@ -140,14 +144,15 @@ def method_missing(method, *arguments, &block)
@xml.__send__(method, *arguments, &block)
end
end
-
+
# True if the method name matches one of the five elements defined
# in the Atom spec as potentially containing XHTML content and
# if :type => 'xhtml' is, in fact, specified.
def xhtml_block?(method, arguments)
- %w( content rights title subtitle summary ).include?(method.to_s) &&
- arguments.last.respond_to?(:[]) &&
- arguments.last[:type].to_s == 'xhtml'
+ if XHTML_TAG_NAMES.include?(method.to_s)
+ last = arguments.last
+ last.is_a?(Hash) && last[:type].to_s == 'xhtml'
+ end
end
end
View
12 actionpack/lib/action_view/helpers/tag_helper.rb
@@ -98,7 +98,7 @@ def cdata_section(content)
# Returns an escaped version of +html+ without affecting existing escaped entities.
#
# ==== Examples
- # escape_once("1 > 2 &amp; 3")
+ # escape_once("1 < 2 &amp; 3")
# # => "1 &lt; 2 &amp; 3"
#
# escape_once("&lt;&lt; Accept & Checkout")
@@ -133,10 +133,12 @@ def tag_options(options, escape = true)
unless options.blank?
attrs = []
if escape
- options.each do |key, value|
- next unless value
- value = BOOLEAN_ATTRIBUTES.include?(key) ? key : escape_once(value)
- attrs << %(#{key}="#{value}")
+ options.each_pair do |key, value|
+ if BOOLEAN_ATTRIBUTES.include?(key)
+ attrs << %(#{key}="#{key}") if value
+ else
+ attrs << %(#{key}="#{escape_once(value)}") if !value.nil?
+ end
end
else
attrs = options.map { |key, value| %(#{key}="#{value}") }
View
4 actionpack/lib/action_view/helpers/text_helper.rb
@@ -559,7 +559,7 @@ def set_cycle(name, cycle_object)
(?:\.[-\w]+)* # remaining subdomains or domain
(?::\d+)? # port
(?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path
- (?:\?[\w\+@%&=.;-]+)? # query string
+ (?:\?[\w\+@%&=.;:-]+)? # query string
(?:\#[\w\-]*)? # trailing anchor
)
([[:punct:]]|<|$|) # trailing text
@@ -598,4 +598,4 @@ def auto_link_email_addresses(text)
end
end
end
-end
+end
View
2  actionpack/lib/action_view/template_handlers/erb.rb
@@ -42,6 +42,8 @@ module TemplateHandlers
class ERB < TemplateHandler
include Compilable
+ ##
+ # :singleton-method:
# Specify trim mode for the ERB compiler. Defaults to '-'.
# See ERb documentation for suitable values.
cattr_accessor :erb_trim_mode
View
4 actionpack/test/controller/html-scanner/sanitizer_test.rb
@@ -253,6 +253,10 @@ def test_should_sanitize_unterminated_cdata_section
assert_sanitized "<![CDATA[<span>neverending...", "&lt;![CDATA[&lt;span>neverending...]]>"
end
+ def test_should_not_mangle_urls_with_ampersand
+ assert_sanitized %{<a href=\"http://www.domain.com?var1=1&amp;var2=2\">my link</a>}
+ end
+
protected
def assert_sanitized(input, expected = nil)
@sanitizer ||= HTML::WhiteListSanitizer.new
View
46 actionpack/test/controller/logging_test.rb
@@ -0,0 +1,46 @@
+require 'abstract_unit'
+
+class LoggingController < ActionController::Base
+ def show
+ render :nothing => true
+ end
+end
+
+class LoggingTest < ActionController::TestCase
+ tests LoggingController
+
+ class MockLogger
+ attr_reader :logged
+
+ def method_missing(method, *args)
+ @logged ||= []
+ @logged << args.first
+ end
+ end
+
+ setup :set_logger
+
+ def test_logging_without_parameters
+ get :show
+ assert_equal 2, logs.size
+ assert_nil logs.detect {|l| l =~ /Parameters/ }
+ end
+
+ def test_logging_with_parameters
+ get :show, :id => 10
+ assert_equal 3, logs.size
+
+ params = logs.detect {|l| l =~ /Parameters/ }
+ assert_equal 'Parameters: {"id"=>"10"}', params
+ end
+
+ private
+
+ def set_logger
+ @controller.logger = MockLogger.new
+ end
+
+ def logs
+ @logs ||= @controller.logger.logged.compact.map {|l| l.strip}
+ end
+end
View
12 actionpack/test/controller/mime_type_test.rb
@@ -61,7 +61,9 @@ def test_type_convenience_methods
types.each do |type|
mime = Mime.const_get(type.to_s.upcase)
assert mime.send("#{type}?"), "#{mime.inspect} is not #{type}?"
- (types - [type]).each { |other_type| assert !mime.send("#{other_type}?"), "#{mime.inspect} is #{other_type}?" }
+ invalid_types = types - [type]
+ invalid_types.delete(:html) if Mime::Type.html_types.include?(type)
+ invalid_types.each { |other_type| assert !mime.send("#{other_type}?"), "#{mime.inspect} is #{other_type}?" }
end
end
@@ -71,14 +73,12 @@ def test_mime_all_is_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}"
+ verified, unverified = all_types.partition { |type| Mime::Type.browser_generated_types.include? type }
+ assert verified.each { |type| assert Mime.const_get(type.to_s.upcase).verify_request?, "Verifiable Mime Type is not verified: #{type.inspect}" }
+ assert unverified.each { |type| assert !Mime.const_get(type.to_s.upcase).verify_request?, "Nonverifiable Mime Type is verified: #{type.inspect}" }
end
end
View
11 actionpack/test/controller/polymorphic_routes_test.rb
@@ -169,6 +169,17 @@ def test_nesting_with_array_containing_singleton_resource_and_format_option
polymorphic_url([@article, :response, @tag], :format => :pdf)
end
+ def test_nesting_with_array_containing_nil
+ expects(:article_response_url).with(@article)
+ polymorphic_url([@article, nil, :response])
+ end
+
+ def test_with_array_containing_single_object
+ @article.save
+ expects(:article_url).with(@article)
+ polymorphic_url([nil, @article])
+ end
+
# TODO: Needs to be updated to correctly know about whether the object is in a hash or not
def xtest_with_hash
expects(:article_url).with(@article)
View
118 actionpack/test/controller/request_forgery_protection_test.rb
@@ -77,57 +77,61 @@ def teardown
ActionController::Base.request_forgery_protection_token = nil
end
+
def test_should_render_form_with_token_tag
- get :index
- assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
+ get :index
+ assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
+ end
+
+ def test_should_render_button_to_with_token_tag
+ get :show_button
+ assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
+ end
+
+ def test_should_render_remote_form_with_only_one_token_parameter
+ get :remote_form
+ assert_equal 1, @response.body.scan(@token).size
+ end
+
+ def test_should_allow_get
+ get :index
+ assert_response :success
+ end
+
+ def test_should_allow_post_without_token_on_unsafe_action
+ post :unsafe
+ assert_response :success
+ end
+
+ def test_should_not_allow_html_post_without_token
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ assert_raises(ActionController::InvalidAuthenticityToken) { post :index, :format => :html }
end
- def test_should_render_button_to_with_token_tag
- get :show_button
- assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
- end
-
- def test_should_render_remote_form_with_only_one_token_parameter
- get :remote_form
- assert_equal 1, @response.body.scan(@token).size
- end
-
- def test_should_allow_get
- get :index
- assert_response :success
+ def test_should_not_allow_html_put_without_token
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ assert_raises(ActionController::InvalidAuthenticityToken) { put :index, :format => :html }
end
- def test_should_allow_post_without_token_on_unsafe_action
- post :unsafe
- assert_response :success
+ def test_should_not_allow_html_delete_without_token
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ assert_raises(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html }
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
+ def test_should_allow_api_formatted_post_without_token
+ assert_nothing_raised do
post :index, :format => 'xml'
end
end
def test_should_not_allow_api_formatted_put_without_token
- assert_raises(ActionController::InvalidAuthenticityToken) do
+ assert_nothing_raised do
put :index, :format => 'xml'
end
end
- def test_should_not_allow_api_formatted_delete_without_token
- assert_raises(ActionController::InvalidAuthenticityToken) do
+ def test_should_allow_api_formatted_delete_without_token
+ assert_nothing_raised do
delete :index, :format => 'xml'
end
end
@@ -174,16 +178,20 @@ def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_to
end
end
- def test_should_not_allow_xhr_post_without_token
- assert_raises(ActionController::InvalidAuthenticityToken) { xhr :post, :index }
+ def test_should_allow_xhr_post_without_token
+ assert_nothing_raised { xhr :post, :index }
+ end
+ def test_should_not_allow_xhr_post_with_html_without_token
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
+ assert_raise(ActionController::InvalidAuthenticityToken) { xhr :post, :index }
end
- def test_should_not_allow_xhr_put_without_token
- assert_raises(ActionController::InvalidAuthenticityToken) { xhr :put, :index }
+ def test_should_allow_xhr_put_without_token
+ assert_nothing_raised { xhr :put, :index }
end
- def test_should_not_allow_xhr_delete_without_token
- assert_raises(ActionController::InvalidAuthenticityToken) { xhr :delete, :index }
+ def test_should_allow_xhr_delete_without_token
+ assert_nothing_raised { xhr :delete, :index }
end
def test_should_allow_post_with_token
@@ -227,6 +235,7 @@ class RequestForgeryProtectionControllerTest < Test::Unit::TestCase
def setup
@controller = RequestForgeryProtectionController.new
@request = ActionController::TestRequest.new
+ @request.format = :html
@response = ActionController::TestResponse.new
class << @request.session
def session_id() '123' end
@@ -248,11 +257,11 @@ def session_id() '123' end
ActionController::Base.request_forgery_protection_token = :authenticity_token
end
- def test_should_raise_error_without_secret
- assert_raises ActionController::InvalidAuthenticityToken do
- get :index
- end
- end
+ # def test_should_raise_error_without_secret
+ # assert_raises ActionController::InvalidAuthenticityToken do
+ # get :index
+ # end
+ # end
end
class CsrfCookieMonsterControllerTest < Test::Unit::TestCase
@@ -304,10 +313,15 @@ def setup
@token = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA1'), 'abc', '123')
end
- def test_should_raise_correct_exception
- @request.session = {} # session(:off) doesn't appear to work with controller tests
- assert_raises(ActionController::InvalidAuthenticityToken) do
- post :index, :authenticity_token => @token
- end
- end
+ # TODO: Rewrite this test.
+ # This test was passing but for the wrong reason.
+ # Sessions aren't really being turned off, so an exception was raised
+ # because sessions weren't on - not because the token didn't match.
+ #
+ # def test_should_raise_correct_exception
+ # @request.session = {} # session(:off) doesn't appear to work with controller tests
+ # assert_raises(ActionController::InvalidAuthenticityToken) do
+ # post :index, :authenticity_token => @token, :format => :html
+ # end
+ # end
end
View
266 actionpack/test/controller/resources_test.rb
<
@@ -14,6 +14,8 @@ class LogosController < ResourcesController; end
class AccountsController < ResourcesController; end
class AdminController < ResourcesController; end
+class ProductsController < ResourcesController; end
+class ImagesController < ResourcesController; end
module Backoffice
class ProductsController < ResourcesController; end
@@ -776,6 +778,225 @@ def test_with_path_segment_path_prefix_requirements
end
end
+ def test_resource_has_only_show_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :only => :show
+ end
+
+ assert_resource_allowed_routes('products', {}, { :id => '1' }, :show, [:index, :new, :create, :edit, :update, :destroy])
+ assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :show, [:index, :new, :create, :edit, :update, :destroy])
+ end
+ end
+
+ def test_singleton_resource_has_only_show_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :account, :only => :show
+ end
+
+ assert_singleton_resource_allowed_routes('accounts', {}, :show, [:index, :new, :create, :edit, :update, :destroy])
+ assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :show, [:index, :new, :create, :edit, :update, :destroy])
+ end
+ end
+
+ def test_resource_does_not_have_destroy_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :except => :destroy
+ end
+
+ assert_resource_allowed_routes('products', {}, { :id => '1' }, [:index, :new, :create, :show, :edit, :update], :destroy)
+ assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, [:index, :new, :create, :show, :edit, :update], :destroy)
+ end
+ end
+
+ def test_singleton_resource_does_not_have_destroy_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :account, :except => :destroy
+ end
+
+ assert_singleton_resource_allowed_routes('accounts', {}, [:new, :create, :show, :edit, :update], :destroy)
+ assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, [:new, :create, :show, :edit, :update], :destroy)
+ end
+ end
+
+ def test_resource_has_only_create_action_and_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :only => :create
+ end
+
+ assert_resource_allowed_routes('products', {}, { :id => '1' }, :create, [:index, :new, :show, :edit, :update, :destroy])
+ assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :create, [:index, :new, :show, :edit, :update, :destroy])
+
+ assert_not_nil set.named_routes[:products]
+ end
+ end
+
+ def test_resource_has_only_update_action_and_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :only => :update
+ end
+
+ assert_resource_allowed_routes('products', {}, { :id => '1' }, :update, [:index, :new, :create, :show, :edit, :destroy])
+ assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :update, [:index, :new, :create, :show, :edit, :destroy])
+
+ assert_not_nil set.named_routes[:product]
+ end
+ end
+
+ def test_resource_has_only_destroy_action_and_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :only => :destroy
+ end
+
+ assert_resource_allowed_routes('products', {}, { :id => '1' }, :destroy, [:index, :new, :create, :show, :edit, :update])
+ assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :destroy, [:index, :new, :create, :show, :edit, :update])
+
+ assert_not_nil set.named_routes[:product]
+ end
+ end
+
+ def test_singleton_resource_has_only_create_action_and_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :account, :only => :create
+ end
+
+ assert_singleton_resource_allowed_routes('accounts', {}, :create, [:new, :show, :edit, :update, :destroy])
+ assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :create, [:new, :show, :edit, :update, :destroy])
+
+ assert_not_nil set.named_routes[:account]
+ end
+ end
+
+ def test_singleton_resource_has_only_update_action_and_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :account, :only => :update
+ end
+
+ assert_singleton_resource_allowed_routes('accounts', {}, :update, [:new, :create, :show, :edit, :destroy])
+ assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :update, [:new, :create, :show, :edit, :destroy])
+
+ assert_not_nil set.named_routes[:account]
+ end
+ end
+
+ def test_singleton_resource_has_only_destroy_action_and_named_route
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :account, :only => :destroy
+ end
+
+ assert_singleton_resource_allowed_routes('accounts', {}, :destroy, [:new, :create, :show, :edit, :update])
+ assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :destroy, [:new, :create, :show, :edit, :update])
+
+ assert_not_nil set.named_routes[:account]
+ end
+ end
+
+ def test_resource_has_only_collection_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :except => :all, :collection => { :sale => :get }
+ end
+
+ assert_resource_allowed_routes('products', {}, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+ assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+
+ assert_recognizes({ :controller => 'products', :action => 'sale' }, :path => 'products/sale', :method => :get)
+ assert_recognizes({ :controller => 'products', :action => 'sale', :format => 'xml' }, :path => 'products/sale.xml', :method => :get)
+ end
+ end
+
+ def test_resource_has_only_member_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :except => :all, :member => { :preview => :get }
+ end
+
+ assert_resource_allowed_routes('products', {}, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+ assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy])
+
+ assert_recognizes({ :controller => 'products', :action => 'preview', :id => '1' }, :path => 'products/1/preview', :method => :get)
+ assert_recognizes({ :controller => 'products', :action => 'preview', :id => '1', :format => 'xml' }, :path => 'products/1/preview.xml', :method => :get)
+ end
+ end
+
+ def test_singleton_resource_has_only_member_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resource :account, :except => :all, :member => { :signup => :get }
+ end
+
+ assert_singleton_resource_allowed_routes('accounts', {}, [], [:new, :create, :show, :edit, :update, :destroy])
+ assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, [], [:new, :create, :show, :edit, :update, :destroy])
+
+ assert_recognizes({ :controller => 'accounts', :action => 'signup' }, :path => 'account/signup', :method => :get)
+ assert_recognizes({ :controller => 'accounts', :action => 'signup', :format => 'xml' }, :path => 'account/signup.xml', :method => :get)
+ end
+ end
+
+ def test_nested_resource_inherits_only_show_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :only => :show do |product|
+ product.resources :images
+ end
+ end
+
+ assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+ assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+ end
+ end
+
+ def test_nested_resource_has_only_show_and_member_action
+ with_routing do |set|
+ set.draw do |map|
+ map.resources :products, :only => [:index, :show] do |product|
+ product.resources :images, :member => { :thumbnail => :get }, :only => :show
+ end
+ end
+
+ assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+ assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images')
+
+ assert_recognizes({ :controller => 'images', :action => 'thumbnail', :product_id => '1', :id => '2' }, :path => 'products/1/images/2/thumbnail', :method => :get)
+ assert_recognizes({ :controller => 'images', :action => 'thumbnail', :product_id => '1', :id => '2', :format => 'jpg' }, :path => 'products/1/images/2/thumbnail.jpg', :method => :get)
+ end
+ end