Permalink
Browse files

Merge branch 'master' into nested_has_many_through

Conflicts:
	activerecord/CHANGELOG
	activerecord/lib/active_record/associations/class_methods/join_dependency.rb
	activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
	activerecord/lib/active_record/associations/has_many_through_association.rb
  • Loading branch information...
2 parents 3a7f43c + 3074439 commit 9a98c766e045aebc2ef6d5b716936b73407f095d @jonleighton jonleighton committed Dec 12, 2010
Showing with 2,445 additions and 1,249 deletions.
  1. +3 −1 actionmailer/lib/action_mailer/base.rb
  2. +2 −2 actionmailer/lib/action_mailer/railtie.rb
  3. +4 −0 actionpack/CHANGELOG
  4. +1 −1 actionpack/actionpack.gemspec
  5. +3 −4 actionpack/lib/abstract_controller/base.rb
  6. +3 −6 actionpack/lib/abstract_controller/layouts.rb
  7. +41 −27 actionpack/lib/action_controller/caching/actions.rb
  8. +13 −7 actionpack/lib/action_controller/caching/pages.rb
  9. +2 −2 actionpack/lib/action_controller/railtie.rb
  10. +22 −15 actionpack/lib/action_dispatch/http/mime_type.rb
  11. +63 −16 actionpack/lib/action_dispatch/http/url.rb
  12. +15 −41 actionpack/lib/action_dispatch/routing/mapper.rb
  13. +110 −0 actionpack/lib/action_dispatch/routing/redirection.rb
  14. +18 −44 actionpack/lib/action_dispatch/routing/route_set.rb
  15. +1 −3 actionpack/lib/action_dispatch/testing/integration.rb
  16. +2 −2 actionpack/lib/action_view/base.rb
  17. +1 −1 actionpack/lib/action_view/helpers/form_tag_helper.rb
  18. +25 −7 actionpack/lib/action_view/helpers/translation_helper.rb
  19. +1 −1 actionpack/lib/action_view/lookup_context.rb
  20. +2 −4 actionpack/lib/action_view/path_set.rb
  21. +1 −1 actionpack/lib/action_view/renderer/template_renderer.rb
  22. +1 −1 actionpack/lib/action_view/template.rb
  23. +7 −1 actionpack/lib/action_view/template/handlers.rb
  24. +2 −2 actionpack/lib/action_view/template/resolver.rb
  25. +16 −0 actionpack/test/controller/caching_test.rb
  26. +9 −7 actionpack/test/controller/mime_responds_test.rb
  27. +297 −284 actionpack/test/controller/routing_test.rb
  28. +10 −18 actionpack/test/dispatch/mime_type_test.rb
  29. +18 −0 actionpack/test/dispatch/mount_test.rb
  30. +25 −0 actionpack/test/dispatch/request_test.rb
  31. +65 −0 actionpack/test/dispatch/routing_test.rb
  32. +0 −1 actionpack/test/fixtures/test/scoped_translation.erb
  33. +0 −1 actionpack/test/fixtures/test/translation.erb
  34. +1 −0 actionpack/test/fixtures/translations/templates/array.erb
  35. +1 −0 actionpack/test/fixtures/translations/templates/found.erb
  36. +1 −0 actionpack/test/fixtures/translations/templates/missing.erb
  37. +1 −1 actionpack/test/lib/controller/fake_models.rb
  38. +5 −0 actionpack/test/template/render_test.rb
  39. +45 −27 actionpack/test/template/translation_helper_test.rb
  40. +1 −1 activemodel/activemodel.gemspec
  41. +2 −2 activemodel/lib/active_model/attribute_methods.rb
  42. +7 −1 activemodel/lib/active_model/errors.rb
  43. +14 −14 activemodel/lib/active_model/lint.rb
  44. +1 −0 activemodel/lib/active_model/serializers/xml.rb
  45. +1 −1 activemodel/lib/active_model/validations/numericality.rb
  46. +5 −0 activemodel/test/cases/errors_test.rb
  47. +8 −7 activemodel/test/cases/serializeration/json_serialization_test.rb
  48. +17 −0 activemodel/test/cases/translation_test.rb
  49. +2 −2 activemodel/test/cases/validations_test.rb
  50. +6 −1 activerecord/CHANGELOG
  51. +41 −0 activerecord/lib/active_record/associations.rb
  52. +21 −13 activerecord/lib/active_record/associations/alias_tracker.rb
  53. +11 −11 activerecord/lib/active_record/associations/association_collection.rb
  54. +1 −1 activerecord/lib/active_record/associations/association_proxy.rb
  55. +10 −8 activerecord/lib/active_record/associations/class_methods/join_dependency.rb
  56. +24 −27 activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
  57. +1 −9 activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb
  58. +1 −1 activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb
  59. +9 −7 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
  60. +3 −2 activerecord/lib/active_record/associations/has_many_association.rb
  61. +2 −2 activerecord/lib/active_record/associations/has_many_through_association.rb
  62. +1 −1 activerecord/lib/active_record/associations/has_one_association.rb
  63. +1 −1 activerecord/lib/active_record/associations/has_one_through_association.rb
  64. +1 −1 activerecord/lib/active_record/attribute_methods.rb
  65. +9 −8 activerecord/lib/active_record/attribute_methods/before_type_cast.rb
  66. +2 −1 activerecord/lib/active_record/attribute_methods/read.rb
  67. +7 −7 activerecord/lib/active_record/autosave_association.rb
  68. +23 −25 activerecord/lib/active_record/base.rb
  69. +2 −2 activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
  70. +5 −3 activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
  71. +7 −6 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
  72. +2 −1 activerecord/lib/active_record/counter_cache.rb
  73. +4 −2 activerecord/lib/active_record/locking/optimistic.rb
  74. +43 −26 activerecord/lib/active_record/migration.rb
  75. +7 −33 activerecord/lib/active_record/observer.rb
  76. +8 −6 activerecord/lib/active_record/persistence.rb
  77. +35 −29 activerecord/lib/active_record/railties/databases.rake
  78. +1 −1 activerecord/lib/active_record/reflection.rb
  79. +22 −19 activerecord/lib/active_record/relation.rb
  80. +1 −1 activerecord/lib/active_record/relation/calculations.rb
  81. +2 −2 activerecord/lib/active_record/relation/finder_methods.rb
  82. +49 −42 activerecord/lib/active_record/relation/query_methods.rb
  83. +4 −3 activerecord/lib/active_record/relation/spawn_methods.rb
  84. +3 −3 activerecord/lib/active_record/schema.rb
  85. +4 −4 activerecord/lib/active_record/session_store.rb
  86. +19 −15 activerecord/lib/active_record/transactions.rb
  87. +1 −1 activerecord/lib/active_record/validations.rb
  88. +37 −0 activerecord/test/cases/associations/belongs_to_associations_test.rb
  89. +5 −0 activerecord/test/cases/associations/eager_test.rb
  90. +17 −0 activerecord/test/cases/associations/has_one_through_associations_test.rb
  91. +0 −10 activerecord/test/cases/associations/join_model_test.rb
  92. +9 −0 activerecord/test/cases/attribute_methods_test.rb
  93. +16 −0 activerecord/test/cases/base_test.rb
  94. +2 −2 activerecord/test/cases/connection_pool_test.rb
  95. +16 −0 activerecord/test/cases/lifecycle_test.rb
  96. +43 −53 activerecord/test/cases/migration_test.rb
  97. +1 −2 activerecord/test/cases/nested_attributes_test.rb
  98. +1 −1 activerecord/test/cases/reflection_test.rb
  99. +16 −0 activerecord/test/cases/relation_scoping_test.rb
  100. +139 −0 activerecord/test/cases/relation_test.rb
  101. +21 −0 activerecord/test/cases/xml_serialization_test.rb
  102. +9 −0 activerecord/test/cases/yaml_serialization_test.rb
  103. +2 −2 ...rations/interleaved/{pass_2/3_innocent_jointable.rb → pass_1/3_interleaved_innocent_jointable.rb}
  104. +2 −2 ...interleaved/{pass_3/1_people_have_last_names.rb → pass_2/1_interleaved_people_have_last_names.rb}
  105. +2 −2 ...rations/interleaved/{pass_3/3_innocent_jointable.rb → pass_2/3_interleaved_innocent_jointable.rb}
  106. +2 −2 .../{valid/1_people_have_last_names.rb → interleaved/pass_3/1_interleaved_people_have_last_names.rb}
  107. +0 −8 activerecord/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb
  108. +8 −0 activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb
  109. +2 −2 ...rations/interleaved/{pass_1/3_innocent_jointable.rb → pass_3/3_interleaved_innocent_jointable.rb}
  110. +2 −2 ...th_timestamps/20100101010101_people_have_last_names.rb → valid/1_valid_people_have_last_names.rb}
  111. +2 −2 ..._names.rb → valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb}
  112. +2 −2 ...{20100201010101_we_need_reminders.rb → 20100201010101_valid_with_timestamps_we_need_reminders.rb}
  113. +2 −2 ...0100301010101_innocent_jointable.rb → 20100301010101_valid_with_timestamps_innocent_jointable.rb}
  114. +5 −1 activerecord/test/models/pet.rb
  115. +3 −3 activesupport/lib/active_support/cache.rb
  116. +45 −47 activesupport/lib/active_support/callbacks.rb
  117. +12 −0 activesupport/lib/active_support/configurable.rb
  118. +0 −5 activesupport/lib/active_support/core_ext/object/try.rb
  119. +1 −1 activesupport/lib/active_support/dependencies.rb
  120. +0 −2 railties/guides/source/action_controller_overview.textile
  121. +9 −55 railties/guides/source/active_record_querying.textile
  122. +4 −0 railties/guides/source/association_basics.textile
  123. +2 −0 railties/guides/source/command_line.textile
  124. +237 −31 railties/guides/source/configuring.textile
  125. +1 −1 railties/guides/source/contributing_to_rails.textile
  126. +256 −1 railties/guides/source/generators.textile
  127. +1 −1 railties/guides/source/i18n.textile
  128. +74 −74 railties/guides/source/routing.textile
  129. +1 −1 railties/guides/source/security.textile
  130. +16 −10 railties/guides/source/testing.textile
  131. +4 −0 railties/lib/rails/application.rb
  132. +3 −16 railties/lib/rails/application/configuration.rb
  133. +0 −8 railties/lib/rails/application/railties.rb
  134. +1 −2 railties/lib/rails/commands.rb
  135. +11 −1 railties/lib/rails/engine.rb
  136. +10 −0 railties/lib/rails/engine/railties.rb
  137. +1 −1 railties/lib/rails/generators/actions.rb
  138. +1 −1 railties/lib/rails/generators/app_base.rb
  139. +1 −1 railties/lib/rails/generators/migration.rb
  140. +2 −2 railties/lib/rails/generators/rails/generator/generator_generator.rb
  141. +8 −0 railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
  142. +2 −0 railties/lib/rails/generators/rails/plugin_new/templates/test/integration/navigation_test.rb
  143. +0 −5 railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb
  144. +5 −0 railties/test/application/configuration_test.rb
  145. +39 −0 railties/test/generators/generator_generator_test.rb
  146. +15 −3 railties/test/generators/model_generator_test.rb
  147. +1 −1 railties/test/generators/plugin_new_generator_test.rb
  148. +9 −0 railties/test/generators/shared_generator_tests.rb
  149. +19 −0 railties/test/railties/engine_test.rb
View
4 actionmailer/lib/action_mailer/base.rb
@@ -290,7 +290,9 @@ module ActionMailer #:nodoc:
# * <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>.
+ # This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
+ # send password BASE64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
+ # information and a cryptographic Message Digest 5 algorithm to hash important information)
# * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
# and starts to use it.
#
View
4 actionmailer/lib/action_mailer/railtie.rb
@@ -19,8 +19,8 @@ class Railtie < Rails::Railtie
options.stylesheets_dir ||= paths["public/stylesheets"].first
# make sure readers methods get compiled
- options.asset_path ||= nil
- options.asset_host ||= nil
+ options.asset_path ||= app.config.asset_path
+ options.asset_host ||= app.config.asset_host
ActiveSupport.on_load(:action_mailer) do
include AbstractController::UrlFor
View
4 actionpack/CHANGELOG
@@ -1,5 +1,9 @@
*Rails 3.1.0 (unreleased)*
+* url_for and named url helpers now accept :subdomain and :domain as options [Josh Kalderimis]
+
+* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). [Josh Kalderimis]
+
* Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). [Piotr Sarnacki]
* Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options:
View
2 actionpack/actionpack.gemspec
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
s.add_dependency('activemodel', version)
s.add_dependency('rack-cache', '~> 0.5.3')
s.add_dependency('builder', '~> 3.0.0')
- s.add_dependency('i18n', '~> 0.4.2')
+ s.add_dependency('i18n', '~> 0.5.0')
s.add_dependency('rack', '~> 1.2.1')
s.add_dependency('rack-test', '~> 0.5.6')
s.add_dependency('rack-mount', '~> 0.6.13')
View
7 actionpack/lib/abstract_controller/base.rb
@@ -31,10 +31,9 @@ def abstract!
# A list of all internal methods for a controller. This finds the first
# abstract superclass of a controller, and gets a list of all public
# instance methods on that abstract class. Public instance methods of
- # a controller would normally be considered action methods, so we
- # are removing those methods on classes declared as abstract
- # (ActionController::Metal and ActionController::Base are defined
- # as abstract)
+ # a controller would normally be considered action methods, so methods
+ # declared on abstract classes are being removed.
+ # (ActionController::Metal and ActionController::Base are defined as abstract)
def internal_methods
controller = self
controller = controller.superclass until controller.abstract?
View
9 actionpack/lib/abstract_controller/layouts.rb
@@ -235,13 +235,10 @@ def _implied_layout_name
controller_path
end
- # Takes the specified layout and creates a _layout method to be called
- # by _default_layout
+ # Creates a _layout method to be called by _default_layout .
#
- # If there is no explicit layout specified:
- # If a layout is found in the view paths with the controller's
- # name, return that string. Otherwise, use the superclass'
- # layout (which might also be implied)
+ # If a layout is not explicitly mentioned then look for a layout with the controller's name.
+ # if nothing is found then try same procedure to find super class's layout.
def _write_layout_method
remove_possible_method(:_layout)
View
68 actionpack/lib/action_controller/caching/actions.rb
@@ -4,25 +4,26 @@ module ActionController #:nodoc:
module Caching
# Action caching is similar to page caching by the fact that the entire
# output of the response is cached, but unlike page caching, every
- # request still goes through the Action Pack. The key benefit
- # of this is that filters are run before the cache is served, which
- # allows for authentication and other restrictions on whether someone
- # is allowed to see the cache. Example:
+ # request still goes through Action Pack. The key benefit of this is
+ # that filters run before the cache is served, which allows for
+ # authentication and other restrictions on whether someone is allowed
+ # to execute such action. Example:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
+ #
# caches_page :public
- # caches_action :index, :show, :feed
+ # caches_action :index, :show
# 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 show and feed action are to be shielded behind the authenticate
- # filter, so we need to implement those as action caches.
+ # In this example, the +public+ action doesn't require authentication
+ # so it's possible to use the faster page caching. On the other hand
+ # +index+ and +show+ require authentication. They can still be cached,
+ # but we need action caching for them.
#
- # Action caching internally uses the fragment caching and an around
- # filter to do the job. The fragment cache is named according to both
- # the current host and the path. So a page that is accessed at
+ # Action caching uses fragment caching internally and an around
+ # filter to do the job. The fragment cache is named according to
+ # the host and path of the request. A page that is accessed at
# <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
# <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
# differentiate between <tt>david.example.com/lists/</tt> and
@@ -38,39 +39,52 @@ module Caching
# <tt>:action => 'list', :format => :xml</tt>.
#
# 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.
+ # <tt>:cache_path</tt> option. This will be passed directly to
+ # <tt>ActionCachePath.path_for</tt>. 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 <tt>:if</tt> (or <tt>:unless</tt>) to pass a
+ # proc that specifies when the action should be cached.
#
- # And you can also use :if (or :unless) to pass a Proc that
- # specifies when the action should be cached.
+ # Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
#
- # Finally, if you are using memcached, you can also pass :expires_in.
+ # The following example depicts some of the points made above:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
- # caches_page :public
+ #
+ # caches_page :public
+ #
# caches_action :index, :if => proc do |c|
# !c.request.format.json? # cache if is not a JSON request
# end
#
# caches_action :show, :cache_path => { :project => 1 },
# :expires_in => 1.hour
#
- # caches_action :feed, :cache_path => proc do |controller|
- # if controller.params[:user_id]
- # controller.send(:user_list_url,
- # controller.params[:user_id], controller.params[:id])
+ # caches_action :feed, :cache_path => proc do |c|
+ # if c.params[:user_id]
+ # c.send(:user_list_url,
+ # c.params[:user_id], c.params[:id])
# else
- # controller.send(:list_url, controller.params[:id])
+ # c.send(:list_url, c.params[:id])
# end
# end
# end
#
- # If you pass :layout => false, it will only cache your action
- # content. It is useful when your layout has dynamic information.
+ # If you pass <tt>:layout => false</tt>, it will only cache your action
+ # content. That's useful when your layout has dynamic information.
+ #
+ # Warning: If the format of the request is determined by the Accept HTTP
+ # header the Content-Type of the cached response could be wrong because
+ # no information about the MIME type is stored in the cache key. So, if
+ # you first ask for MIME type M in the Accept header, a cache entry is
+ # created, and then perform a second resquest to the same resource asking
+ # for a different MIME type, you'd get the content cached for M.
#
+ # The <tt>:format</tt> parameter is taken into account though. The safest
+ # way to cache by MIME type is to pass the format in the route.
module Actions
extend ActiveSupport::Concern
View
20 actionpack/lib/action_controller/caching/pages.rb
@@ -70,9 +70,9 @@ def expire_page(path)
# Manually cache the +content+ in the key determined by +path+. Example:
# cache_page "I'm the cached content", "/lists/show"
- def cache_page(content, path)
+ def cache_page(content, path, extension = nil)
return unless perform_caching
- path = page_cache_path(path)
+ path = page_cache_path(path, extension)
instrument_page_cache :write_page, path do
FileUtils.makedirs(File.dirname(path))
@@ -97,14 +97,16 @@ def caches_page(*actions)
end
private
- def page_cache_file(path)
+ def page_cache_file(path, extension)
name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
- name << page_cache_extension unless (name.split('/').last || name).include? '.'
+ unless (name.split('/').last || name).include? '.'
+ name << (extension || self.page_cache_extension)
+ end
return name
end
- def page_cache_path(path)
- page_cache_directory + page_cache_file(path)
+ def page_cache_path(path, extension = nil)
+ page_cache_directory + page_cache_file(path, extension)
end
def instrument_page_cache(name, path)
@@ -145,7 +147,11 @@ def cache_page(content = nil, options = nil)
request.path
end
- self.class.cache_page(content || response.body, path)
+ if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
+ extension = ".#{type_symbol}"
+ end
+
+ self.class.cache_page(content || response.body, path, extension)
end
end
View
4 actionpack/lib/action_controller/railtie.rb
@@ -27,8 +27,8 @@ class Railtie < Rails::Railtie
options.page_cache_directory ||= paths["public"].first
# make sure readers methods get compiled
- options.asset_path ||= nil
- options.asset_host ||= nil
+ options.asset_path ||= app.config.asset_path
+ options.asset_host ||= app.config.asset_host
ActiveSupport.on_load(:action_controller) do
include app.routes.mounted_helpers
View
37 actionpack/lib/action_dispatch/http/mime_type.rb
@@ -115,15 +115,20 @@ def parse(accept_header)
end
else
# keep track of creation order to keep the subsequent sort stable
- list = []
- accept_header.split(/,/).each_with_index do |header, index|
+ list, index = [], 0
+ accept_header.split(/,/).each do |header|
params, q = header.split(/;\s*q=/)
- if params
+ if params.present?
params.strip!
+
if params =~ TRAILING_STAR_REGEXP
- parse_data_with_trailing_star($1).each { |m| list << AcceptItem.new(index, m.to_s, q) }
+ parse_data_with_trailing_star($1).each do |m|
+ list << AcceptItem.new(index, m.to_s, q)
+ index += 1
+ end
else
- list << AcceptItem.new(index, params, q) unless params.empty?
+ list << AcceptItem.new(index, params, q)
+ index += 1
end
end
end
@@ -173,25 +178,27 @@ def parse(accept_header)
end
# input: 'text'
- # returend value: [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]
+ # returned value: [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]
#
# input: 'application'
- # returend value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM
+ # returned value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]
def parse_data_with_trailing_star(input)
- keys = Mime::LOOKUP.keys.select{|k| k.include?(input)}
- Mime::LOOKUP.values_at(*keys).uniq
+ Mime::SET.select { |m| m =~ input }
end
# This method is opposite of register method.
#
# Usage:
#
- # Mime::Type.unregister("text/x-mobile", :mobile)
- def unregister(string, symbol)
- EXTENSION_LOOKUP.delete(symbol.to_s)
- LOOKUP.delete(string)
- symbol = symbol.to_s.upcase.intern
- Mime.module_eval { remove_const(symbol) if const_defined?(symbol) }
+ # Mime::Type.unregister(:mobile)
+ def unregister(symbol)
+ symbol = symbol.to_s.upcase
+ mime = Mime.const_get(symbol)
+ Mime.instance_eval { remove_const(symbol) }
+
+ SET.delete_if { |v| v.eql?(mime) }
+ LOOKUP.delete_if { |k,v| v.eql?(mime) }
+ EXTENSION_LOOKUP.delete_if { |k,v| v.eql?(mime) }
end
end
View
79 actionpack/lib/action_dispatch/http/url.rb
@@ -4,26 +4,74 @@ module URL
mattr_accessor :tld_length
self.tld_length = 1
- def self.extract_domain(host, tld_length = @@tld_length)
- return nil unless named_host?(host)
+ class << self
+ def extract_domain(host, tld_length = @@tld_length)
+ return nil unless named_host?(host)
+ host.split('.').last(1 + tld_length).join('.')
+ end
- host.split('.').last(1 + tld_length).join('.')
- end
+ def extract_subdomains(host, tld_length = @@tld_length)
+ return [] unless named_host?(host)
+ parts = host.split('.')
+ parts[0..-(tld_length+2)]
+ end
- def self.extract_subdomains(host, tld_length = @@tld_length)
- return [] unless named_host?(host)
- parts = host.split('.')
- parts[0..-(tld_length+2)]
- end
+ def extract_subdomain(host, tld_length = @@tld_length)
+ extract_subdomains(host, tld_length).join('.')
+ end
- def self.extract_subdomain(host, tld_length = @@tld_length)
- extract_subdomains(host, tld_length).join('.')
- end
+ def url_for(options = {})
+ unless options[:host].present? || options[:only_path].present?
+ raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
+ end
- def self.named_host?(host)
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
- end
+ rewritten_url = ""
+
+ unless options[:only_path]
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << "://" unless rewritten_url.match("://")
+ rewritten_url << rewrite_authentication(options)
+ rewritten_url << host_or_subdomain_and_domain(options)
+ rewritten_url << ":#{options.delete(:port)}" if options[:port]
+ end
+
+ path = options.delete(:path) || ''
+
+ params = options[:params] || {}
+ params.reject! {|k,v| !v }
+
+ rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+ rewritten_url << "?#{params.to_query}" unless params.empty?
+ rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
+ rewritten_url
+ end
+
+ private
+
+ def named_host?(host)
+ !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
+ end
+ def rewrite_authentication(options)
+ if options[:user] && options[:password]
+ "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
+ else
+ ""
+ end
+ end
+
+ def host_or_subdomain_and_domain(options)
+ return options[:host] unless options[:subdomain] || options[:domain]
+
+ tld_length = options[:tld_length] || @@tld_length
+
+ host = ""
+ host << (options[:subdomain] || extract_subdomain(options[:host], tld_length))
+ host << "."
+ host << (options[:domain] || extract_domain(options[:host], tld_length))
+ host
+ end
+ end
# Returns the complete URL used for this request.
def url
@@ -116,7 +164,6 @@ def subdomains(tld_length = @@tld_length)
def subdomain(tld_length = @@tld_length)
subdomains(tld_length)
end
-
end
end
end
View
56 actionpack/lib/action_dispatch/routing/mapper.rb
@@ -2,6 +2,7 @@
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/object/blank'
require 'active_support/inflector'
+require 'action_dispatch/routing/redirection'
module ActionDispatch
module Routing
@@ -246,7 +247,11 @@ def initialize(set) #:nodoc:
#
# root :to => 'pages#main'
#
- # You should put the root route at the end of <tt>config/routes.rb</tt>.
+ # For options, see the +match+ method's documentation, as +root+ uses it internally.
+ #
+ # You should put the root route at the top of <tt>config/routes.rb</tt>,
+ # because this means it will be matched first. As this is the most popular route
+ # of most Rails applications, this is beneficial.
def root(options = {})
match '/', options.reverse_merge(:as => :root)
end
@@ -268,18 +273,18 @@ def match(path, options=nil)
# Mount a Rack-based application to be used within the application.
#
- # mount SomeRackApp, :at => "some_route"
+ # mount SomeRackApp, :at => "some_route"
#
# Alternatively:
#
- # mount(SomeRackApp => "some_route")
+ # mount(SomeRackApp => "some_route")
#
# All mounted applications come with routing helpers to access them.
# These are named after the class specified, so for the above example
# the helper is either +some_rack_app_path+ or +some_rack_app_url+.
# To customize this helper's name, use the +:as+ option:
#
- # mount(SomeRackApp => "some_route", :as => "exciting")
+ # mount(SomeRackApp => "some_route", :as => "exciting")
#
# This will generate the +exciting_path+ and +exciting_url+ helpers
# which can be used to navigate to this mounted app.
@@ -326,7 +331,7 @@ def app_name(app)
end
def define_generate_prefix(app, name)
- return unless app.respond_to?(:routes)
+ return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper)
_route = @set.named_routes.routes[name.to_sym]
_routes = @set
@@ -383,39 +388,6 @@ def delete(*args, &block)
map_method(:delete, *args, &block)
end
- # Redirect any path to another path:
- #
- # match "/stories" => redirect("/posts")
- def redirect(*args)
- options = args.last.is_a?(Hash) ? args.pop : {}
-
- path = args.shift || Proc.new
- path_proc = path.is_a?(Proc) ? path : proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
- status = options[:status] || 301
-
- lambda do |env|
- req = Request.new(env)
-
- params = [req.symbolized_path_parameters]
- params << req if path_proc.arity > 1
-
- uri = URI.parse(path_proc.call(*params))
- uri.scheme ||= req.scheme
- uri.host ||= req.host
- uri.port ||= req.port unless req.standard_port?
-
- body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
-
- headers = {
- 'Location' => uri.to_s,
- 'Content-Type' => 'text/html',
- 'Content-Length' => body.length.to_s
- }
-
- [ status, headers, [body] ]
- end
- end
-
private
def map_method(method, *args, &block)
options = args.extract_options!
@@ -595,7 +567,7 @@ def controller(controller, options={})
# admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
# === Supported options
#
- # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ all default to the name of the namespace.
+ # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ options all default to the name of the namespace.
#
# [:path]
# The path prefix for the routes.
@@ -636,7 +608,7 @@ def namespace(path, options = {})
:shallow_path => path, :shallow_prefix => path }.merge!(options)
scope(options) { yield }
end
-
+
# === Parameter Restriction
# Allows you to constrain the nested routes based on a set of rules.
# For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
@@ -647,7 +619,7 @@ def namespace(path, options = {})
#
# Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
# The +id+ parameter must match the constraint passed in for this example.
- #
+ #
# You may use this to also resrict other parameters:
#
# resources :posts do
@@ -1117,6 +1089,7 @@ def nested
end
end
+ # See ActionDispatch::Routing::Mapper::Scoping#namespace
def namespace(path, options = {})
if resource_scope?
nested { super }
@@ -1369,6 +1342,7 @@ def match(*args)
include Base
include HttpHelpers
+ include Redirection
include Scoping
include Resources
include Shorthand
View
110 actionpack/lib/action_dispatch/routing/redirection.rb
@@ -0,0 +1,110 @@
+require 'action_dispatch/http/request'
+
+module ActionDispatch
+ module Routing
+ module Redirection
+
+ # Redirect any path to another path:
+ #
+ # match "/stories" => redirect("/posts")
+ #
+ # You can also use interpolation in the supplied redirect argument:
+ #
+ # match 'docs/:article', :to => redirect('/wiki/%{article}')
+ #
+ # Alternatively you can use one of the other syntaxes:
+ #
+ # The block version of redirect allows for the easy encapsulation of any logic associated with
+ # the redirect in question. Either the params and request are supplied as arguments, or just
+ # params, depending of how many arguments your block accepts. A string is required as a
+ # return value.
+ #
+ # match 'jokes/:number', :to => redirect do |params, request|
+ # path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp")
+ # "http://#{request.host_with_port}/#{path}"
+ # end
+ #
+ # The options version of redirect allows you to supply only the parts of the url which need
+ # to change, it also supports interpolation of the path similar to the first example.
+ #
+ # match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
+ # match 'stores/:name(*all)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{all}')
+ #
+ # Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
+ # common redirect routes. The call method must accept two arguments, params and request, and return
+ # a string.
+ #
+ # match 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
+ #
+ def redirect(*args, &block)
+ options = args.last.is_a?(Hash) ? args.pop : {}
+ status = options.delete(:status) || 301
+
+ path = args.shift
+
+ path_proc = if path.is_a?(String)
+ proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
+ elsif options.any?
+ options_proc(options)
+ elsif path.respond_to?(:call)
+ proc { |params, request| path.call(params, request) }
+ elsif block
+ block
+ else
+ raise ArgumentError, "redirection argument not supported"
+ end
+
+ redirection_proc(status, path_proc)
+ end
+
+ private
+
+ def options_proc(options)
+ proc do |params, request|
+ path = if options[:path].nil?
+ request.path
+ elsif params.empty? || !options[:path].match(/%\{\w*\}/)
+ options.delete(:path)
+ else
+ (options.delete(:path) % params)
+ end
+
+ default_options = {
+ :protocol => request.protocol,
+ :host => request.host,
+ :port => request.optional_port,
+ :path => path,
+ :params => request.query_parameters
+ }
+
+ ActionDispatch::Http::URL.url_for(options.reverse_merge(default_options))
+ end
+ end
+
+ def redirection_proc(status, path_proc)
+ lambda do |env|
+ req = Request.new(env)
+
+ params = [req.symbolized_path_parameters]
+ params << req if path_proc.arity > 1
+
+ uri = URI.parse(path_proc.call(*params))
+ uri.scheme ||= req.scheme
+ uri.host ||= req.host
+ uri.port ||= req.port unless req.standard_port?
+
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
+
+ headers = {
+ 'Location' => uri.to_s,
+ 'Content-Type' => 'text/html',
+ 'Content-Length' => body.length.to_s
+ }
+
+ [ status, headers, [body] ]
+ end
+ end
+
+ end
+ end
+end
View
62 actionpack/lib/action_dispatch/routing/route_set.rb
@@ -442,12 +442,9 @@ def generate
raise_routing_error unless path
- params.reject! {|k,v| !v }
-
return [path, params.keys] if @extras
- path << "?#{params.to_query}" unless params.empty?
- path
+ [path, params]
rescue Rack::Mount::RoutingError
raise_routing_error
end
@@ -486,7 +483,7 @@ def generate(options, recall = {}, extras = false)
end
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
- :trailing_slash, :script_name, :anchor, :params, :only_path ]
+ :trailing_slash, :anchor, :params, :only_path, :script_name]
def _generate_prefix(options = {})
nil
@@ -498,29 +495,24 @@ def url_for(options)
handle_positional_args(options)
- rewritten_url = ""
-
- path_segments = options.delete(:_path_segments)
- unless options[:only_path]
- rewritten_url << (options[:protocol] || "http")
- rewritten_url << "://" unless rewritten_url.match("://")
- rewritten_url << rewrite_authentication(options)
- rewritten_url << host_from_options(options)
- rewritten_url << ":#{options.delete(:port)}" if options[:port]
- end
+ user, password = extract_authentication(options)
+ path_segments = options.delete(:_path_segments)
+ script_name = options.delete(:script_name)
- script_name = options.delete(:script_name)
path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?
- path << generate(path_options, path_segments || {})
- # ROUTES TODO: This can be called directly, so script_name should probably be set in the routes
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
- rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
+ path_addition, params = generate(path_options, path_segments || {})
+ path << path_addition
- rewritten_url
+ ActionDispatch::Http::URL.url_for(options.merge({
+ :path => path,
+ :params => params,
+ :user => user,
+ :password => password
+ }))
end
def call(env)
@@ -561,23 +553,12 @@ def recognize_path(path, environment = {})
private
- def host_from_options(options)
- computed_host = subdomain_and_domain(options) || options[:host]
- unless computed_host
- raise ArgumentError, "Missing host to link to! Please provide :host parameter or set default_url_options[:host]"
+ def extract_authentication(options)
+ if options[:user] && options[:password]
+ [options.delete(:user), options.delete(:password)]
+ else
+ nil
end
- computed_host
- end
-
- def subdomain_and_domain(options)
- return nil unless options[:subdomain] || options[:domain]
- tld_length = options[:tld_length] || ActionDispatch::Http::URL.tld_length
-
- host = ""
- host << (options[:subdomain] || ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length))
- host << "."
- host << (options[:domain] || ActionDispatch::Http::URL.extract_domain(options[:host], tld_length))
- host
end
def handle_positional_args(options)
@@ -590,13 +571,6 @@ def handle_positional_args(options)
options.merge!(Hash[args.zip(keys).map { |v, k| [k, v] }])
end
- def rewrite_authentication(options)
- if options[:user] && options[:password]
- "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
- else
- ""
- end
- end
end
end
end
View
4 actionpack/lib/action_dispatch/testing/integration.rb
@@ -235,9 +235,7 @@ def https?
# Set the host name to use in the next request.
#
# session.host! "www.example.com"
- def host!(name)
- @host = name
- end
+ alias :host! :host=
private
def _mock_session
View
4 actionpack/lib/action_view/base.rb
@@ -76,8 +76,8 @@ module ActionView #:nodoc:
#
# === Template caching
#
- # By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will
- # check the file's modification time and recompile it.
+ # By default, Rails will compile each template to a method in order to render it. When you alter a template,
+ # Rails will check the file's modification time and recompile it in development mode.
#
# == Builder
#
View
2 actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -39,7 +39,7 @@ module FormTagHelper
# form_tag('/upload', :multipart => true)
# # => <form action="/upload" method="post" enctype="multipart/form-data">
#
- # <%= form_tag('/posts')do -%>
+ # <%= form_tag('/posts') do -%>
# <div><%= submit_tag 'Save' %></div>
# <% end -%>
# # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
View
32 actionpack/lib/action_view/helpers/translation_helper.rb
@@ -1,13 +1,33 @@
require 'action_view/helpers/tag_helper'
+require 'i18n/exceptions'
+
+module I18n
+ class ExceptionHandler
+ include Module.new {
+ def call(exception, locale, key, options)
+ exception.is_a?(MissingTranslationData) ? super.html_safe : super
+ end
+ }
+ end
+end
module ActionView
# = Action View Translation Helpers
module Helpers
module TranslationHelper
# Delegates to I18n#translate but also performs three additional functions.
- # First, it'll catch MissingTranslationData exceptions and turn them into
- # inline spans that contains the missing key, such that you can see in a
- # view what is missing where.
+ #
+ # First, it'll pass the :rescue_format => :html option to I18n so that any caught
+ # MissingTranslationData exceptions will be turned into inline spans that
+ #
+ # * have a "translation-missing" class set,
+ # * contain the missing key as a title attribute and
+ # * a titleized version of the last key segment as a text.
+ #
+ # E.g. the value returned for a missing translation key :"blog.post.title" will be
+ # <span class="translation_missing" title="translation missing: blog.post.title">Title</span>.
+ # This way your views will display rather reasonableful strings but it will still
+ # be easy to spot missing translations.
#
# Second, it'll scope the key by the current partial if the key starts
# with a period. So if you call <tt>translate(".foo")</tt> from the
@@ -24,15 +44,13 @@ module TranslationHelper
# naming convention helps to identify translations that include HTML tags so that
# you know what kind of output to expect when you call translate in a template.
def translate(key, options = {})
- translation = I18n.translate(scope_key_by_partial(key), options.merge!(:raise => true))
+ options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
+ translation = I18n.translate(scope_key_by_partial(key), options)
if html_safe_translation_key?(key) && translation.respond_to?(:html_safe)
translation.html_safe
else
translation
end
- rescue I18n::MissingTranslationData => e
- keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
- content_tag('span', keys.join(', '), :class => 'translation_missing')
end
alias :t :translate
View
2 actionpack/lib/action_view/lookup_context.rb
@@ -109,7 +109,7 @@ def with_fallbacks
def args_for_lookup(name, prefix, partial, keys) #:nodoc:
name, prefix = normalize_name(name, prefix)
- [name, prefix, partial || false, @details, keys, details_key]
+ [name, prefix, partial || false, @details, details_key, keys]
end
# Support legacy foo.erb names even though we now ignore .erb
View
6 actionpack/lib/action_view/path_set.rb
@@ -10,10 +10,8 @@ def #{method}(*args)
METHOD
end
- def find(path, prefix = nil, partial = false, details = {}, keys = [], key = nil)
- template = find_all(path, prefix, partial, details, keys, key).first
- raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
- template
+ def find(*args)
+ find_all(*args).first || raise(MissingTemplate.new(self, "#{args[1]}/#{args[0]}", args[3], args[2]))
end
def find_all(*args)
View
2 actionpack/lib/action_view/renderer/template_renderer.rb
@@ -45,7 +45,7 @@ def determine_template(options) #:nodoc:
elsif options.key?(:file)
with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
elsif options.key?(:inline)
- handler = Template.handler_class_for_extension(options[:type] || "erb")
+ handler = Template.handler_for_extension(options[:type] || "erb")
Template.new(options[:inline], "inline template", handler, :locals => keys)
elsif options.key?(:template)
options[:template].respond_to?(:render) ?
View
2 actionpack/lib/action_view/template.rb
@@ -275,7 +275,7 @@ def compile(view, mod) #:nodoc:
end
arity = @handler.respond_to?(:arity) ? @handler.arity : @handler.method(:call).arity
- code = arity == 1 ? @handler.call(self) : @handler.call(self, view)
+ code = arity.abs == 1 ? @handler.call(self) : @handler.call(self, view)
# Make sure that the resulting String to be evalled is in the
# encoding of the code
View
8 actionpack/lib/action_view/template/handlers.rb
@@ -44,7 +44,13 @@ def register_default_template_handler(extension, klass)
end
def handler_class_for_extension(extension)
- (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers
+ ActiveSupport::Deprecation.warn "handler_class_for_extension is deprecated. " <<
+ "Please use handler_for_extension instead", caller
+ handler_for_extension(extension)
+ end
+
+ def handler_for_extension(extension)
+ registered_template_handler(extension) || @@default_template_handlers
end
end
end
View
4 actionpack/lib/action_view/template/resolver.rb
@@ -15,7 +15,7 @@ def clear_cache
end
# Normalizes the arguments and passes it on to find_template.
- def find_all(name, prefix=nil, partial=false, details={}, locals=[], key=nil)
+ def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details)
end
@@ -129,7 +129,7 @@ def mtime(p)
def extract_handler_and_format(path, default_formats)
pieces = File.basename(path).split(".")
pieces.shift
- handler = Template.handler_class_for_extension(pieces.pop)
+ handler = Template.handler_for_extension(pieces.pop)
format = pieces.last && Mime[pieces.last]
[handler, format]
end
View
16 actionpack/test/controller/caching_test.rb
@@ -16,6 +16,7 @@ class CachingController < ActionController::Base
class PageCachingTestController < CachingController
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
caches_page :found, :not_found
+ caches_page :about_me
def ok
@@ -47,6 +48,14 @@ def expire_custom_path
def trailing_slash
render :text => "Sneak attack"
end
+
+ def about_me
+ respond_to do |format|
+ format.html {render :text => 'I am html'}
+ format.xml {render :text => 'I am xml'}
+ end
+ end
+
end
class PageCachingTest < ActionController::TestCase
@@ -111,6 +120,13 @@ def test_should_cache_without_trailing_slash_on_url
assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
end
+ def test_should_obey_http_accept_attribute
+ @request.env['HTTP_ACCEPT'] = 'text/xml'
+ get :about_me
+ assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/about_me.xml")
+ assert_equal 'I am xml', @response.body
+ end
+
def test_should_cache_with_trailing_slash_on_url
@controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/'
assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
View
16 actionpack/test/controller/mime_responds_test.rb
@@ -201,8 +201,8 @@ def setup
def teardown
super
- Mime::Type.unregister('text/x-mobile', :iphone)
- Mime::Type.unregister('text/iphone', :mobile)
+ Mime::Type.unregister(:iphone)
+ Mime::Type.unregister(:mobile)
end
def test_html
@@ -622,12 +622,14 @@ class RespondWithControllerTest < ActionController::TestCase
def setup
super
@request.host = "www.example.com"
+ Mime::Type.register_alias('text/html', :iphone)
+ Mime::Type.register('text/x-mobile', :mobile)
end
def teardown
super
- Mime::Type.unregister('text/x-mobile', :iphone)
- Mime::Type.unregister('text/iphone', :mobile)
+ Mime::Type.unregister(:iphone)
+ Mime::Type.unregister(:mobile)
end
def test_using_resource
@@ -929,7 +931,8 @@ def test_render_json_object_responds_to_str_still_produce_json
@controller = RenderJsonRespondWithController.new
@request.accept = "application/json"
get :index, :format => :json
- assert_equal %Q{{"message":"boom","error":"RenderJsonTestException"}}, @response.body
+ assert_match(/"message":"boom"/, @response.body)
+ assert_match(/"error":"RenderJsonTestException"/, @response.body)
end
def test_no_double_render_is_raised
@@ -1020,8 +1023,7 @@ def setup
def teardown
super
- Mime::Type.unregister('text/x-mobile', :iphone)
- Mime::Type.unregister('text/iphone', :mobile)
+ Mime::Type.unregister(:iphone)
end
def test_missing_layout_renders_properly
View
581 actionpack/test/controller/routing_test.rb
@@ -12,8 +12,16 @@ def rescue_action(e) raise e end
ROUTING = ActionDispatch::Routing
+module RoutingTestHelpers
+ def url_for(set, options, recall = nil)
+ set.send(:url_for, options.merge(:only_path => true, :_path_segments => recall))
+ end
+end
+
# See RFC 3986, section 3.3 for allowed path characters.
class UriReservedCharactersRoutingTest < Test::Unit::TestCase
+ include RoutingTestHelpers
+
def setup
@set = ActionDispatch::Routing::RouteSet.new
@set.draw do
@@ -28,12 +36,13 @@ def setup
end
def test_route_generation_escapes_unsafe_path_characters
- @set.generate(:controller => "content", :action => "act#{@segment}ion", :variable => "variable", :additional => "foo")
assert_equal "/content/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2",
- @set.generate(:controller => "content",
- :action => "act#{@segment}ion",
- :variable => "var#{@segment}iable",
- :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"])
+ url_for(@set, {
+ :controller => "content",
+ :action => "act#{@segment}ion",
+ :variable => "var#{@segment}iable",
+ :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"]
+ })
end
def test_route_recognition_unescapes_path_components
@@ -45,10 +54,13 @@ def test_route_recognition_unescapes_path_components
end
def test_route_generation_allows_passing_non_string_values_to_generated_helper
- assert_equal "/content/action/variable/1/2", @set.generate(:controller => "content",
- :action => "action",
- :variable => "variable",
- :additional => [1, 2])
+ assert_equal "/content/action/variable/1/2",
+ url_for(@set, {
+ :controller => "content",
+ :action => "action",
+ :variable => "variable",
+ :additional => [1, 2]
+ })
end
end
@@ -68,6 +80,8 @@ def url_for(options)
end
class LegacyRouteSetTests < Test::Unit::TestCase
+ include RoutingTestHelpers
+
attr_reader :rs
def setup
@@ -81,18 +95,18 @@ def teardown
def test_default_setup
@rs.draw { match '/:controller(/:action(/:id))' }
assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content"))
- assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list"))
+ assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list"))
assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/content/show/10"))
assert_equal({:controller => "admin/user", :action => 'show', :id => '10'}, rs.recognize_path("/admin/user/show/10"))
- assert_equal '/admin/user/show/10', rs.generate(:controller => 'admin/user', :action => 'show', :id => 10)
+ assert_equal '/admin/user/show/10', url_for(rs, { :controller => 'admin/user', :action => 'show', :id => 10 })
- assert_equal '/admin/user/show', rs.generate({:action => 'show'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
- assert_equal '/admin/user/list/10', rs.generate({}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+ assert_equal '/admin/user/show', url_for(rs, { :action => 'show' }, { :controller => 'admin/user', :action => 'list', :id => '10' })
+ assert_equal '/admin/user/list/10', url_for(rs, {}, { :controller => 'admin/user', :action => 'list', :id => '10' })
- assert_equal '/admin/stuff', rs.generate({:controller => 'stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
- assert_equal '/stuff', rs.generate({:controller => '/stuff'}, {:controller => 'admin/user', :action => 'list', :id => '10'})
+ assert_equal '/admin/stuff', url_for(rs, { :controller => 'stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' })
+ assert_equal '/stuff', url_for(rs, { :controller => '/stuff' }, { :controller => 'admin/user', :action => 'list', :id => '10' })
end
def test_ignores_leading_slash
@@ -143,11 +157,14 @@ def test_route_with_regexp_for_controller
match ':controller/:admintoken(/:action(/:id))', :controller => /admin\/.+/
match '/:controller(/:action(/:id))'
end
+
assert_equal({:controller => "admin/user", :admintoken => "foo", :action => "index"},
rs.recognize_path("/admin/user/foo"))
- assert_equal({:controller => "content", :action => "foo"}, rs.recognize_path("/content/foo"))
- assert_equal '/admin/user/foo', rs.generate(:controller => "admin/user", :admintoken => "foo", :action => "index")
- assert_equal '/content/foo', rs.generate(:controller => "content", :action => "foo")
+ assert_equal({:controller => "content", :action => "foo"},
+ rs.recognize_path("/content/foo"))
+
+ assert_equal '/admin/user/foo', url_for(rs, { :controller => "admin/user", :admintoken => "foo", :action => "index" })
+ assert_equal '/content/foo', url_for(rs, { :controller => "content", :action => "foo" })
end
def test_route_with_regexp_and_captures_for_controller
@@ -169,46 +186,42 @@ def test_route_with_regexp_and_dot
end
# Without a file extension
assert_equal '/user/download/file',
- rs.generate(:controller => "user", :action => "download", :file => "file")
- assert_equal(
- {:controller => "user", :action => "download", :file => "file"},
+ url_for(rs, { :controller => "user", :action => "download", :file => "file" })
+
+ assert_equal({:controller => "user", :action => "download", :file => "file"},
rs.recognize_path("/user/download/file"))
# Now, let's try a file with an extension, really a dot (.)
assert_equal '/user/download/file.jpg',
- rs.generate(
- :controller => "user", :action => "download", :file => "file.jpg")
- assert_equal(
- {:controller => "user", :action => "download", :file => "file.jpg"},
+ url_for(rs, { :controller => "user", :action => "download", :file => "file.jpg" })
+
+ assert_equal({:controller => "user", :action => "download", :file => "file.jpg"},
rs.recognize_path("/user/download/file.jpg"))
end
def test_basic_named_route
rs.draw do
root :to => 'content#list', :as => 'home'
end
- x = setup_for_named_route
- assert_equal("http://test.host/",
- x.send(:home_url))
+ assert_equal("http://test.host/", setup_for_named_route.send(:home_url))
end
def test_named_route_with_option
rs.draw do
match 'page/:title' => 'content#show_page', :as => 'page'
end
- x = setup_for_named_route
+
assert_equal("http://test.host/page/new%20stuff",
- x.send(:page_url, :title => 'new stuff'))
+ setup_for_named_route.send(:page_url, :title => 'new stuff'))
end
def test_named_route_with_default
rs.draw do
match 'page/:title' => 'content#show_page', :title => 'AboutPage', :as => 'page'
end
- x = setup_for_named_route
- assert_equal("http://test.host/page/AboutRails",
- x.send(:page_url, :title => "AboutRails"))
+ assert_equal("http://test.host/page/AboutRails",
+ setup_for_named_route.send(:page_url, :title => "AboutRails"))
end
def test_named_route_with_path_prefix
@@ -217,9 +230,9 @@ def test_named_route_with_path_prefix
match 'page' => 'content#show_page', :as => 'page'
end
end
- x = setup_for_named_route
+
assert_equal("http://test.host/my/page",
- x.send(:page_url))
+ setup_for_named_route.send(:page_url))
end
def test_named_route_with_blank_path_prefix
@@ -228,27 +241,33 @@ def test_named_route_with_blank_path_prefix
match 'page' => 'content#show_page', :as => 'page'
end
end
- x = setup_for_named_route
+
assert_equal("http://test.host/page",
- x.send(:page_url))
+ setup_for_named_route.send(:page_url))
end
def test_named_route_with_nested_controller
rs.draw do
match 'admin/user' => 'admin/user#index', :as => "users"
end
- x = setup_for_named_route
+
assert_equal("http://test.host/admin/user",
- x.send(:users_url))
+ setup_for_named_route.send(:users_url))
end
def test_optimised_named_route_with_host
rs.draw do
match 'page' => 'content#show_page', :as => 'pages', :host => 'foo.com'
end
- x = setup_for_named_route
- x.expects(:url_for).with(:host => 'foo.com', :only_path => false, :controller => 'content', :action => 'show_page', :use_route => 'pages').once
- x.send(:pages_url)
+ routes = setup_for_named_route
+ routes.expects(:url_for).with({
+ :host => 'foo.com',
+ :only_path => false,
+ :controller => 'content',
+ :action => 'show_page',
+ :use_route => 'pages'
+ }).once
+ routes.send(:pages_url)
end
def setup_for_named_route
@@ -265,9 +284,9 @@ def test_named_route_root
rs.draw do
root :to => "hello#index"
end
- x = setup_for_named_route
- assert_equal("http://test.host/", x.send(:root_url))
- assert_equal("/", x.send(:root_path))
+ routes = setup_for_named_route
+ assert_equal("http://test.host/", routes.send(:root_url))
+ assert_equal("/", routes.send(:root_path))
end
def test_named_route_with_regexps
@@ -276,24 +295,19 @@ def test_named_route_with_regexps
:year => /\d+/, :month => /\d+/, :day => /\d+/
match ':controller/:action/:id'
end
- x = setup_for_named_route
- # assert_equal(
- # {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false},
- # x.send(:article_url, :title => 'hi')
- # )
- assert_equal(
- "http://test.host/page/2005/6/10/hi",
- x.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6)
- )
+
+ routes = setup_for_named_route
+
+ assert_equal "http://test.host/page/2005/6/10/hi",
+ routes.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6)
end
def test_changing_controller
@rs.draw { match ':controller/:action/:id' }
- assert_equal '/admin/stuff/show/10', rs.generate(
- {:controller => 'stuff', :action => 'show', :id => 10},
- {:controller => 'admin/user', :action => 'index'}
- )
+ assert_equal '/admin/stuff/show/10',
+ url_for(rs, {:controller => 'stuff', :action => 'show', :id => 10},
+ {:controller => 'admin/user', :action => 'index'})
end
def test_paths_escaped
@@ -319,8 +333,7 @@ def test_paths_slashes_unescaped_with_ordered_parameters
end
# No / to %2F in URI, only for query params.
- x = setup_for_named_route
- assert_equal("/file/hello/world", x.send(:path_path, ['hello', 'world']))
+ assert_equal("/file/hello/world", setup_for_named_route.send(:path_path, ['hello', 'world']))
end
def test_non_controllers_cannot_be_matched
@@ -334,23 +347,27 @@ def test_should_list_options_diff_when_routing_constraints_dont_match
rs.draw do
match 'post/:id' => 'post#show', :constraints => { :id => /\d+/ }, :as => 'post'
end
- assert_raise(ActionController::RoutingError) { rs.generate(:controller => 'post', :action => 'show', :bad_param => "foo", :use_route => "post") }
+ assert_raise(ActionController::RoutingError) do
+ url_for(rs, { :controller => 'post', :action => 'show', :bad_param => "foo", :use_route => "post" })
+ end
end
def test_dynamic_path_allowed
rs.draw do
match '*path' => 'content#show_file'
end
- assert_equal '/pages/boo', rs.generate(:controller => 'content', :action => 'show_file', :path => %w(pages boo))
+ assert_equal '/pages/boo',
+ url_for(rs, { :controller => 'content', :action => 'show_file', :path => %w(pages boo) })
end
def test_dynamic_recall_paths_allowed
rs.draw do
match '*path' => 'content#show_file'
end
- assert_equal '/pages/boo', rs.generate({}, :controller => 'content', :action => 'show_file', :path => %w(pages boo))
+ assert_equal '/pages/boo',
+ url_for(rs, {}, { :controller => 'content', :action => 'show_file', :path => %w(pages boo) })
end
def test_backwards
@@ -359,9 +376,9 @@ def test_backwards
match ':controller(/:action(/:id))'
end
- assert_equal '/page/20', rs.generate({:id => 20}, {:controller => 'pages', :action => 'show'})
- assert_equal '/page/20', rs.generate(:controller => 'pages', :id => 20, :action => 'show')
- assert_equal '/pages/boo', rs.generate(:controller => 'pages', :action => 'boo')
+ assert_equal '/page/20', url_for(rs, { :id => 20 }, { :controller => 'pages', :action => 'show' })
+ assert_equal '/page/20', url_for(rs, { :controller => 'pages', :id => 20, :action => 'show' })
+ assert_equal '/pages/boo', url_for(rs, { :controller => 'pages', :action => 'boo' })
end
def test_route_with_fixnum_default
@@ -370,10 +387,10 @@ def test_route_with_fixnum_default
match ':controller/:action/:id'
end
- assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page')
- assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => 1)
- assert_equal '/page', rs.generate(:controller => 'content', :action => 'show_page', :id => '1')
- assert_equal '/page/10', rs.generate(:controller => 'content', :action => 'show_page', :id => 10)
+ assert_equal '/page', url_for(rs, { :controller => 'content', :action => 'show_page' })
+ assert_equal '/page', url_for(rs, { :controller => 'content', :action => 'show_page', :id => 1 })
+ assert_equal '/page', url_for(rs, { :controller => 'content', :action => 'show_page', :id => '1' })
+ assert_equal '/page/10', url_for(rs, { :controller => 'content', :action => 'show_page', :id => 10 })
assert_equal({:controller => "content", :action => 'show_page', :id => 1 }, rs.recognize_path("/page"))
assert_equal({:controller => "content", :action => 'show_page', :id => '1'}, rs.recognize_path("/page/1"))
@@ -387,31 +404,31 @@ def test_route_with_text_default
match ':controller/:action/:id'
end
- assert_equal '/page/foo', rs.generate(:controller => 'content', :action => 'show_page', :id => 'foo')
- assert_equal({:controller => "content", :action => 'show_page', :id => 'foo'}, rs.recognize_path("/page/foo"))
+ assert_equal '/page/foo', url_for(rs, { :controller => 'content', :action => 'show_page', :id => 'foo' })
+ assert_equal({ :controller => "content", :action => 'show_page', :id => 'foo' }, rs.recognize_path("/page/foo"))
token = "\321\202\320\265\320\272\321\201\321\202" # 'text' in russian
token.force_encoding(Encoding::BINARY) if token.respond_to?(:force_encoding)
escaped_token = CGI::escape(token)
- assert_equal '/page/' + escaped_token, rs.generate(:controller => 'content', :action => 'show_page', :id => token)
- assert_equal({:controller => "content", :action => 'show_page', :id => token}, rs.recognize_path("/page/#{escaped_token}"))
+ assert_equal '/page/' + escaped_token, url_for(rs, { :controller => 'content', :action => 'show_page', :id => token })
+ assert_equal({ :controller => "content", :action => 'show_page', :id => token }, rs.recognize_path("/page/#{escaped_token}"))
end
def test_action_expiry
@rs.draw { match ':controller(/:action(/:id))' }
- assert_equal '/content', rs.generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
+ assert_equal '/content', url_for(rs, { :controller => 'content' }, { :controller => 'content', :action => 'show' })
end
def test_requirement_should_prevent_optional_id
rs.draw do
match 'post/:id' => 'post#show', :constraints => {:id => /\d+/}, :as => 'post'
end
- assert_equal '/post/10', rs.generate(:controller => 'post', :action => 'show', :id => 10)
+ assert_equal '/post/10', url_for(rs, { :controller => 'post', :action => 'show', :id => 10 })
assert_raise ActionController::RoutingError do
- rs.generate(:controller => 'post', :action => 'show')
+ url_for(rs, { :controller => 'post', :action => 'show' })
end
end
@@ -424,12 +441,10 @@ def test_both_requirement_and_optional
match ':controller/:action/:id'
end
- assert_equal '/test', rs.generate(:controller => 'post', :action => 'show')
- assert_equal '/test', rs.generate(:controller => 'post', :action => 'show', :year => nil)
+ assert_equal '/test', url_for(rs, { :controller => 'post', :action => 'show' })
+ assert_equal '/test', url_for(rs, { :controller => 'post', :action => 'show', :year => nil })
- x = setup_for_named_route
- assert_equal("http://test.host/test",
- x.send(:blog_url))
+ assert_equal("http://test.host/test", setup_for_named_route.send(:blog_url))
end
def test_set_to_nil_forgets
@@ -439,42 +454,40 @@ def test_set_to_nil_forgets
end
assert_equal '/pages/2005',
- rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005)
+ url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005 })
assert_equal '/pages/2005/6',
- rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6)
+ url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005, :month => 6 })
assert_equal '/pages/2005/6/12',
- rs.generate(:controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12)
+ url_for(rs, { :controller => 'content', :action => 'list_pages', :year => 2005, :month => 6, :day => 12 })
assert_equal '/pages/2005/6/4',
- rs.generate({:day => 4}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+ url_for(rs, { :day => 4 }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' })
assert_equal '/pages/2005/6',
- rs.generate({:day => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+ url_for(rs, { :day => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' })
assert_equal '/pages/2005',
- rs.generate({:day => nil, :month => nil}, {:controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12'})
+ url_for(rs, { :day => nil, :month => nil }, { :controller => 'content', :action => 'list_pages', :year => '2005', :month => '6', :day => '12' })
end
def test_root_url_generation_with_controller_and_action
rs.draw do
root :to => "content#index"
end
- assert_equal '/', rs.generate(:controller => 'content', :action => 'index')
- assert_equal '/', rs.generate(:controller => 'content')
+ assert_equal '/', url_for(rs, { :controller => 'content', :action => 'index' })
+ assert_equal '/', url_for(rs, { :controller => 'content' })
end
def test_named_root_url_generation_with_controller_and_action
rs.draw do
root :to => "content#index", :as => 'home'
end
- assert_equal '/', rs.generate(:controller => 'content', :action => 'index')
- assert_equal '/', rs.generate(:controller => 'content')
+ assert_equal '/', url_for(rs, { :controller => 'content', :action => 'index' })
+ assert_equal '/', url_for(rs, { :controller => 'content' })
- x = setup_for_named_route
- assert_equal("http://test.host/",
- x.send(:home_url))
+ assert_equal("http://test.host/", setup_for_named_route.send(:home_url))
end
def test_named_route_method
@@ -483,8 +496,8 @@ def test_named_route_method
match ':controller(/:action(/:id))'
end
- assert_equal '/categories', rs.generate(:controller => 'content', :action => 'categories')
- assert_equal '/content/hi', rs.generate({:controller => 'content', :action => 'hi'})
+ assert_equal '/categories', url_for(rs, { :controller => 'content', :action => 'categories' })
+ assert_equal '/content/hi', url_for(rs, { :controller => 'content', :action => 'hi' })
end
def test_named_routes_array
@@ -499,7 +512,12 @@ def test_nil_defaults
match ':controller/:action/:id'
end
- assert_equal '/journal', rs.generate(:controller => 'content', :action => 'list_journal', :date => nil, :user_id => nil)
+ assert_equal '/journal', url_for(rs, {
+ :controller => 'content',
+ :action => 'list_journal',
+ :date => nil,
+ :user_id => nil
+ })
end
def setup_request_method_routes_for(method)
@@ -564,19 +582,18 @@ def test_subpath_generated
match '/posts/new/:action' => 'subpath_books'
end
- assert_equal "/books/7/edit", rs.generate(:controller => "subpath_books", :id => 7, :action => "edit")
- assert_equal "/items/15/complete", rs.generate(:controller => "subpath_books", :id => 15, :action => "complete")
- assert_equal "/posts/new/preview", rs.generate(:controller => "subpath_books", :action => "preview")
+ assert_equal "/books/7/edit", url_for(rs, { :controller => "subpath_books", :id => 7, :action => "edit" })
+ assert_equal "/items/15/complete", url_for(rs, { :controller => "subpath_books", :id => 15, :action => "complete" })
+ assert_equal "/posts/new/preview", url_for(rs, { :controller => "subpath_books", :action => "preview" })
end
def test_failed_constraints_raises_exception_with_violated_constraints
rs.draw do
match 'foos/:id' => 'foos#show', :as => 'foo_with_requirement', :constraints => { :id => /\d+/ }
end
- x = setup_for_named_route
assert_raise(ActionController::RoutingError) do
- x.send(:foo_with_requirement_url, "I am Against the constraints")
+ setup_for_named_route.send(:foo_with_requirement_url, "I am Against the constraints")
end
end
@@ -606,11 +623,12 @@ def test_routes_changed_correctly_after_clear
assert_not_nil hash
assert_equal %w(cc ac), [hash[:controller], hash[:action]]
-
end
end
class RouteSetTest < ActiveSupport::TestCase
+ include RoutingTestHelpers
+
def set
@set ||= ROUTING::RouteSet.new
end
@@ -657,7 +675,8 @@ def test_generate_not_first
match ':controller/:action/:id.:format'
match ':controller/:action/:id'
end
- assert_equal "/foo/bar/15?this=hello", set.generate(:controller => "foo", :action => "bar", :id => 15, :this => "hello")
+ assert_equal "/foo/bar/15?this=hello",
+ url_for(set, { :controller => "foo", :action => "bar", :id => 15, :this => "hello" })
end
def test_extra_keys_not_first
@@ -742,7 +761,7 @@ def test_named_route_url_method
assert_equal "http://test.host/admin/users", controller.send(:users_url)
assert_equal '/admin/users', controller.send(:users_path)
- assert_equal '/admin/users', set.generate(controller.send(:hash_for_users_url), {:controller => 'users', :action => 'index'})
+ assert_equal '/admin/users', url_for(set, controller.send(:hash_for_users_url), { :controller => 'users', :action => 'index' })
end
def test_named_route_url_method_with_anchor
@@ -813,8 +832,8 @@ def test_draw_default_route
assert_equal 1, set.routes.size
- assert_equal '/users/show/10', set.generate(:controller => 'users', :action => 'show', :id => 10)
- assert_equal '/users/index/10', set.generate(:controller => 'users', :id => 10)
+ assert_equal '/users/show/10', url_for(set, { :controller => 'users', :action => 'show', :id => 10 })
+ assert_equal '/users/index/10', url_for(set, { :controller => 'users', :id => 10 })
assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10'))
assert_equal({:controller => 'users', :action => 'index', :id => '10'}, set.recognize_path('/users/index/10/'))
@@ -992,7 +1011,7 @@ def test_generate_with_default_action
match "/people/list", :controller => "people", :action => "list"