aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorrick <technoweenie@gmail.com>2008-08-26 11:53:33 -0700
committerrick <technoweenie@gmail.com>2008-08-26 11:53:33 -0700
commit0aef9d1a2651fa0acd2adcd2de308eeb0ec8cdd2 (patch)
tree1a782151632dd80c8a18c3960536bdf8643debe3 /actionpack
parent0a6d75dedd79407376aae1f01302164dfd3e44b6 (diff)
parent229eedfda87a7706dbb5e3e51af8707b3adae375 (diff)
downloadrails-0aef9d1a2651fa0acd2adcd2de308eeb0ec8cdd2.tar.gz
rails-0aef9d1a2651fa0acd2adcd2de308eeb0ec8cdd2.tar.bz2
rails-0aef9d1a2651fa0acd2adcd2de308eeb0ec8cdd2.zip
Merge branch 'master' of git@github.com:rails/rails
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG30
-rw-r--r--actionpack/README18
-rw-r--r--[-rwxr-xr-x]actionpack/lib/action_controller.rb15
-rw-r--r--actionpack/lib/action_controller/assertions/response_assertions.rb4
-rw-r--r--actionpack/lib/action_controller/assertions/routing_assertions.rb44
-rw-r--r--actionpack/lib/action_controller/assertions/selector_assertions.rb32
-rw-r--r--[-rwxr-xr-x]actionpack/lib/action_controller/base.rb105
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb6
-rw-r--r--actionpack/lib/action_controller/cgi_process.rb45
-rw-r--r--actionpack/lib/action_controller/cookies.rb10
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb24
-rw-r--r--actionpack/lib/action_controller/filters.rb19
-rw-r--r--actionpack/lib/action_controller/headers.rb30
-rw-r--r--actionpack/lib/action_controller/integration.rb100
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb16
-rw-r--r--actionpack/lib/action_controller/rack_process.rb156
-rw-r--r--[-rwxr-xr-x]actionpack/lib/action_controller/request.rb204
-rw-r--r--actionpack/lib/action_controller/request_forgery_protection.rb2
-rw-r--r--[-rwxr-xr-x]actionpack/lib/action_controller/request_profiler.rb0
-rw-r--r--actionpack/lib/action_controller/rescue.rb30
-rw-r--r--actionpack/lib/action_controller/resources.rb63
-rw-r--r--[-rwxr-xr-x]actionpack/lib/action_controller/response.rb151
-rw-r--r--actionpack/lib/action_controller/routing.rb58
-rw-r--r--actionpack/lib/action_controller/routing/builder.rb44
-rw-r--r--actionpack/lib/action_controller/routing/optimisations.rb25
-rw-r--r--actionpack/lib/action_controller/routing/recognition_optimisation.rb40
-rw-r--r--actionpack/lib/action_controller/routing/route.rb309
-rw-r--r--actionpack/lib/action_controller/routing/route_set.rb7
-rw-r--r--actionpack/lib/action_controller/routing/routing_ext.rb1
-rw-r--r--actionpack/lib/action_controller/routing/segments.rb37
-rw-r--r--actionpack/lib/action_controller/session/cookie_store.rb2
-rwxr-xr-x[-rw-r--r--]actionpack/lib/action_controller/session/drb_server.rb14
-rw-r--r--actionpack/lib/action_controller/streaming.rb23
-rw-r--r--actionpack/lib/action_controller/templates/rescues/diagnostics.erb4
-rw-r--r--actionpack/lib/action_controller/templates/rescues/template_error.erb4
-rw-r--r--actionpack/lib/action_controller/test_case.rb63
-rw-r--r--actionpack/lib/action_controller/test_process.rb91
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb103
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb4
-rw-r--r--actionpack/lib/action_view.rb20
-rw-r--r--actionpack/lib/action_view/base.rb111
-rw-r--r--actionpack/lib/action_view/helpers.rb39
-rw-r--r--actionpack/lib/action_view/helpers/active_record_helper.rb71
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb96
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb24
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb17
-rw-r--r--[-rwxr-xr-x]actionpack/lib/action_view/helpers/date_helper.rb764
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb33
-rw-r--r--actionpack/lib/action_view/helpers/form_country_helper.rb92
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb73
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb75
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb21
-rw-r--r--actionpack/lib/action_view/helpers/javascripts/prototype.js304
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb253
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb416
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb87
-rw-r--r--actionpack/lib/action_view/helpers/scriptaculous_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb206
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb22
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb99
-rw-r--r--actionpack/lib/action_view/locale/en-US.yml91
-rw-r--r--actionpack/lib/action_view/partials.rb140
-rw-r--r--actionpack/lib/action_view/paths.rb87
-rw-r--r--actionpack/lib/action_view/renderable.rb83
-rw-r--r--actionpack/lib/action_view/renderable_partial.rb2
-rw-r--r--actionpack/lib/action_view/template.rb19
-rw-r--r--actionpack/lib/action_view/template_error.rb2
-rw-r--r--actionpack/lib/action_view/template_handler.rb27
-rw-r--r--actionpack/lib/action_view/template_handlers.rb1
-rw-r--r--actionpack/lib/action_view/template_handlers/builder.rb10
-rw-r--r--actionpack/lib/action_view/template_handlers/compilable.rb20
-rw-r--r--actionpack/lib/action_view/template_handlers/erb.rb11
-rw-r--r--actionpack/lib/action_view/template_handlers/rjs.rb11
-rw-r--r--actionpack/lib/action_view/test_case.rb4
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/activerecord/render_partial_with_record_identification_test.rb10
-rw-r--r--actionpack/test/controller/assert_select_test.rb33
-rw-r--r--actionpack/test/controller/base_test.rb20
-rw-r--r--actionpack/test/controller/caching_test.rb107
-rw-r--r--[-rwxr-xr-x]actionpack/test/controller/cgi_test.rb52
-rw-r--r--actionpack/test/controller/content_type_test.rb28
-rw-r--r--actionpack/test/controller/cookie_test.rb2
-rw-r--r--actionpack/test/controller/integration_test.rb90
-rw-r--r--actionpack/test/controller/layout_test.rb31
-rw-r--r--actionpack/test/controller/mime_responds_test.rb62
-rw-r--r--actionpack/test/controller/new_render_test.rb18
-rw-r--r--actionpack/test/controller/polymorphic_routes_test.rb22
-rw-r--r--actionpack/test/controller/rack_test.rb125
-rw-r--r--[-rwxr-xr-x]actionpack/test/controller/redirect_test.rb109
-rw-r--r--actionpack/test/controller/render_test.rb198
-rw-r--r--actionpack/test/controller/request_test.rb219
-rw-r--r--actionpack/test/controller/rescue_test.rb9
-rw-r--r--actionpack/test/controller/resources_test.rb20
-rw-r--r--actionpack/test/controller/routing_test.rb178
-rw-r--r--[-rwxr-xr-x]actionpack/test/controller/session/cookie_store_test.rb0
-rw-r--r--actionpack/test/controller/test_test.rb25
-rw-r--r--actionpack/test/controller/url_rewriter_test.rb55
-rw-r--r--actionpack/test/controller/view_paths_test.rb10
-rw-r--r--actionpack/test/fixtures/_top_level_partial.html.erb1
-rw-r--r--actionpack/test/fixtures/_top_level_partial_only.erb1
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb3
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs6
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder5
-rw-r--r--actionpack/test/fixtures/test/_counter.html.erb1
-rw-r--r--actionpack/test/fixtures/test/_layout_for_block_with_args.html.erb3
-rw-r--r--actionpack/test/fixtures/test/implicit_content_type.atom.builder2
-rw-r--r--actionpack/test/fixtures/test/using_layout_around_block_with_args.html.erb1
-rw-r--r--actionpack/test/template/active_record_helper_i18n_test.rb46
-rw-r--r--actionpack/test/template/active_record_helper_test.rb48
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb56
-rw-r--r--actionpack/test/template/atom_feed_helper_test.rb37
-rw-r--r--actionpack/test/template/compiled_templates_test.rb47
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb113
-rw-r--r--[-rwxr-xr-x]actionpack/test/template/date_helper_test.rb437
-rw-r--r--actionpack/test/template/form_country_helper_test.rb1549
-rw-r--r--actionpack/test/template/form_helper_test.rb116
-rw-r--r--actionpack/test/template/form_options_helper_test.rb1539
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb6
-rw-r--r--actionpack/test/template/javascript_helper_test.rb6
-rw-r--r--actionpack/test/template/number_helper_i18n_test.rb54
-rw-r--r--actionpack/test/template/number_helper_test.rb56
-rw-r--r--actionpack/test/template/prototype_helper_test.rb26
-rw-r--r--actionpack/test/template/render_test.rb72
-rw-r--r--actionpack/test/template/sanitize_helper_test.rb6
-rw-r--r--actionpack/test/template/text_helper_test.rb51
-rw-r--r--actionpack/test/template/translation_helper_test.rb28
-rw-r--r--actionpack/test/template/url_helper_test.rb22
130 files changed, 6608 insertions, 4445 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 5b7bfe9c30..be490093ac 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,35 @@
*Edge*
+* Allow polymorphic_url helper to take url options. #880 [Tarmo TĂ€nav]
+
+* Switched integration test runner to use Rack processor instead of CGI [Josh Peek]
+
+* Made AbstractRequest.if_modified_sense return nil if the header could not be parsed [Jamis Buck]
+
+* Added back ActionController::Base.allow_concurrency flag [Josh Peek]
+
+* AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root [Josh Peek]
+
+* Update Prototype to 1.6.0.2 #599 [Patrick Joyce]
+
+* Conditional GET utility methods. [Jeremy Kemper]
+ response.last_modified = @post.updated_at
+ response.etag = [:admin, @post, current_user]
+
+ if request.fresh?(response)
+ head :not_modified
+ else
+ # render ...
+ end
+
+* All 2xx requests are considered successful [Josh Peek]
+
+* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [DHH]
+
+* Removed config.action_view.cache_template_loading, use config.cache_classes instead [Josh Peek]
+
+* Get buffer for fragment cache from template's @output_buffer [Josh Peek]
+
* Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results in an expensive disk operation that could be cached [Josh Peek]
* Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class [Josh Peek]
diff --git a/actionpack/README b/actionpack/README
index 2746c3cc43..6090089bb9 100644
--- a/actionpack/README
+++ b/actionpack/README
@@ -31,7 +31,7 @@ http://www.rubyonrails.org.
A short rundown of the major features:
* Actions grouped in controller as methods instead of separate command objects
- and can therefore share helper methods.
+ and can therefore share helper methods
BlogController < ActionController::Base
def show
@@ -168,7 +168,7 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionController/Base.html]
-* Javascript and Ajax integration.
+* Javascript and Ajax integration
link_to_function "Greeting", "alert('Hello world!')"
link_to_remote "Delete this post", :update => "posts",
@@ -177,7 +177,7 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html]
-* Pagination for navigating lists of results.
+* Pagination for navigating lists of results
# controller
def list
@@ -192,15 +192,9 @@ A short rundown of the major features:
{Learn more}[link:classes/ActionController/Pagination.html]
-* Easy testing of both controller and template result through TestRequest/Response
-
- class LoginControllerTest < Test::Unit::TestCase
- def setup
- @controller = LoginController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
+* Easy testing of both controller and rendered template through ActionController::TestCase
+ class LoginControllerTest < ActionController::TestCase
def test_failing_authenticate
process :authenticate, :user_name => "nop", :password => ""
assert flash.has_key?(:alert)
@@ -208,7 +202,7 @@ A short rundown of the major features:
end
end
- {Learn more}[link:classes/ActionController/TestRequest.html]
+ {Learn more}[link:classes/ActionController/TestCase.html]
* Automated benchmarking and integrated logging
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 3c4a339d50..e58071d4af 100755..100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -21,16 +21,13 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-$:.unshift(File.dirname(__FILE__)) unless
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
-
-unless defined?(ActiveSupport)
- begin
- $:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
+begin
+ require 'active_support'
+rescue LoadError
+ activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+ if File.directory?(activesupport_path)
+ $:.unshift activesupport_path
require 'active_support'
- rescue LoadError
- require 'rubygems'
- gem 'activesupport'
end
end
diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb
index 765225ae24..e2e8bbdc71 100644
--- a/actionpack/lib/action_controller/assertions/response_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/response_assertions.rb
@@ -87,11 +87,11 @@ module ActionController
#
def assert_template(expected = nil, message=nil)
clean_backtrace do
- rendered = @response.rendered_template
+ rendered = @response.rendered_template.to_s
msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered)
assert_block(msg) do
if expected.nil?
- @response.rendered_template.nil?
+ @response.rendered_template.blank?
else
rendered.to_s.match(expected)
end
diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/assertions/routing_assertions.rb
index 491b72d586..312b4e228b 100644
--- a/actionpack/lib/action_controller/assertions/routing_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/routing_assertions.rb
@@ -2,7 +2,7 @@ module ActionController
module Assertions
# Suite of assertions to test routes generated by Rails and the handling of requests made to them.
module RoutingAssertions
- # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
+ # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
# match +path+. Basically, it asserts that Rails recognizes the route given by +expected_options+.
#
# Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
@@ -14,16 +14,16 @@ module ActionController
#
# You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
# to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
- # extras argument, appending the query string on the path directly will not work. For example:
+ # extras argument, appending the query string on the path directly will not work. For example:
#
# # assert that a path of '/items/list/1?view=print' returns the correct options
- # assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" })
+ # assert_recognizes({:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" })
#
- # The +message+ parameter allows you to pass in an error message that is displayed upon failure.
+ # The +message+ parameter allows you to pass in an error message that is displayed upon failure.
#
# ==== Examples
# # Check the default route (i.e., the index action)
- # assert_recognizes({:controller => 'items', :action => 'index'}, 'items')
+ # assert_recognizes({:controller => 'items', :action => 'index'}, 'items')
#
# # Test a specific action
# assert_recognizes({:controller => 'items', :action => 'list'}, 'items/list')
@@ -44,16 +44,16 @@ module ActionController
request_method = nil
end
- clean_backtrace do
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+ clean_backtrace do
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
request = recognized_request_for(path, request_method)
-
+
expected_options = expected_options.clone
extras.each_key { |key| expected_options.delete key } unless extras.nil?
-
+
expected_options.stringify_keys!
routing_diff = expected_options.diff(request.path_parameters)
- msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
+ msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
assert_block(msg) { request.path_parameters == expected_options }
end
@@ -64,7 +64,7 @@ module ActionController
# a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
#
# The +defaults+ parameter is unused.
- #
+ #
# ==== Examples
# # Asserts that the default action is generated for a route with no action
# assert_generates("/items", :controller => "items", :action => "index")
@@ -73,34 +73,34 @@ module ActionController
# assert_generates("/items/list", :controller => "items", :action => "list")
#
# # Tests the generation of a route with a parameter
- # assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" })
+ # assert_generates("/items/list/1", { :controller => "items", :action => "list", :id => "1" })
#
# # Asserts that the generated route gives us our custom route
# assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" }
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
- clean_backtrace do
+ clean_backtrace do
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
# Load routes.rb if it hasn't been loaded.
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
-
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
+
generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
found_extras = options.reject {|k, v| ! extra_keys.include? k}
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
assert_block(msg) { found_extras == extras }
-
- msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
+
+ msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
expected_path)
assert_block(msg) { expected_path == generated_path }
end
end
- # Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
+ # Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
# <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
# and +assert_generates+ into one step.
#
# The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
- # +message+ parameter allows you to specify a custom error message to display upon failure.
+ # +message+ parameter allows you to specify a custom error message to display upon failure.
#
# ==== Examples
# # Assert a basic route: a controller with the default action (index)
@@ -119,12 +119,12 @@ module ActionController
# assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
def assert_routing(path, options, defaults={}, extras={}, message=nil)
assert_recognizes(options, path, extras, message)
-
- controller, default_controller = options[:controller], defaults[:controller]
+
+ controller, default_controller = options[:controller], defaults[:controller]
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
options[:controller] = "/#{controller}"
end
-
+
assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
end
diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb
index d3594e711c..9114894b1d 100644
--- a/actionpack/lib/action_controller/assertions/selector_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb
@@ -21,10 +21,8 @@ module ActionController
# from the response HTML or elements selected by the enclosing assertion.
#
# In addition to HTML responses, you can make the following assertions:
- # * +assert_select_rjs+ - Assertions on HTML content of RJS update and
- # insertion operations.
- # * +assert_select_encoded+ - Assertions on HTML encoded inside XML,
- # for example for dealing with feed item descriptions.
+ # * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations.
+ # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
# * +assert_select_email+ - Assertions on the HTML body of an e-mail.
#
# Also see HTML::Selector to learn how to use selectors.
@@ -409,6 +407,7 @@ module ActionController
if rjs_type == :insert
arg = args.shift
+ position = arg
insertion = "insert_#{arg}".to_sym
raise ArgumentError, "Unknown RJS insertion type #{arg}" unless RJS_STATEMENTS[insertion]
statement = "(#{RJS_STATEMENTS[insertion]})"
@@ -420,6 +419,7 @@ module ActionController
else
statement = "#{RJS_STATEMENTS[:any]}"
end
+ position ||= Regexp.new(RJS_INSERTIONS.join('|'))
# Next argument we're looking for is the element identifier. If missing, we pick
# any element.
@@ -436,9 +436,14 @@ module ActionController
Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
when :remove, :show, :hide, :toggle
Regexp.new("#{statement}\\(\"#{id}\"\\)")
- else
- Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
- end
+ when :replace, :replace_html
+ Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)")
+ when :insert, :insert_html
+ Regexp.new("Element.insert\\(\\\"#{id}\\\", \\{ #{position}: #{RJS_PATTERN_HTML} \\}\\);")
+ else
+ Regexp.union(Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)"),
+ Regexp.new("Element.insert\\(\\\"#{id}\\\", \\{ #{position}: #{RJS_PATTERN_HTML} \\}\\);"))
+ end
# Duplicate the body since the next step involves destroying it.
matches = nil
@@ -447,7 +452,7 @@ module ActionController
matches = @response.body.match(pattern)
else
@response.body.gsub(pattern) do |match|
- html = unescape_rjs($2)
+ html = unescape_rjs(match)
matches ||= []
matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
""
@@ -587,17 +592,16 @@ module ActionController
:hide => /Element\.hide/,
:toggle => /Element\.toggle/
}
+ RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
+ RJS_PATTERN_HTML = /"((\\"|[^"])*)"/
RJS_INSERTIONS = [:top, :bottom, :before, :after]
RJS_INSERTIONS.each do |insertion|
- RJS_STATEMENTS["insert_#{insertion}".to_sym] = Regexp.new(Regexp.quote("new Insertion.#{insertion.to_s.camelize}"))
+ RJS_STATEMENTS["insert_#{insertion}".to_sym] = /Element.insert\(\"([^\"]*)\", \{ #{insertion.to_s.downcase}: #{RJS_PATTERN_HTML} \}\);/
end
- RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
RJS_STATEMENTS[:insert_html] = Regexp.new(RJS_INSERTIONS.collect do |insertion|
- Regexp.quote("new Insertion.#{insertion.to_s.camelize}")
+ /Element.insert\(\"([^\"]*)\", \{ #{insertion.to_s.downcase}: #{RJS_PATTERN_HTML} \}\);/
end.join('|'))
- RJS_PATTERN_HTML = /"((\\"|[^"])*)"/
- RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)",
- Regexp::MULTILINE)
+ RJS_PATTERN_EVERYTHING = Regexp.new("#{RJS_STATEMENTS[:any]}\\(\"([^\"]*)\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
end
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index df94f78f18..91023cd774 100755..100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -283,6 +283,14 @@ module ActionController #:nodoc:
@@debug_routes = true
cattr_accessor :debug_routes
+ # 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
+
+ @@guard = Monitor.new
+
# 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.
@@ -354,6 +362,15 @@ module ActionController #:nodoc:
class_inheritable_accessor :allow_forgery_protection
self.allow_forgery_protection = true
+ # 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_writer :relative_url_root
+
+ def self.relative_url_root
+ @@relative_url_root || ENV['RAILS_RELATIVE_URL_ROOT']
+ end
+
# Holds the request object that's primarily used to get environment variables through access like
# <tt>request.env["REQUEST_URI"]</tt>.
attr_internal :request
@@ -411,11 +428,7 @@ module ActionController #:nodoc:
# By default, all methods defined in ActionController::Base and included modules are hidden.
# More methods can be hidden using <tt>hide_actions</tt>.
def hidden_actions
- unless read_inheritable_attribute(:hidden_actions)
- write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map { |m| m.to_s })
- end
-
- read_inheritable_attribute(:hidden_actions)
+ read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
end
# Hide each of the given methods from being callable as actions.
@@ -519,6 +532,8 @@ module ActionController #:nodoc:
public
# Extracts the action_name from the request parameters and performs that action.
def process(request, response, method = :perform_action, *arguments) #:nodoc:
+ response.request = request
+
initialize_template_class(response)
assign_shortcuts(request, response)
initialize_current_url
@@ -526,11 +541,13 @@ module ActionController #:nodoc:
forget_variables_added_to_assigns
log_processing
- send(method, *arguments)
- assign_default_content_type_and_charset
+ if @@allow_concurrency
+ send(method, *arguments)
+ else
+ @@guard.synchronize { send(method, *arguments) }
+ end
- response.request = request
response.prepare! unless component_request?
response
ensure
@@ -763,9 +780,6 @@ module ActionController #:nodoc:
# render :file => "/path/to/some/template.erb", :layout => true, :status => 404
# render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
#
- # # Renders a template relative to the template root and chooses the proper file extension
- # render :file => "some/template", :use_full_path => true
- #
# === Rendering text
#
# Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
@@ -896,21 +910,10 @@ module ActionController #:nodoc:
response.content_type ||= Mime::JSON
render_for_text(json, options[:status])
- elsif partial = options[:partial]
- partial = default_template_name if partial == true
+ elsif options[:partial]
+ options[:partial] = default_template_name if options[:partial] == true
add_variables_to_assigns
-
- if collection = options[:collection]
- render_for_text(
- @template.send!(:render_partial_collection, partial, collection,
- options[:spacer_template], options[:locals], options[:as]), options[:status]
- )
- else
- render_for_text(
- @template.send!(:render_partial, partial,
- options[:object], options[:locals]), options[:status]
- )
- end
+ render_for_text(@template.render(options), options[:status])
elsif options[:update]
add_variables_to_assigns
@@ -921,8 +924,7 @@ module ActionController #:nodoc:
render_for_text(generator.to_s, options[:status])
elsif options[:nothing]
- # Safari doesn't pass the headers of the return if the response is zero length
- render_for_text(" ", options[:status])
+ render_for_text(nil, options[:status])
else
render_for_file(default_template_name, options[:status], true)
@@ -968,6 +970,17 @@ module ActionController #:nodoc:
render :nothing => true, :status => status
end
+ # Sets the Last-Modified response header. Returns 304 Not Modified if the
+ # If-Modified-Since request header is <= last modified.
+ def last_modified!(utc_time)
+ head(:not_modified) if response.last_modified!(utc_time)
+ end
+
+ # Sets the ETag response header. Returns 304 Not Modified if the
+ # If-None-Match request header matches.
+ def etag!(etag)
+ head(:not_modified) if response.etag!(etag)
+ end
# Clears the rendered results, allowing for another render to be performed.
def erase_render_results #:nodoc:
@@ -1125,7 +1138,11 @@ module ActionController #:nodoc:
response.body ||= ''
response.body << text.to_s
else
- response.body = text.is_a?(Proc) ? text : text.to_s
+ response.body = case text
+ when Proc then text
+ when nil then " " # Safari doesn't pass the headers of the return if the response is zero length
+ else text.to_s
+ end
end
end
@@ -1155,7 +1172,7 @@ module ActionController #:nodoc:
def log_processing
if logger && logger.info?
- logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
+ logger.info "\n\nProcessing #{self.class.name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
end
@@ -1166,16 +1183,16 @@ module ActionController #:nodoc:
end
def perform_action
- if self.class.action_methods.include?(action_name)
+ if action_methods.include?(action_name)
send(action_name)
default_render unless performed?
elsif respond_to? :method_missing
method_missing action_name
default_render unless performed?
- elsif template_exists? && template_public?
+ elsif template_exists?
default_render
else
- raise UnknownAction, "No action responded to #{action_name}", caller
+ raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
end
end
@@ -1188,20 +1205,24 @@ module ActionController #:nodoc:
end
def assign_default_content_type_and_charset
- response.content_type ||= Mime::HTML
- response.charset ||= self.class.default_charset unless sending_file?
- end
-
- def sending_file?
- response.headers["Content-Transfer-Encoding"] == "binary"
+ response.assign_default_content_type_and_charset!
end
+ deprecate :assign_default_content_type_and_charset => :'response.assign_default_content_type_and_charset!'
def action_methods
self.class.action_methods
end
def self.action_methods
- @action_methods ||= Set.new(public_instance_methods.map { |m| m.to_s }) - hidden_actions
+ @action_methods ||=
+ # All public instance methods of this class, including ancestors
+ public_instance_methods(true).map { |m| m.to_s }.to_set -
+ # Except for public instance methods of Base and its ancestors
+ Base.public_instance_methods(true).map { |m| m.to_s } +
+ # Be sure to include shadowed public instance methods of this class
+ public_instance_methods(false).map { |m| m.to_s } -
+ # And always exclude explicitly hidden actions
+ hidden_actions
end
def add_variables_to_assigns
@@ -1243,13 +1264,11 @@ module ActionController #:nodoc:
@template.file_exists?(template_name)
end
- def template_public?(template_name = default_template_name)
- @template.file_public?(template_name)
- end
-
def template_exempt_from_layout?(template_name = default_template_name)
template_name = @template.pick_template(template_name).to_s if @template
@@exempt_from_layout.any? { |ext| template_name =~ ext }
+ rescue ActionView::MissingTemplate
+ false
end
def default_template_name(action_name = self.action_name)
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 57b31ec9d1..e9b434dd25 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -2,7 +2,7 @@ module ActionController #:nodoc:
module Caching
# Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
# certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
- # parties. The caching is doing using the cache helper available in the Action View. A template with caching might look something like:
+ # parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
#
# <b>Hello <%= @name %></b>
# <% cache do %>
@@ -60,10 +60,8 @@ module ActionController #:nodoc:
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
- def fragment_for(block, name = {}, options = nil) #:nodoc:
+ def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
if perform_caching
- buffer = yield
-
if cache = read_fragment(name, options)
buffer.concat(cache)
else
diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb
index 8bc5e4c3a7..0ca27b30db 100644
--- a/actionpack/lib/action_controller/cgi_process.rb
+++ b/actionpack/lib/action_controller/cgi_process.rb
@@ -43,7 +43,7 @@ module ActionController #:nodoc:
:session_path => "/", # available to all paths in app
:session_key => "_session_id",
:cookie_only => true
- } unless const_defined?(:DEFAULT_SESSION_OPTIONS)
+ }
def initialize(cgi, session_options = {})
@cgi = cgi
@@ -61,53 +61,14 @@ module ActionController #:nodoc:
end
end
- # The request body is an IO input stream. If the RAW_POST_DATA environment
- # variable is already set, wrap it in a StringIO.
- def body
- if raw_post = env['RAW_POST_DATA']
- raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
- StringIO.new(raw_post)
- else
- @cgi.stdinput
- end
- end
-
- def query_parameters
- @query_parameters ||= self.class.parse_query_parameters(query_string)
- end
-
- def request_parameters
- @request_parameters ||= parse_formatted_request_parameters
+ def body_stream #:nodoc:
+ @cgi.stdinput
end
def cookies
@cgi.cookies.freeze
end
- def host_with_port_without_standard_port_handling
- if forwarded = env["HTTP_X_FORWARDED_HOST"]
- forwarded.split(/,\s?/).last
- elsif http_host = env['HTTP_HOST']
- http_host
- elsif server_name = env['SERVER_NAME']
- server_name
- else
- "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
- end
- end
-
- def host
- host_with_port_without_standard_port_handling.sub(/:\d+$/, '')
- end
-
- def port
- if host_with_port_without_standard_port_handling =~ /:(\d+)$/
- $1.to_i
- else
- standard_port
- end
- end
-
def session
unless defined?(@session)
if @session_options == false
diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/cookies.rb
index a4cddbcea2..0428f2a23d 100644
--- a/actionpack/lib/action_controller/cookies.rb
+++ b/actionpack/lib/action_controller/cookies.rb
@@ -22,6 +22,16 @@ module ActionController #:nodoc:
#
# cookies.delete :user_name
#
+ # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
+ #
+ # cookies[:key] = {
+ # :value => 'a yummy cookie',
+ # :expires => 1.year.from_now,
+ # :domain => 'domain.com'
+ # }
+ #
+ # cookies.delete(:key, :domain => 'domain.com')
+ #
# The option symbols for setting cookies are:
#
# * <tt>:value</tt> - The cookie's value or list of values (as an array).
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index 7df987d525..bdae5f9d86 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -2,8 +2,6 @@ module ActionController
# Dispatches requests to the appropriate controller and takes care of
# reloading the app after each request when Dependencies.load? is true.
class Dispatcher
- @@guard = Mutex.new
-
class << self
def define_dispatcher_callbacks(cache_classes)
unless cache_classes
@@ -26,7 +24,7 @@ module ActionController
to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
end
- after_dispatch :flush_logger if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:flush)
+ after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush)
end
# Backward-compatible class method takes CGI-specific args. Deprecated
@@ -46,7 +44,7 @@ module ActionController
def to_prepare(identifier = nil, &block)
@prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
- @prepare_dispatch_callbacks | callback
+ @prepare_dispatch_callbacks.replace_or_append!(callback)
end
# If the block raises, send status code as a last-ditch response.
@@ -101,15 +99,13 @@ module ActionController
end
def dispatch
- @@guard.synchronize do
- begin
- run_callbacks :before_dispatch
- handle_request
- rescue Exception => exception
- failsafe_rescue exception
- ensure
- run_callbacks :after_dispatch, :enumerator => :reverse_each
- end
+ begin
+ run_callbacks :before_dispatch
+ handle_request
+ rescue Exception => exception
+ failsafe_rescue exception
+ ensure
+ run_callbacks :after_dispatch, :enumerator => :reverse_each
end
end
@@ -146,7 +142,7 @@ module ActionController
end
def flush_logger
- RAILS_DEFAULT_LOGGER.flush
+ Base.logger.flush
end
protected
diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb
index 10dc0cc45b..1d7236f18a 100644
--- a/actionpack/lib/action_controller/filters.rb
+++ b/actionpack/lib/action_controller/filters.rb
@@ -109,16 +109,17 @@ module ActionController #:nodoc:
update_options! options
end
+ # override these to return true in appropriate subclass
def before?
- self.class == BeforeFilter
+ false
end
def after?
- self.class == AfterFilter
+ false
end
def around?
- self.class == AroundFilter
+ false
end
# Make sets of strings from :only/:except options
@@ -170,6 +171,10 @@ module ActionController #:nodoc:
:around
end
+ def around?
+ true
+ end
+
def call(controller, &block)
if should_run_callback?(controller)
method = filter_responds_to_before_and_after? ? around_proc : self.method
@@ -212,6 +217,10 @@ module ActionController #:nodoc:
:before
end
+ def before?
+ true
+ end
+
def call(controller, &block)
super
if controller.send!(:performed?)
@@ -224,6 +233,10 @@ module ActionController #:nodoc:
def type
:after
end
+
+ def after?
+ true
+ end
end
# Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
diff --git a/actionpack/lib/action_controller/headers.rb b/actionpack/lib/action_controller/headers.rb
index 7239438c49..139669c66f 100644
--- a/actionpack/lib/action_controller/headers.rb
+++ b/actionpack/lib/action_controller/headers.rb
@@ -1,31 +1,33 @@
+require 'active_support/memoizable'
+
module ActionController
module Http
class Headers < ::Hash
-
- def initialize(constructor = {})
- if constructor.is_a?(Hash)
+ extend ActiveSupport::Memoizable
+
+ def initialize(*args)
+ if args.size == 1 && args[0].is_a?(Hash)
super()
- update(constructor)
+ update(args[0])
else
- super(constructor)
+ super
end
end
-
+
def [](header_name)
if include?(header_name)
- super
+ super
else
- super(normalize_header(header_name))
+ super(env_name(header_name))
end
end
-
-
+
private
- # Takes an HTTP header name and returns it in the
- # format
- def normalize_header(header_name)
+ # Converts a HTTP header name to an environment variable name.
+ def env_name(header_name)
"HTTP_#{header_name.upcase.gsub(/-/, '_')}"
end
+ memoize :env_name
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index 18c2df8b37..198a22e8dc 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -101,7 +101,7 @@ module ActionController
@https = flag
end
- # Return +true+ if the session is mimicing a secure HTTPS request.
+ # Return +true+ if the session is mimicking a secure HTTPS request.
#
# if session.https?
# ...
@@ -165,11 +165,19 @@ module ActionController
status/100 == 3
end
- # Performs a GET request with the given parameters. The parameters may
- # be +nil+, a Hash, or a string that is appropriately encoded
- # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
- # The headers should be a hash. The keys will automatically be upcased, with the
- # prefix 'HTTP_' added if needed.
+ # Performs a GET request with the given parameters.
+ #
+ # - +path+: The URI (as a String) on which you want to perform a GET request.
+ # - +parameters+: The HTTP parameters that you want to pass. This may be +nil+,
+ # a Hash, or a String that is appropriately encoded
+ # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
+ # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
+ # automatically be upcased, with the prefix 'HTTP_' added if needed.
+ #
+ # This method returns an AbstractResponse object, which one can use to inspect
+ # the details of the response. Furthermore, if this method was called from an
+ # ActionController::IntegrationTest object, then that object's <tt>@response</tt>
+ # instance variable will point to the same response object.
#
# You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
# +put+, +delete+, and +head+.
@@ -220,21 +228,6 @@ module ActionController
end
private
- class StubCGI < CGI #:nodoc:
- attr_accessor :stdinput, :stdoutput, :env_table
-
- def initialize(env, stdinput = nil)
- self.env_table = env
- self.stdoutput = StringIO.new
-
- super
-
- stdinput.set_encoding(Encoding::BINARY) if stdinput.respond_to?(:set_encoding)
- stdinput.force_encoding(Encoding::BINARY) if stdinput.respond_to?(:force_encoding)
- @stdinput = stdinput.is_a?(IO) ? stdinput : StringIO.new(stdinput || '')
- end
- end
-
# Tailors the session based on the given URI, setting the HTTPS value
# and the hostname.
def interpret_uri(path)
@@ -282,9 +275,8 @@ module ActionController
ActionController::Base.clear_last_instantiation!
- cgi = StubCGI.new(env, data)
- ActionController::Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, cgi.stdoutput)
- @result = cgi.stdoutput.string
+ env['rack.input'] = data.is_a?(IO) ? data : StringIO.new(data || '')
+ @status, @headers, result_body = ActionController::Dispatcher.new.call(env)
@request_count += 1
@controller = ActionController::Base.last_instantiation
@@ -298,32 +290,34 @@ module ActionController
@html_document = nil
- parse_result
- return status
- rescue MultiPartNeededException
- boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
- status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
- return status
- end
+ # Inject status back in for backwords compatibility with CGI
+ @headers['Status'] = @status
- # Parses the result of the response and extracts the various values,
- # like cookies, status, headers, etc.
- def parse_result
- response_headers, result_body = @result.split(/\r\n\r\n/, 2)
+ @status, @status_message = @status.split(/ /)
+ @status = @status.to_i
- @headers = Hash.new { |h,k| h[k] = [] }
- response_headers.to_s.each_line do |line|
- key, value = line.strip.split(/:\s*/, 2)
- @headers[key.downcase] << value
+ cgi_headers = Hash.new { |h,k| h[k] = [] }
+ @headers.each do |key, value|
+ cgi_headers[key.downcase] << value
end
+ cgi_headers['set-cookie'] = cgi_headers['set-cookie'].first
+ @headers = cgi_headers
- (@headers['set-cookie'] || [] ).each do |string|
- name, value = string.match(/^([^=]*)=([^;]*);/)[1,2]
+ @response.headers['cookie'] ||= []
+ (@headers['set-cookie'] || []).each do |cookie|
+ name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2]
@cookies[name] = value
+
+ # Fake CGI cookie header
+ # DEPRECATE: Use response.headers["Set-Cookie"] instead
+ @response.headers['cookie'] << CGI::Cookie::new("name" => name, "value" => value)
end
- @status, @status_message = @headers["status"].first.to_s.split(/ /)
- @status = @status.to_i
+ return status
+ rescue MultiPartNeededException
+ boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
+ status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
+ return status
end
# Encode the cookies hash in a format suitable for passing to a
@@ -336,13 +330,15 @@ module ActionController
# Get a temporary URL writer object
def generic_url_rewriter
- cgi = StubCGI.new('REQUEST_METHOD' => "GET",
- 'QUERY_STRING' => "",
- "REQUEST_URI" => "/",
- "HTTP_HOST" => host,
- "SERVER_PORT" => https? ? "443" : "80",
- "HTTPS" => https? ? "on" : "off")
- ActionController::UrlRewriter.new(ActionController::CgiRequest.new(cgi), {})
+ env = {
+ 'REQUEST_METHOD' => "GET",
+ 'QUERY_STRING' => "",
+ "REQUEST_URI" => "/",
+ "HTTP_HOST" => host,
+ "SERVER_PORT" => https? ? "443" : "80",
+ "HTTPS" => https? ? "on" : "off"
+ }
+ ActionController::UrlRewriter.new(ActionController::RackRequest.new(env), {})
end
def name_with_prefix(prefix, name)
@@ -443,7 +439,7 @@ EOF
end
%w(get post put head delete cookies assigns
- xml_http_request get_via_redirect post_via_redirect).each do |method|
+ xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
define_method(method) do |*args|
reset! unless @integration_session
# reset the html_document variable, but only for new get/post calls
@@ -499,7 +495,7 @@ EOF
# Delegate unhandled messages to the current session instance.
def method_missing(sym, *args, &block)
reset! unless @integration_session
- returning @integration_session.send!(sym, *args, &block) do
+ returning @integration_session.__send__(sym, *args, &block) do
copy_session_variables!
end
end
diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index 7c30bf0778..30564c7bb3 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -102,6 +102,12 @@ module ActionController
args << format if format
named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
+
+ url_options = options.except(:action, :routing_type, :format)
+ unless url_options.empty?
+ args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
+ end
+
send!(named_route, *args)
end
@@ -114,19 +120,19 @@ module ActionController
%w(edit new formatted).each do |action|
module_eval <<-EOT, __FILE__, __LINE__
- def #{action}_polymorphic_url(record_or_hash)
- polymorphic_url(record_or_hash, :action => "#{action}")
+ def #{action}_polymorphic_url(record_or_hash, options = {})
+ polymorphic_url(record_or_hash, options.merge(:action => "#{action}"))
end
- def #{action}_polymorphic_path(record_or_hash)
- polymorphic_url(record_or_hash, :action => "#{action}", :routing_type => :path)
+ def #{action}_polymorphic_path(record_or_hash, options = {})
+ polymorphic_url(record_or_hash, options.merge(:action => "#{action}", :routing_type => :path))
end
EOT
end
private
def action_prefix(options)
- options[:action] ? "#{options[:action]}_" : ""
+ options[:action] ? "#{options[:action]}_" : options[:format] ? "formatted_" : ""
end
def routing_type(options)
diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb
index 01bc1ebb26..1ace16da07 100644
--- a/actionpack/lib/action_controller/rack_process.rb
+++ b/actionpack/lib/action_controller/rack_process.rb
@@ -3,7 +3,7 @@ require 'action_controller/session/cookie_store'
module ActionController #:nodoc:
class RackRequest < AbstractRequest #:nodoc:
- attr_accessor :env, :session_options
+ attr_accessor :session_options
attr_reader :cgi
class SessionFixationAttempt < StandardError #:nodoc:
@@ -15,7 +15,7 @@ module ActionController #:nodoc:
:session_path => "/", # available to all paths in app
:session_key => "_session_id",
:cookie_only => true
- } unless const_defined?(:DEFAULT_SESSION_OPTIONS)
+ }
def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
@session_options = session_options
@@ -24,39 +24,34 @@ module ActionController #:nodoc:
super()
end
- %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
- PATH_TRANSLATED QUERY_STRING REMOTE_HOST
+ %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO
+ PATH_TRANSLATED REMOTE_HOST
REMOTE_IDENT REMOTE_USER SCRIPT_NAME
SERVER_NAME SERVER_PROTOCOL
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
- HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
+ HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
define_method(env.sub(/^HTTP_/n, '').downcase) do
@env[env]
end
end
- # The request body is an IO input stream. If the RAW_POST_DATA environment
- # variable is already set, wrap it in a StringIO.
- def body
- if raw_post = env['RAW_POST_DATA']
- StringIO.new(raw_post)
+ def query_string
+ qs = super
+ if !qs.blank?
+ qs
else
- @env['rack.input']
+ @env['QUERY_STRING']
end
end
- def key?(key)
- @env.key?(key)
- end
-
- def query_parameters
- @query_parameters ||= self.class.parse_query_parameters(query_string)
+ def body_stream #:nodoc:
+ @env['rack.input']
end
- def request_parameters
- @request_parameters ||= parse_formatted_request_parameters
+ def key?(key)
+ @env.key?(key)
end
def cookies
@@ -70,38 +65,6 @@ module ActionController #:nodoc:
@env["rack.request.cookie_hash"]
end
- def host_with_port_without_standard_port_handling
- if forwarded = @env["HTTP_X_FORWARDED_HOST"]
- forwarded.split(/,\s?/).last
- elsif http_host = @env['HTTP_HOST']
- http_host
- elsif server_name = @env['SERVER_NAME']
- server_name
- else
- "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
- end
- end
-
- def host
- host_with_port_without_standard_port_handling.sub(/:\d+$/, '')
- end
-
- def port
- if host_with_port_without_standard_port_handling =~ /:(\d+)$/
- $1.to_i
- else
- standard_port
- end
- end
-
- def remote_addr
- @env['REMOTE_ADDR']
- end
-
- def request_method
- @env['REQUEST_METHOD'].downcase.to_sym
- end
-
def server_port
@env['SERVER_PORT'].to_i
end
@@ -189,23 +152,30 @@ end_msg
end
class RackResponse < AbstractResponse #:nodoc:
- attr_accessor :status
-
def initialize(request)
- @request = request
+ @cgi = request.cgi
@writer = lambda { |x| @body << x }
@block = nil
super()
end
+ # Retrieve status from instance variable if has already been delete
+ def status
+ @status || super
+ end
+
def out(output = $stdout, &block)
+ # Nasty hack because CGI sessions are closed after the normal
+ # prepare! statement
+ set_cookies!
+
@block = block
- normalize_headers(@headers)
- if [204, 304].include?(@status.to_i)
- @headers.delete "Content-Type"
- [status, @headers.to_hash, []]
+ @status = headers.delete("Status")
+ if [204, 304].include?(status.to_i)
+ headers.delete("Content-Type")
+ [status, headers.to_hash, []]
else
- [status, @headers.to_hash, self]
+ [status, headers.to_hash, self]
end
end
alias to_a out
@@ -237,43 +207,57 @@ end_msg
@block == nil && @body.empty?
end
- private
- def normalize_headers(options = "text/html")
- if options.is_a?(String)
- headers['Content-Type'] = options unless headers['Content-Type']
- else
- headers['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
+ def prepare!
+ super
- headers['Content-Type'] = options.delete('type') || "text/html"
- headers['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
+ convert_language!
+ convert_expires!
+ set_status!
+ # set_cookies!
+ end
- headers['Content-Language'] = options.delete('language') if options['language']
- headers['Expires'] = options.delete('expires') if options['expires']
+ private
+ def convert_language!
+ headers["Content-Language"] = headers.delete("language") if headers["language"]
+ end
- @status = options['Status'] || "200 OK"
+ def convert_expires!
+ headers["Expires"] = headers.delete("") if headers["expires"]
+ end
- # Convert 'cookie' header to 'Set-Cookie' headers.
- # Because Set-Cookie header can appear more the once in the response body,
- # we store it in a line break seperated string that will be translated to
- # multiple Set-Cookie header by the handler.
- if cookie = options.delete('cookie')
- cookies = []
+ def convert_content_type!
+ super
+ headers['Content-Type'] = headers.delete('type') || "text/html"
+ headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset']
+ end
- case cookie
- when Array then cookie.each { |c| cookies << c.to_s }
- when Hash then cookie.each { |_, c| cookies << c.to_s }
- else cookies << cookie.to_s
- end
+ def set_content_length!
+ super
+ headers["Content-Length"] = headers["Content-Length"].to_s if headers["Content-Length"]
+ end
- @request.cgi.output_cookies.each { |c| cookies << c.to_s } if @request.cgi.output_cookies
+ def set_status!
+ self.status ||= "200 OK"
+ end
- headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact
+ def set_cookies!
+ # Convert 'cookie' header to 'Set-Cookie' headers.
+ # Because Set-Cookie header can appear more the once in the response body,
+ # we store it in a line break separated string that will be translated to
+ # multiple Set-Cookie header by the handler.
+ if cookie = headers.delete('cookie')
+ cookies = []
+
+ case cookie
+ when Array then cookie.each { |c| cookies << c.to_s }
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
+ else cookies << cookie.to_s
end
- options.each { |k,v| headers[k] = v }
- end
+ @cgi.output_cookies.each { |c| cookies << c.to_s } if @cgi.output_cookies
- ""
+ headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact
+ end
end
end
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 2d9f6c3e6f..364e6201cc 100755..100644
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -2,14 +2,21 @@ require 'tempfile'
require 'stringio'
require 'strscan'
-module ActionController
- # HTTP methods which are accepted by default.
- ACCEPTED_HTTP_METHODS = Set.new(%w( get head put post delete options ))
+require 'active_support/memoizable'
+module ActionController
# CgiRequest and TestRequest provide concrete implementations.
class AbstractRequest
- cattr_accessor :relative_url_root
- remove_method :relative_url_root
+ extend ActiveSupport::Memoizable
+
+ def self.relative_url_root=(*args)
+ ActiveSupport::Deprecation.warn(
+ "ActionController::AbstractRequest.relative_url_root= has been renamed." +
+ "You can now set it with config.action_controller.relative_url_root=", caller)
+ end
+
+ HTTP_METHODS = %w(get head put post delete options)
+ HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
# The hash of environment variables for this request,
# such as { 'RAILS_ENV' => 'production' }.
@@ -18,15 +25,12 @@ module ActionController
# The true HTTP request method as a lowercase symbol, such as <tt>:get</tt>.
# UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS.
def request_method
- @request_method ||= begin
- method = ((@env['REQUEST_METHOD'] == 'POST' && !parameters[:_method].blank?) ? parameters[:_method].to_s : @env['REQUEST_METHOD']).downcase
- if ACCEPTED_HTTP_METHODS.include?(method)
- method.to_sym
- else
- raise UnknownHttpMethod, "#{method}, accepted HTTP methods are #{ACCEPTED_HTTP_METHODS.to_a.to_sentence}"
- end
- end
+ method = @env['REQUEST_METHOD']
+ method = parameters[:_method] if method == 'POST' && !parameters[:_method].blank?
+
+ HTTP_METHOD_LOOKUP[method] || raise(UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
end
+ memoize :request_method
# The HTTP request method as a lowercase symbol, such as <tt>:get</tt>.
# Note, HEAD is returned as <tt>:get</tt> since the two are functionally
@@ -61,36 +65,62 @@ module ActionController
request_method == :head
end
- # Provides acccess to the request's HTTP headers, for example:
+ # Provides access to the request's HTTP headers, for example:
# request.headers["Content-Type"] # => "text/plain"
def headers
- @headers ||= ActionController::Http::Headers.new(@env)
+ ActionController::Http::Headers.new(@env)
end
+ memoize :headers
def content_length
- @content_length ||= env['CONTENT_LENGTH'].to_i
+ @env['CONTENT_LENGTH'].to_i
end
+ memoize :content_length
# The MIME type of the HTTP request, such as Mime::XML.
#
# For backward compatibility, the post format is extracted from the
# X-Post-Data-Format HTTP header if present.
def content_type
- @content_type ||= Mime::Type.lookup(content_type_without_parameters)
+ Mime::Type.lookup(content_type_without_parameters)
end
+ memoize :content_type
# Returns the accepted MIME type for the request
def accepts
- @accepts ||=
- begin
- header = @env['HTTP_ACCEPT'].to_s.strip
+ header = @env['HTTP_ACCEPT'].to_s.strip
- if header.empty?
- [content_type, Mime::ALL].compact
- else
- Mime::Type.parse(header)
- end
- end
+ if header.empty?
+ [content_type, Mime::ALL].compact
+ else
+ Mime::Type.parse(header)
+ end
+ end
+ memoize :accepts
+
+ def if_modified_since
+ if since = env['HTTP_IF_MODIFIED_SINCE']
+ Time.rfc2822(since) rescue nil
+ end
+ end
+ memoize :if_modified_since
+
+ def if_none_match
+ env['HTTP_IF_NONE_MATCH']
+ end
+
+ def not_modified?(modified_at)
+ if_modified_since && modified_at && if_modified_since >= modified_at
+ end
+
+ def etag_matches?(etag)
+ if_none_match && if_none_match == etag
+ end
+
+ # Check response freshness (Last-Modified and ETag) against request
+ # If-Modified-Since and If-None-Match conditions.
+ def fresh?(response)
+ not_modified?(response.last_modified) || etag_matches?(response.etag)
end
# Returns the Mime type for the format used in the request.
@@ -99,7 +129,7 @@ module ActionController
# GET /posts/5.xhtml | request.format => Mime::HTML
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
def format
- @format ||= begin
+ @format ||=
if parameters[:format]
Mime::Type.lookup_by_extension(parameters[:format])
elsif ActionController::Base.use_accept_header
@@ -109,16 +139,15 @@ module ActionController
else
Mime::Type.lookup_by_extension("html")
end
- end
end
-
-
+
+
# Sets the format by string extension, which can be used to force custom formats that are not controlled by the extension.
# Example:
#
# class ApplicationController < ActionController::Base
# before_filter :adjust_format_for_iphone
- #
+ #
# private
# def adjust_format_for_iphone
# request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
@@ -197,42 +226,62 @@ EOM
@env['REMOTE_ADDR']
end
+ memoize :remote_ip
# Returns the lowercase name of the HTTP server software.
def server_software
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
end
+ memoize :server_software
# Returns the complete URL used for this request
def url
protocol + host_with_port + request_uri
end
+ memoize :url
# Return 'https://' if this is an SSL request and 'http://' otherwise.
def protocol
ssl? ? 'https://' : 'http://'
end
+ memoize :protocol
# Is this an SSL request?
def ssl?
@env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
end
+ def raw_host_with_port
+ if forwarded = env["HTTP_X_FORWARDED_HOST"]
+ forwarded.split(/,\s?/).last
+ else
+ env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
+ end
+ end
+
# Returns the host for this request, such as example.com.
def host
+ raw_host_with_port.sub(/:\d+$/, '')
end
+ memoize :host
# Returns a host:port string for this request, such as example.com or
# example.com:8080.
def host_with_port
- @host_with_port ||= host + port_string
+ "#{host}#{port_string}"
end
+ memoize :host_with_port
# Returns the port number of this request as an integer.
def port
- @port_as_int ||= @env['SERVER_PORT'].to_i
+ if raw_host_with_port =~ /:(\d+)$/
+ $1.to_i
+ else
+ standard_port
+ end
end
+ memoize :port
# Returns the standard port number for this request's protocol
def standard_port
@@ -245,7 +294,7 @@ EOM
# Returns a port suffix like ":8080" if the port number of this request
# is not the default HTTP port 80 or HTTPS port 443.
def port_string
- (port == standard_port) ? '' : ":#{port}"
+ port == standard_port ? '' : ":#{port}"
end
# Returns the domain part of a host, such as rubyonrails.org in "www.rubyonrails.org". You can specify
@@ -265,7 +314,7 @@ EOM
parts[0..-(tld_length+2)]
end
- # Return the query string, accounting for server idiosyncracies.
+ # Return the query string, accounting for server idiosyncrasies.
def query_string
if uri = @env['REQUEST_URI']
uri.split('?', 2)[1] || ''
@@ -273,8 +322,9 @@ EOM
@env['QUERY_STRING'] || ''
end
end
+ memoize :query_string
- # Return the request URI, accounting for server idiosyncracies.
+ # Return the request URI, accounting for server idiosyncrasies.
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
def request_uri
if uri = @env['REQUEST_URI']
@@ -282,46 +332,33 @@ EOM
(%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
else
# Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
- script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
- uri = @env['PATH_INFO']
- uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
- unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
- uri << '?' << env_qs
+ uri = @env['PATH_INFO'].to_s
+
+ if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
+ uri = uri.sub(/#{script_filename}\//, '')
end
- if uri.nil?
+ env_qs = @env['QUERY_STRING'].to_s
+ uri += "?#{env_qs}" unless env_qs.empty?
+
+ if uri.blank?
@env.delete('REQUEST_URI')
- uri
else
@env['REQUEST_URI'] = uri
end
end
end
+ memoize :request_uri
# Returns the interpreted path to requested resource after all the installation directory of this application was taken into account
def path
path = (uri = request_uri) ? uri.split('?').first.to_s : ''
# Cut off the path to the installation directory if given
- path.sub!(%r/^#{relative_url_root}/, '')
- path || ''
- end
-
- # Returns the path minus the web server relative installation directory.
- # This can be set with the environment variable RAILS_RELATIVE_URL_ROOT.
- # It can be automatically extracted for Apache setups. If the server is not
- # Apache, this method returns an empty string.
- def relative_url_root
- @@relative_url_root ||= case
- when @env["RAILS_RELATIVE_URL_ROOT"]
- @env["RAILS_RELATIVE_URL_ROOT"]
- when server_software == 'apache'
- @env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '')
- else
- ''
- end
+ path.sub!(%r/^#{ActionController::Base.relative_url_root}/, '')
+ path || ''
end
-
+ memoize :path
# Read the request body. This is useful for web services that need to
# work with raw requests directly.
@@ -343,34 +380,56 @@ EOM
@symbolized_path_parameters = @parameters = nil
end
- # The same as <tt>path_parameters</tt> with explicitly symbolized keys
- def symbolized_path_parameters
+ # The same as <tt>path_parameters</tt> with explicitly symbolized keys
+ def symbolized_path_parameters
@symbolized_path_parameters ||= path_parameters.symbolize_keys
end
# Returns a hash with the parameters used to form the path of the request.
# Returned hash keys are strings. See <tt>symbolized_path_parameters</tt> for symbolized keys.
#
- # Example:
+ # Example:
#
# {'action' => 'my_action', 'controller' => 'my_controller'}
def path_parameters
@path_parameters ||= {}
end
+ # The request body is an IO input stream. If the RAW_POST_DATA environment
+ # variable is already set, wrap it in a StringIO.
+ def body
+ if raw_post = env['RAW_POST_DATA']
+ raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
+ StringIO.new(raw_post)
+ else
+ body_stream
+ end
+ end
- #--
- # Must be implemented in the concrete request
- #++
+ def remote_addr
+ @env['REMOTE_ADDR']
+ end
- # The request body is an IO input stream.
- def body
+ def referrer
+ @env['HTTP_REFERER']
+ end
+ alias referer referrer
+
+
+ def query_parameters
+ @query_parameters ||= self.class.parse_query_parameters(query_string)
end
- def query_parameters #:nodoc:
+ def request_parameters
+ @request_parameters ||= parse_formatted_request_parameters
end
- def request_parameters #:nodoc:
+
+ #--
+ # Must be implemented in the concrete request
+ #++
+
+ def body_stream #:nodoc:
end
def cookies #:nodoc:
@@ -397,8 +456,9 @@ EOM
# The raw content type string with its parameters stripped off.
def content_type_without_parameters
- @content_type_without_parameters ||= self.class.extract_content_type_without_parameters(content_type_with_parameters)
+ self.class.extract_content_type_without_parameters(content_type_with_parameters)
end
+ memoize :content_type_without_parameters
private
def content_type_from_legacy_post_data_format_header
diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb
index 02c9d59d07..05a6d8bb79 100644
--- a/actionpack/lib/action_controller/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/request_forgery_protection.rb
@@ -17,7 +17,7 @@ module ActionController #:nodoc:
# forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
- # scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
+ # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway.
#
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
diff --git a/actionpack/lib/action_controller/request_profiler.rb b/actionpack/lib/action_controller/request_profiler.rb
index a6471d0c08..a6471d0c08 100755..100644
--- a/actionpack/lib/action_controller/request_profiler.rb
+++ b/actionpack/lib/action_controller/request_profiler.rb
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index 163ed87fbb..a1a9d68a35 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -112,19 +112,23 @@ module ActionController #:nodoc:
protected
# Exception handler called when the performance of an action raises an exception.
def rescue_action(exception)
- log_error(exception) if logger
- erase_results if performed?
+ if handler_for_rescue(exception)
+ rescue_action_with_handler(exception)
+ else
+ log_error(exception) if logger
+ erase_results if performed?
- # Let the exception alter the response if it wants.
- # For example, MethodNotAllowed sets the Allow header.
- if exception.respond_to?(:handle_response!)
- exception.handle_response!(response)
- end
+ # Let the exception alter the response if it wants.
+ # For example, MethodNotAllowed sets the Allow header.
+ if exception.respond_to?(:handle_response!)
+ exception.handle_response!(response)
+ end
- if consider_all_requests_local || local_request?
- rescue_action_locally(exception)
- else
- rescue_action_in_public(exception)
+ if consider_all_requests_local || local_request?
+ rescue_action_locally(exception)
+ else
+ rescue_action_in_public(exception)
+ end
end
end
@@ -178,7 +182,7 @@ module ActionController #:nodoc:
@template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
@template.send!(:assign_variables_from_controller)
- @template.instance_variable_set("@contents", @template.render(:file => template_path_for_local_rescue(exception), :use_full_path => false))
+ @template.instance_variable_set("@contents", @template.render(:file => template_path_for_local_rescue(exception)))
response.content_type = Mime::HTML
render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
@@ -200,7 +204,7 @@ module ActionController #:nodoc:
def perform_action_with_rescue #:nodoc:
perform_action_without_rescue
rescue Exception => exception
- rescue_action_with_handler(exception) || rescue_action(exception)
+ rescue_action(exception)
end
def rescues_path(template_name)
diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb
index af2fcaf3ad..5f579cdb11 100644
--- a/actionpack/lib/action_controller/resources.rb
+++ b/actionpack/lib/action_controller/resources.rb
@@ -14,10 +14,10 @@ module ActionController
#
# === The Different Methods and their Usage
#
- # +GET+ Requests for a resource, no saving or editing of a resource should occur in a GET request
- # +POST+ Creation of resources
- # +PUT+ Editing of attributes on a resource
- # +DELETE+ Deletion of a resource
+ # [+GET+] Requests for a resource, no saving or editing of a resource should occur in a GET request
+ # [+POST+] Creation of resources
+ # [+PUT+] Editing of attributes on a resource
+ # [+DELETE+] Deletion of a resource
#
# === Examples
#
@@ -296,6 +296,10 @@ module ActionController
# article_comments_url(:article_id => @article)
# article_comment_url(:article_id => @article, :id => @comment)
#
+ # If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
+ #
+ # articles_comments_url(@comment.article_id, @comment)
+ #
# * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
# Use this if you have named routes that may clash.
#
@@ -303,13 +307,13 @@ module ActionController
# map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
#
# You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested resource:
- #
+ #
# map.resources :articles do |article|
# article.resources :comments, :name_prefix => nil
- # end
- #
+ # end
+ #
# This will yield named resources like so:
- #
+ #
# comments_url(@article)
# comment_url(@article, @comment)
#
@@ -477,8 +481,7 @@ module ActionController
resource.collection_methods.each do |method, actions|
actions.each do |action|
action_options = action_options_for(action, resource, method)
- map.named_route("#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
- map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}.:format", action_options)
+ map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
end
end
end
@@ -491,18 +494,15 @@ module ActionController
index_route_name << "_index"
end
- map.named_route(index_route_name, resource.path, index_action_options)
- map.named_route("formatted_#{index_route_name}", "#{resource.path}.:format", index_action_options)
+ map_named_routes(map, index_route_name, resource.path, index_action_options)
create_action_options = action_options_for("create", resource)
- map.connect(resource.path, create_action_options)
- map.connect("#{resource.path}.:format", create_action_options)
+ map_unnamed_routes(map, resource.path, create_action_options)
end
def map_default_singleton_actions(map, resource)
create_action_options = action_options_for("create", resource)
- map.connect(resource.path, create_action_options)
- map.connect("#{resource.path}.:format", create_action_options)
+ map_unnamed_routes(map, resource.path, create_action_options)
end
def map_new_actions(map, resource)
@@ -510,11 +510,9 @@ module ActionController
actions.each do |action|
action_options = action_options_for(action, resource, method)
if action == :new
- map.named_route("new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
- map.named_route("formatted_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}.:format", action_options)
+ map_named_routes(map, "new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
else
- map.named_route("#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options)
- map.named_route("formatted_#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}.:format", action_options)
+ map_named_routes(map, "#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options)
end
end
end
@@ -528,22 +526,28 @@ module ActionController
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_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options)
- map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}.:format",action_options)
+ map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options)
end
end
show_action_options = action_options_for("show", resource)
- map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
- map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options)
+ map_named_routes(map, "#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
update_action_options = action_options_for("update", resource)
- map.connect(resource.member_path, update_action_options)
- map.connect("#{resource.member_path}.:format", update_action_options)
+ map_unnamed_routes(map, resource.member_path, update_action_options)
destroy_action_options = action_options_for("destroy", resource)
- map.connect(resource.member_path, destroy_action_options)
- map.connect("#{resource.member_path}.:format", destroy_action_options)
+ 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)
+ 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)
end
def add_conditions_for(conditions, method)
@@ -555,6 +559,7 @@ module ActionController
def action_options_for(action, resource, method = nil)
default_options = { :action => action.to_s }
require_id = !resource.kind_of?(SingletonResource)
+
case default_options[:action]
when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
@@ -569,4 +574,4 @@ end
class ActionController::Routing::RouteSet::Mapper
include ActionController::Resources
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb
index 1d9f6676ba..5dac4128bb 100755..100644
--- a/actionpack/lib/action_controller/response.rb
+++ b/actionpack/lib/action_controller/response.rb
@@ -1,58 +1,161 @@
require 'digest/md5'
-module ActionController
- class AbstractResponse #:nodoc:
+module ActionController # :nodoc:
+ # Represents an HTTP response generated by a controller action. One can use an
+ # ActionController::AbstractResponse object to retrieve the current state of the
+ # response, or customize the response. An AbstractResponse object can either
+ # represent a "real" HTTP response (i.e. one that is meant to be sent back to the
+ # web browser) or a test response (i.e. one that is generated from integration
+ # tests). See CgiResponse and TestResponse, respectively.
+ #
+ # AbstractResponse is mostly a Ruby on Rails framework implement detail, and should
+ # never be used directly in controllers. Controllers should use the methods defined
+ # in ActionController::Base instead. For example, if you want to set the HTTP
+ # response's content MIME type, then use ActionControllerBase#headers instead of
+ # AbstractResponse#headers.
+ #
+ # Nevertheless, integration tests may want to inspect controller responses in more
+ # detail, and that's when AbstractResponse can be useful for application developers.
+ # Integration test methods such as ActionController::Integration::Session#get and
+ # ActionController::Integration::Session#post return objects of type TestResponse
+ # (which are of course also of type AbstractResponse).
+ #
+ # For example, the following demo integration "test" prints the body of the
+ # controller response to the console:
+ #
+ # class DemoControllerTest < ActionController::IntegrationTest
+ # def test_print_root_path_to_console
+ # get('/')
+ # puts @response.body
+ # end
+ # end
+ class AbstractResponse
DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
attr_accessor :request
- attr_accessor :body, :headers, :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params, :layout
+
+ # The body content (e.g. HTML) of the response, as a String.
+ attr_accessor :body
+ # The headers of the response, as a Hash. It maps header names to header values.
+ attr_accessor :headers
+ attr_accessor :session, :cookies, :assigns, :template, :layout
+ attr_accessor :redirected_to, :redirected_to_method_params
+
+ delegate :default_charset, :to => 'ActionController::Base'
def initialize
@body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], []
end
+ def status; headers['Status'] end
+ def status=(status) headers['Status'] = status end
+
+ def location; headers['Location'] end
+ def location=(url) headers['Location'] = url end
+
+
+ # Sets the HTTP response's content MIME type. For example, in the controller
+ # you could write this:
+ #
+ # response.content_type = "text/plain"
+ #
+ # If a character set has been defined for this response (see charset=) then
+ # the character set information will also be included in the content type
+ # information.
def content_type=(mime_type)
- self.headers["Content-Type"] = charset ? "#{mime_type}; charset=#{charset}" : mime_type
+ self.headers["Content-Type"] =
+ if mime_type =~ /charset/ || (c = charset).nil?
+ mime_type.to_s
+ else
+ "#{mime_type}; charset=#{c}"
+ end
end
-
+
+ # Returns the response's content MIME type, or nil if content type has been set.
def content_type
content_type = String(headers["Content-Type"] || headers["type"]).split(";")[0]
content_type.blank? ? nil : content_type
end
-
- def charset=(encoding)
- self.headers["Content-Type"] = "#{content_type || Mime::HTML}; charset=#{encoding}"
+
+ # Set the charset of the Content-Type header. Set to nil to remove it.
+ # If no content type is set, it defaults to HTML.
+ def charset=(charset)
+ headers["Content-Type"] =
+ if charset
+ "#{content_type || Mime::HTML}; charset=#{charset}"
+ else
+ content_type || Mime::HTML.to_s
+ end
end
-
+
def charset
charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
charset.blank? ? nil : charset.strip.split("=")[1]
end
- def redirect(to_url, response_status)
- self.headers["Status"] = response_status
- self.headers["Location"] = to_url
+ def last_modified
+ if last = headers['Last-Modified']
+ Time.httpdate(last)
+ end
+ end
+
+ def last_modified?
+ headers.include?('Last-Modified')
+ end
+
+ def last_modified=(utc_time)
+ headers['Last-Modified'] = utc_time.httpdate
+ end
+
+ def etag; headers['ETag'] end
+ def etag?; headers.include?('ETag') end
+ def etag=(etag)
+ headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
+ end
+
+ def redirect(url, status)
+ self.status = status
+ self.location = url
+ self.body = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
+ end
- self.body = "<html><body>You are being <a href=\"#{to_url}\">redirected</a>.</body></html>"
+ def sending_file?
+ headers["Content-Transfer-Encoding"] == "binary"
+ end
+
+ def assign_default_content_type_and_charset!
+ self.content_type ||= Mime::HTML
+ self.charset ||= default_charset unless sending_file?
end
def prepare!
+ assign_default_content_type_and_charset!
+ set_content_length!
handle_conditional_get!
convert_content_type!
- set_content_length!
end
-
private
def handle_conditional_get!
- if body.is_a?(String) && (headers['Status'] ? headers['Status'][0..2] == '200' : true) && !body.empty?
- self.headers['ETag'] ||= %("#{Digest::MD5.hexdigest(body)}")
- self.headers['Cache-Control'] = 'private, max-age=0, must-revalidate' if headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control']
-
- if request.headers['HTTP_IF_NONE_MATCH'] == headers['ETag']
- self.headers['Status'] = '304 Not Modified'
+ if nonempty_ok_response?
+ self.etag ||= body
+ if request && request.etag_matches?(etag)
+ self.status = '304 Not Modified'
self.body = ''
end
end
+
+ set_conditional_cache_control! if etag? || last_modified?
+ end
+
+ def nonempty_ok_response?
+ ok = !status || status[0..2] == '200'
+ ok && body.is_a?(String) && !body.empty?
+ end
+
+ def set_conditional_cache_control!
+ if headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control']
+ headers['Cache-Control'] = 'private, max-age=0, must-revalidate'
+ end
end
def convert_content_type!
@@ -70,7 +173,9 @@ module ActionController
# Don't set the Content-Length for block-based bodies as that would mean reading it all into memory. Not nice
# for, say, a 2GB streaming file.
def set_content_length!
- self.headers["Content-Length"] = body.size unless body.respond_to?(:call)
+ unless body.respond_to?(:call) || (status && status[0..2] == '304')
+ self.headers["Content-Length"] ||= body.size
+ end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
index dfbaa53b7c..8d51e823a6 100644
--- a/actionpack/lib/action_controller/routing.rb
+++ b/actionpack/lib/action_controller/routing.rb
@@ -201,7 +201,7 @@ module ActionController
# With conditions you can define restrictions on routes. Currently the only valid condition is <tt>:method</tt>.
#
# * <tt>:method</tt> - Allows you to specify which method can access the route. Possible values are <tt>:post</tt>,
- # <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. The default value is <tt>:any</tt>,
+ # <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. The default value is <tt>:any</tt>,
# <tt>:any</tt> means that any method can access the route.
#
# Example:
@@ -213,7 +213,7 @@ module ActionController
#
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
# URL will route to the <tt>show</tt> action.
- #
+ #
# == Reloading routes
#
# You can reload routes if you feel you must:
@@ -281,9 +281,9 @@ module ActionController
end
class << self
- # Expects an array of controller names as the first argument.
- # Executes the passed block with only the named controllers named available.
- # This method is used in internal Rails testing.
+ # Expects an array of controller names as the first argument.
+ # Executes the passed block with only the named controllers named available.
+ # This method is used in internal Rails testing.
def with_controllers(names)
prior_controllers = @possible_controllers
use_controllers! names
@@ -292,10 +292,10 @@ module ActionController
use_controllers! prior_controllers
end
- # Returns an array of paths, cleaned of double-slashes and relative path references.
- # * "\\\" and "//" become "\\" or "/".
- # * "/foo/bar/../config" becomes "/foo/config".
- # The returned array is sorted by length, descending.
+ # Returns an array of paths, cleaned of double-slashes and relative path references.
+ # * "\\\" and "//" become "\\" or "/".
+ # * "/foo/bar/../config" becomes "/foo/config".
+ # The returned array is sorted by length, descending.
def normalize_paths(paths)
# do the hokey-pokey of path normalization...
paths = paths.collect do |path|
@@ -314,7 +314,7 @@ module ActionController
paths = paths.uniq.sort_by { |path| - path.length }
end
- # Returns the array of controller names currently available to ActionController::Routing.
+ # Returns the array of controller names currently available to ActionController::Routing.
def possible_controllers
unless @possible_controllers
@possible_controllers = []
@@ -339,28 +339,27 @@ module ActionController
@possible_controllers
end
- # Replaces the internal list of controllers available to ActionController::Routing with the passed argument.
- # ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ])
+ # Replaces the internal list of controllers available to ActionController::Routing with the passed argument.
+ # ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ])
def use_controllers!(controller_names)
@possible_controllers = controller_names
end
- # Returns a controller path for a new +controller+ based on a +previous+ controller path.
- # Handles 4 scenarios:
- #
- # * stay in the previous controller:
- # controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion"
- #
- # * stay in the previous namespace:
- # controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts"
- #
- # * forced move to the root namespace:
- # controller_relative_to( "/posts", "groups/discussion" ) # => "posts"
- #
- # * previous namespace is root:
- # controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts"
- #
-
+ # Returns a controller path for a new +controller+ based on a +previous+ controller path.
+ # Handles 4 scenarios:
+ #
+ # * stay in the previous controller:
+ # controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion"
+ #
+ # * stay in the previous namespace:
+ # controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts"
+ #
+ # * forced move to the root namespace:
+ # controller_relative_to( "/posts", "groups/discussion" ) # => "posts"
+ #
+ # * previous namespace is root:
+ # controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts"
+ #
def controller_relative_to(controller, previous)
if controller.nil? then previous
elsif controller[0] == ?/ then controller[1..-1]
@@ -369,12 +368,11 @@ module ActionController
end
end
end
-
Routes = RouteSet.new
ActiveSupport::Inflector.module_eval do
- # Ensures that routes are reloaded when Rails inflections are updated.
+ # Ensures that routes are reloaded when Rails inflections are updated.
def inflections_with_route_reloading(&block)
returning(inflections_without_route_reloading(&block)) {
ActionController::Routing::Routes.reload! if block_given?
diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb
index b8323847fd..03427e41de 100644
--- a/actionpack/lib/action_controller/routing/builder.rb
+++ b/actionpack/lib/action_controller/routing/builder.rb
@@ -48,14 +48,10 @@ module ActionController
end
when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true)
when /\A\?(.*?)\?/
- returning segment = StaticSegment.new($1) do
- segment.is_optional = true
- end
+ StaticSegment.new($1, :optional => true)
when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1)
when Regexp.new(separator_pattern) then
- returning segment = DividerSegment.new($&) do
- segment.is_optional = (optional_separators.include? $&)
- end
+ DividerSegment.new($&, :optional => (optional_separators.include? $&))
end
[segment, $~.post_match]
end
@@ -76,6 +72,8 @@ module ActionController
defaults = (options.delete(:defaults) || {}).dup
conditions = (options.delete(:conditions) || {}).dup
+ validate_route_conditions(conditions)
+
path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact
options.each do |key, value|
hash = (path_keys.include?(key) && ! value.is_a?(Regexp)) ? defaults : requirements
@@ -174,30 +172,30 @@ module ActionController
defaults, requirements, conditions = divide_route_options(segments, options)
requirements = assign_route_options(segments, defaults, requirements)
- route = Route.new
-
- route.segments = segments
- route.requirements = requirements
- route.conditions = conditions
+ # TODO: Segments should be frozen on initialize
+ segments.each { |segment| segment.freeze }
- if !route.significant_keys.include?(:action) && !route.requirements[:action]
- route.requirements[:action] = "index"
- route.significant_keys << :action
- end
-
- # Routes cannot use the current string interpolation method
- # if there are user-supplied <tt>:requirements</tt> as the interpolation
- # code won't raise RoutingErrors when generating
- if options.key?(:requirements) || route.requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION
- route.optimise = false
- end
+ route = Route.new(segments, requirements, conditions)
if !route.significant_keys.include?(:controller)
raise ArgumentError, "Illegal route: the :controller must be specified!"
end
- route
+ route.freeze
end
+
+ private
+ def validate_route_conditions(conditions)
+ if method = conditions[:method]
+ if method == :head
+ raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers"
+ end
+
+ unless HTTP_METHODS.include?(method.to_sym)
+ raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}"
+ end
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb
index cd4a423e6b..0fe836606c 100644
--- a/actionpack/lib/action_controller/routing/optimisations.rb
+++ b/actionpack/lib/action_controller/routing/optimisations.rb
@@ -1,14 +1,14 @@
module ActionController
module Routing
- # Much of the slow performance from routes comes from the
+ # Much of the slow performance from routes comes from the
# complexity of expiry, <tt>:requirements</tt> matching, defaults providing
- # and figuring out which url pattern to use. With named routes
- # we can avoid the expense of finding the right route. So if
+ # and figuring out which url pattern to use. With named routes
+ # we can avoid the expense of finding the right route. So if
# they've provided the right number of arguments, and have no
# <tt>:requirements</tt>, we can just build up a string and return it.
- #
- # To support building optimisations for other common cases, the
- # generation code is separated into several classes
+ #
+ # To support building optimisations for other common cases, the
+ # generation code is separated into several classes
module Optimisation
def generate_optimisation_block(route, kind)
return "" unless route.optimise?
@@ -20,6 +20,7 @@ module ActionController
class Optimiser
attr_reader :route, :kind
+
def initialize(route, kind)
@route = route
@kind = kind
@@ -53,12 +54,12 @@ module ActionController
# map.person '/people/:id'
#
# If the user calls <tt>person_url(@person)</tt>, we can simply
- # return a string like "/people/#{@person.to_param}"
+ # return a string like "/people/#{@person.to_param}"
# rather than triggering the expensive logic in +url_for+.
class PositionalArguments < Optimiser
def guard_condition
number_of_arguments = route.segment_keys.size
- # if they're using foo_url(:id=>2) it's one
+ # if they're using foo_url(:id=>2) it's one
# argument, but we don't want to generate /foos/id2
if number_of_arguments == 1
"(!defined?(default_url_options) || default_url_options.blank?) && defined?(request) && request && args.size == 1 && !args.first.is_a?(Hash)"
@@ -76,7 +77,7 @@ module ActionController
elements << '#{request.host_with_port}'
end
- elements << '#{request.relative_url_root if request.relative_url_root}'
+ elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}'
# The last entry in <tt>route.segments</tt> appears to *always* be a
# 'divider segment' for '/' but we have assertions to ensure that
@@ -94,14 +95,14 @@ module ActionController
end
# This case is mostly the same as the positional arguments case
- # above, but it supports additional query parameters as the last
+ # above, but it supports additional query parameters as the last
# argument
class PositionalArgumentsWithAdditionalParams < PositionalArguments
def guard_condition
"(!defined?(default_url_options) || default_url_options.blank?) && defined?(request) && request && args.size == #{route.segment_keys.size + 1} && !args.last.has_key?(:anchor) && !args.last.has_key?(:port) && !args.last.has_key?(:host)"
end
- # This case uses almost the same code as positional arguments,
+ # This case uses almost the same code as positional arguments,
# but add an args.last.to_query on the end
def generation_code
super.insert(-2, '?#{args.last.to_query}')
@@ -110,7 +111,7 @@ module ActionController
# To avoid generating "http://localhost/?host=foo.example.com" we
# can't use this optimisation on routes without any segments
def applicable?
- super && route.segment_keys.size > 0
+ super && route.segment_keys.size > 0
end
end
diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
index cf8f5232c1..6d54d0334c 100644
--- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb
+++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
@@ -51,7 +51,6 @@ module ActionController
# 3) segm test for /users/:id
# (jump to list index = 5)
# 4) full test for /users/:id => here we are!
-
class RouteSet
def recognize_path(path, environment={})
result = recognize_optimized(path, environment) and return result
@@ -68,28 +67,6 @@ module ActionController
end
end
- def recognize_optimized(path, env)
- write_recognize_optimized
- recognize_optimized(path, env)
- end
-
- def write_recognize_optimized
- tree = segment_tree(routes)
- body = generate_code(tree)
- instance_eval %{
- def recognize_optimized(path, env)
- segments = to_plain_segments(path)
- index = #{body}
- return nil unless index
- while index < routes.size
- result = routes[index].recognize(path, env) and return result
- index += 1
- end
- nil
- end
- }, __FILE__, __LINE__
- end
-
def segment_tree(routes)
tree = [0]
@@ -153,6 +130,23 @@ module ActionController
segments
end
+ private
+ def write_recognize_optimized!
+ tree = segment_tree(routes)
+ body = generate_code(tree)
+ instance_eval %{
+ def recognize_optimized(path, env)
+ segments = to_plain_segments(path)
+ index = #{body}
+ return nil unless index
+ while index < routes.size
+ result = routes[index].recognize(path, env) and return result
+ index += 1
+ end
+ nil
+ end
+ }, __FILE__, __LINE__
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb
index a0d108ba03..2106ac09e0 100644
--- a/actionpack/lib/action_controller/routing/route.rb
+++ b/actionpack/lib/action_controller/routing/route.rb
@@ -3,11 +3,25 @@ module ActionController
class Route #:nodoc:
attr_accessor :segments, :requirements, :conditions, :optimise
- def initialize
- @segments = []
- @requirements = {}
- @conditions = {}
- @optimise = true
+ def initialize(segments = [], requirements = {}, conditions = {})
+ @segments = segments
+ @requirements = requirements
+ @conditions = conditions
+
+ if !significant_keys.include?(:action) && !requirements[:action]
+ @requirements[:action] = "index"
+ @significant_keys << :action
+ end
+
+ # Routes cannot use the current string interpolation method
+ # if there are user-supplied <tt>:requirements</tt> as the interpolation
+ # code won't raise RoutingErrors when generating
+ has_requirements = @segments.detect { |segment| segment.respond_to?(:regexp) && segment.regexp }
+ if has_requirements || @requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION
+ @optimise = false
+ else
+ @optimise = true
+ end
end
# Indicates whether the routes should be optimised with the string interpolation
@@ -22,129 +36,6 @@ module ActionController
end.compact
end
- # Write and compile a +generate+ method for this Route.
- def write_generation
- # Build the main body of the generation
- body = "expired = false\n#{generation_extraction}\n#{generation_structure}"
-
- # If we have conditions that must be tested first, nest the body inside an if
- body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements
- args = "options, hash, expire_on = {}"
-
- # Nest the body inside of a def block, and then compile it.
- raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend"
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
-
- # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash
- # are the same as the keys that were recalled from the previous request. Thus,
- # we can use the expire_on.keys to determine which keys ought to be used to build
- # the query string. (Never use keys from the recalled request when building the
- # query string.)
-
- method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend"
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
-
- method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend"
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
- raw_method
- end
-
- # Build several lines of code that extract values from the options hash. If any
- # of the values are missing or rejected then a return will be executed.
- def generation_extraction
- segments.collect do |segment|
- segment.extraction_code
- end.compact * "\n"
- end
-
- # Produce a condition expression that will check the requirements of this route
- # upon generation.
- def generation_requirements
- requirement_conditions = requirements.collect do |key, req|
- if req.is_a? Regexp
- value_regexp = Regexp.new "\\A#{req.to_s}\\Z"
- "hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]"
- else
- "hash[:#{key}] == #{req.inspect}"
- end
- end
- requirement_conditions * ' && ' unless requirement_conditions.empty?
- end
-
- def generation_structure
- segments.last.string_structure segments[0..-2]
- end
-
- # Write and compile a +recognize+ method for this Route.
- def write_recognition
- # Create an if structure to extract the params from a match if it occurs.
- body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
- body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"
-
- # Build the method declaration and compile it
- method_decl = "def recognize(path, env={})\n#{body}\nend"
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
- method_decl
- end
-
- # Plugins may override this method to add other conditions, like checks on
- # host, subdomain, and so forth. Note that changes here only affect route
- # recognition, not generation.
- def recognition_conditions
- result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"]
- result << "conditions[:method] === env[:method]" if conditions[:method]
- result
- end
-
- # Build the regular expression pattern that will match this route.
- def recognition_pattern(wrap = true)
- pattern = ''
- segments.reverse_each do |segment|
- pattern = segment.build_pattern pattern
- end
- wrap ? ("\\A" + pattern + "\\Z") : pattern
- end
-
- # Write the code to extract the parameters from a matched route.
- 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
- x
- end
- extraction.compact
- end
-
- # Write the real generation implementation and then resend the message.
- def generate(options, hash, expire_on = {})
- write_generation
- generate options, hash, expire_on
- end
-
- def generate_extras(options, hash, expire_on = {})
- write_generation
- generate_extras options, hash, expire_on
- end
-
- # Generate the query string with any extra keys in the hash and append
- # it to the given path, returning the new path.
- def append_query_string(path, hash, query_keys=nil)
- return nil unless path
- query_keys ||= extra_keys(hash)
- "#{path}#{build_query_string(hash, query_keys)}"
- end
-
- # Determine which keys in the given hash are "extra". Extra keys are
- # those that were not used to generate a particular route. The extra
- # keys also do not include those recalled from the prior request, nor
- # do they include any keys that were implied in the route (like a
- # <tt>:controller</tt> that is required, but not explicitly used in the
- # text of the route.)
- def extra_keys(hash, recall={})
- (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
- end
-
# Build a query string from the keys of the given hash. If +only_keys+
# is given (as an array), only the keys indicated will be used to build
# the query string. The query string will correctly build array parameter
@@ -161,12 +52,6 @@ module ActionController
elements.empty? ? '' : "?#{elements.sort * '&'}"
end
- # Write the real recognition implementation and then resend the message.
- def recognize(path, environment={})
- write_recognition
- recognize path, environment
- end
-
# A route's parameter shell contains parameter values that are not in the
# route's path, but should be placed in the recognized hash.
#
@@ -186,7 +71,7 @@ module ActionController
# includes keys that appear inside the path, and keys that have requirements
# placed upon them.
def significant_keys
- @significant_keys ||= returning [] do |sk|
+ @significant_keys ||= returning([]) do |sk|
segments.each { |segment| sk << segment.key if segment.respond_to? :key }
sk.concat requirements.keys
sk.uniq!
@@ -209,12 +94,7 @@ module ActionController
end
def matches_controller_and_action?(controller, action)
- unless defined? @matching_prepared
- @controller_requirement = requirement_for(:controller)
- @action_requirement = requirement_for(:action)
- @matching_prepared = true
- end
-
+ prepare_matching!
(@controller_requirement.nil? || @controller_requirement === controller) &&
(@action_requirement.nil? || @action_requirement === action)
end
@@ -226,15 +106,150 @@ module ActionController
end
end
- protected
- def requirement_for(key)
- return requirements[key] if requirements.key? key
- segments.each do |segment|
- return segment.regexp if segment.respond_to?(:key) && segment.key == key
+ # TODO: Route should be prepared and frozen on initialize
+ def freeze
+ unless frozen?
+ write_generation!
+ write_recognition!
+ prepare_matching!
+
+ parameter_shell
+ significant_keys
+ defaults
+ to_s
end
- nil
+
+ super
end
+ private
+ def requirement_for(key)
+ return requirements[key] if requirements.key? key
+ segments.each do |segment|
+ return segment.regexp if segment.respond_to?(:key) && segment.key == key
+ end
+ nil
+ end
+
+ # Write and compile a +generate+ method for this Route.
+ def write_generation!
+ # Build the main body of the generation
+ body = "expired = false\n#{generation_extraction}\n#{generation_structure}"
+
+ # If we have conditions that must be tested first, nest the body inside an if
+ body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements
+ args = "options, hash, expire_on = {}"
+
+ # Nest the body inside of a def block, and then compile it.
+ raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend"
+ instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+
+ # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash
+ # are the same as the keys that were recalled from the previous request. Thus,
+ # we can use the expire_on.keys to determine which keys ought to be used to build
+ # the query string. (Never use keys from the recalled request when building the
+ # query string.)
+
+ method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend"
+ instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+
+ method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend"
+ instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+ raw_method
+ end
+
+ # Build several lines of code that extract values from the options hash. If any
+ # of the values are missing or rejected then a return will be executed.
+ def generation_extraction
+ segments.collect do |segment|
+ segment.extraction_code
+ end.compact * "\n"
+ end
+
+ # Produce a condition expression that will check the requirements of this route
+ # upon generation.
+ def generation_requirements
+ requirement_conditions = requirements.collect do |key, req|
+ if req.is_a? Regexp
+ value_regexp = Regexp.new "\\A#{req.to_s}\\Z"
+ "hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]"
+ else
+ "hash[:#{key}] == #{req.inspect}"
+ end
+ end
+ requirement_conditions * ' && ' unless requirement_conditions.empty?
+ end
+
+ def generation_structure
+ segments.last.string_structure segments[0..-2]
+ end
+
+ # Write and compile a +recognize+ method for this Route.
+ def write_recognition!
+ # Create an if structure to extract the params from a match if it occurs.
+ body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
+ body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"
+
+ # Build the method declaration and compile it
+ method_decl = "def recognize(path, env = {})\n#{body}\nend"
+ instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
+ method_decl
+ end
+
+ # Plugins may override this method to add other conditions, like checks on
+ # host, subdomain, and so forth. Note that changes here only affect route
+ # recognition, not generation.
+ def recognition_conditions
+ result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"]
+ result << "conditions[:method] === env[:method]" if conditions[:method]
+ result
+ end
+
+ # Build the regular expression pattern that will match this route.
+ def recognition_pattern(wrap = true)
+ pattern = ''
+ segments.reverse_each do |segment|
+ pattern = segment.build_pattern pattern
+ end
+ wrap ? ("\\A" + pattern + "\\Z") : pattern
+ end
+
+ # Write the code to extract the parameters from a matched route.
+ 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
+ x
+ end
+ extraction.compact
+ end
+
+ # Generate the query string with any extra keys in the hash and append
+ # it to the given path, returning the new path.
+ def append_query_string(path, hash, query_keys = nil)
+ return nil unless path
+ query_keys ||= extra_keys(hash)
+ "#{path}#{build_query_string(hash, query_keys)}"
+ end
+
+ # Determine which keys in the given hash are "extra". Extra keys are
+ # those that were not used to generate a particular route. The extra
+ # keys also do not include those recalled from the prior request, nor
+ # do they include any keys that were implied in the route (like a
+ # <tt>:controller</tt> that is required, but not explicitly used in the
+ # text of the route.)
+ def extra_keys(hash, recall = {})
+ (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
+ end
+
+ def prepare_matching!
+ unless defined? @matching_prepared
+ @controller_requirement = requirement_for(:controller)
+ @action_requirement = requirement_for(:action)
+ @matching_prepared = true
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb
index 5bc13cf268..8dfc22f94f 100644
--- a/actionpack/lib/action_controller/routing/route_set.rb
+++ b/actionpack/lib/action_controller/routing/route_set.rb
@@ -1,6 +1,6 @@
module ActionController
module Routing
- class RouteSet #:nodoc:
+ class RouteSet #:nodoc:
# Mapper instances are used to build routes. The object passed to the draw
# block in config/routes.rb is a Mapper instance.
#
@@ -194,6 +194,8 @@ module ActionController
def initialize
self.routes = []
self.named_routes = NamedRouteCollection.new
+
+ write_recognize_optimized!
end
# Subclasses and plugins may override this method to specify a different
@@ -231,7 +233,6 @@ module ActionController
Routing.use_controllers! nil # Clear the controller cache so we may discover new ones
clear!
load_routes!
- install_helpers
end
# reload! will always force a reload whereas load checks the timestamp first
@@ -432,4 +433,4 @@ module ActionController
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/routing/routing_ext.rb b/actionpack/lib/action_controller/routing/routing_ext.rb
index 2ad20ee699..5f4ba90d0c 100644
--- a/actionpack/lib/action_controller/routing/routing_ext.rb
+++ b/actionpack/lib/action_controller/routing/routing_ext.rb
@@ -1,4 +1,3 @@
-
class Object
def to_param
to_s
diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb
index f0ad066bad..9d4b740a44 100644
--- a/actionpack/lib/action_controller/routing/segments.rb
+++ b/actionpack/lib/action_controller/routing/segments.rb
@@ -2,13 +2,15 @@ module ActionController
module Routing
class Segment #:nodoc:
RESERVED_PCHAR = ':@&=+$,;'
- UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze
+ SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
+ UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
+ # TODO: Convert :is_optional accessor to read only
attr_accessor :is_optional
alias_method :optional?, :is_optional
def initialize
- self.is_optional = false
+ @is_optional = false
end
def extraction_code
@@ -63,12 +65,14 @@ module ActionController
end
class StaticSegment < Segment #:nodoc:
- attr_accessor :value, :raw
+ attr_reader :value, :raw
alias_method :raw?, :raw
- def initialize(value = nil)
+ def initialize(value = nil, options = {})
super()
- self.value = value
+ @value = value
+ @raw = options[:raw] if options.key?(:raw)
+ @is_optional = options[:optional] if options.key?(:optional)
end
def interpolation_chunk
@@ -97,10 +101,8 @@ module ActionController
end
class DividerSegment < StaticSegment #:nodoc:
- def initialize(value = nil)
- super(value)
- self.raw = true
- self.is_optional = true
+ def initialize(value = nil, options = {})
+ super(value, {:raw => true, :optional => true}.merge(options))
end
def optionality_implied?
@@ -109,13 +111,17 @@ module ActionController
end
class DynamicSegment < Segment #:nodoc:
- attr_accessor :key, :default, :regexp
+ attr_reader :key
+
+ # TODO: Convert these accessors to read only
+ attr_accessor :default, :regexp
def initialize(key = nil, options = {})
super()
- self.key = key
- self.default = options[:default] if options.key? :default
- self.is_optional = true if options[:optional] || options.key?(:default)
+ @key = key
+ @default = options[:default] if options.key?(:default)
+ @regexp = options[:regexp] if options.key?(:regexp)
+ @is_optional = true if options[:optional] || options.key?(:default)
end
def to_s
@@ -130,6 +136,7 @@ module ActionController
def extract_value
"#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}"
end
+
def value_check
if default # Then we know it won't be nil
"#{value_regexp.inspect} =~ #{local_name}" if regexp
@@ -141,6 +148,7 @@ module ActionController
"#{local_name} #{"&& #{value_regexp.inspect} =~ #{local_name}" if regexp}"
end
end
+
def expiry_statement
"expired, hash = true, options if !expired && expire_on[:#{key}]"
end
@@ -175,7 +183,7 @@ module ActionController
end
def regexp_chunk
- if regexp
+ if regexp
if regexp_has_modifiers?
"(#{regexp.to_s})"
else
@@ -214,7 +222,6 @@ module ActionController
def regexp_has_modifiers?
regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
end
-
end
class ControllerSegment < DynamicSegment #:nodoc:
diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb
index b477c1f7da..5bf7503f04 100644
--- a/actionpack/lib/action_controller/session/cookie_store.rb
+++ b/actionpack/lib/action_controller/session/cookie_store.rb
@@ -129,7 +129,7 @@ class CGI::Session::CookieStore
private
# Marshal a session hash into safe cookie data. Include an integrity hash.
def marshal(session)
- data = ActiveSupport::Base64.encode64(Marshal.dump(session)).chop
+ data = ActiveSupport::Base64.encode64s(Marshal.dump(session))
"#{data}--#{generate_digest(data)}"
end
diff --git a/actionpack/lib/action_controller/session/drb_server.rb b/actionpack/lib/action_controller/session/drb_server.rb
index 6f90db6747..2caa27f62a 100644..100755
--- a/actionpack/lib/action_controller/session/drb_server.rb
+++ b/actionpack/lib/action_controller/session/drb_server.rb
@@ -1,8 +1,8 @@
-#!/usr/local/bin/ruby -w
-
-# This is a really simple session storage daemon, basically just a hash,
+#!/usr/bin/env ruby
+
+# This is a really simple session storage daemon, basically just a hash,
# which is enabled for DRb access.
-
+
require 'drb'
session_hash = Hash.new
@@ -14,13 +14,13 @@ class <<session_hash
super(key, value)
end
end
-
+
def [](key)
@mutex.synchronize do
super(key)
end
end
-
+
def delete(key)
@mutex.synchronize do
super(key)
@@ -29,4 +29,4 @@ class <<session_hash
end
DRb.start_service('druby://127.0.0.1:9192', session_hash)
-DRb.thread.join \ No newline at end of file
+DRb.thread.join
diff --git a/actionpack/lib/action_controller/streaming.rb b/actionpack/lib/action_controller/streaming.rb
index 186e0e5531..333fb61b45 100644
--- a/actionpack/lib/action_controller/streaming.rb
+++ b/actionpack/lib/action_controller/streaming.rb
@@ -12,19 +12,21 @@ module ActionController #:nodoc:
X_SENDFILE_HEADER = 'X-Sendfile'.freeze
protected
- # Sends the file by streaming it 4096 bytes at a time. This way the
- # whole file doesn't need to be read into memory at once. This makes
- # it feasible to send even large files.
+ # Sends the file, by default streaming it 4096 bytes at a time. This way the
+ # whole file doesn't need to be read into memory at once. This makes it
+ # feasible to send even large files. You can optionally turn off streaming
+ # and send the whole file at once.
#
- # Be careful to sanitize the path parameter if it coming from a web
+ # Be careful to sanitize the path parameter if it is coming from a web
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
# download any file on your server.
#
# Options:
# * <tt>:filename</tt> - suggests a filename for the browser to use.
# Defaults to <tt>File.basename(path)</tt>.
- # * <tt>:type</tt> - specifies an HTTP content type.
- # Defaults to 'application/octet-stream'.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
+ # * <tt>:length</tt> - used to manually override the length (in bytes) of the content that
+ # is going to be sent to the client. Defaults to <tt>File.size(path)</tt>.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
# * <tt>:stream</tt> - whether to send the file to the user agent as it is read (+true+)
@@ -35,6 +37,12 @@ module ActionController #:nodoc:
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
# the URL, which is necessary for i18n filenames on certain browsers
# (setting <tt>:filename</tt> overrides this option).
+ # * <tt>:x_sendfile</tt> - uses X-Sendfile to send the file when set to +true+. This is currently
+ # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this
+ # uses the web server to send the file, this may lower memory consumption on your server and
+ # it will not block your application for further requests.
+ # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and
+ # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+.
#
# The default Content-Type and Content-Disposition headers are
# set to download arbitrary binary files in as many browsers as
@@ -99,8 +107,7 @@ module ActionController #:nodoc:
#
# Options:
# * <tt>:filename</tt> - suggests a filename for the browser to use.
- # * <tt>:type</tt> - specifies an HTTP content type.
- # Defaults to 'application/octet-stream'.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
diff --git a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb
index 385c6c1b09..b5483eccae 100644
--- a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb
+++ b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb
@@ -6,6 +6,6 @@
</h1>
<pre><%=h @exception.clean_message %></pre>
-<%= render(:file => @rescues_path + "/_trace.erb", :use_full_path => false) %>
+<%= render(:file => @rescues_path + "/_trace.erb") %>
-<%= render(:file => @rescues_path + "/_request_and_response.erb", :use_full_path => false) %>
+<%= render(:file => @rescues_path + "/_request_and_response.erb") %>
diff --git a/actionpack/lib/action_controller/templates/rescues/template_error.erb b/actionpack/lib/action_controller/templates/rescues/template_error.erb
index 4aecc68d18..76fa3df89d 100644
--- a/actionpack/lib/action_controller/templates/rescues/template_error.erb
+++ b/actionpack/lib/action_controller/templates/rescues/template_error.erb
@@ -15,7 +15,7 @@
<% @real_exception = @exception
@exception = @exception.original_exception || @exception %>
-<%= render(:file => @rescues_path + "/_trace.erb", :use_full_path => false) %>
+<%= render(:file => @rescues_path + "/_trace.erb") %>
<% @exception = @real_exception %>
-<%= render(:file => @rescues_path + "/_request_and_response.erb", :use_full_path => false) %>
+<%= render(:file => @rescues_path + "/_request_and_response.erb") %>
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 77c6f26eac..3e66947d5f 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -15,6 +15,65 @@ module ActionController
end
end
+ # Superclass for ActionController functional tests. Functional tests allow you to
+ # test a single controller action per test method. This should not be confused with
+ # integration tests (see ActionController::IntegrationTest), which are more like
+ # "stories" that can involve multiple controllers and mutliple actions (i.e. multiple
+ # different HTTP requests).
+ #
+ # == Basic example
+ #
+ # Functional tests are written as follows:
+ # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
+ # an HTTP request.
+ # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
+ # the controller's HTTP response, the database contents, etc.
+ #
+ # For example:
+ #
+ # class BooksControllerTest < ActionController::TestCase
+ # def test_create
+ # # Simulate a POST response with the given HTTP parameters.
+ # post(:create, :book => { :title => "Love Hina" })
+ #
+ # # Assert that the controller tried to redirect us to
+ # # the created book's URI.
+ # assert_response :found
+ #
+ # # Assert that the controller really put the book in the database.
+ # assert_not_nil Book.find_by_title("Love Hina")
+ # end
+ # end
+ #
+ # == Special instance variables
+ #
+ # ActionController::TestCase will also automatically provide the following instance
+ # variables for use in the tests:
+ #
+ # <b>@controller</b>::
+ # The controller instance that will be tested.
+ # <b>@request</b>::
+ # An ActionController::TestRequest, representing the current HTTP
+ # request. You can modify this object before sending the HTTP request. For example,
+ # you might want to set some session properties before sending a GET request.
+ # <b>@response</b>::
+ # An ActionController::TestResponse object, representing the response
+ # of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
+ # after calling +post+. If the various assert methods are not sufficient, then you
+ # may use this object to inspect the HTTP response in detail.
+ #
+ # (Earlier versions of Rails required each functional test to subclass
+ # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
+ #
+ # == Controller is automatically inferred
+ #
+ # ActionController::TestCase will automatically infer the controller under test
+ # from the test class name. If the controller cannot be inferred from the test
+ # class name, you can explicity set it with +tests+.
+ #
+ # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
+ # tests WidgetController
+ # end
class TestCase < ActiveSupport::TestCase
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
# (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
@@ -41,6 +100,8 @@ module ActionController
@@controller_class = nil
class << self
+ # Sets the controller class name. Useful if the name can't be inferred from test class.
+ # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
def tests(controller_class)
self.controller_class = controller_class
end
@@ -80,4 +141,4 @@ module ActionController
@request.remote_addr = '208.77.188.166' # example.com
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index a6e0c98936..c6b1470070 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -23,7 +23,7 @@ module ActionController #:nodoc:
class TestRequest < AbstractRequest #:nodoc:
attr_accessor :cookies, :session_options
- attr_accessor :query_parameters, :request_parameters, :path, :session, :env
+ attr_accessor :query_parameters, :request_parameters, :path, :session
attr_accessor :host, :user_agent
def initialize(query_parameters = nil, request_parameters = nil, session = nil)
@@ -42,7 +42,7 @@ module ActionController #:nodoc:
end
# Wraps raw_post in a StringIO.
- def body
+ def body_stream #:nodoc:
StringIO.new(raw_post)
end
@@ -54,7 +54,7 @@ module ActionController #:nodoc:
def port=(number)
@env["SERVER_PORT"] = number.to_i
- @port_as_int = nil
+ port(true)
end
def action=(action_name)
@@ -68,6 +68,8 @@ module ActionController #:nodoc:
@env["REQUEST_URI"] = value
@request_uri = nil
@path = nil
+ request_uri(true)
+ path(true)
end
def request_uri=(uri)
@@ -77,21 +79,26 @@ module ActionController #:nodoc:
def accept=(mime_types)
@env["HTTP_ACCEPT"] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(",")
+ accepts(true)
end
- def remote_addr=(addr)
- @env['REMOTE_ADDR'] = addr
+ def if_modified_since=(last_modified)
+ @env["HTTP_IF_MODIFIED_SINCE"] = last_modified
end
- def remote_addr
- @env['REMOTE_ADDR']
+ def if_none_match=(etag)
+ @env["HTTP_IF_NONE_MATCH"] = etag
end
- def request_uri
+ def remote_addr=(addr)
+ @env['REMOTE_ADDR'] = addr
+ end
+
+ def request_uri(*args)
@request_uri || super
end
- def path
+ def path(*args)
@path || super
end
@@ -113,17 +120,13 @@ module ActionController #:nodoc:
end
end
@parameters = nil # reset TestRequest#parameters to use the new path_parameters
- end
-
+ end
+
def recycle!
self.request_parameters = {}
self.query_parameters = {}
self.path_parameters = {}
- @request_method, @accepts, @content_type = nil, nil, nil
- end
-
- def referer
- @env["HTTP_REFERER"]
+ unmemoize_all
end
private
@@ -135,7 +138,7 @@ module ActionController #:nodoc:
@host = "test.host"
@request_uri = "/"
@user_agent = "Rails Testing"
- self.remote_addr = "0.0.0.0"
+ self.remote_addr = "0.0.0.0"
@env["SERVER_PORT"] = 80
@env['REQUEST_METHOD'] = "GET"
end
@@ -157,21 +160,21 @@ module ActionController #:nodoc:
module TestResponseBehavior #:nodoc:
# The response code of the request
def response_code
- headers['Status'][0,3].to_i rescue 0
+ status[0,3].to_i rescue 0
end
-
+
# Returns a String to ensure compatibility with Net::HTTPResponse
def code
- headers['Status'].to_s.split(' ')[0]
+ status.to_s.split(' ')[0]
end
def message
- headers['Status'].to_s.split(' ',2)[1]
+ status.to_s.split(' ',2)[1]
end
# Was the response successful?
def success?
- response_code == 200
+ (200..299).include?(response_code)
end
# Was the URL not found?
@@ -211,7 +214,7 @@ module ActionController #:nodoc:
template._first_render
end
- # A shortcut to the flash. Returns an empyt hash if no session flash exists.
+ # A shortcut to the flash. Returns an empty hash if no session flash exists.
def flash
session['flash'] || {}
end
@@ -243,11 +246,11 @@ module ActionController #:nodoc:
# Does the specified template object exist?
def has_template_object?(name=nil)
- !template_objects[name].nil?
+ !template_objects[name].nil?
end
# Returns the response cookies, converted to a Hash of (name => CGI::Cookie) pairs
- #
+ #
# assert_equal ['AuthorOfNewPage'], r.cookies['author'].value
def cookies
headers['cookie'].inject({}) { |hash, cookie| hash[cookie.name] = cookie; hash }
@@ -266,7 +269,13 @@ module ActionController #:nodoc:
end
end
- class TestResponse < AbstractResponse #:nodoc:
+ # Integration test methods such as ActionController::Integration::Session#get
+ # and ActionController::Integration::Session#post return objects of class
+ # TestResponse, which represent the HTTP response results of the requested
+ # controller actions.
+ #
+ # See AbstractResponse for more information on controller response objects.
+ class TestResponse < AbstractResponse
include TestResponseBehavior
end
@@ -313,7 +322,7 @@ module ActionController #:nodoc:
#
# Usage example, within a functional test:
# post :change_avatar, :avatar => ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/spongebob.png', 'image/png')
- #
+ #
# Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows):
# post :change_avatar, :avatar => ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary)
require 'tempfile'
@@ -348,6 +357,7 @@ module ActionController #:nodoc:
module TestProcess
def self.included(base)
# execute the request simulating a specific HTTP method and set/volley the response
+ # TODO: this should be un-DRY'ed for the sake of API documentation.
%w( get post put delete head ).each do |method|
base.class_eval <<-EOV, __FILE__, __LINE__
def #{method}(action, parameters = nil, session = nil, flash = nil)
@@ -393,13 +403,13 @@ module ActionController #:nodoc:
end
alias xhr :xml_http_request
- def assigns(key = nil)
- if key.nil?
- @response.template.assigns
- else
- @response.template.assigns[key.to_s]
- end
- end
+ def assigns(key = nil)
+ if key.nil?
+ @response.template.assigns
+ else
+ @response.template.assigns[key.to_s]
+ end
+ end
def session
@response.session
@@ -441,10 +451,13 @@ module ActionController #:nodoc:
end
def method_missing(selector, *args)
- return @controller.send!(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
- return super
+ if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+ @controller.send(selector, *args)
+ else
+ super
+ end
end
-
+
# Shortcut for <tt>ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type)</tt>:
#
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
@@ -455,7 +468,7 @@ module ActionController #:nodoc:
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
def fixture_file_upload(path, mime_type = nil, binary = false)
ActionController::TestUploadedFile.new(
- Test::Unit::TestCase.respond_to?(:fixture_path) ? Test::Unit::TestCase.fixture_path + path : path,
+ Test::Unit::TestCase.respond_to?(:fixture_path) ? Test::Unit::TestCase.fixture_path + path : path,
mime_type,
binary
)
@@ -463,7 +476,7 @@ module ActionController #:nodoc:
# A helper to make it easier to test different route configurations.
# This method temporarily replaces ActionController::Routing::Routes
- # with a new RouteSet instance.
+ # with a new RouteSet instance.
#
# The new instance is yielded to the passed block. Typically the block
# will create some routes using <tt>map.draw { map.connect ... }</tt>:
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
index 3a38f23396..d86e2db67d 100644
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ b/actionpack/lib/action_controller/url_rewriter.rb
@@ -1,19 +1,96 @@
module ActionController
- # Write URLs from arbitrary places in your codebase, such as your mailers.
+ # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
+ # is also possible: an URL can be generated from one of your routing definitions.
+ # URL generation functionality is centralized in this module.
#
- # Example:
+ # See ActionController::Routing and ActionController::Resources for general
+ # information about routing and routes.rb.
#
- # class MyMailer
- # include ActionController::UrlWriter
- # default_url_options[:host] = 'www.basecamphq.com'
+ # <b>Tip:</b> If you need to generate URLs from your models or some other place,
+ # then ActionController::UrlWriter is what you're looking for. Read on for
+ # an introduction.
#
- # def signup_url(token)
- # url_for(:controller => 'signup', action => 'index', :token => token)
+ # == URL generation from parameters
+ #
+ # As you may know, some functions - such as ActionController::Base#url_for
+ # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
+ # of parameters. For example, you've probably had the chance to write code
+ # like this in one of your views:
+ #
+ # <%= link_to('Click here', :controller => 'users',
+ # :action => 'new', :message => 'Welcome!') %>
+ #
+ # #=> Generates a link to: /users/new?message=Welcome%21
+ #
+ # link_to, and all other functions that require URL generation functionality,
+ # actually use ActionController::UrlWriter under the hood. And in particular,
+ # they use the ActionController::UrlWriter#url_for method. One can generate
+ # the same path as the above example by using the following code:
+ #
+ # include UrlWriter
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :only_path => true)
+ # # => "/users/new?message=Welcome%21"
+ #
+ # Notice the <tt>:only_path => true</tt> part. This is because UrlWriter has no
+ # information about the website hostname that your Rails app is serving. So if you
+ # want to include the hostname as well, then you must also pass the <tt>:host</tt>
+ # argument:
+ #
+ # include UrlWriter
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :host => 'www.example.com') # Changed this.
+ # # => "http://www.example.com/users/new?message=Welcome%21"
+ #
+ # By default, all controllers and views have access to a special version of url_for,
+ # that already knows what the current hostname is. So if you use url_for in your
+ # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
+ # argument.
+ #
+ # For convenience reasons, mailers provide a shortcut for ActionController::UrlWriter#url_for.
+ # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlWriter#url_for'
+ # in full. However, mailers don't have hostname information, and what's why you'll still
+ # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
+ #
+ #
+ # == URL generation for named routes
+ #
+ # UrlWriter also allows one to access methods that have been auto-generated from
+ # named routes. For example, suppose that you have a 'users' resource in your
+ # <b>routes.rb</b>:
+ #
+ # map.resources :users
+ #
+ # This generates, among other things, the method <tt>users_path</tt>. By default,
+ # this method is accessible from your controllers, views and mailers. If you need
+ # to access this auto-generated method from other places (such as a model), then
+ # you can do that in two ways.
+ #
+ # The first way is to include ActionController::UrlWriter in your class:
+ #
+ # class User < ActiveRecord::Base
+ # include ActionController::UrlWriter # !!!
+ #
+ # def name=(value)
+ # write_attribute('name', value)
+ # write_attribute('base_uri', users_path) # !!!
# end
- # end
+ # end
#
- # In addition to providing +url_for+, named routes are also accessible after
- # including UrlWriter.
+ # The second way is to access them through ActionController::UrlWriter.
+ # The autogenerated named routes methods are available as class methods:
+ #
+ # class User < ActiveRecord::Base
+ # def name=(value)
+ # write_attribute('name', value)
+ # path = ActionController::UrlWriter.users_path # !!!
+ # write_attribute('base_uri', path) # !!!
+ # end
+ # end
module UrlWriter
# The default options for urls written by this writer. Typically a <tt>:host</tt>
# pair is provided.
@@ -37,7 +114,7 @@ module ActionController
# * <tt>:port</tt> - Optionally specify the port to connect to.
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
# * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in ActionController::AbstractRequest.relative_url_root.
+ # +relative_url_root+ set in ActionController::Base.relative_url_root.
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
#
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
@@ -67,7 +144,7 @@ module ActionController
[:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
end
trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
- url << ActionController::AbstractRequest.relative_url_root.to_s unless options[:skip_relative_url_root]
+ url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
generated = Routing::Routes.generate(options, {})
url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
@@ -108,7 +185,7 @@ module ActionController
end
path = rewrite_path(options)
- rewritten_url << @request.relative_url_root.to_s unless options[:skip_relative_url_root]
+ rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
rewritten_url << "##{options[:anchor]}" if options[:anchor]
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
index 1a3c770254..376bb87409 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
@@ -64,7 +64,7 @@ module HTML
#
# When using a combination of the above, the element name comes first
# followed by identifier, class names, attributes, pseudo classes and
- # negation in any order. Do not seprate these parts with spaces!
+ # negation in any order. Do not separate these parts with spaces!
# Space separation is used for descendant selectors.
#
# For example:
@@ -158,7 +158,7 @@ module HTML
# * <tt>:not(selector)</tt> -- Match the element only if the element does not
# match the simple selector.
#
- # As you can see, <tt>:nth-child<tt> pseudo class and its varient can get quite
+ # As you can see, <tt>:nth-child<tt> pseudo class and its variant can get quite
# tricky and the CSS specification doesn't do a much better job explaining it.
# But after reading the examples and trying a few combinations, it's easy to
# figure out.
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 9ab615c7a5..3590ab6d49 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -21,6 +21,15 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
+begin
+ require 'active_support'
+rescue LoadError
+ activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+ if File.directory?(activesupport_path)
+ $:.unshift activesupport_path
+ require 'active_support'
+ end
+end
require 'action_view/template_handlers'
require 'action_view/renderable'
@@ -34,10 +43,13 @@ require 'action_view/base'
require 'action_view/partials'
require 'action_view/template_error'
+I18n.backend.populate do
+ I18n.load_translations "#{File.dirname(__FILE__)}/action_view/locale/en-US.yml"
+end
+
+require 'action_view/helpers'
+
ActionView::Base.class_eval do
include ActionView::Partials
-
- ActionView::Base.helper_modules.each do |helper_module|
- include helper_module
- end
+ include ActionView::Helpers
end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 04e8d3a358..d174c784f3 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -158,6 +158,7 @@ module ActionView #:nodoc:
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
include ERB::Util
+ extend ActiveSupport::Memoizable
attr_accessor :base_path, :assigns, :template_extension
attr_accessor :controller
@@ -169,15 +170,19 @@ module ActionView #:nodoc:
class << self
delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'
+ delegate :logger, :to => 'ActionController::Base'
end
- # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
- @@cache_template_loading = false
- cattr_accessor :cache_template_loading
+ def self.cache_template_loading=(*args)
+ ActiveSupport::Deprecation.warn(
+ "config.action_view.cache_template_loading option has been deprecated" +
+ "and has no effect. Please remove it from your config files.", caller)
+ end
def self.cache_template_extensions=(*args)
- ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no affect. " <<
- "Please remove it from your config files.", caller)
+ ActiveSupport::Deprecation.warn(
+ "config.action_view.cache_template_extensions option has been" +
+ "deprecated and has no effect. Please remove it from your config files.", caller)
end
# Specify whether RJS responses should be wrapped in a try/catch block
@@ -199,23 +204,6 @@ module ActionView #:nodoc:
end
include CompiledTemplates
- # Cache public asset paths
- cattr_reader :computed_public_paths
- @@computed_public_paths = {}
-
- def self.helper_modules #:nodoc:
- helpers = []
- Dir.entries(File.expand_path("#{File.dirname(__FILE__)}/helpers")).sort.each do |file|
- next unless file =~ /^([a-z][a-z_]*_helper).rb$/
- require "action_view/helpers/#{$1}"
- helper_module_name = $1.camelize
- if Helpers.const_defined?(helper_module_name)
- helpers << Helpers.const_get(helper_module_name)
- end
- end
- return helpers
- end
-
def self.process_view_paths(value)
ActionView::PathSet.new(Array(value))
end
@@ -239,7 +227,7 @@ module ActionView #:nodoc:
local_assigns ||= {}
if options.is_a?(String)
- render_file(options, nil, local_assigns)
+ render(:file => options, :locals => local_assigns)
elsif options == :update
update_page(&block)
elsif options.is_a?(Hash)
@@ -247,31 +235,34 @@ module ActionView #:nodoc:
if partial_layout = options.delete(:layout)
if block_given?
- wrap_content_for_layout capture(&block) do
+ begin
+ @_proc_for_layout = block
concat(render(options.merge(:partial => partial_layout)))
+ ensure
+ @_proc_for_layout = nil
end
else
- wrap_content_for_layout render(options) do
+ begin
+ original_content_for_layout, @content_for_layout = @content_for_layout, render(options)
render(options.merge(:partial => partial_layout))
+ ensure
+ @content_for_layout = original_content_for_layout
end
end
elsif options[:file]
- render_file(options[:file], nil, options[:locals])
- elsif options[:partial] && options[:collection]
- render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals], options[:as])
+ if options[:use_full_path]
+ ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
+ end
+
+ pick_template(options[:file]).render_template(self, options[:locals])
elsif options[:partial]
- render_partial(options[:partial], options[:object], options[:locals])
+ render_partial(options)
elsif options[:inline]
- render_inline(options[:inline], options[:locals], options[:type])
+ InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals])
end
end
end
- # Returns true is the file may be rendered implicitly.
- def file_public?(template_path)#:nodoc:
- template_path.split('/').last[0,1] != '_'
- end
-
# The format to be used when choosing between multiple templates with
# the same name but differing formats. See +Request#template_format+
# for more details.
@@ -301,6 +292,8 @@ module ActionView #:nodoc:
# # => 'users/legacy.rhtml'
#
def pick_template(template_path)
+ return template_path if template_path.respond_to?(:render)
+
path = template_path.sub(/^\//, '')
if m = path.match(/(.*)\.(\w+)$/)
template_file_name, template_file_extension = m[1], m[2]
@@ -321,54 +314,20 @@ module ActionView #:nodoc:
else
template = Template.new(template_path, view_paths)
- if self.class.warn_cache_misses && logger = ActionController::Base.logger
+ if self.class.warn_cache_misses && logger
logger.debug "[PERFORMANCE] Rendering a template that was " +
"not found in view path. Templates outside the view path are " +
- "not cached and result in expensive disk operations. Move this " +
- "file into #{view_paths.join(':')} or add the folder to your " +
+ "not cached and result in expensive disk operations. Move this " +
+ "file into #{view_paths.join(':')} or add the folder to your " +
"view path list"
end
template
end
end
+ memoize :pick_template
private
- # Renders the template present at <tt>template_path</tt>. The hash in <tt>local_assigns</tt>
- # is made available as local variables.
- def render_file(template_path, use_full_path = nil, local_assigns = {}) #:nodoc:
- unless use_full_path == nil
- ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller)
- end
-
- if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")
- raise ActionViewError, <<-END_ERROR
- Due to changes in ActionMailer, you need to provide the mailer_name along with the template name.
-
- render "user_mailer/signup"
- render :file => "user_mailer/signup"
-
- If you are rendering a subtemplate, you must now use controller-like partial syntax:
-
- render :partial => 'signup' # no mailer_name necessary
- END_ERROR
- end
-
- template = pick_template(template_path)
- template.render_template(self, local_assigns)
- end
-
- def render_inline(text, local_assigns = {}, type = nil)
- InlineTemplate.new(text, type).render(self, local_assigns)
- end
-
- def wrap_content_for_layout(content)
- original_content_for_layout, @content_for_layout = @content_for_layout, content
- yield
- ensure
- @content_for_layout = original_content_for_layout
- end
-
# Evaluate the local assigns and pushes them to the view.
def evaluate_assigns
unless @assigns_added
@@ -382,9 +341,9 @@ module ActionView #:nodoc:
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
- def execute(template, local_assigns = {})
- send(template.method(local_assigns), local_assigns) do |*names|
- instance_variable_get "@content_for_#{names.first || 'layout'}"
+ def set_controller_content_type(content_type)
+ if controller.respond_to?(:response)
+ controller.response.content_type ||= content_type
end
end
end
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
new file mode 100644
index 0000000000..05e1cf990a
--- /dev/null
+++ b/actionpack/lib/action_view/helpers.rb
@@ -0,0 +1,39 @@
+Dir.entries(File.expand_path("#{File.dirname(__FILE__)}/helpers")).sort.each do |file|
+ next unless file =~ /^([a-z][a-z_]*_helper).rb$/
+ require "action_view/helpers/#{$1}"
+end
+
+module ActionView #:nodoc:
+ module Helpers #:nodoc:
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ include SanitizeHelper::ClassMethods
+ end
+
+ include ActiveRecordHelper
+ include AssetTagHelper
+ include AtomFeedHelper
+ include BenchmarkHelper
+ include CacheHelper
+ include CaptureHelper
+ include DateHelper
+ include DebugHelper
+ include FormCountryHelper
+ include FormHelper
+ include FormOptionsHelper
+ include FormTagHelper
+ include NumberHelper
+ include PrototypeHelper
+ include RecordIdentificationHelper
+ include RecordTagHelper
+ include SanitizeHelper
+ include ScriptaculousHelper
+ include TagHelper
+ include TextHelper
+ include TranslationHelper
+ include UrlHelper
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb
index f3f204cc97..c339e10701 100644
--- a/actionpack/lib/action_view/helpers/active_record_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_record_helper.rb
@@ -25,7 +25,7 @@ module ActionView
# Returns an entire form with all needed input tags for a specified Active Record object. For example, if <tt>@post</tt>
# has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then
#
- # form("post")
+ # form("post")
#
# would yield a form like the following (modulus formatting):
#
@@ -90,23 +90,41 @@ module ActionView
end
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
- # This error message is wrapped in a <tt>DIV</tt> tag, which can be extended to include a +prepend_text+ and/or +append_text+
- # (to properly explain the error), and a +css_class+ to style it accordingly. +object+ should either be the name of an instance variable or
- # the actual object. As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
+ # This error message is wrapped in a <tt>DIV</tt> tag, which can be extended to include a <tt>:prepend_text</tt>
+ # and/or <tt>:append_text</tt> (to properly explain the error), and a <tt>:css_class</tt> to style it
+ # accordingly. +object+ should either be the name of an instance variable or the actual object. The method can be
+ # passed in either as a string or a symbol.
+ # As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
#
# <%= error_message_on "post", "title" %>
# # => <div class="formError">can't be empty</div>
#
- # <%= error_message_on @post, "title" %>
+ # <%= error_message_on @post, :title %>
# # => <div class="formError">can't be empty</div>
#
- # <%= error_message_on "post", "title", "Title simply ", " (or it won't work).", "inputError" %>
- # # => <div class="inputError">Title simply can't be empty (or it won't work).</div>
- def error_message_on(object, method, prepend_text = "", append_text = "", css_class = "formError")
+ # <%= error_message_on "post", "title",
+ # :prepend_text => "Title simply ",
+ # :append_text => " (or it won't work).",
+ # :css_class => "inputError" %>
+ def error_message_on(object, method, *args)
+ options = args.extract_options!
+ unless args.empty?
+ ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' +
+ 'prepend_text, append_text, and css_class arguments', caller)
+
+ options[:prepend_text] = args[0] || ''
+ options[:append_text] = args[1] || ''
+ options[:css_class] = args[2] || 'formError'
+ end
+ options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError')
+
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
(errors = obj.errors.on(method))
- content_tag("div", "#{prepend_text}#{errors.is_a?(Array) ? errors.first : errors}#{append_text}", :class => css_class)
- else
+ content_tag("div",
+ "#{options[:prepend_text]}#{errors.is_a?(Array) ? errors.first : errors}#{options[:append_text]}",
+ :class => options[:css_class]
+ )
+ else
''
end
end
@@ -133,7 +151,7 @@ module ActionView
#
# To specify the display for one object, you simply provide its name as a parameter.
# For example, for the <tt>@user</tt> model:
- #
+ #
# error_messages_for 'user'
#
# To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
@@ -141,7 +159,7 @@ module ActionView
#
# error_messages_for 'user_common', 'user', :object_name => 'user'
#
- # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> paremeter which gives the actual
+ # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
# object (or array of objects to use):
#
# error_messages_for 'user', :object => @question.user
@@ -151,12 +169,14 @@ module ActionView
# instance yourself and set it up. View the source of this method to see how easy it is.
def error_messages_for(*params)
options = params.extract_options!.symbolize_keys
+
if object = options.delete(:object)
objects = [object].flatten
else
objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
end
- count = objects.inject(0) {|sum, object| sum + object.errors.count }
+
+ count = objects.inject(0) {|sum, object| sum + object.errors.count }
unless count.zero?
html = {}
[:id, :class].each do |key|
@@ -168,16 +188,25 @@ module ActionView
end
end
options[:object_name] ||= params.first
- options[:header_message] = "#{pluralize(count, 'error')} prohibited this #{options[:object_name].to_s.gsub('_', ' ')} from being saved" unless options.include?(:header_message)
- options[:message] ||= 'There were problems with the following fields:' unless options.include?(:message)
- error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
- contents = ''
- contents << content_tag(options[:header_tag] || :h2, options[:header_message]) unless options[:header_message].blank?
- contents << content_tag(:p, options[:message]) unless options[:message].blank?
- contents << content_tag(:ul, error_messages)
+ I18n.with_options :locale => options[:locale], :scope => [:activerecord, :errors, :template] do |locale|
+ header_message = if options.include?(:header_message)
+ options[:header_message]
+ else
+ object_name = options[:object_name].to_s.gsub('_', ' ')
+ object_name = I18n.t(object_name, :default => object_name, :scope => [:activerecord, :models], :count => 1)
+ locale.t :header, :count => count, :model => object_name
+ end
+ message = options.include?(:message) ? options[:message] : locale.t(:body)
+ error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
- content_tag(:div, contents, html)
+ contents = ''
+ contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
+ contents << content_tag(:p, message) unless message.blank?
+ contents << content_tag(:ul, error_messages)
+
+ content_tag(:div, contents, html)
+ end
else
''
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index bf13945844..623ed1e8df 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -5,12 +5,12 @@ require 'action_view/helpers/tag_helper'
module ActionView
module Helpers #:nodoc:
# This module provides methods for generating HTML that links views to assets such
- # as images, javascripts, stylesheets, and feeds. These methods do not verify
- # the assets exist before linking to them.
+ # as images, javascripts, stylesheets, and feeds. These methods do not verify
+ # the assets exist before linking to them.
#
# === Using asset hosts
# By default, Rails links to these assets on the current host in the public
- # folder, but you can direct Rails to link to assets from a dedicated assets server by
+ # folder, but you can direct Rails to link to assets from a dedicated assets server by
# setting ActionController::Base.asset_host in your <tt>config/environment.rb</tt>. For example,
# let's say your asset host is <tt>assets.example.com</tt>.
#
@@ -22,16 +22,16 @@ module ActionView
#
# This is useful since browsers typically open at most two connections to a single host,
# which means your assets often wait in single file for their turn to load. You can
- # alleviate this by using a <tt>%d</tt> wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
+ # alleviate this by using a <tt>%d</tt> wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com")
# to automatically distribute asset requests among four hosts (e.g., "assets0.example.com" through "assets3.example.com")
- # so browsers will open eight connections rather than two.
+ # so browsers will open eight connections rather than two.
#
# image_tag("rails.png")
# => <img src="http://assets0.example.com/images/rails.png" alt="Rails" />
# stylesheet_link_tag("application")
# => <link href="http://assets3.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
#
- # To do this, you can either setup 4 actual hosts, or you can use wildcard DNS to CNAME
+ # To do this, you can either setup 4 actual hosts, or you can use wildcard DNS to CNAME
# the wildcard to a single asset host. You can read more about setting up your DNS CNAME records from
# your ISP.
#
@@ -86,7 +86,7 @@ module ActionView
# asset far into the future, but still be able to instantly invalidate it by simply updating the file (and hence updating the timestamp,
# which then updates the URL as the timestamp is part of that, which in turn busts the cache).
#
- # It's the responsibility of the web server you use to set the far-future expiration date on cache assets that you need to take
+ # It's the responsibility of the web server you use to set the far-future expiration date on cache assets that you need to take
# advantage of this feature. Here's an example for Apache:
#
# # Asset Expiration
@@ -95,16 +95,17 @@ module ActionView
# ExpiresDefault "access plus 1 year"
# </FilesMatch>
#
- # Also note that in order for this to work, all your application servers must return the same timestamps. This means that they must
+ # Also note that in order for this to work, all your application servers must return the same timestamps. This means that they must
# have their clocks synchronized. If one of them drift out of sync, you'll see different timestamps at random and the cache won't
# work. Which means that the browser will request the same assets over and over again even thought they didn't change. You can use
- # something like Live HTTP Headers for Firefox to verify that the cache is indeed working (and that the assets are not being
+ # something like Live HTTP Headers for Firefox to verify that the cache is indeed working (and that the assets are not being
# requested over and over).
module AssetTagHelper
ASSETS_DIR = defined?(Rails.public_path) ? Rails.public_path : "public"
JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts"
STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets"
-
+ JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].map(&:to_s).freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
+
# Returns a link tag that browsers and news readers can use to auto-detect
# an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or
# <tt>:atom</tt>. Control the link options in url_for format using the
@@ -154,10 +155,6 @@ module ActionView
end
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
- JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'] unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
- @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
- @@stylesheet_expansions = {}
-
# Returns an html script tag for each of the +sources+ provided. You
# can pass in the filename (.js extension is optional) of javascript files
# that exist in your public/javascripts directory for inclusion into the
@@ -193,7 +190,7 @@ module ActionView
#
# * = The application.js file is only referenced if it exists
#
- # Though it's not really recommended practice, if you need to extend the default JavaScript set for any reason
+ # Though it's not really recommended practice, if you need to extend the default JavaScript set for any reason
# (e.g., you're going to be using a certain .js file in every action), then take a look at the register_javascript_include_default method.
#
# You can also include all javascripts in the javascripts directory using <tt>:all</tt> as the source:
@@ -218,7 +215,7 @@ module ActionView
# You can also cache multiple javascripts into one file, which requires less HTTP connections to download and can better be
# compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching
# is set to <tt>true</tt> (which is the case by default for the Rails production environment, but not for the development
- # environment).
+ # environment).
#
# ==== Examples
# javascript_include_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
@@ -259,6 +256,8 @@ module ActionView
end
end
+ @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
+
# Register one or more javascript files to be included when <tt>symbol</tt>
# is passed to <tt>javascript_include_tag</tt>. This method is typically intended
# to be called from plugin initialization to register javascript files
@@ -274,6 +273,8 @@ module ActionView
@@javascript_expansions.merge!(expansions)
end
+ @@stylesheet_expansions = {}
+
# Register one or more stylesheet files to be included when <tt>symbol</tt>
# is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
# to be called from plugin initialization to register stylesheet files
@@ -439,9 +440,9 @@ module ActionView
# <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
# image_tag("/icons/icon.gif", :class => "menu_icon") # =>
# <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
- # image_tag("mouse.png", :mouseover => "/images/mouse_over.png") # =>
+ # image_tag("mouse.png", :mouseover => "/images/mouse_over.png") # =>
# <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
- # image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # =>
+ # image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # =>
# <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
def image_tag(source, options = {})
options.symbolize_keys!
@@ -454,23 +455,15 @@ module ActionView
end
if mouseover = options.delete(:mouseover)
- options[:onmouseover] = "this.src='#{image_path(mouseover)}'"
- options[:onmouseout] = "this.src='#{image_path(options[:src])}'"
+ options[:onmouseover] = "this.src='#{image_path(mouseover)}'"
+ options[:onmouseout] = "this.src='#{image_path(options[:src])}'"
end
tag("img", options)
end
private
- def file_exist?(path)
- @@file_exist_cache ||= {}
- if !(@@file_exist_cache[path] ||= File.exist?(path))
- @@file_exist_cache[path] = true
- false
- else
- true
- end
- end
+ COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence!
# Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
@@ -483,14 +476,14 @@ module ActionView
if has_request
[ @controller.request.protocol,
ActionController::Base.asset_host.to_s,
- @controller.request.relative_url_root,
+ ActionController::Base.relative_url_root,
dir, source, ext, include_host ].join
else
[ ActionController::Base.asset_host.to_s,
dir, source, ext, include_host ].join
end
- ActionView::Base.computed_public_paths[cache_key] ||=
+ source = COMPUTED_PUBLIC_PATHS.fetch(cache_key) do
begin
source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))
@@ -499,25 +492,27 @@ module ActionView
else
source = "/#{dir}/#{source}" unless source[0] == ?/
if has_request
- unless source =~ %r{^#{@controller.request.relative_url_root}/}
- source = "#{@controller.request.relative_url_root}#{source}"
+ unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
+ source = "#{ActionController::Base.relative_url_root}#{source}"
end
end
- source = rewrite_asset_path(source)
- if include_host
- host = compute_asset_host(source)
+ rewrite_asset_path(source)
+ end
+ end
+ end
- if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
- host = "#{@controller.request.protocol}#{host}"
- end
+ if include_host && source !~ %r{^[-a-z]+://}
+ host = compute_asset_host(source)
- "#{host}#{source}"
- else
- source
- end
- end
+ if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
+ host = "#{@controller.request.protocol}#{host}"
end
+
+ "#{host}#{source}"
+ else
+ source
+ end
end
# Pick an asset host for this source. Returns +nil+ if no host is set,
@@ -591,7 +586,7 @@ module ActionView
expanded_sources = sources.collect do |source|
determine_source(source, @@javascript_expansions)
end.flatten
- expanded_sources << "application" if sources.include?(:defaults) && file_exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
+ expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
expanded_sources
end
end
@@ -617,12 +612,21 @@ module ActionView
end
def join_asset_file_contents(paths)
- paths.collect { |path| File.read(File.join(ASSETS_DIR, path.split("?").first)) }.join("\n\n")
+ paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n")
end
def write_asset_file_contents(joined_asset_path, asset_paths)
FileUtils.mkdir_p(File.dirname(joined_asset_path))
File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
+
+ # Set mtime to the latest of the combined files to allow for
+ # consistent ETag without a shared filesystem.
+ mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
+ File.utime(mt, mt, joined_asset_path)
+ end
+
+ def asset_file_path(path)
+ File.join(ASSETS_DIR, path.split('?').first)
end
def collect_asset_files(*path)
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index ebb1cb34bc..e65d5d1f60 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -17,7 +17,7 @@ module ActionView
# # GET /posts.atom
# def index
# @posts = Post.find(:all)
- #
+ #
# respond_to do |format|
# format.html
# format.atom
@@ -29,12 +29,12 @@ module ActionView
# atom_feed do |feed|
# feed.title("My great blog!")
# feed.updated((@posts.first.created_at))
- #
+ #
# for post in @posts
# feed.entry(post) do |entry|
# entry.title(post.title)
# entry.content(post.body, :type => 'html')
- #
+ #
# entry.author do |author|
# author.name("DHH")
# end
@@ -47,8 +47,9 @@ module ActionView
# * <tt>:language</tt>: Defaults to "en-US".
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
- # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
- # created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
+ # * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}"
+ # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
+ # created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
# 2005 is used (as an "I don't care" value).
#
# Other namespaces can be added to the root element:
@@ -81,7 +82,7 @@ module ActionView
else
options[:schema_date] = "2005" # The Atom spec copyright date
end
-
+
xml = options[:xml] || eval("xml", block.binding)
xml.instruct!
@@ -89,10 +90,10 @@ module ActionView
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
xml.feed(feed_opts) do
- xml.id("tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
+ xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
-
+
yield AtomFeedBuilder.new(xml, self, options)
end
end
@@ -102,7 +103,7 @@ module ActionView
def initialize(xml, view, feed_options = {})
@xml, @view, @feed_options = xml, view, feed_options
end
-
+
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
def updated(date_or_time = nil)
@xml.updated((date_or_time || Time.now.utc).xmlschema)
@@ -115,9 +116,10 @@ module ActionView
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
+ # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
def entry(record, options = {})
- @xml.entry do
- @xml.id("tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
+ @xml.entry do
+ @xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
@xml.published((options[:published] || record.created_at).xmlschema)
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index 2cdbae6e40..64d1ad2715 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -32,7 +32,7 @@ module ActionView
# <i>Topics listed alphabetically</i>
# <% end %>
def cache(name = {}, options = nil, &block)
- _last_render.handler.new(@controller).cache_fragment(block, name, options)
+ @controller.fragment_for(output_buffer, name, options, &block)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 720e2da8cc..e86ca27f31 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -122,14 +122,15 @@ module ActionView
nil
end
- private
- def with_output_buffer(buf = '')
- self.output_buffer, old_buffer = buf, output_buffer
- yield
- output_buffer
- ensure
- self.output_buffer = old_buffer
- end
+ # Use an alternate output buffer for the duration of the block.
+ # Defaults to a new empty string.
+ def with_output_buffer(buf = '') #:nodoc:
+ self.output_buffer, old_buffer = buf, output_buffer
+ yield
+ output_buffer
+ ensure
+ self.output_buffer = old_buffer
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index d018034ebe..953a2a9f86 100755..100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -3,18 +3,16 @@ require 'action_view/helpers/tag_helper'
module ActionView
module Helpers
- # The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the select-type methods
- # share a number of common options that are as follows:
+ # The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the
+ # select-type methods share a number of common options that are as follows:
#
- # * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday" would give
- # birthday[month] instead of date[month] if passed to the select_month method.
+ # * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
+ # would give birthday[month] instead of date[month] if passed to the select_month method.
# * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
- # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true, the select_month
- # method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead of "date[month]".
+ # * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
+ # the select_month method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead of
+ # "date[month]".
module DateHelper
- include ActionView::Helpers::TagHelper
- DEFAULT_PREFIX = 'date' unless const_defined?('DEFAULT_PREFIX')
-
# Reports the approximate distance in time between two Time or Date objects or integers as seconds.
# Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs
# Distances are reported based on the following table:
@@ -51,40 +49,45 @@ module ActionView
# distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
- # distance_of_time_in_words(from_time, from_time + 4.years + 15.days + 30.minutes + 5.seconds) # => over 4 years
+ # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => over 4 years
#
# to_time = Time.now + 6.years + 19.days
# distance_of_time_in_words(from_time, to_time, true) # => over 6 years
# distance_of_time_in_words(to_time, from_time, true) # => over 6 years
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
#
- def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
+ def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
distance_in_minutes = (((to_time - from_time).abs)/60).round
distance_in_seconds = ((to_time - from_time).abs).round
- case distance_in_minutes
- when 0..1
- return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds
- case distance_in_seconds
- when 0..4 then 'less than 5 seconds'
- when 5..9 then 'less than 10 seconds'
- when 10..19 then 'less than 20 seconds'
- when 20..39 then 'half a minute'
- when 40..59 then 'less than a minute'
- else '1 minute'
- end
-
- when 2..44 then "#{distance_in_minutes} minutes"
- when 45..89 then 'about 1 hour'
- when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
- when 1440..2879 then '1 day'
- when 2880..43199 then "#{(distance_in_minutes / 1440).round} days"
- when 43200..86399 then 'about 1 month'
- when 86400..525599 then "#{(distance_in_minutes / 43200).round} months"
- when 525600..1051199 then 'about 1 year'
- else "over #{(distance_in_minutes / 525600).round} years"
+ I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
+ case distance_in_minutes
+ when 0..1
+ return distance_in_minutes == 0 ?
+ locale.t(:less_than_x_minutes, :count => 1) :
+ locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds
+
+ case distance_in_seconds
+ when 0..4 then locale.t :less_than_x_seconds, :count => 5
+ when 5..9 then locale.t :less_than_x_seconds, :count => 10
+ when 10..19 then locale.t :less_than_x_seconds, :count => 20
+ when 20..39 then locale.t :half_a_minute
+ when 40..59 then locale.t :less_than_x_minutes, :count => 1
+ else locale.t :x_minutes, :count => 1
+ end
+
+ when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
+ when 45..89 then locale.t :about_x_hours, :count => 1
+ when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
+ when 1440..2879 then locale.t :x_days, :count => 1
+ when 2880..43199 then locale.t :x_days, :count => (distance_in_minutes / 1440).round
+ when 43200..86399 then locale.t :about_x_months, :count => 1
+ when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes / 43200).round
+ when 525600..1051199 then locale.t :about_x_years, :count => 1
+ else locale.t :over_x_years, :count => (distance_in_minutes / 525600).round
+ end
end
end
@@ -102,17 +105,37 @@ module ActionView
alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
- # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based attribute (identified by
- # +method+) on an object assigned to the template (identified by +object+). It's possible to tailor the selects through the +options+ hash,
- # which accepts all the keys that each of the individual select builders do (like <tt>:use_month_numbers</tt> for select_month) as well as a range of
- # discard options. The discard options are <tt>:discard_year</tt>, <tt>:discard_month</tt> and <tt>:discard_day</tt>. Set to true, they'll
- # drop the respective select. Discarding the month select will also automatically discard the day select. It's also possible to explicitly
- # set the order of the tags using the <tt>:order</tt> option with an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in
- # the desired order. Symbols may be omitted and the respective select is not included.
- #
- # Pass the <tt>:default</tt> option to set the default date. Use a Time object or a Hash of <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt>, and <tt>:second</tt>.
- #
- # Passing <tt>:disabled => true</tt> as part of the +options+ will make elements inaccessible for change.
+ # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
+ # attribute (identified by +method+) on an object assigned to the template (identified by +object+). You can
+ # the output in the +options+ hash.
+ #
+ # ==== Options
+ # * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
+ # "2" instead of "February").
+ # * <tt>:use_short_month</tt> - Set to true if you want to use the abbreviated month name instead of the full
+ # name (e.g. "Feb" instead of "February").
+ # * <tt>:add_month_number</tt> - Set to true if you want to show both, the month's number and name (e.g.
+ # "2 - February" instead of "February").
+ # * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
+ # Note: You can also use Rails' new i18n functionality for this.
+ # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
+ # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
+ # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
+ # * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
+ # as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
+ # first of the given month in order to not create invalid dates like 31 February.
+ # * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
+ # as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
+ # * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
+ # as a hidden field instead of showing a select field.
+ # * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> do
+ # customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
+ # select will not be shown (like when you set <tt>:discard_xxx => true</tt>. Defaults to the order defined in
+ # the respective locale (e.g. [:year, :month, :day] in the en-US locale that ships with Rails).
+ # * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
+ # dates.
+ # * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
+ # * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
#
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
#
@@ -128,7 +151,7 @@ module ActionView
#
# # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute,
# # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
- # # and without a day select box.
+ # # and without a day select box.
# date_select("post", "written_on", :start_year => 1995, :use_month_numbers => true,
# :discard_day => true, :include_blank => true)
#
@@ -150,15 +173,15 @@ module ActionView
#
# The selects are prepared for multi-parameter assignment to an Active Record object.
#
- # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that all month
- # choices are valid.
+ # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
+ # all month choices are valid.
def date_select(object_name, method, options = {}, html_options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options)
end
- # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a specified
- # time-based attribute (identified by +method+) on an object assigned to the template (identified by +object+).
- # You can include the seconds with <tt>:include_seconds</tt>.
+ # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
+ # specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
+ # +object+). You can include the seconds with <tt>:include_seconds</tt>.
#
# This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
# <tt>:ignore_date</tt> is set to +true+.
@@ -169,18 +192,19 @@ module ActionView
# # Creates a time select tag that, when POSTed, will be stored in the post variable in the sunrise attribute
# time_select("post", "sunrise")
#
- # # Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted attribute
+ # # Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted
+ # # attribute
# time_select("order", "submitted")
#
# # Creates a time select tag that, when POSTed, will be stored in the mail variable in the sent_at attribute
# time_select("mail", "sent_at")
#
- # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the post variables in
- # # the sunrise attribute.
+ # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the post variables in
+ # # the sunrise attribute.
# time_select("post", "start_time", :include_seconds => true)
#
- # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the entry variables in
- # # the submission_time attribute.
+ # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the entry variables in
+ # # the submission_time attribute.
# time_select("entry", "submission_time", :include_seconds => true)
#
# # You can set the :minute_step to 15 which will give you: 00, 15, 30 and 45.
@@ -188,31 +212,33 @@ module ActionView
#
# The selects are prepared for multi-parameter assignment to an Active Record object.
#
- # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that all month
- # choices are valid.
+ # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
+ # all month choices are valid.
def time_select(object_name, method, options = {}, html_options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_time_select_tag(options, html_options)
end
- # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a specified datetime-based
- # attribute (identified by +method+) on an object assigned to the template (identified by +object+). Examples:
+ # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
+ # specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
+ # by +object+). Examples:
#
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
# ==== Examples
- # # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on attribute
+ # # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on
+ # # attribute
# datetime_select("post", "written_on")
#
- # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
+ # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
# # post variable in the written_on attribute.
# datetime_select("post", "written_on", :start_year => 1995)
#
- # # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will be stored in the
- # # trip variable in the departing attribute.
+ # # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
+ # # be stored in the trip variable in the departing attribute.
# datetime_select("trip", "departing", :default => 3.days.from_now)
#
- # # Generates a datetime select that discards the type that, when POSTed, will be stored in the post variable as the written_on
- # # attribute.
+ # # Generates a datetime select that discards the type that, when POSTed, will be stored in the post variable
+ # # as the written_on attribute.
# datetime_select("post", "written_on", :discard_type => true)
#
# The selects are prepared for multi-parameter assignment to an Active Record object.
@@ -220,11 +246,12 @@ module ActionView
InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
end
- # Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the +datetime+.
- # It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
- # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol, it
- # will be appended onto the <tt>:order</tt> passed in. You can also add <tt>:date_separator</tt> and <tt>:time_separator</tt>
- # keys to the +options+ to control visual display of the elements.
+ # Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the
+ # +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
+ # an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
+ # supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
+ # <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
+ # control visual display of the elements.
#
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
@@ -245,7 +272,12 @@ module ActionView
# # with a '/' between each date field.
# select_datetime(my_date_time, :date_separator => '/')
#
- # # Generates a datetime select that discards the type of the field and defaults to the datetime in
+ # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
+ # # with a date fields separated by '/', time fields separated by '' and the date and time fields
+ # # separated by a comma (',').
+ # select_datetime(my_date_time, :date_separator => '/', :time_separator => '', :datetime_separator => ',')
+ #
+ # # Generates a datetime select that discards the type of the field and defaults to the datetime in
# # my_date_time (four days after today)
# select_datetime(my_date_time, :discard_type => true)
#
@@ -254,14 +286,13 @@ module ActionView
# select_datetime(my_date_time, :prefix => 'payday')
#
def select_datetime(datetime = Time.current, options = {}, html_options = {})
- separator = options[:datetime_separator] || ''
- select_date(datetime, options, html_options) + separator + select_time(datetime, options, html_options)
- end
+ DateTimeSelector.new(datetime, options, html_options).select_datetime
+ end
# Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
# It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
- # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol, it
- # will be appended onto the <tt>:order</tt> passed in.
+ # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol,
+ # it will be appended onto the <tt>:order</tt> passed in.
#
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
@@ -278,27 +309,24 @@ module ActionView
# # with the fields ordered year, month, day rather than month, day, year.
# select_date(my_date, :order => [:year, :month, :day])
#
- # # Generates a date select that discards the type of the field and defaults to the date in
+ # # Generates a date select that discards the type of the field and defaults to the date in
# # my_date (six days after today)
- # select_datetime(my_date_time, :discard_type => true)
+ # select_date(my_date, :discard_type => true)
+ #
+ # # Generates a date select that defaults to the date in my_date,
+ # # which has fields separated by '/'
+ # select_date(my_date, :date_separator => '/')
#
# # Generates a date select that defaults to the datetime in my_date (six days after today)
# # prefixed with 'payday' rather than 'date'
- # select_datetime(my_date_time, :prefix => 'payday')
+ # select_date(my_date, :prefix => 'payday')
#
def select_date(date = Date.current, options = {}, html_options = {})
- options[:order] ||= []
- [:year, :month, :day].each { |o| options[:order].push(o) unless options[:order].include?(o) }
-
- select_date = ''
- options[:order].each do |o|
- select_date << self.send("select_#{o}", date, options, html_options)
- end
- select_date
+ DateTimeSelector.new(date, options, html_options).select_date
end
# Returns a set of html select-tags (one for hour and minute)
- # You can set <tt>:time_separator</tt> key to format the output, and
+ # You can set <tt>:time_separator</tt> key to format the output, and
# the <tt>:include_seconds</tt> option to include an input for seconds.
#
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
@@ -313,7 +341,7 @@ module ActionView
# select_time()
#
# # Generates a time select that defaults to the time in my_time,
- # # which has fields separated by ':'
+ # # which has fields separated by ':'
# select_time(my_time, :time_separator => ':')
#
# # Generates a time select that defaults to the time in my_time,
@@ -325,8 +353,7 @@ module ActionView
# select_time(my_time, :time_separator => ':', :include_seconds => true)
#
def select_time(datetime = Time.current, options = {}, html_options = {})
- separator = options[:time_separator] || ''
- select_hour(datetime, options, html_options) + separator + select_minute(datetime, options, html_options) + (options[:include_seconds] ? separator + select_second(datetime, options, html_options) : '')
+ DateTimeSelector.new(datetime, options, html_options).select_time
end
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
@@ -341,31 +368,18 @@ module ActionView
#
# # Generates a select field for seconds that defaults to the number given
# select_second(33)
- #
+ #
# # Generates a select field for seconds that defaults to the seconds for the time in my_time
# # that is named 'interval' rather than 'second'
# select_second(my_time, :field_name => 'interval')
#
def select_second(datetime, options = {}, html_options = {})
- val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.sec) : ''
- if options[:use_hidden]
- options[:include_seconds] ? hidden_html(options[:field_name] || 'second', val, options) : ''
- else
- second_options = []
- 0.upto(59) do |second|
- second_options << ((val == second) ?
- content_tag(:option, leading_zero_on_single_digits(second), :value => leading_zero_on_single_digits(second), :selected => "selected") :
- content_tag(:option, leading_zero_on_single_digits(second), :value => leading_zero_on_single_digits(second))
- )
- second_options << "\n"
- end
- select_html(options[:field_name] || 'second', second_options.join, options, html_options)
- end
+ DateTimeSelector.new(datetime, options, html_options).select_second
end
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
- # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute selected
- # The <tt>minute</tt> can also be substituted for a minute number.
+ # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
+ # selected. The <tt>minute</tt> can also be substituted for a minute number.
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
#
# ==== Examples
@@ -376,26 +390,13 @@ module ActionView
#
# # Generates a select field for minutes that defaults to the number given
# select_minute(14)
- #
+ #
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
# # that is named 'stride' rather than 'second'
# select_minute(my_time, :field_name => 'stride')
#
def select_minute(datetime, options = {}, html_options = {})
- val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.min) : ''
- if options[:use_hidden]
- hidden_html(options[:field_name] || 'minute', val, options)
- else
- minute_options = []
- 0.step(59, options[:minute_step] || 1) do |minute|
- minute_options << ((val == minute) ?
- content_tag(:option, leading_zero_on_single_digits(minute), :value => leading_zero_on_single_digits(minute), :selected => "selected") :
- content_tag(:option, leading_zero_on_single_digits(minute), :value => leading_zero_on_single_digits(minute))
- )
- minute_options << "\n"
- end
- select_html(options[:field_name] || 'minute', minute_options.join, options, html_options)
- end
+ DateTimeSelector.new(datetime, options, html_options).select_minute
end
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
@@ -410,26 +411,13 @@ module ActionView
#
# # Generates a select field for minutes that defaults to the number given
# select_minute(14)
- #
+ #
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
# # that is named 'stride' rather than 'second'
# select_minute(my_time, :field_name => 'stride')
#
def select_hour(datetime, options = {}, html_options = {})
- val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.hour) : ''
- if options[:use_hidden]
- hidden_html(options[:field_name] || 'hour', val, options)
- else
- hour_options = []
- 0.upto(23) do |hour|
- hour_options << ((val == hour) ?
- content_tag(:option, leading_zero_on_single_digits(hour), :value => leading_zero_on_single_digits(hour), :selected => "selected") :
- content_tag(:option, leading_zero_on_single_digits(hour), :value => leading_zero_on_single_digits(hour))
- )
- hour_options << "\n"
- end
- select_html(options[:field_name] || 'hour', hour_options.join, options, html_options)
- end
+ DateTimeSelector.new(datetime, options, html_options).select_hour
end
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
@@ -444,36 +432,23 @@ module ActionView
#
# # Generates a select field for days that defaults to the number given
# select_day(5)
- #
+ #
# # Generates a select field for days that defaults to the day for the date in my_date
# # that is named 'due' rather than 'day'
# select_day(my_time, :field_name => 'due')
#
def select_day(date, options = {}, html_options = {})
- val = date ? (date.kind_of?(Fixnum) ? date : date.day) : ''
- if options[:use_hidden]
- hidden_html(options[:field_name] || 'day', val, options)
- else
- day_options = []
- 1.upto(31) do |day|
- day_options << ((val == day) ?
- content_tag(:option, day, :value => day, :selected => "selected") :
- content_tag(:option, day, :value => day)
- )
- day_options << "\n"
- end
- select_html(options[:field_name] || 'day', day_options.join, options, html_options)
- end
+ DateTimeSelector.new(date, options, html_options).select_day
end
- # Returns a select tag with options for each of the months January through December with the current month selected.
- # The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are used as values
- # (what's submitted to the server). It's also possible to use month numbers for the presentation instead of names --
- # set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you want both numbers and names,
- # set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer to show month names as abbreviations,
- # set the <tt>:use_short_month</tt> key in +options+ to true. If you want to use your own month names, set the
- # <tt>:use_month_names</tt> key in +options+ to an array of 12 month names. Override the field name using the
- # <tt>:field_name</tt> option, 'month' by default.
+ # Returns a select tag with options for each of the months January through December with the current month
+ # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
+ # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
+ # instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
+ # want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
+ # to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
+ # to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
+ # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
#
# ==== Examples
# # Generates a select field for months that defaults to the current month that
@@ -485,7 +460,7 @@ module ActionView
# select_month(Date.today, :field_name => 'start')
#
# # Generates a select field for months that defaults to the current month that
- # # will use keys like "1", "3".
+ # # will use keys like "1", "3".
# select_month(Date.today, :use_month_numbers => true)
#
# # Generates a select field for months that defaults to the current month that
@@ -501,36 +476,14 @@ module ActionView
# select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
#
def select_month(date, options = {}, html_options = {})
- val = date ? (date.kind_of?(Fixnum) ? date : date.month) : ''
- if options[:use_hidden]
- hidden_html(options[:field_name] || 'month', val, options)
- else
- month_options = []
- month_names = options[:use_month_names] || (options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES)
- month_names.unshift(nil) if month_names.size < 13
- 1.upto(12) do |month_number|
- month_name = if options[:use_month_numbers]
- month_number
- elsif options[:add_month_numbers]
- month_number.to_s + ' - ' + month_names[month_number]
- else
- month_names[month_number]
- end
-
- month_options << ((val == month_number) ?
- content_tag(:option, month_name, :value => month_number, :selected => "selected") :
- content_tag(:option, month_name, :value => month_number)
- )
- month_options << "\n"
- end
- select_html(options[:field_name] || 'month', month_options.join, options, html_options)
- end
+ DateTimeSelector.new(date, options, html_options).select_month
end
- # Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius
- # can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the +options+. Both ascending and descending year
- # lists are supported by making <tt>:start_year</tt> less than or greater than <tt>:end_year</tt>. The <tt>date</tt> can also be
- # substituted for a year given as a number. Override the field name using the <tt>:field_name</tt> option, 'year' by default.
+ # Returns a select tag with options for each of the five years on each side of the current, which is selected.
+ # The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
+ # +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
+ # greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
+ # Override the field name using the <tt>:field_name</tt> option, 'year' by default.
#
# ==== Examples
# # Generates a select field for years that defaults to the current year that
@@ -550,159 +503,384 @@ module ActionView
# select_year(2006, :start_year => 2000, :end_year => 2010)
#
def select_year(date, options = {}, html_options = {})
- if !date || date == 0
- value = ''
- middle_year = Date.today.year
- elsif date.kind_of?(Fixnum)
- value = middle_year = date
+ DateTimeSelector.new(date, options, html_options).select_year
+ end
+ end
+
+ class DateTimeSelector #:nodoc:
+ extend ActiveSupport::Memoizable
+ include ActionView::Helpers::TagHelper
+
+ DEFAULT_PREFIX = 'date'.freeze unless const_defined?('DEFAULT_PREFIX')
+ POSITION = {
+ :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
+ }.freeze unless const_defined?('POSITION')
+
+ def initialize(datetime, options = {}, html_options = {})
+ @options = options.dup
+ @html_options = html_options.dup
+ @datetime = datetime
+ end
+
+ def select_datetime
+ # TODO: Remove tag conditional
+ # Ideally we could just join select_date and select_date for the tag case
+ if @options[:tag] && @options[:ignore_date]
+ select_time
+ elsif @options[:tag]
+ order = date_order.dup
+ order -= [:hour, :minute, :second]
+
+ @options[:discard_year] ||= true unless order.include?(:year)
+ @options[:discard_month] ||= true unless order.include?(:month)
+ @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
+ @options[:discard_minute] ||= true if @options[:discard_hour]
+ @options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
+
+ # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
+ # valid (otherwise it could be 31 and february wouldn't be a valid date)
+ if @options[:discard_day] && !@options[:discard_month]
+ @datetime = @datetime.change(:day => 1)
+ end
+
+ [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
+ order += [:hour, :minute, :second] unless @options[:discard_hour]
+
+ build_selects_from_types(order)
else
- value = middle_year = date.year
+ "#{select_date}#{@options[:datetime_separator]}#{select_time}"
end
+ end
- if options[:use_hidden]
- hidden_html(options[:field_name] || 'year', value, options)
- else
- year_options = ''
- start_year = options[:start_year] || middle_year - 5
- end_year = options[:end_year] || middle_year + 5
- step_val = start_year < end_year ? 1 : -1
-
- start_year.step(end_year, step_val) do |year|
- if value == year
- year_options << content_tag(:option, year, :value => year, :selected => "selected")
- else
- year_options << content_tag(:option, year, :value => year)
- end
- year_options << "\n"
+ def select_date
+ order = date_order.dup
+
+ # TODO: Remove tag conditional
+ if @options[:tag]
+ @options[:discard_hour] = true
+ @options[:discard_minute] = true
+ @options[:discard_second] = true
+
+ @options[:discard_year] ||= true unless order.include?(:year)
+ @options[:discard_month] ||= true unless order.include?(:month)
+ @options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
+
+ # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
+ # valid (otherwise it could be 31 and february wouldn't be a valid date)
+ if @options[:discard_day] && !@options[:discard_month]
+ @datetime = @datetime.change(:day => 1)
end
- select_html(options[:field_name] || 'year', year_options, options, html_options)
end
+
+ [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
+
+ build_selects_from_types(order)
end
- private
+ def select_time
+ order = []
- def select_html(type, html_options, options, select_tag_options = {})
- name_and_id_from_options(options, type)
- select_options = {:id => options[:id], :name => options[:name]}
- select_options.merge!(:disabled => 'disabled') if options[:disabled]
- select_options.merge!(select_tag_options) unless select_tag_options.empty?
- select_html = "\n"
- select_html << content_tag(:option, '', :value => '') + "\n" if options[:include_blank]
- select_html << html_options.to_s
- content_tag(:select, select_html, select_options) + "\n"
- end
+ # TODO: Remove tag conditional
+ if @options[:tag]
+ @options[:discard_month] = true
+ @options[:discard_year] = true
+ @options[:discard_day] = true
+ @options[:discard_second] ||= true unless @options[:include_seconds]
- def hidden_html(type, value, options)
- name_and_id_from_options(options, type)
- hidden_html = tag(:input, :type => "hidden", :id => options[:id], :name => options[:name], :value => value) + "\n"
+ order += [:year, :month, :day] unless @options[:ignore_date]
end
- def name_and_id_from_options(options, type)
- options[:name] = (options[:prefix] || DEFAULT_PREFIX) + (options[:discard_type] ? '' : "[#{type}]")
- options[:id] = options[:name].gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
+ order += [:hour, :minute]
+ order << :second if @options[:include_seconds]
+
+ build_selects_from_types(order)
+ end
+
+ def select_second
+ if @options[:use_hidden] || @options[:discard_second]
+ build_hidden(:second, sec) if @options[:include_seconds]
+ else
+ build_options_and_select(:second, sec)
end
+ end
- def leading_zero_on_single_digits(number)
- number > 9 ? number : "0#{number}"
+ def select_minute
+ if @options[:use_hidden] || @options[:discard_minute]
+ build_hidden(:minute, min)
+ else
+ build_options_and_select(:minute, min, :step => @options[:minute_step])
end
- end
+ end
- class InstanceTag #:nodoc:
- include DateHelper
+ def select_hour
+ if @options[:use_hidden] || @options[:discard_hour]
+ build_hidden(:hour, hour)
+ else
+ build_options_and_select(:hour, hour, :end => 23)
+ end
+ end
- def to_date_select_tag(options = {}, html_options = {})
- date_or_time_select(options.merge(:discard_hour => true), html_options)
+ def select_day
+ if @options[:use_hidden] || @options[:discard_day]
+ build_hidden(:day, day)
+ else
+ build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false)
+ end
end
- def to_time_select_tag(options = {}, html_options = {})
- date_or_time_select(options.merge(:discard_year => true, :discard_month => true), html_options)
+ def select_month
+ if @options[:use_hidden] || @options[:discard_month]
+ build_hidden(:month, month)
+ else
+ month_options = []
+ 1.upto(12) do |month_number|
+ options = { :value => month_number }
+ options[:selected] = "selected" if month == month_number
+ month_options << content_tag(:option, month_name(month_number), options) + "\n"
+ end
+ build_select(:month, month_options.join)
+ end
end
- def to_datetime_select_tag(options = {}, html_options = {})
- date_or_time_select(options, html_options)
+ def select_year
+ if !@datetime || @datetime == 0
+ val = ''
+ middle_year = Date.today.year
+ else
+ val = middle_year = year
+ end
+
+ if @options[:use_hidden] || @options[:discard_year]
+ build_hidden(:year, val)
+ else
+ options = {}
+ options[:start] = @options[:start_year] || middle_year - 5
+ options[:end] = @options[:end_year] || middle_year + 5
+ options[:step] = options[:start] < options[:end] ? 1 : -1
+ options[:leading_zeros] = false
+
+ build_options_and_select(:year, val, options)
+ end
end
private
- def date_or_time_select(options, html_options = {})
- defaults = { :discard_type => true }
- options = defaults.merge(options)
- datetime = value(object)
- datetime ||= default_time_from_options(options[:default]) unless options[:include_blank]
-
- position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
-
- order = (options[:order] ||= [:year, :month, :day])
-
- # Discard explicit and implicit by not being included in the :order
- discard = {}
- discard[:year] = true if options[:discard_year] or !order.include?(:year)
- discard[:month] = true if options[:discard_month] or !order.include?(:month)
- discard[:day] = true if options[:discard_day] or discard[:month] or !order.include?(:day)
- discard[:hour] = true if options[:discard_hour]
- discard[:minute] = true if options[:discard_minute] or discard[:hour]
- discard[:second] = true unless options[:include_seconds] && !discard[:minute]
-
- # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are valid
- # (otherwise it could be 31 and february wouldn't be a valid date)
- if datetime && discard[:day] && !discard[:month]
- datetime = datetime.change(:day => 1)
+ %w( sec min hour day month year ).each do |method|
+ define_method(method) do
+ @datetime.kind_of?(Fixnum) ? @datetime : @datetime.send(method) if @datetime
end
+ end
- # Maintain valid dates by including hidden fields for discarded elements
- [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
+ # Returns translated month names, but also ensures that a custom month
+ # name array has a leading nil element
+ def month_names
+ month_names = @options[:use_month_names] || translated_month_names
+ month_names.unshift(nil) if month_names.size < 13
+ month_names
+ end
+ memoize :month_names
+
+ # Returns translated month names
+ # => [nil, "January", "February", "March",
+ # "April", "May", "June", "July",
+ # "August", "September", "October",
+ # "November", "December"]
+ #
+ # If :use_short_month option is set
+ # => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ def translated_month_names
+ begin
+ key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
+ I18n.translate(key, :locale => @options[:locale])
+ end
+ end
- # Ensure proper ordering of :hour, :minute and :second
- [:hour, :minute, :second].each { |o| order.delete(o); order.push(o) }
+ # Lookup month name for number
+ # month_name(1) => "January"
+ #
+ # If :use_month_numbers option is passed
+ # month_name(1) => 1
+ #
+ # If :add_month_numbers option is passed
+ # month_name(1) => "1 - January"
+ def month_name(number)
+ if @options[:use_month_numbers]
+ number
+ elsif @options[:add_month_numbers]
+ "#{number} - #{month_names[number]}"
+ else
+ month_names[number]
+ end
+ end
+
+ def date_order
+ @options[:order] || translated_date_order
+ end
+ memoize :date_order
- date_or_time_select = ''
- order.reverse.each do |param|
- # Send hidden fields for discarded elements once output has started
- # This ensures AR can reconstruct valid dates using ParseDate
- next if discard[param] && (date_or_time_select.empty? || options[:ignore_date])
+ def translated_date_order
+ begin
+ I18n.translate(:'date.order', :locale => @options[:locale]) || []
+ end
+ end
- date_or_time_select.insert(0, self.send("select_#{param}", datetime, options_with_prefix(position[param], options.merge(:use_hidden => discard[param])), html_options))
- date_or_time_select.insert(0,
- case param
- when :hour then (discard[:year] && discard[:day] ? "" : " &mdash; ")
- when :minute then " : "
- when :second then options[:include_seconds] ? " : " : ""
- else ""
- end)
+ # Build full select tag from date type and options
+ def build_options_and_select(type, selected, options = {})
+ build_select(type, build_options(selected, options))
+ end
+ # Build select option html from date value and options
+ # build_options(15, :start => 1, :end => 31)
+ # => "<option value="1">1</option>
+ # <option value=\"2\">2</option>
+ # <option value=\"3\">3</option>..."
+ def build_options(selected, options = {})
+ start = options.delete(:start) || 0
+ stop = options.delete(:end) || 59
+ step = options.delete(:step) || 1
+ leading_zeros = options.delete(:leading_zeros).nil? ? true : false
+
+ select_options = []
+ start.step(stop, step) do |i|
+ value = leading_zeros ? sprintf("%02d", i) : i
+ tag_options = { :value => value }
+ tag_options[:selected] = "selected" if selected == i
+ select_options << content_tag(:option, value, tag_options)
end
+ select_options.join("\n") + "\n"
+ end
- date_or_time_select
+ # Builds select tag from date type and html select options
+ # build_select(:month, "<option value="1">January</option>...")
+ # => "<select id="post_written_on_2i" name="post[written_on(2i)]">
+ # <option value="1">January</option>...
+ # </select>"
+ def build_select(type, select_options_as_html)
+ select_options = {
+ :id => input_id_from_type(type),
+ :name => input_name_from_type(type)
+ }.merge(@html_options)
+ select_options.merge!(:disabled => 'disabled') if @options[:disabled]
+
+ select_html = "\n"
+ select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
+ select_html << select_options_as_html.to_s
+
+ content_tag(:select, select_html, select_options) + "\n"
end
- def options_with_prefix(position, options)
- prefix = "#{@object_name}"
- if options[:index]
- prefix << "[#{options[:index]}]"
- elsif @auto_index
- prefix << "[#{@auto_index}]"
+ # Builds hidden input tag for date part and value
+ # build_hidden(:year, 2008)
+ # => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
+ def build_hidden(type, value)
+ tag(:input, {
+ :type => "hidden",
+ :id => input_id_from_type(type),
+ :name => input_name_from_type(type),
+ :value => value
+ }) + "\n"
+ end
+
+ # Returns the name attribute for the input tag
+ # => post[written_on(1i)]
+ def input_name_from_type(type)
+ prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
+ prefix += "[#{@options[:index]}]" if @options[:index]
+
+ field_name = @options[:field_name] || type
+ if @options[:include_position]
+ field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
end
- options.merge(:prefix => "#{prefix}[#{@method_name}(#{position}i)]")
+
+ @options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
+ end
+
+ # Returns the id attribute for the input tag
+ # => "post_written_on_1i"
+ def input_id_from_type(type)
+ input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
+ end
+
+ # Given an ordering of datetime components, create the selection html
+ # and join them with their appropriate seperators
+ def build_selects_from_types(order)
+ select = ''
+ order.reverse.each do |type|
+ separator = separator(type) unless type == order.first # don't add on last field
+ select.insert(0, separator.to_s + send("select_#{type}").to_s)
+ end
+ select
+ end
+
+ # Returns the separator for a given datetime component
+ def separator(type)
+ case type
+ when :month, :day
+ @options[:date_separator]
+ when :hour
+ (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
+ when :minute
+ @options[:time_separator]
+ when :second
+ @options[:include_seconds] ? @options[:time_separator] : ""
+ end
+ end
+ end
+
+ class InstanceTag #:nodoc:
+ def to_date_select_tag(options = {}, html_options = {})
+ datetime_selector(options, html_options).select_date
+ end
+
+ def to_time_select_tag(options = {}, html_options = {})
+ datetime_selector(options, html_options).select_time
+ end
+
+ def to_datetime_select_tag(options = {}, html_options = {})
+ datetime_selector(options, html_options).select_datetime
+ end
+
+ private
+ def datetime_selector(options, html_options)
+ datetime = value(object) || default_datetime(options)
+
+ options = options.dup
+ options[:field_name] = @method_name
+ options[:include_position] = true
+ options[:prefix] ||= @object_name
+ options[:index] ||= @auto_index
+ options[:datetime_separator] ||= ' &mdash; '
+ options[:time_separator] ||= ' : '
+
+ DateTimeSelector.new(datetime, options.merge(:tag => true), html_options)
end
- def default_time_from_options(default)
- case default
+ def default_datetime(options)
+ return if options[:include_blank]
+
+ case options[:default]
when nil
Time.current
when Date, Time
- default
+ options[:default]
else
+ default = options[:default].dup
+
# Rename :minute and :second to :min and :sec
default[:min] ||= default[:minute]
default[:sec] ||= default[:second]
time = Time.current
-
+
[:year, :month, :day, :hour, :min, :sec].each do |key|
default[key] ||= time.send(key)
end
- Time.utc_time(default[:year], default[:month], default[:day], default[:hour], default[:min], default[:sec])
- end
+ Time.utc_time(
+ default[:year], default[:month], default[:day],
+ default[:hour], default[:min], default[:sec]
+ )
+ end
end
end
diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb
index 20de7e465f..90863fca08 100644
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -2,21 +2,28 @@ module ActionView
module Helpers
# Provides a set of methods for making it easier to debug Rails objects.
module DebugHelper
- # Returns a <pre>-tag that has +object+ dumped by YAML. This creates a very
- # readable way to inspect an object.
+ # Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
+ # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
+ # Useful for inspecting an object at the time of rendering.
#
# ==== Example
- # my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
- # debug(my_hash)
#
- # => <pre class='debug_dump'>---
- # first: 1
- # second: two
- # third:
- # - 1
- # - 2
- # - 3
- # </pre>
+ # @user = User.new({ :username => 'testing', :password => 'xyz', :age => 42}) %>
+ # debug(@user)
+ # # =>
+ # <pre class='debug_dump'>--- !ruby/object:User
+ # attributes:
+ # &nbsp; updated_at:
+ # &nbsp; username: testing
+ #
+ # &nbsp; age: 42
+ # &nbsp; password: xyz
+ # &nbsp; created_at:
+ # attributes_cache: {}
+ #
+ # new_record: true
+ # </pre>
+
def debug(object)
begin
Marshal::dump(object)
@@ -28,4 +35,4 @@ module ActionView
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/form_country_helper.rb b/actionpack/lib/action_view/helpers/form_country_helper.rb
new file mode 100644
index 0000000000..84e811f61d
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/form_country_helper.rb
@@ -0,0 +1,92 @@
+require 'action_view/helpers/form_options_helper'
+
+module ActionView
+ module Helpers
+ module FormCountryHelper
+
+ # Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
+ def country_select(object, method, priority_countries = nil, options = {}, html_options = {})
+ InstanceTag.new(object, method, self, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options)
+ end
+
+ # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to
+ # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so
+ # that they will be listed above the rest of the (long) list.
+ #
+ # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
+ def country_options_for_select(selected = nil, priority_countries = nil)
+ country_options = ""
+
+ if priority_countries
+ country_options += options_for_select(priority_countries, selected)
+ country_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
+ end
+
+ return country_options + options_for_select(COUNTRIES, selected)
+ end
+
+ private
+
+ # All the countries included in the country_options output.
+ COUNTRIES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola",
+ "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria",
+ "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin",
+ "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil",
+ "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia",
+ "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+ "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+ "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba",
+ "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt",
+ "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)",
+ "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia",
+ "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea",
+ "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)",
+ "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq",
+ "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya",
+ "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan",
+ "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya",
+ "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of",
+ "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique",
+ "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of",
+ "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru",
+ "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger",
+ "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau",
+ "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines",
+ "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation",
+ "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia",
+ "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino",
+ "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore",
+ "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa",
+ "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname",
+ "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic",
+ "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste",
+ "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan",
+ "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom",
+ "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela",
+ "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara",
+ "Yemen", "Zambia", "Zimbabwe"] unless const_defined?("COUNTRIES")
+ end
+
+ class InstanceTag #:nodoc:
+ include FormCountryHelper
+
+ def to_country_select_tag(priority_countries, options, html_options)
+ html_options = html_options.stringify_keys
+ add_default_name_and_id(html_options)
+ value = value(object)
+ content_tag("select",
+ add_options(
+ country_options_for_select(value, priority_countries),
+ options, value
+ ), html_options
+ )
+ end
+ end
+
+ class FormBuilder
+ def country_select(method, priority_countries = nil, options = {}, html_options = {})
+ @template.country_select(@object_name, method, priority_countries, objectify_options(options), @default_options.merge(html_options))
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index bafc635ad2..7bb82ba5bb 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -76,7 +76,7 @@ module ActionView
# Creates a form and a scope around a specific model object that is used as
# a base for questioning about values for the fields.
#
- # Rails provides succint resource-oriented form generation with +form_for+
+ # Rails provides succinct resource-oriented form generation with +form_for+
# like this:
#
# <% form_for @offer do |f| %>
@@ -304,10 +304,6 @@ module ActionView
when String, Symbol
object_name = record_or_name_or_array
object = args.first
- when Array
- object = record_or_name_or_array.last
- object_name = ActionController::RecordIdentifier.singular_class_name(object)
- apply_form_for_options!(record_or_name_or_array, options)
else
object = record_or_name_or_array
object_name = ActionController::RecordIdentifier.singular_class_name(object)
@@ -449,8 +445,37 @@ module ActionView
# assigned to the template (identified by +object+). It's intended that +method+ returns an integer and if that
# integer is above zero, then the checkbox is checked. Additional options on the input tag can be passed as a
# hash with +options+. The +checked_value+ defaults to 1 while the default +unchecked_value+
- # is set to 0 which is convenient for boolean values. Since HTTP standards say that unchecked checkboxes don't post anything,
- # we add a hidden value with the same name as the checkbox as a work around.
+ # is set to 0 which is convenient for boolean values.
+ #
+ # ==== Gotcha
+ #
+ # The HTML specification says unchecked check boxes are not successful, and
+ # thus web browsers do not send them. Unfortunately this introduces a gotcha:
+ # if an Invoice model has a +paid+ flag, and in the form that edits a paid
+ # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
+ # any mass-assignment idiom like
+ #
+ # @invoice.update_attributes(params[:invoice])
+ #
+ # wouldn't update the flag.
+ #
+ # To prevent this the helper generates a hidden field with the same name as
+ # the checkbox after the very check box. So, the client either sends only the
+ # hidden field (representing the check box is unchecked), or both fields.
+ # Since the HTML specification says key/value pairs have to be sent in the
+ # same order they appear in the form and Rails parameters extraction always
+ # gets the first occurrence of any given key, that works in ordinary forms.
+ #
+ # Unfortunately that workaround does not work when the check box goes
+ # within an array-like parameter, as in
+ #
+ # <% fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
+ # <%= form.check_box :paid %>
+ # ...
+ # <% end %>
+ #
+ # because parameter name repetition is precisely what Rails seeks to distinguish
+ # the elements of the array.
#
# ==== Examples
# # Let's say that @post.validated? is 1:
@@ -503,10 +528,10 @@ module ActionView
def initialize(object_name, method_name, template_object, object = nil)
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
- @template_object= template_object
+ @template_object = template_object
@object = object
- if @object_name.sub!(/\[\]$/,"")
- if object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
+ if @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
+ if (object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
@auto_index = object.to_param
else
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
@@ -683,7 +708,7 @@ module ActionView
end
def sanitized_object_name
- @sanitized_object_name ||= @object_name.gsub(/[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
+ @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
end
def sanitized_method_name
@@ -701,6 +726,13 @@ module ActionView
def initialize(object_name, object, template, options, proc)
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
@default_options = @options ? @options.slice(:index) : {}
+ if @object_name.to_s.match(/\[\]$/)
+ if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
+ @auto_index = object.to_param
+ else
+ raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
+ end
+ end
end
(field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
@@ -713,16 +745,25 @@ module ActionView
end
def fields_for(record_or_name_or_array, *args, &block)
+ if options.has_key?(:index)
+ index = "[#{options[:index]}]"
+ elsif defined?(@auto_index)
+ self.object_name = @object_name.to_s.sub(/\[\]$/,"")
+ index = "[#{@auto_index}]"
+ else
+ index = ""
+ end
+
case record_or_name_or_array
when String, Symbol
- name = "#{object_name}[#{record_or_name_or_array}]"
+ name = "#{object_name}#{index}[#{record_or_name_or_array}]"
when Array
object = record_or_name_or_array.last
- name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
+ name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
args.unshift(object)
else
object = record_or_name_or_array
- name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
+ name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
args.unshift(object)
end
@@ -741,8 +782,8 @@ module ActionView
@template.radio_button(@object_name, method, tag_value, objectify_options(options))
end
- def error_message_on(method, prepend_text = "", append_text = "", css_class = "formError")
- @template.error_message_on(@object, method, prepend_text, append_text, css_class)
+ def error_message_on(method, *args)
+ @template.error_message_on(@object, method, *args)
end
def error_messages(options = {})
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 87d49397c6..9aae945408 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -133,11 +133,6 @@ module ActionView
InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
end
- # Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
- def country_select(object, method, priority_countries = nil, options = {}, html_options = {})
- InstanceTag.new(object, method, self, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options)
- end
-
# Return select and option tags for the given object and method, using
# #time_zone_options_for_select to generate the list of option tags.
#
@@ -274,22 +269,6 @@ module ActionView
end
end
- # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to
- # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so
- # that they will be listed above the rest of the (long) list.
- #
- # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
- def country_options_for_select(selected = nil, priority_countries = nil)
- country_options = ""
-
- if priority_countries
- country_options += options_for_select(priority_countries, selected)
- country_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
- end
-
- return country_options + options_for_select(COUNTRIES, selected)
- end
-
# Returns a string of option tags for pretty much any time zone in the
# world. Supply a TimeZone name as +selected+ to have it marked as the
# selected option tag. You can also supply an array of TimeZone objects
@@ -347,43 +326,7 @@ module ActionView
end
# All the countries included in the country_options output.
- COUNTRIES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola",
- "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria",
- "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin",
- "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil",
- "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia",
- "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
- "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba",
- "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt",
- "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)",
- "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia",
- "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea",
- "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)",
- "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq",
- "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya",
- "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan",
- "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya",
- "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of",
- "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique",
- "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of",
- "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru",
- "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger",
- "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau",
- "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines",
- "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation",
- "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia",
- "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino",
- "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore",
- "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa",
- "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname",
- "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic",
- "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste",
- "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan",
- "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom",
- "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela",
- "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara",
- "Yemen", "Zambia", "Zimbabwe"] unless const_defined?("COUNTRIES")
+ COUNTRIES = ActiveSupport::Deprecation::DeprecatedConstantProxy.new 'COUNTRIES', 'ActionView::Helpers::FormCountryHelper::COUNTRIES'
end
class InstanceTag #:nodoc:
@@ -406,18 +349,6 @@ module ActionView
)
end
- def to_country_select_tag(priority_countries, options, html_options)
- html_options = html_options.stringify_keys
- add_default_name_and_id(html_options)
- value = value(object)
- content_tag("select",
- add_options(
- country_options_for_select(value, priority_countries),
- options, value
- ), html_options
- )
- end
-
def to_time_zone_select_tag(priority_zones, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
@@ -452,10 +383,6 @@ module ActionView
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
end
- def country_select(method, priority_countries = nil, options = {}, html_options = {})
- @template.country_select(@object_name, method, priority_countries, objectify_options(options), @default_options.merge(html_options))
- end
-
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index bdfb2eebd7..e8ca02d760 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -129,7 +129,7 @@ module ActionView
# label_tag 'name', nil, :class => 'small_label'
# # => <label for="name" class="small_label">Name</label>
def label_tag(name, text = nil, options = {})
- content_tag :label, text || name.humanize, { "for" => name }.update(options.stringify_keys)
+ content_tag :label, text || name.to_s.humanize, { "for" => name }.update(options.stringify_keys)
end
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 22bd5cb440..32089442b7 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -44,13 +44,22 @@ module ActionView
include PrototypeHelper
- # Returns a link that will trigger a JavaScript +function+ using the
+ # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
# onclick handler and return false after the fact.
#
+ # The first argument +name+ is used as the link text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
# The +function+ argument can be omitted in favor of an +update_page+
# block, which evaluates to a string when the template is rendered
# (instead of making an Ajax request first).
#
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
+ #
# Examples:
# link_to_function "Greeting", "alert('Hello world!')"
# Produces:
@@ -89,13 +98,21 @@ module ActionView
content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
end
- # Returns a button that'll trigger a JavaScript +function+ using the
+ # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
# onclick handler.
#
+ # The first argument +name+ is used as the button's value or display text.
+ #
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ #
# The +function+ argument can be omitted in favor of an +update_page+
# block, which evaluates to a string when the template is rendered
# (instead of making an Ajax request first).
#
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ #
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
+ #
# Examples:
# button_to_function "Greeting", "alert('Hello world!')"
# button_to_function "Delete", "if (confirm('Really?')) do_delete()"
diff --git a/actionpack/lib/action_view/helpers/javascripts/prototype.js b/actionpack/lib/action_view/helpers/javascripts/prototype.js
index 546f9fe449..2c70b8a7e8 100644
--- a/actionpack/lib/action_view/helpers/javascripts/prototype.js
+++ b/actionpack/lib/action_view/helpers/javascripts/prototype.js
@@ -1,5 +1,5 @@
-/* Prototype JavaScript framework, version 1.6.0.1
- * (c) 2005-2007 Sam Stephenson
+/* Prototype JavaScript framework, version 1.6.0.2
+ * (c) 2005-2008 Sam Stephenson
*
* Prototype is freely distributable under the terms of an MIT-style license.
* For details, see the Prototype web site: http://www.prototypejs.org/
@@ -7,7 +7,7 @@
*--------------------------------------------------------------------------*/
var Prototype = {
- Version: '1.6.0.1',
+ Version: '1.6.0.2',
Browser: {
IE: !!(window.attachEvent && !window.opera),
@@ -110,7 +110,7 @@ Object.extend(Object, {
try {
if (Object.isUndefined(object)) return 'undefined';
if (object === null) return 'null';
- return object.inspect ? object.inspect() : object.toString();
+ return object.inspect ? object.inspect() : String(object);
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
@@ -171,7 +171,8 @@ Object.extend(Object, {
},
isArray: function(object) {
- return object && object.constructor === Array;
+ return object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object;
},
isHash: function(object) {
@@ -578,7 +579,7 @@ var Template = Class.create({
}
return before + String.interpret(ctx);
- }.bind(this));
+ });
}
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
@@ -806,20 +807,20 @@ Object.extend(Enumerable, {
function $A(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
- var length = iterable.length, results = new Array(length);
+ var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
}
if (Prototype.Browser.WebKit) {
- function $A(iterable) {
+ $A = function(iterable) {
if (!iterable) return [];
if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
iterable.toArray) return iterable.toArray();
- var length = iterable.length, results = new Array(length);
+ var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
- }
+ };
}
Array.from = $A;
@@ -1298,7 +1299,7 @@ Ajax.Request = Class.create(Ajax.Base, {
var contentType = response.getHeader('Content-type');
if (this.options.evalJS == 'force'
- || (this.options.evalJS && contentType
+ || (this.options.evalJS && this.isSameOrigin() && contentType
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
this.evalResponse();
}
@@ -1316,9 +1317,18 @@ Ajax.Request = Class.create(Ajax.Base, {
}
},
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
getHeader: function(name) {
try {
- return this.transport.getResponseHeader(name);
+ return this.transport.getResponseHeader(name) || null;
} catch (e) { return null }
},
@@ -1391,7 +1401,8 @@ Ajax.Response = Class.create({
if (!json) return null;
json = decodeURIComponent(escape(json));
try {
- return json.evalJSON(this.request.options.sanitizeJSON);
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
@@ -1404,7 +1415,8 @@ Ajax.Response = Class.create({
this.responseText.blank())
return null;
try {
- return this.responseText.evalJSON(options.sanitizeJSON);
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
@@ -1608,24 +1620,28 @@ Element.Methods = {
Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
insertions = {bottom:insertions};
- var content, t, range;
+ var content, insert, tagName, childNodes;
- for (position in insertions) {
+ for (var position in insertions) {
content = insertions[position];
position = position.toLowerCase();
- t = Element._insertionTranslations[position];
+ insert = Element._insertionTranslations[position];
if (content && content.toElement) content = content.toElement();
if (Object.isElement(content)) {
- t.insert(element, content);
+ insert(element, content);
continue;
}
content = Object.toHTML(content);
- range = element.ownerDocument.createRange();
- t.initializeRange(element, range);
- t.insert(element, range.createContextualFragment(content.stripScripts()));
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
content.evalScripts.bind(content).defer();
}
@@ -1670,7 +1686,7 @@ Element.Methods = {
},
descendants: function(element) {
- return $(element).getElementsBySelector("*");
+ return $(element).select("*");
},
firstDescendant: function(element) {
@@ -1709,32 +1725,31 @@ Element.Methods = {
element = $(element);
if (arguments.length == 1) return $(element.parentNode);
var ancestors = element.ancestors();
- return expression ? Selector.findElement(ancestors, expression, index) :
- ancestors[index || 0];
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Selector.findElement(ancestors, expression, index);
},
down: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return element.firstDescendant();
- var descendants = element.descendants();
- return expression ? Selector.findElement(descendants, expression, index) :
- descendants[index || 0];
+ return Object.isNumber(expression) ? element.descendants()[expression] :
+ element.select(expression)[index || 0];
},
previous: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
var previousSiblings = element.previousSiblings();
- return expression ? Selector.findElement(previousSiblings, expression, index) :
- previousSiblings[index || 0];
+ return Object.isNumber(expression) ? previousSiblings[expression] :
+ Selector.findElement(previousSiblings, expression, index);
},
next: function(element, expression, index) {
element = $(element);
if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
var nextSiblings = element.nextSiblings();
- return expression ? Selector.findElement(nextSiblings, expression, index) :
- nextSiblings[index || 0];
+ return Object.isNumber(expression) ? nextSiblings[expression] :
+ Selector.findElement(nextSiblings, expression, index);
},
select: function() {
@@ -1860,7 +1875,8 @@ Element.Methods = {
do { ancestor = ancestor.parentNode; }
while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
}
- if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
+ if (nextAncestor && nextAncestor.sourceIndex)
+ return (e > a && e < nextAncestor.sourceIndex);
}
while (element = element.parentNode)
@@ -2004,7 +2020,7 @@ Element.Methods = {
if (element) {
if (element.tagName == 'BODY') break;
var p = Element.getStyle(element, 'position');
- if (p == 'relative' || p == 'absolute') break;
+ if (p !== 'static') break;
}
} while (element);
return Element._returnOffset(valueL, valueT);
@@ -2153,46 +2169,6 @@ Element._attributeTranslations = {
}
};
-
-if (!document.createRange || Prototype.Browser.Opera) {
- Element.Methods.insert = function(element, insertions) {
- element = $(element);
-
- if (Object.isString(insertions) || Object.isNumber(insertions) ||
- Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
- insertions = { bottom: insertions };
-
- var t = Element._insertionTranslations, content, position, pos, tagName;
-
- for (position in insertions) {
- content = insertions[position];
- position = position.toLowerCase();
- pos = t[position];
-
- if (content && content.toElement) content = content.toElement();
- if (Object.isElement(content)) {
- pos.insert(element, content);
- continue;
- }
-
- content = Object.toHTML(content);
- tagName = ((position == 'before' || position == 'after')
- ? element.parentNode : element).tagName.toUpperCase();
-
- if (t.tags[tagName]) {
- var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
- if (position == 'top' || position == 'after') fragments.reverse();
- fragments.each(pos.insert.curry(element));
- }
- else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
-
- content.evalScripts.bind(content).defer();
- }
-
- return element;
- };
-}
-
if (Prototype.Browser.Opera) {
Element.Methods.getStyle = Element.Methods.getStyle.wrap(
function(proceed, element, style) {
@@ -2237,12 +2213,31 @@ if (Prototype.Browser.Opera) {
}
else if (Prototype.Browser.IE) {
- $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
+ // IE doesn't report offsets correctly for static elements, so we change them
+ // to "relative" to get the values, then change them back.
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
Element.Methods[method] = Element.Methods[method].wrap(
function(proceed, element) {
element = $(element);
var position = element.getStyle('position');
- if (position != 'static') return proceed(element);
+ if (position !== 'static') return proceed(element);
+ // Trigger hasLayout on the offset parent so that IE6 reports
+ // accurate offsetTop and offsetLeft values for position: fixed.
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ offsetParent.setStyle({ zoom: 1 });
element.setStyle({ position: 'relative' });
var value = proceed(element);
element.setStyle({ position: position });
@@ -2324,7 +2319,10 @@ else if (Prototype.Browser.IE) {
};
Element._attributeTranslations.write = {
- names: Object.clone(Element._attributeTranslations.read.names),
+ names: Object.extend({
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ }, Element._attributeTranslations.read.names),
values: {
checked: function(element, value) {
element.checked = !!value;
@@ -2444,7 +2442,7 @@ if (Prototype.Browser.IE || Prototype.Browser.Opera) {
};
}
-if (document.createElement('div').outerHTML) {
+if ('outerHTML' in document.createElement('div')) {
Element.Methods.replace = function(element, content) {
element = $(element);
@@ -2482,45 +2480,25 @@ Element._returnOffset = function(l, t) {
Element._getContentFromAnonymousElement = function(tagName, html) {
var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
- div.innerHTML = t[0] + html + t[1];
- t[2].times(function() { div = div.firstChild });
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ } else div.innerHTML = html;
return $A(div.childNodes);
};
Element._insertionTranslations = {
- before: {
- adjacency: 'beforeBegin',
- insert: function(element, node) {
- element.parentNode.insertBefore(node, element);
- },
- initializeRange: function(element, range) {
- range.setStartBefore(element);
- }
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
},
- top: {
- adjacency: 'afterBegin',
- insert: function(element, node) {
- element.insertBefore(node, element.firstChild);
- },
- initializeRange: function(element, range) {
- range.selectNodeContents(element);
- range.collapse(true);
- }
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
},
- bottom: {
- adjacency: 'beforeEnd',
- insert: function(element, node) {
- element.appendChild(node);
- }
+ bottom: function(element, node) {
+ element.appendChild(node);
},
- after: {
- adjacency: 'afterEnd',
- insert: function(element, node) {
- element.parentNode.insertBefore(node, element.nextSibling);
- },
- initializeRange: function(element, range) {
- range.setStartAfter(element);
- }
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
},
tags: {
TABLE: ['<table>', '</table>', 1],
@@ -2532,7 +2510,6 @@ Element._insertionTranslations = {
};
(function() {
- this.bottom.initializeRange = this.top.initializeRange;
Object.extend(this.tags, {
THEAD: this.tags.TBODY,
TFOOT: this.tags.TBODY,
@@ -2716,7 +2693,7 @@ document.viewport = {
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
}
};
-/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+/* Portions of the Selector class are derived from Jack Slocumñ€ℱs DomQuery,
* part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
* license. Please see http://www.yui-ext.com/ for more information. */
@@ -2959,13 +2936,13 @@ Object.extend(Selector, {
},
criteria: {
- tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
- className: 'n = h.className(n, r, "#{1}", c); c = false;',
- id: 'n = h.id(n, r, "#{1}", c); c = false;',
- attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
attr: function(m) {
m[3] = (m[5] || m[6]);
- return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
},
pseudo: function(m) {
if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
@@ -2989,7 +2966,8 @@ Object.extend(Selector, {
tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
id: /^#([\w\-\*]+)(\b|$)/,
className: /^\.([\w\-\*]+)(\b|$)/,
- pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
+ pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
attrPresence: /^\[([\w]+)\]/,
attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
},
@@ -3014,7 +2992,7 @@ Object.extend(Selector, {
attr: function(element, matches) {
var nodeValue = Element.readAttribute(element, matches[1]);
- return Selector.operators[matches[2]](nodeValue, matches[3]);
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
}
},
@@ -3029,14 +3007,15 @@ Object.extend(Selector, {
// marks an array of nodes for counting
mark: function(nodes) {
+ var _true = Prototype.emptyFunction;
for (var i = 0, node; node = nodes[i]; i++)
- node._counted = true;
+ node._countedByPrototype = _true;
return nodes;
},
unmark: function(nodes) {
for (var i = 0, node; node = nodes[i]; i++)
- node._counted = undefined;
+ node._countedByPrototype = undefined;
return nodes;
},
@@ -3044,15 +3023,15 @@ Object.extend(Selector, {
// "ofType" flag indicates whether we're indexing for nth-of-type
// rather than nth-child
index: function(parentNode, reverse, ofType) {
- parentNode._counted = true;
+ parentNode._countedByPrototype = Prototype.emptyFunction;
if (reverse) {
for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
var node = nodes[i];
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
}
} else {
for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
}
},
@@ -3061,8 +3040,8 @@ Object.extend(Selector, {
if (nodes.length == 0) return nodes;
var results = [], n;
for (var i = 0, l = nodes.length; i < l; i++)
- if (!(n = nodes[i])._counted) {
- n._counted = true;
+ if (!(n = nodes[i])._countedByPrototype) {
+ n._countedByPrototype = Prototype.emptyFunction;
results.push(Element.extend(n));
}
return Selector.handlers.unmark(results);
@@ -3114,7 +3093,7 @@ Object.extend(Selector, {
// TOKEN FUNCTIONS
tagName: function(nodes, root, tagName, combinator) {
- tagName = tagName.toUpperCase();
+ var uTagName = tagName.toUpperCase();
var results = [], h = Selector.handlers;
if (nodes) {
if (combinator) {
@@ -3127,7 +3106,7 @@ Object.extend(Selector, {
if (tagName == "*") return nodes;
}
for (var i = 0, node; node = nodes[i]; i++)
- if (node.tagName.toUpperCase() == tagName) results.push(node);
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
return results;
} else return root.getElementsByTagName(tagName);
},
@@ -3174,16 +3153,18 @@ Object.extend(Selector, {
return results;
},
- attrPresence: function(nodes, root, attr) {
+ attrPresence: function(nodes, root, attr, combinator) {
if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
var results = [];
for (var i = 0, node; node = nodes[i]; i++)
if (Element.hasAttribute(node, attr)) results.push(node);
return results;
},
- attr: function(nodes, root, attr, value, operator) {
+ attr: function(nodes, root, attr, value, operator, combinator) {
if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
var handler = Selector.operators[operator], results = [];
for (var i = 0, node; node = nodes[i]; i++) {
var nodeValue = Element.readAttribute(node, attr);
@@ -3262,7 +3243,7 @@ Object.extend(Selector, {
var h = Selector.handlers, results = [], indexed = [], m;
h.mark(nodes);
for (var i = 0, node; node = nodes[i]; i++) {
- if (!node.parentNode._counted) {
+ if (!node.parentNode._countedByPrototype) {
h.index(node.parentNode, reverse, ofType);
indexed.push(node.parentNode);
}
@@ -3300,7 +3281,7 @@ Object.extend(Selector, {
var exclusions = new Selector(selector).findElements(root);
h.mark(exclusions);
for (var i = 0, results = [], node; node = nodes[i]; i++)
- if (!node._counted) results.push(node);
+ if (!node._countedByPrototype) results.push(node);
h.unmark(exclusions);
return results;
},
@@ -3334,11 +3315,19 @@ Object.extend(Selector, {
'|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
},
+ split: function(expression) {
+ var expressions = [];
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ return expressions;
+ },
+
matchElements: function(elements, expression) {
- var matches = new Selector(expression).findElements(), h = Selector.handlers;
+ var matches = $$(expression), h = Selector.handlers;
h.mark(matches);
for (var i = 0, results = [], element; element = elements[i]; i++)
- if (element._counted) results.push(element);
+ if (element._countedByPrototype) results.push(element);
h.unmark(matches);
return results;
},
@@ -3351,11 +3340,7 @@ Object.extend(Selector, {
},
findChildElements: function(element, expressions) {
- var exprs = expressions.join(',');
- expressions = [];
- exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
- expressions.push(m[1].strip());
- });
+ expressions = Selector.split(expressions.join(','));
var results = [], h = Selector.handlers;
for (var i = 0, l = expressions.length, selector; i < l; i++) {
selector = new Selector(expressions[i].strip());
@@ -3366,13 +3351,22 @@ Object.extend(Selector, {
});
if (Prototype.Browser.IE) {
- // IE returns comment nodes on getElementsByTagName("*").
- // Filter them out.
- Selector.handlers.concat = function(a, b) {
- for (var i = 0, node; node = b[i]; i++)
- if (node.tagName !== "!") a.push(node);
- return a;
- };
+ Object.extend(Selector.handlers, {
+ // IE returns comment nodes on getElementsByTagName("*").
+ // Filter them out.
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ if (node.tagName !== "!") a.push(node);
+ return a;
+ },
+
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node.removeAttribute('_countedByPrototype');
+ return nodes;
+ }
+ });
}
function $$() {
@@ -3850,9 +3844,9 @@ Object.extend(Event, (function() {
var cache = Event.cache;
function getEventID(element) {
- if (element._eventID) return element._eventID;
+ if (element._prototypeEventID) return element._prototypeEventID[0];
arguments.callee.id = arguments.callee.id || 1;
- return element._eventID = ++arguments.callee.id;
+ return element._prototypeEventID = [++arguments.callee.id];
}
function getDOMEventName(eventName) {
@@ -3880,7 +3874,7 @@ Object.extend(Event, (function() {
return false;
Event.extend(event);
- handler.call(element, event)
+ handler.call(element, event);
};
wrapper.handler = handler;
@@ -3962,11 +3956,12 @@ Object.extend(Event, (function() {
if (element == document && document.createEvent && !element.dispatchEvent)
element = document.documentElement;
+ var event;
if (document.createEvent) {
- var event = document.createEvent("HTMLEvents");
+ event = document.createEvent("HTMLEvents");
event.initEvent("dataavailable", true, true);
} else {
- var event = document.createEventObject();
+ event = document.createEventObject();
event.eventType = "ondataavailable";
}
@@ -3995,20 +3990,21 @@ Element.addMethods({
Object.extend(document, {
fire: Element.Methods.fire.methodize(),
observe: Element.Methods.observe.methodize(),
- stopObserving: Element.Methods.stopObserving.methodize()
+ stopObserving: Element.Methods.stopObserving.methodize(),
+ loaded: false
});
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
Matthias Miller, Dean Edwards and John Resig. */
- var timer, fired = false;
+ var timer;
function fireContentLoadedEvent() {
- if (fired) return;
+ if (document.loaded) return;
if (timer) window.clearInterval(timer);
document.fire("dom:loaded");
- fired = true;
+ document.loaded = true;
}
if (document.addEventListener) {
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 120bb4cc1f..77f19b36a6 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -22,14 +22,14 @@ module ActionView
# number_to_phone(1235551234, :country_code => 1) # => +1-123-555-1234
#
# number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
- # => +1.123.555.1234 x 1343
+ # => +1.123.555.1234 x 1343
def number_to_phone(number, options = {})
number = number.to_s.strip unless number.nil?
- options = options.stringify_keys
- area_code = options["area_code"] || nil
- delimiter = options["delimiter"] || "-"
- extension = options["extension"].to_s.strip || nil
- country_code = options["country_code"] || nil
+ options = options.symbolize_keys
+ area_code = options[:area_code] || nil
+ delimiter = options[:delimiter] || "-"
+ extension = options[:extension].to_s.strip || nil
+ country_code = options[:country_code] || nil
begin
str = ""
@@ -51,10 +51,10 @@ module ActionView
#
# ==== Options
# * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
- # * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
+ # * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
- # * <tt>:format</tt> - Sets the format of the output string (defaults to "%u%n"). The field types are:
+ # * <tt>:format</tt> - Sets the format of the output string (defaults to "%u%n"). The field types are:
#
# %u The currency unit
# %n The number
@@ -69,16 +69,25 @@ module ActionView
# number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "", :format => "%n %u")
# # => 1234567890,50 &pound;
def number_to_currency(number, options = {})
- options = options.stringify_keys
- precision = options["precision"] || 2
- unit = options["unit"] || "$"
- separator = precision > 0 ? options["separator"] || "." : ""
- delimiter = options["delimiter"] || ","
- format = options["format"] || "%u%n"
+ options.symbolize_keys!
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+ currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
+ defaults = defaults.merge(currency)
+
+ precision = options[:precision] || defaults[:precision]
+ unit = options[:unit] || defaults[:unit]
+ separator = options[:separator] || defaults[:separator]
+ delimiter = options[:delimiter] || defaults[:delimiter]
+ format = options[:format] || defaults[:format]
+ separator = '' if precision == 0
begin
- parts = number_with_precision(number, precision).split('.')
- format.gsub(/%n/, number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s).gsub(/%u/, unit)
+ format.gsub(/%n/, number_with_precision(number,
+ :precision => precision,
+ :delimiter => delimiter,
+ :separator => separator)
+ ).gsub(/%u/, unit)
rescue
number
end
@@ -90,96 +99,192 @@ module ActionView
# ==== Options
# * <tt>:precision</tt> - Sets the level of precision (defaults to 3).
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
#
# ==== Examples
- # number_to_percentage(100) # => 100.000%
- # number_to_percentage(100, :precision => 0) # => 100%
- #
- # number_to_percentage(302.24398923423, :precision => 5)
- # # => 302.24399%
+ # number_to_percentage(100) # => 100.000%
+ # number_to_percentage(100, :precision => 0) # => 100%
+ # number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
+ # number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
def number_to_percentage(number, options = {})
- options = options.stringify_keys
- precision = options["precision"] || 3
- separator = options["separator"] || "."
+ options.symbolize_keys!
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+ percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :raise => true) rescue {}
+ defaults = defaults.merge(percentage)
+
+ precision = options[:precision] || defaults[:precision]
+ separator = options[:separator] || defaults[:separator]
+ delimiter = options[:delimiter] || defaults[:delimiter]
begin
- number = number_with_precision(number, precision)
- parts = number.split('.')
- if parts.at(1).nil?
- parts[0] + "%"
- else
- parts[0] + separator + parts[1].to_s + "%"
- end
+ number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter) + "%"
rescue
number
end
end
- # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You
- # can customize the format using optional <em>delimiter</em> and <em>separator</em> parameters.
+ # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
+ # customize the format in the +options+ hash.
#
# ==== Options
- # * <tt>delimiter</tt> - Sets the thousands delimiter (defaults to ",").
- # * <tt>separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
#
# ==== Examples
- # number_with_delimiter(12345678) # => 12,345,678
- # number_with_delimiter(12345678.05) # => 12,345,678.05
- # number_with_delimiter(12345678, ".") # => 12.345.678
- #
- # number_with_delimiter(98765432.98, " ", ",")
+ # number_with_delimiter(12345678) # => 12,345,678
+ # number_with_delimiter(12345678.05) # => 12,345,678.05
+ # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
+ # number_with_delimiter(12345678, :seperator => ",") # => 12,345,678
+ # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
# # => 98 765 432,98
- def number_with_delimiter(number, delimiter=",", separator=".")
+ #
+ # You can still use <tt>number_with_delimiter</tt> with the old API that accepts the
+ # +delimiter+ as its optional second and the +separator+ as its
+ # optional third parameter:
+ # number_with_delimiter(12345678, " ") # => 12 345.678
+ # number_with_delimiter(12345678.05, ".", ",") # => 12.345.678,05
+ def number_with_delimiter(number, *args)
+ options = args.extract_options!
+ options.symbolize_keys!
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+
+ unless args.empty?
+ ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' +
+ 'instead of separate delimiter and precision arguments.', caller)
+ delimiter = args[0] || defaults[:delimiter]
+ separator = args[1] || defaults[:separator]
+ end
+
+ delimiter ||= (options[:delimiter] || defaults[:delimiter])
+ separator ||= (options[:separator] || defaults[:separator])
+
begin
parts = number.to_s.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
- parts.join separator
+ parts.join(separator)
rescue
number
end
end
- # Formats a +number+ with the specified level of +precision+ (e.g., 112.32 has a precision of 2). The default
- # level of precision is 3.
+ # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision of 2).
+ # You can customize the format in the +options+ hash.
+ #
+ # ==== Options
+ # * <tt>:precision</tt> - Sets the level of precision (defaults to 3).
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
#
# ==== Examples
- # number_with_precision(111.2345) # => 111.235
- # number_with_precision(111.2345, 2) # => 111.23
- # number_with_precision(13, 5) # => 13.00000
- # number_with_precision(389.32314, 0) # => 389
- def number_with_precision(number, precision=3)
- "%01.#{precision}f" % ((Float(number) * (10 ** precision)).round.to_f / 10 ** precision)
- rescue
- number
+ # number_with_precision(111.2345) # => 111.235
+ # number_with_precision(111.2345, :precision => 2) # => 111.23
+ # number_with_precision(13, :precision => 5) # => 13.00000
+ # number_with_precision(389.32314, :precision => 0) # => 389
+ # number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
+ # # => 1.111,23
+ #
+ # You can still use <tt>number_with_precision</tt> with the old API that accepts the
+ # +precision+ as its optional second parameter:
+ # number_with_precision(number_with_precision(111.2345, 2) # => 111.23
+ def number_with_precision(number, *args)
+ options = args.extract_options!
+ options.symbolize_keys!
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+ precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale],
+ :raise => true) rescue {}
+ defaults = defaults.merge(precision_defaults)
+
+ unless args.empty?
+ ActiveSupport::Deprecation.warn('number_with_precision takes an option hash ' +
+ 'instead of a separate precision argument.', caller)
+ precision = args[0] || defaults[:precision]
+ end
+
+ precision ||= (options[:precision] || defaults[:precision])
+ separator ||= (options[:separator] || defaults[:separator])
+ delimiter ||= (options[:delimiter] || defaults[:delimiter])
+
+ begin
+ rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
+ number_with_delimiter("%01.#{precision}f" % rounded_number,
+ :separator => separator,
+ :delimiter => delimiter)
+ rescue
+ number
+ end
end
+ STORAGE_UNITS = %w( Bytes KB MB GB TB ).freeze
+
# Formats the bytes in +size+ into a more understandable representation
- # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
+ # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
# reporting file sizes to users. This method returns nil if
- # +size+ cannot be converted into a number. You can change the default
- # precision of 1 using the precision parameter +precision+.
+ # +size+ cannot be converted into a number. You can customize the
+ # format in the +options+ hash.
+ #
+ # ==== Options
+ # * <tt>:precision</tt> - Sets the level of precision (defaults to 1).
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
#
# ==== Examples
- # number_to_human_size(123) # => 123 Bytes
- # number_to_human_size(1234) # => 1.2 KB
- # number_to_human_size(12345) # => 12.1 KB
- # number_to_human_size(1234567) # => 1.2 MB
- # number_to_human_size(1234567890) # => 1.1 GB
- # number_to_human_size(1234567890123) # => 1.1 TB
+ # number_to_human_size(123) # => 123 Bytes
+ # number_to_human_size(1234) # => 1.2 KB
+ # number_to_human_size(12345) # => 12.1 KB
+ # number_to_human_size(1234567) # => 1.2 MB
+ # number_to_human_size(1234567890) # => 1.1 GB
+ # number_to_human_size(1234567890123) # => 1.1 TB
+ # number_to_human_size(1234567, :precision => 2) # => 1.18 MB
+ # number_to_human_size(483989, :precision => 0) # => 473 KB
+ # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
+ #
+ # You can still use <tt>number_to_human_size</tt> with the old API that accepts the
+ # +precision+ as its optional second parameter:
# number_to_human_size(1234567, 2) # => 1.18 MB
- # number_to_human_size(483989, 0) # => 4 MB
- def number_to_human_size(size, precision=1)
- size = Kernel.Float(size)
- case
- when size.to_i == 1; "1 Byte"
- when size < 1.kilobyte; "%d Bytes" % size
- when size < 1.megabyte; "%.#{precision}f KB" % (size / 1.0.kilobyte)
- when size < 1.gigabyte; "%.#{precision}f MB" % (size / 1.0.megabyte)
- when size < 1.terabyte; "%.#{precision}f GB" % (size / 1.0.gigabyte)
- else "%.#{precision}f TB" % (size / 1.0.terabyte)
- end.sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ')
- rescue
- nil
+ # number_to_human_size(483989, 0) # => 473 KB
+ def number_to_human_size(number, *args)
+ return number.nil? ? nil : pluralize(number.to_i, "Byte") if number.to_i < 1024
+
+ options = args.extract_options!
+ options.symbolize_keys!
+
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
+ human = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {}
+ defaults = defaults.merge(human)
+
+ unless args.empty?
+ ActiveSupport::Deprecation.warn('number_to_human_size takes an option hash ' +
+ 'instead of a separate precision argument.', caller)
+ precision = args[0] || defaults[:precision]
+ end
+
+ precision ||= (options[:precision] || defaults[:precision])
+ separator ||= (options[:separator] || defaults[:separator])
+ delimiter ||= (options[:delimiter] || defaults[:delimiter])
+
+ max_exp = STORAGE_UNITS.size - 1
+ number = Float(number)
+ exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
+ exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
+ number /= 1024 ** exponent
+ unit = STORAGE_UNITS[exponent]
+
+ begin
+ escaped_separator = Regexp.escape(separator)
+ number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter
+ ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') + " #{unit}"
+ rescue
+ number
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index d0c281c803..ff83494e94 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -3,25 +3,25 @@ require 'set'
module ActionView
module Helpers
# Prototype[http://www.prototypejs.org/] is a JavaScript library that provides
- # DOM[http://en.wikipedia.org/wiki/Document_Object_Model] manipulation,
+ # DOM[http://en.wikipedia.org/wiki/Document_Object_Model] manipulation,
# Ajax[http://www.adaptivepath.com/publications/essays/archives/000385.php]
- # functionality, and more traditional object-oriented facilities for JavaScript.
+ # functionality, and more traditional object-oriented facilities for JavaScript.
# This module provides a set of helpers to make it more convenient to call
- # functions from Prototype using Rails, including functionality to call remote
- # Rails methods (that is, making a background request to a Rails action) using Ajax.
- # This means that you can call actions in your controllers without
- # reloading the page, but still update certain parts of it using
+ # functions from Prototype using Rails, including functionality to call remote
+ # Rails methods (that is, making a background request to a Rails action) using Ajax.
+ # This means that you can call actions in your controllers without
+ # reloading the page, but still update certain parts of it using
# injections into the DOM. A common use case is having a form that adds
# a new element to a list without reloading the page or updating a shopping
# cart total when a new item is added.
#
# == Usage
- # To be able to use these helpers, you must first include the Prototype
- # JavaScript framework in your pages.
+ # To be able to use these helpers, you must first include the Prototype
+ # JavaScript framework in your pages.
#
# javascript_include_tag 'prototype'
#
- # (See the documentation for
+ # (See the documentation for
# ActionView::Helpers::JavaScriptHelper for more information on including
# this and other JavaScript files in your Rails templates.)
#
@@ -29,7 +29,7 @@ module ActionView
#
# link_to_remote "Add to cart",
# :url => { :action => "add", :id => product.id },
- # :update => { :success => "cart", :failure => "error" }
+ # :update => { :success => "cart", :failure => "error" }
#
# ...through a form...
#
@@ -50,8 +50,8 @@ module ActionView
# :update => :hits,
# :with => 'query'
# %>
- #
- # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than
+ #
+ # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than
# are listed here); check out the documentation for each method to find out more about its usage and options.
#
# === Common Options
@@ -61,9 +61,9 @@ module ActionView
#
# == Designing your Rails actions for Ajax
# When building your action handlers (that is, the Rails actions that receive your background requests), it's
- # important to remember a few things. First, whatever your action would normall return to the browser, it will
+ # important to remember a few things. First, whatever your action would normally return to the browser, it will
# return to the Ajax call. As such, you typically don't want to render with a layout. This call will cause
- # the layout to be transmitted back to your page, and, if you have a full HTML/CSS, will likely mess a lot of things up.
+ # the layout to be transmitted back to your page, and, if you have a full HTML/CSS, will likely mess a lot of things up.
# You can turn the layout off on particular actions by doing the following:
#
# class SiteController < ActionController::Base
@@ -74,8 +74,8 @@ module ActionView
#
# render :layout => false
#
- # You can tell the type of request from within your action using the <tt>request.xhr?</tt> (XmlHttpRequest, the
- # method that Ajax uses to make background requests) method.
+ # You can tell the type of request from within your action using the <tt>request.xhr?</tt> (XmlHttpRequest, the
+ # method that Ajax uses to make background requests) method.
# def name
# # Is this an XmlHttpRequest request?
# if (request.xhr?)
@@ -93,7 +93,7 @@ module ActionView
#
# Dropping this in your ApplicationController turns the layout off for every request that is an "xhr" request.
#
- # If you are just returning a little data or don't want to build a template for your output, you may opt to simply
+ # If you are just returning a little data or don't want to build a template for your output, you may opt to simply
# render text output, like this:
#
# render :text => 'Return this from my method!'
@@ -103,7 +103,7 @@ module ActionView
#
# == Updating multiple elements
# See JavaScriptGenerator for information on updating multiple elements
- # on the page in an Ajax response.
+ # on the page in an Ajax response.
module PrototypeHelper
unless const_defined? :CALLBACKS
CALLBACKS = Set.new([ :uninitialized, :loading, :loaded,
@@ -111,67 +111,67 @@ module ActionView
(100..599).to_a)
AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
:asynchronous, :method, :insertion, :position,
- :form, :with, :update, :script ]).merge(CALLBACKS)
+ :form, :with, :update, :script, :type ]).merge(CALLBACKS)
end
- # Returns a link to a remote action defined by <tt>options[:url]</tt>
- # (using the url_for format) that's called in the background using
+ # Returns a link to a remote action defined by <tt>options[:url]</tt>
+ # (using the url_for format) that's called in the background using
# XMLHttpRequest. The result of that request can then be inserted into a
- # DOM object whose id can be specified with <tt>options[:update]</tt>.
+ # DOM object whose id can be specified with <tt>options[:update]</tt>.
# Usually, the result would be a partial prepared by the controller with
- # render :partial.
+ # render :partial.
#
# Examples:
- # # Generates: <a href="#" onclick="new Ajax.Updater('posts', '/blog/destroy/3', {asynchronous:true, evalScripts:true});
+ # # Generates: <a href="#" onclick="new Ajax.Updater('posts', '/blog/destroy/3', {asynchronous:true, evalScripts:true});
# # return false;">Delete this post</a>
- # link_to_remote "Delete this post", :update => "posts",
+ # link_to_remote "Delete this post", :update => "posts",
# :url => { :action => "destroy", :id => post.id }
#
- # # Generates: <a href="#" onclick="new Ajax.Updater('emails', '/mail/list_emails', {asynchronous:true, evalScripts:true});
+ # # Generates: <a href="#" onclick="new Ajax.Updater('emails', '/mail/list_emails', {asynchronous:true, evalScripts:true});
# # return false;"><img alt="Refresh" src="/images/refresh.png?" /></a>
- # link_to_remote(image_tag("refresh"), :update => "emails",
+ # link_to_remote(image_tag("refresh"), :update => "emails",
# :url => { :action => "list_emails" })
- #
+ #
# You can override the generated HTML options by specifying a hash in
# <tt>options[:html]</tt>.
- #
+ #
# link_to_remote "Delete this post", :update => "posts",
- # :url => post_url(@post), :method => :delete,
- # :html => { :class => "destructive" }
+ # :url => post_url(@post), :method => :delete,
+ # :html => { :class => "destructive" }
#
# You can also specify a hash for <tt>options[:update]</tt> to allow for
- # easy redirection of output to an other DOM element if a server-side
+ # easy redirection of output to an other DOM element if a server-side
# error occurs:
#
# Example:
- # # Generates: <a href="#" onclick="new Ajax.Updater({success:'posts',failure:'error'}, '/blog/destroy/5',
+ # # Generates: <a href="#" onclick="new Ajax.Updater({success:'posts',failure:'error'}, '/blog/destroy/5',
# # {asynchronous:true, evalScripts:true}); return false;">Delete this post</a>
# link_to_remote "Delete this post",
# :url => { :action => "destroy", :id => post.id },
# :update => { :success => "posts", :failure => "error" }
#
- # Optionally, you can use the <tt>options[:position]</tt> parameter to
- # influence how the target DOM element is updated. It must be one of
+ # Optionally, you can use the <tt>options[:position]</tt> parameter to
+ # influence how the target DOM element is updated. It must be one of
# <tt>:before</tt>, <tt>:top</tt>, <tt>:bottom</tt>, or <tt>:after</tt>.
#
# The method used is by default POST. You can also specify GET or you
# can simulate PUT or DELETE over POST. All specified with <tt>options[:method]</tt>
#
# Example:
- # # Generates: <a href="#" onclick="new Ajax.Request('/person/4', {asynchronous:true, evalScripts:true, method:'delete'});
+ # # Generates: <a href="#" onclick="new Ajax.Request('/person/4', {asynchronous:true, evalScripts:true, method:'delete'});
# # return false;">Destroy</a>
# link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete
#
- # By default, these remote requests are processed asynchronous during
- # which various JavaScript callbacks can be triggered (for progress
- # indicators and the likes). All callbacks get access to the
- # <tt>request</tt> object, which holds the underlying XMLHttpRequest.
+ # By default, these remote requests are processed asynchronous during
+ # which various JavaScript callbacks can be triggered (for progress
+ # indicators and the likes). All callbacks get access to the
+ # <tt>request</tt> object, which holds the underlying XMLHttpRequest.
#
# To access the server response, use <tt>request.responseText</tt>, to
# find out the HTTP status, use <tt>request.status</tt>.
#
# Example:
- # # Generates: <a href="#" onclick="new Ajax.Request('/words/undo?n=33', {asynchronous:true, evalScripts:true,
+ # # Generates: <a href="#" onclick="new Ajax.Request('/words/undo?n=33', {asynchronous:true, evalScripts:true,
# # onComplete:function(request){undoRequestCompleted(request)}}); return false;">hello</a>
# word = 'hello'
# link_to_remote word,
@@ -180,43 +180,43 @@ module ActionView
#
# The callbacks that may be specified are (in order):
#
- # <tt>:loading</tt>:: Called when the remote document is being
+ # <tt>:loading</tt>:: Called when the remote document is being
# loaded with data by the browser.
# <tt>:loaded</tt>:: Called when the browser has finished loading
# the remote document.
- # <tt>:interactive</tt>:: Called when the user can interact with the
- # remote document, even though it has not
+ # <tt>:interactive</tt>:: Called when the user can interact with the
+ # remote document, even though it has not
# finished loading.
# <tt>:success</tt>:: Called when the XMLHttpRequest is completed,
# and the HTTP status code is in the 2XX range.
# <tt>:failure</tt>:: Called when the XMLHttpRequest is completed,
# and the HTTP status code is not in the 2XX
# range.
- # <tt>:complete</tt>:: Called when the XMLHttpRequest is complete
- # (fires after success/failure if they are
+ # <tt>:complete</tt>:: Called when the XMLHttpRequest is complete
+ # (fires after success/failure if they are
# present).
- #
- # You can further refine <tt>:success</tt> and <tt>:failure</tt> by
+ #
+ # You can further refine <tt>:success</tt> and <tt>:failure</tt> by
# adding additional callbacks for specific status codes.
#
# Example:
- # # Generates: <a href="#" onclick="new Ajax.Request('/testing/action', {asynchronous:true, evalScripts:true,
- # # on404:function(request){alert('Not found...? Wrong URL...?')},
+ # # Generates: <a href="#" onclick="new Ajax.Request('/testing/action', {asynchronous:true, evalScripts:true,
+ # # on404:function(request){alert('Not found...? Wrong URL...?')},
# # onFailure:function(request){alert('HTTP Error ' + request.status + '!')}}); return false;">hello</a>
# link_to_remote word,
# :url => { :action => "action" },
# 404 => "alert('Not found...? Wrong URL...?')",
# :failure => "alert('HTTP Error ' + request.status + '!')"
#
- # A status code callback overrides the success/failure handlers if
+ # A status code callback overrides the success/failure handlers if
# present.
#
# If you for some reason or another need synchronous processing (that'll
- # block the browser while the request is happening), you can specify
+ # block the browser while the request is happening), you can specify
# <tt>options[:type] = :synchronous</tt>.
#
# You can customize further browser side call logic by passing in
- # JavaScript code snippets via some optional parameters. In their order
+ # JavaScript code snippets via some optional parameters. In their order
# of use these are:
#
# <tt>:confirm</tt>:: Adds confirmation dialog.
@@ -228,7 +228,7 @@ module ActionView
# <tt>:after</tt>:: Called immediately after request was
# initiated and before <tt>:loading</tt>.
# <tt>:submit</tt>:: Specifies the DOM element ID that's used
- # as the parent of the form elements. By
+ # as the parent of the form elements. By
# default this is the current form, but
# it could just as well be the ID of a
# table row or any other DOM element.
@@ -238,10 +238,10 @@ module ActionView
# URL query string.
#
# Example:
- #
+ #
# :with => "'name=' + $('name').value"
#
- # You can generate a link that uses AJAX in the general case, while
+ # You can generate a link that uses AJAX in the general case, while
# degrading gracefully to plain link behavior in the absence of
# JavaScript by setting <tt>html_options[:href]</tt> to an alternate URL.
# Note the extra curly braces around the <tt>options</tt> hash separate
@@ -251,7 +251,7 @@ module ActionView
# link_to_remote "Delete this post",
# { :update => "posts", :url => { :action => "destroy", :id => post.id } },
# :href => url_for(:action => "destroy", :id => post.id)
- def link_to_remote(name, options = {}, html_options = nil)
+ def link_to_remote(name, options = {}, html_options = nil)
link_to_function(name, remote_function(options), html_options || options.delete(:html))
end
@@ -262,15 +262,15 @@ module ActionView
# and defining callbacks is the same as link_to_remote.
# Examples:
# # Call get_averages and put its results in 'avg' every 10 seconds
- # # Generates:
- # # new PeriodicalExecuter(function() {new Ajax.Updater('avg', '/grades/get_averages',
+ # # Generates:
+ # # new PeriodicalExecuter(function() {new Ajax.Updater('avg', '/grades/get_averages',
# # {asynchronous:true, evalScripts:true})}, 10)
# periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg')
#
# # Call invoice every 10 seconds with the id of the customer
# # If it succeeds, update the invoice DIV; if it fails, update the error DIV
# # Generates:
- # # new PeriodicalExecuter(function() {new Ajax.Updater({success:'invoice',failure:'error'},
+ # # new PeriodicalExecuter(function() {new Ajax.Updater({success:'invoice',failure:'error'},
# # '/testing/invoice/16', {asynchronous:true, evalScripts:true})}, 10)
# periodically_call_remote(:url => { :action => 'invoice', :id => customer.id },
# :update => { :success => "invoice", :failure => "error" }
@@ -286,11 +286,11 @@ module ActionView
javascript_tag(code)
end
- # Returns a form tag that will submit using XMLHttpRequest in the
- # background instead of the regular reloading POST arrangement. Even
+ # Returns a form tag that will submit using XMLHttpRequest in the
+ # background instead of the regular reloading POST arrangement. Even
# though it's using JavaScript to serialize the form elements, the form
# submission will work just like a regular submission as viewed by the
- # receiving side (all elements available in <tt>params</tt>). The options for
+ # receiving side (all elements available in <tt>params</tt>). The options for
# specifying the target with <tt>:url</tt> and defining callbacks is the same as
# +link_to_remote+.
#
@@ -299,21 +299,21 @@ module ActionView
#
# Example:
# # Generates:
- # # <form action="/some/place" method="post" onsubmit="new Ajax.Request('',
+ # # <form action="/some/place" method="post" onsubmit="new Ajax.Request('',
# # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;">
- # form_remote_tag :html => { :action =>
+ # form_remote_tag :html => { :action =>
# url_for(:controller => "some", :action => "place") }
#
# The Hash passed to the <tt>:html</tt> key is equivalent to the options (2nd)
# argument in the FormTagHelper.form_tag method.
#
- # By default the fall-through action is the same as the one specified in
+ # By default the fall-through action is the same as the one specified in
# the <tt>:url</tt> (and the default method is <tt>:post</tt>).
#
# form_remote_tag also takes a block, like form_tag:
# # Generates:
- # # <form action="/" method="post" onsubmit="new Ajax.Request('/',
- # # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)});
+ # # <form action="/" method="post" onsubmit="new Ajax.Request('/',
+ # # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)});
# # return false;"> <div><input name="commit" type="submit" value="Save" /></div>
# # </form>
# <% form_remote_tag :url => '/posts' do -%>
@@ -323,19 +323,19 @@ module ActionView
options[:form] = true
options[:html] ||= {}
- options[:html][:onsubmit] =
- (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") +
+ options[:html][:onsubmit] =
+ (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") +
"#{remote_function(options)}; return false;"
form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block)
end
- # Creates a form that will submit using XMLHttpRequest in the background
- # instead of the regular reloading POST arrangement and a scope around a
+ # Creates a form that will submit using XMLHttpRequest in the background
+ # instead of the regular reloading POST arrangement and a scope around a
# specific resource that is used as a base for questioning about
- # values for the fields.
+ # values for the fields.
#
- # === Resource
+ # === Resource
#
# Example:
# <% remote_form_for(@post) do |f| %>
@@ -348,7 +348,7 @@ module ActionView
# ...
# <% end %>
#
- # === Nested Resource
+ # === Nested Resource
#
# Example:
# <% remote_form_for([@post, @comment]) do |f| %>
@@ -387,23 +387,23 @@ module ActionView
concat('</form>')
end
alias_method :form_remote_for, :remote_form_for
-
+
# Returns a button input tag with the element name of +name+ and a value (i.e., display text) of +value+
# that will submit form using XMLHttpRequest in the background instead of a regular POST request that
- # reloads the page.
+ # reloads the page.
#
# # Create a button that submits to the create action
- # #
- # # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',
- # # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
+ # #
+ # # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',
+ # # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Create" />
# <%= button_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
#
# # Submit to the remote action update and update the DIV succeed or fail based
# # on the success or failure of the request
# #
- # # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},
- # # '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
+ # # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},
+ # # '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
# # return false;" type="button" value="Update" />
# <%= button_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
# :update => { :success => "succeed", :failure => "fail" }
@@ -423,7 +423,7 @@ module ActionView
tag("input", options[:html], false)
end
alias_method :submit_to_remote, :button_to_remote
-
+
# Returns '<tt>eval(request.responseText)</tt>' which is the JavaScript function
# that +form_remote_tag+ can call in <tt>:complete</tt> to evaluate a multiple
# update return document using +update_element_function+ calls.
@@ -433,11 +433,11 @@ module ActionView
# Returns the JavaScript needed for a remote function.
# Takes the same arguments as link_to_remote.
- #
+ #
# Example:
- # # Generates: <select id="options" onchange="new Ajax.Updater('options',
+ # # Generates: <select id="options" onchange="new Ajax.Updater('options',
# # '/testing/update_options', {asynchronous:true, evalScripts:true})">
- # <select id="options" onchange="<%= remote_function(:update => "options",
+ # <select id="options" onchange="<%= remote_function(:update => "options",
# :url => { :action => :update_options }) %>">
# <option value="0">Hello</option>
# <option value="1">World</option>
@@ -455,7 +455,7 @@ module ActionView
update << "'#{options[:update]}'"
end
- function = update.empty? ?
+ function = update.empty? ?
"new Ajax.Request(" :
"new Ajax.Updater(#{update}, "
@@ -476,9 +476,9 @@ module ActionView
# callback when its contents have changed. The default callback is an
# Ajax call. By default the value of the observed field is sent as a
# parameter with the Ajax call.
- #
+ #
# Example:
- # # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest',
+ # # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest',
# # '/testing/find_suggestion', {asynchronous:true, evalScripts:true, parameters:'q=' + value})})
# <%= observe_field :suggest, :url => { :action => :find_suggestion },
# :frequency => 0.25,
@@ -500,14 +500,14 @@ module ActionView
# new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')})
# The element parameter is the DOM element being observed, and the value is its value at the
# time the observer is triggered.
- #
+ #
# Additional options are:
# <tt>:frequency</tt>:: The frequency (in seconds) at which changes to
# this field will be detected. Not setting this
# option at all or to a value equal to or less than
# zero will use event based observation instead of
# time based observation.
- # <tt>:update</tt>:: Specifies the DOM ID of the element whose
+ # <tt>:update</tt>:: Specifies the DOM ID of the element whose
# innerHTML should be updated with the
# XMLHttpRequest response text.
# <tt>:with</tt>:: A JavaScript expression specifying the parameters
@@ -518,7 +518,7 @@ module ActionView
# variable +value+.
#
# Examples
- #
+ #
# :with => "'my_custom_key=' + value"
# :with => "'person[name]=' + prompt('New name')"
# :with => "Form.Element.serialize('other-field')"
@@ -544,7 +544,7 @@ module ActionView
# observe_field 'book_title',
# :url => 'http://example.com/books/edit/1',
# :with => 'title'
- #
+ #
# # Sends params: {:book_title => 'Title of the book'} when the focus leaves
# # the input field.
# observe_field 'book_title',
@@ -558,7 +558,7 @@ module ActionView
build_observer('Form.Element.EventObserver', field_id, options)
end
end
-
+
# Observes the form with the DOM ID specified by +form_id+ and calls a
# callback when its contents have changed. The default callback is an
# Ajax call. By default all fields of the observed field are sent as
@@ -574,39 +574,57 @@ module ActionView
build_observer('Form.EventObserver', form_id, options)
end
end
-
- # All the methods were moved to GeneratorMethods so that
+
+ # All the methods were moved to GeneratorMethods so that
# #include_helpers_from_context has nothing to overwrite.
class JavaScriptGenerator #:nodoc:
def initialize(context, &block) #:nodoc:
@context, @lines = context, []
include_helpers_from_context
- @context.instance_exec(self, &block)
+ @context.with_output_buffer(@lines) do
+ @context.instance_exec(self, &block)
+ end
end
-
+
private
def include_helpers_from_context
- @context.extended_by.each do |mod|
- extend mod unless mod.name =~ /^ActionView::Helpers/
+ unless generator_methods_module = @context.instance_variable_get(:@__javascript_generator_methods__)
+ modules = @context.extended_by - ([ActionView::Helpers] + ActionView::Helpers.included_modules)
+
+ generator_methods_module = Module.new do
+ modules.each do |mod|
+ begin
+ include mod
+ rescue Exception => e
+ # HACK: Probably not a good idea to suppress these warnings
+ # AFAIK exceptions are only raised in while testing with mocha
+ # because the module does not like to be included into other
+ # non TestUnit classes
+ end
+ end
+ include GeneratorMethods
+ end
+ @context.instance_variable_set(:@__javascript_generator_methods__, generator_methods_module)
end
- extend GeneratorMethods
+
+ extend generator_methods_module
end
-
- # JavaScriptGenerator generates blocks of JavaScript code that allow you
- # to change the content and presentation of multiple DOM elements. Use
+
+ # JavaScriptGenerator generates blocks of JavaScript code that allow you
+ # to change the content and presentation of multiple DOM elements. Use
# this in your Ajax response bodies, either in a <script> tag or as plain
# JavaScript sent with a Content-type of "text/javascript".
#
- # Create new instances with PrototypeHelper#update_page or with
- # ActionController::Base#render, then call +insert_html+, +replace_html+,
- # +remove+, +show+, +hide+, +visual_effect+, or any other of the built-in
- # methods on the yielded generator in any order you like to modify the
- # content and appearance of the current page.
+ # Create new instances with PrototypeHelper#update_page or with
+ # ActionController::Base#render, then call +insert_html+, +replace_html+,
+ # +remove+, +show+, +hide+, +visual_effect+, or any other of the built-in
+ # methods on the yielded generator in any order you like to modify the
+ # content and appearance of the current page.
#
# Example:
#
# # Generates:
- # # new Insertion.Bottom("list", "<li>Some item</li>");
+ # # new Element.insert("list", { bottom: <li>Some item</li>" });
# # new Effect.Highlight("list");
# # ["status-indicator", "cancel-link"].each(Element.hide);
# update_page do |page|
@@ -614,12 +632,12 @@ module ActionView
# page.visual_effect :highlight, 'list'
# page.hide 'status-indicator', 'cancel-link'
# end
- #
+ #
#
# Helper methods can be used in conjunction with JavaScriptGenerator.
- # When a helper method is called inside an update block on the +page+
+ # When a helper method is called inside an update block on the +page+
# object, that method will also have access to a +page+ object.
- #
+ #
# Example:
#
# module ApplicationHelper
@@ -655,7 +673,7 @@ module ActionView
# end
# end
#
- # You can also use PrototypeHelper#update_page_tag instead of
+ # You can also use PrototypeHelper#update_page_tag instead of
# PrototypeHelper#update_page to wrap the generated JavaScript in a
# <script> tag.
module GeneratorMethods
@@ -668,7 +686,7 @@ module ActionView
end
end
end
-
+
# Returns a element reference by finding it through +id+ in the DOM. This element can then be
# used for further method calls. Examples:
#
@@ -689,31 +707,31 @@ module ActionView
JavaScriptElementProxy.new(self, ActionController::RecordIdentifier.dom_id(id))
end
end
-
- # Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
+
+ # Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
# expression as an argument to another JavaScriptGenerator method.
def literal(code)
ActiveSupport::JSON::Variable.new(code.to_s)
end
-
+
# Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
# used for further method calls. Examples:
#
# page.select('p') # => $$('p');
# page.select('p.welcome b').first # => $$('p.welcome b').first();
# page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
- #
+ #
# You can also use prototype enumerations with the collection. Observe:
- #
+ #
# # Generates: $$('#items li').each(function(value) { value.hide(); });
# page.select('#items li').each do |value|
# value.hide
- # end
+ # end
#
- # Though you can call the block param anything you want, they are always rendered in the
+ # Though you can call the block param anything you want, they are always rendered in the
# javascript as 'value, index.' Other enumerations, like collect() return the last statement:
#
- # # Generates: var hidden = $$('#items li').collect(function(value, index) { return value.hide(); });
+ # # Generates: var hidden = $$('#items li').collect(function(value, index) { return value.hide(); });
# page.select('#items li').collect('hidden') do |item|
# item.hide
# end
@@ -721,13 +739,13 @@ module ActionView
def select(pattern)
JavaScriptElementCollectionProxy.new(self, pattern)
end
-
+
# Inserts HTML at the specified +position+ relative to the DOM element
# identified by the given +id+.
- #
+ #
# +position+ may be one of:
- #
- # <tt>:top</tt>:: HTML is inserted inside the element, before the
+ #
+ # <tt>:top</tt>:: HTML is inserted inside the element, before the
# element's existing content.
# <tt>:bottom</tt>:: HTML is inserted inside the element, after the
# element's existing content.
@@ -739,18 +757,18 @@ module ActionView
#
# # Insert the rendered 'navigation' partial just before the DOM
# # element with ID 'content'.
- # # Generates: new Insertion.Before("content", "-- Contents of 'navigation' partial --");
+ # # Generates: Element.insert("content", { before: "-- Contents of 'navigation' partial --" });
# page.insert_html :before, 'content', :partial => 'navigation'
#
# # Add a list item to the bottom of the <ul> with ID 'list'.
- # # Generates: new Insertion.Bottom("list", "<li>Last item</li>");
+ # # Generates: Element.insert("list", { bottom: "<li>Last item</li>" });
# page.insert_html :bottom, 'list', '<li>Last item</li>'
#
def insert_html(position, id, *options_for_render)
- insertion = position.to_s.camelize
- call "new Insertion.#{insertion}", id, render(*options_for_render)
+ content = javascript_object_for(render(*options_for_render))
+ record "Element.insert(\"#{id}\", { #{position.to_s.downcase}: #{content} });"
end
-
+
# Replaces the inner HTML of the DOM element with the given +id+.
#
# +options_for_render+ may be either a string of HTML to insert, or a hash
@@ -764,7 +782,7 @@ module ActionView
def replace_html(id, *options_for_render)
call 'Element.update', id, render(*options_for_render)
end
-
+
# Replaces the "outer HTML" (i.e., the entire element, not just its
# contents) of the DOM element with the given +id+.
#
@@ -786,7 +804,7 @@ module ActionView
# </div>
#
# # Insert a new person
- # #
+ # #
# # Generates: new Insertion.Bottom({object: "Matz", partial: "person"}, "");
# page.insert_html :bottom, :partial => 'person', :object => @person
#
@@ -798,7 +816,7 @@ module ActionView
def replace(id, *options_for_render)
call 'Element.replace', id, render(*options_for_render)
end
-
+
# Removes the DOM elements with the given +ids+ from the page.
#
# Example:
@@ -810,9 +828,9 @@ module ActionView
def remove(*ids)
loop_on_multiple_args 'Element.remove', ids
end
-
+
# Shows hidden DOM elements with the given +ids+.
- #
+ #
# Example:
#
# # Show a few people
@@ -822,7 +840,7 @@ module ActionView
def show(*ids)
loop_on_multiple_args 'Element.show', ids
end
-
+
# Hides the visible DOM elements with the given +ids+.
#
# Example:
@@ -832,9 +850,9 @@ module ActionView
# page.hide 'person_29', 'person_9', 'person_0'
#
def hide(*ids)
- loop_on_multiple_args 'Element.hide', ids
+ loop_on_multiple_args 'Element.hide', ids
end
-
+
# Toggles the visibility of the DOM elements with the given +ids+.
# Example:
#
@@ -844,9 +862,9 @@ module ActionView
# page.toggle 'person_14', 'person_12', 'person_23' # Shows the previously hidden elements
#
def toggle(*ids)
- loop_on_multiple_args 'Element.toggle', ids
+ loop_on_multiple_args 'Element.toggle', ids
end
-
+
# Displays an alert dialog with the given +message+.
#
# Example:
@@ -856,21 +874,21 @@ module ActionView
def alert(message)
call 'alert', message
end
-
+
# Redirects the browser to the given +location+ using JavaScript, in the same form as +url_for+.
#
# Examples:
#
# # Generates: window.location.href = "/mycontroller";
# page.redirect_to(:action => 'index')
- #
+ #
# # Generates: window.location.href = "/account/signup";
# page.redirect_to(:controller => 'account', :action => 'signup')
def redirect_to(location)
url = location.is_a?(String) ? location : @context.url_for(location)
record "window.location.href = #{url.inspect}"
end
-
+
# Reloads the browser's current +location+ using JavaScript
#
# Examples:
@@ -884,17 +902,17 @@ module ActionView
# Calls the JavaScript +function+, optionally with the given +arguments+.
#
# If a block is given, the block will be passed to a new JavaScriptGenerator;
- # the resulting JavaScript code will then be wrapped inside <tt>function() { ... }</tt>
+ # the resulting JavaScript code will then be wrapped inside <tt>function() { ... }</tt>
# and passed as the called function's final argument.
- #
+ #
# Examples:
#
# # Generates: Element.replace(my_element, "My content to replace with.")
# page.call 'Element.replace', 'my_element', "My content to replace with."
- #
+ #
# # Generates: alert('My message!')
# page.call 'alert', 'My message!'
- #
+ #
# # Generates:
# # my_method(function() {
# # $("one").show();
@@ -907,7 +925,7 @@ module ActionView
def call(function, *arguments, &block)
record "#{function}(#{arguments_for_call(arguments, block)})"
end
-
+
# Assigns the JavaScript +variable+ the given +value+.
#
# Examples:
@@ -918,13 +936,13 @@ module ActionView
# # Generates: record_count = 33;
# page.assign 'record_count', 33
#
- # # Generates: tabulated_total = 47
+ # # Generates: tabulated_total = 47
# page.assign 'tabulated_total', @total_from_cart
#
def assign(variable, value)
record "#{variable} = #{javascript_object_for(value)}"
end
-
+
# Writes raw JavaScript to the page.
#
# Example:
@@ -933,10 +951,10 @@ module ActionView
def <<(javascript)
@lines << javascript
end
-
+
# Executes the content of the block after a delay of +seconds+. Example:
#
- # # Generates:
+ # # Generates:
# # setTimeout(function() {
# # ;
# # new Effect.Fade("notice",{});
@@ -949,13 +967,13 @@ module ActionView
yield
record "}, #{(seconds * 1000).to_i})"
end
-
- # Starts a script.aculo.us visual effect. See
+
+ # Starts a script.aculo.us visual effect. See
# ActionView::Helpers::ScriptaculousHelper for more information.
def visual_effect(name, id = nil, options = {})
record @context.send(:visual_effect, name, id, options)
end
-
+
# Creates a script.aculo.us sortable element. Useful
# to recreate sortable elements after items get added
# or deleted.
@@ -963,66 +981,66 @@ module ActionView
def sortable(id, options = {})
record @context.send(:sortable_element_js, id, options)
end
-
+
# Creates a script.aculo.us draggable element.
# See ActionView::Helpers::ScriptaculousHelper for more information.
def draggable(id, options = {})
record @context.send(:draggable_element_js, id, options)
end
-
+
# Creates a script.aculo.us drop receiving element.
# See ActionView::Helpers::ScriptaculousHelper for more information.
def drop_receiving(id, options = {})
record @context.send(:drop_receiving_element_js, id, options)
end
-
+
private
def loop_on_multiple_args(method, ids)
- record(ids.size>1 ?
- "#{javascript_object_for(ids)}.each(#{method})" :
+ record(ids.size>1 ?
+ "#{javascript_object_for(ids)}.each(#{method})" :
"#{method}(#{ids.first.to_json})")
end
-
+
def page
self
end
-
+
def record(line)
returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do
self << line
end
end
-
+
def render(*options_for_render)
old_format = @context && @context.template_format
@context.template_format = :html if @context
- Hash === options_for_render.first ?
- @context.render(*options_for_render) :
+ Hash === options_for_render.first ?
+ @context.render(*options_for_render) :
options_for_render.first.to_s
ensure
@context.template_format = old_format if @context
end
-
+
def javascript_object_for(object)
object.respond_to?(:to_json) ? object.to_json : object.inspect
end
-
+
def arguments_for_call(arguments, block = nil)
arguments << block_to_function(block) if block
arguments.map { |argument| javascript_object_for(argument) }.join ', '
end
-
+
def block_to_function(block)
generator = self.class.new(@context, &block)
literal("function() { #{generator.to_s} }")
- end
+ end
def method_missing(method, *arguments)
JavaScriptProxy.new(self, method.to_s.camelize)
end
end
end
-
+
# Yields a JavaScriptGenerator and returns the generated JavaScript code.
# Use this to update multiple elements on a page in an Ajax response.
# See JavaScriptGenerator for more information.
@@ -1035,13 +1053,13 @@ module ActionView
def update_page(&block)
JavaScriptGenerator.new(@template, &block).to_s
end
-
+
# Works like update_page but wraps the generated JavaScript in a <script>
# tag. Use this to include generated JavaScript in an ERb template.
# See JavaScriptGenerator for more information.
#
# +html_options+ may be a hash of <script> attributes to be passed
- # to ActionView::Helpers::JavaScriptHelper#javascript_tag.
+ # to ActionView::Helpers::JavaScriptHelper#javascript_tag.
def update_page_tag(html_options = {}, &block)
javascript_tag update_page(&block), html_options
end
@@ -1049,10 +1067,10 @@ module ActionView
protected
def options_for_ajax(options)
js_options = build_callbacks(options)
-
+
js_options['asynchronous'] = options[:type] != :synchronous
js_options['method'] = method_option_to_s(options[:method]) if options[:method]
- js_options['insertion'] = "Insertion.#{options[:position].to_s.camelize}" if options[:position]
+ js_options['insertion'] = options[:position].to_s.downcase if options[:position]
js_options['evalScripts'] = options[:script].nil? || options[:script]
if options[:form]
@@ -1062,7 +1080,7 @@ module ActionView
elsif options[:with]
js_options['parameters'] = options[:with]
end
-
+
if protect_against_forgery? && !options[:form]
if js_options['parameters']
js_options['parameters'] << " + '&"
@@ -1071,14 +1089,14 @@ module ActionView
end
js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
end
-
+
options_for_javascript(js_options)
end
- def method_option_to_s(method)
+ def method_option_to_s(method)
(method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
end
-
+
def build_observer(klass, name, options = {})
if options[:with] && (options[:with] !~ /[\{=(.]/)
options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)"
@@ -1095,7 +1113,7 @@ module ActionView
javascript << ")"
javascript_tag(javascript)
end
-
+
def build_callbacks(options)
callbacks = {}
options.each do |callback, code|
@@ -1108,7 +1126,7 @@ module ActionView
end
end
- # Converts chained method calls on DOM proxy elements into JavaScript chains
+ # Converts chained method calls on DOM proxy elements into JavaScript chains
class JavaScriptProxy < ActiveSupport::BasicObject #:nodoc:
def initialize(generator, root = nil)
@@ -1124,7 +1142,7 @@ module ActionView
call("#{method.to_s.camelize(:lower)}", *arguments, &block)
end
end
-
+
def call(function, *arguments, &block)
append_to_function_chain!("#{function}(#{@generator.send(:arguments_for_call, arguments, block)})")
self
@@ -1133,23 +1151,23 @@ module ActionView
def assign(variable, value)
append_to_function_chain!("#{variable} = #{@generator.send(:javascript_object_for, value)}")
end
-
+
def function_chain
@function_chain ||= @generator.instance_variable_get(:@lines)
end
-
+
def append_to_function_chain!(call)
function_chain[-1].chomp!(';')
function_chain[-1] += ".#{call};"
end
end
-
+
class JavaScriptElementProxy < JavaScriptProxy #:nodoc:
def initialize(generator, id)
@id = id
super(generator, "$(#{id.to_json})")
end
-
+
# Allows access of element attributes through +attribute+. Examples:
#
# page['foo']['style'] # => $('foo').style;
@@ -1160,11 +1178,11 @@ module ActionView
append_to_function_chain!(attribute)
self
end
-
+
def []=(variable, value)
assign(variable, value)
end
-
+
def replace_html(*options_for_render)
call 'update', @generator.send(:render, *options_for_render)
end
@@ -1172,11 +1190,11 @@ module ActionView
def replace(*options_for_render)
call 'replace', @generator.send(:render, *options_for_render)
end
-
+
def reload(options_for_replace = {})
replace(options_for_replace.merge({ :partial => @id.to_s }))
end
-
+
end
class JavaScriptVariableProxy < JavaScriptProxy #:nodoc:
@@ -1195,7 +1213,7 @@ module ActionView
def to_json(options = nil)
@variable
end
-
+
private
def append_to_function_chain!(call)
@generator << @variable if @empty
@@ -1213,7 +1231,7 @@ module ActionView
def initialize(generator, pattern)
super(generator, @pattern = pattern)
end
-
+
def each_slice(variable, number, &block)
if block
enumerate :eachSlice, :variable => variable, :method_args => [number], :yield_args => %w(value index), :return => true, &block
@@ -1222,18 +1240,18 @@ module ActionView
append_enumerable_function!("eachSlice(#{number.to_json});")
end
end
-
+
def grep(variable, pattern, &block)
enumerate :grep, :variable => variable, :return => true, :method_args => [pattern], :yield_args => %w(value index), &block
end
-
+
def in_groups_of(variable, number, fill_with = nil)
arguments = [number]
arguments << fill_with unless fill_with.nil?
add_variable_assignment!(variable)
append_enumerable_function!("inGroupsOf(#{arguments_for_call arguments});")
- end
-
+ end
+
def inject(variable, memo, &block)
enumerate :inject, :variable => variable, :method_args => [memo], :yield_args => %w(memo value index), :return => true, &block
end
@@ -1295,13 +1313,13 @@ module ActionView
function_chain.push("return #{function_chain.pop.chomp(';')};")
end
end
-
+
def append_enumerable_function!(call)
function_chain[-1].chomp!(';')
function_chain[-1] += ".#{call}"
end
end
-
+
class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\
def initialize(generator, pattern)
super(generator, "$$(#{pattern.to_json})")
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index b0dacfe964..637caf203b 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -6,17 +6,13 @@ module ActionView
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
# These helper methods extend ActionView making them callable within your template files.
module SanitizeHelper
- def self.included(base)
- base.extend(ClassMethods)
- end
-
# This +sanitize+ helper will html encode all tags and strip all attributes that aren't specifically allowed.
# It also strips href/src tags with invalid protocols, like javascript: especially. It does its best to counter any
# tricks that hackers may use, like throwing in unicode/ascii/hex values to get past the javascript: filters. Check out
# the extensive test suite.
#
# <%= sanitize @article.body %>
- #
+ #
# You can add or remove tags/attributes if you want to customize it a bit. See ActionView::Base for full docs on the
# available options. You can add tags/attributes for single uses of +sanitize+ by passing either the <tt>:attributes</tt> or <tt>:tags</tt> options:
#
@@ -27,27 +23,27 @@ module ActionView
# Custom Use (only the mentioned tags and attributes are allowed, nothing else)
#
# <%= sanitize @article.body, :tags => %w(table tr td), :attributes => %w(id class style)
- #
+ #
# Add table tags to the default allowed tags
- #
+ #
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
# end
- #
+ #
# Remove tags to the default allowed tags
- #
+ #
# Rails::Initializer.run do |config|
# config.after_initialize do
# ActionView::Base.sanitized_allowed_tags.delete 'div'
# end
# end
- #
+ #
# Change allowed default attributes
- #
+ #
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
# end
- #
+ #
# Please note that sanitizing user-provided text does not guarantee that the
# resulting markup is valid (conforming to a document type) or even well-formed.
# The output may still contain e.g. unescaped '<', '>', '&' characters and
@@ -62,8 +58,8 @@ module ActionView
self.class.white_list_sanitizer.sanitize_css(style)
end
- # Strips all HTML tags from the +html+, including comments. This uses the
- # html-scanner tokenizer and so its HTML parsing ability is limited by
+ # Strips all HTML tags from the +html+, including comments. This uses the
+ # html-scanner tokenizer and so its HTML parsing ability is limited by
# that of html-scanner.
#
# ==== Examples
@@ -73,10 +69,10 @@ module ActionView
#
# strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
# # => Bold no more! See more here...
- #
+ #
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
# # => Welcome to my website!
- def strip_tags(html)
+ def strip_tags(html)
self.class.full_sanitizer.sanitize(html)
end
@@ -96,21 +92,48 @@ module ActionView
end
module ClassMethods #:nodoc:
- def self.extended(base)
- class << base
- attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
-
- # we want these to be class methods on ActionView::Base, they'll get mattr_readers for these below.
- helper_def = [:sanitized_protocol_separator, :sanitized_uri_attributes, :sanitized_bad_tags, :sanitized_allowed_tags,
- :sanitized_allowed_attributes, :sanitized_allowed_css_properties, :sanitized_allowed_css_keywords,
- :sanitized_shorthand_css_properties, :sanitized_allowed_protocols, :sanitized_protocol_separator=].collect! do |prop|
- prop = prop.to_s
- "def #{prop}(#{:value if prop =~ /=$/}) white_list_sanitizer.#{prop.sub /sanitized_/, ''} #{:value if prop =~ /=$/} end"
- end.join("\n")
- eval helper_def
- end
- end
-
+ attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
+
+ def sanitized_protocol_separator
+ white_list_sanitizer.protocol_separator
+ end
+
+ def sanitized_uri_attributes
+ white_list_sanitizer.uri_attributes
+ end
+
+ def sanitized_bad_tags
+ white_list_sanitizer.bad_tags
+ end
+
+ def sanitized_allowed_tags
+ white_list_sanitizer.allowed_tags
+ end
+
+ def sanitized_allowed_attributes
+ white_list_sanitizer.allowed_attributes
+ end
+
+ def sanitized_allowed_css_properties
+ white_list_sanitizer.allowed_css_properties
+ end
+
+ def sanitized_allowed_css_keywords
+ white_list_sanitizer.allowed_css_keywords
+ end
+
+ def sanitized_shorthand_css_properties
+ white_list_sanitizer.shorthand_css_properties
+ end
+
+ def sanitized_allowed_protocols
+ white_list_sanitizer.allowed_protocols
+ end
+
+ def sanitized_protocol_separator=(value)
+ white_list_sanitizer.protocol_separator = value
+ end
+
# Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
# any object that responds to +sanitize+.
#
@@ -184,7 +207,7 @@ module ActionView
HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
end
- # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ heleprs.
+ # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
#
# Rails::Initializer.run do |config|
# config.action_view.sanitized_allowed_css_properties = 'expression'
diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
index c9b2761cb8..b938c1a801 100644
--- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
+++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
@@ -193,7 +193,7 @@ module ActionView
#
# * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto
# this element. Override this callback with a JavaScript expression to
- # change the default drop behavour. Example:
+ # change the default drop behaviour. Example:
#
# :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
#
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index aeafd3906d..de08672d2d 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -64,7 +64,7 @@ module ActionView
# <% content_tag :div, :class => "strong" do -%>
# Hello world!
# <% end -%>
- # # => <div class="strong"><p>Hello world!</p></div>
+ # # => <div class="strong">Hello world!</div>
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
if block_given?
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
@@ -110,12 +110,18 @@ module ActionView
private
BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
- # Check whether we're called from an erb template.
- # We'd return a string in any other case, but erb <%= ... %>
- # can't take an <% end %> later on, so we have to use <% ... %>
- # and implicitly concat.
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block)
+ if RUBY_VERSION < '1.9.0'
+ # Check whether we're called from an erb template.
+ # We'd return a string in any other case, but erb <%= ... %>
+ # can't take an <% end %> later on, so we have to use <% ... %>
+ # and implicitly concat.
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block)
+ end
+ else
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
+ end
end
def content_tag_string(name, content, options, escape = true)
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 3e3452b615..f9096d0029 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -34,40 +34,69 @@ module ActionView
end
if RUBY_VERSION < '1.9'
- # If +text+ is longer than +length+, +text+ will be truncated to the length of
- # +length+ (defaults to 30) and the last characters will be replaced with the +truncate_string+
- # (defaults to "...").
+ # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
+ # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...").
#
# ==== Examples
- # truncate("Once upon a time in a world far far away", 14)
- # # => Once upon a...
#
# truncate("Once upon a time in a world far far away")
# # => Once upon a time in a world f...
#
- # truncate("And they found that many people were sleeping better.", 25, "(clipped)")
+ # truncate("Once upon a time in a world far far away", :length => 14)
+ # # => Once upon a...
+ #
+ # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)")
# # => And they found that many (clipped)
#
+ # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15)
+ # # => And they found... (continued)
+ #
+ # You can still use <tt>truncate</tt> with the old API that accepts the
+ # +length+ as its optional second and the +ellipsis+ as its
+ # optional third parameter:
+ # truncate("Once upon a time in a world far far away", 14)
+ # # => Once upon a time in a world f...
+ #
# truncate("And they found that many people were sleeping better.", 15, "... (continued)")
# # => And they found... (continued)
- def truncate(text, length = 30, truncate_string = "...")
+ def truncate(text, *args)
+ options = args.extract_options!
+ unless args.empty?
+ ActiveSupport::Deprecation.warn('truncate takes an option hash instead of separate ' +
+ 'length and omission arguments', caller)
+
+ options[:length] = args[0] || 30
+ options[:omission] = args[1] || "..."
+ end
+ options.reverse_merge!(:length => 30, :omission => "...")
+
if text
- l = length - truncate_string.chars.length
+ l = options[:length] - options[:omission].chars.length
chars = text.chars
- (chars.length > length ? chars[0...l] + truncate_string : text).to_s
+ (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s
end
end
else
- def truncate(text, length = 30, truncate_string = "...") #:nodoc:
+ def truncate(text, *args) #:nodoc:
+ options = args.extract_options!
+ unless args.empty?
+ ActiveSupport::Deprecation.warn('truncate takes an option hash instead of separate ' +
+ 'length and omission arguments', caller)
+
+ options[:length] = args[0] || 30
+ options[:omission] = args[1] || "..."
+ end
+ options.reverse_merge!(:length => 30, :omission => "...")
+
if text
- l = length - truncate_string.length
- (text.length > length ? text[0...l] + truncate_string : text).to_s
+ l = options[:length].to_i - options[:omission].length
+ (text.length > options[:length].to_i ? text[0...l] + options[:omission] : text).to_s
end
end
end
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
- # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+
+ # a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
# as a single-quoted string with \1 where the phrase is to be inserted (defaults to
# '<strong class="highlight">\1</strong>')
#
@@ -78,52 +107,75 @@ module ActionView
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
# # => You searched for: ruby, rails, dhh
#
- # highlight('You searched for: rails', ['for', 'rails'], '<em>\1</em>')
+ # highlight('You searched for: rails', ['for', 'rails'], :highlighter => '<em>\1</em>')
# # => You searched <em>for</em>: <em>rails</em>
#
- # highlight('You searched for: rails', 'rails', "<a href='search?q=\1'>\1</a>")
- # # => You searched for: <a href='search?q=rails>rails</a>
- def highlight(text, phrases, highlighter = '<strong class="highlight">\1</strong>')
+ # highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>')
+ # # => You searched for: <a href="search?q=rails">rails</a>
+ #
+ # You can still use <tt>highlight</tt> with the old API that accepts the
+ # +highlighter+ as its optional third parameter:
+ # highlight('You searched for: rails', 'rails', '<a href="search?q=\1">\1</a>') # => You searched for: <a href="search?q=rails">rails</a>
+ def highlight(text, phrases, *args)
+ options = args.extract_options!
+ unless args.empty?
+ options[:highlighter] = args[0] || '<strong class="highlight">\1</strong>'
+ end
+ options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
+
if text.blank? || phrases.blank?
text
else
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
- text.gsub(/(#{match})/i, highlighter)
+ text.gsub(/(#{match})/i, options[:highlighter])
end
end
if RUBY_VERSION < '1.9'
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
- # The +radius+ expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
- # defined in +radius+ (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
- # then the +excerpt_string+ will be prepended/appended accordingly. The resulting string will be stripped in any case.
- # If the +phrase+ isn't found, nil is returned.
+ # The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
+ # defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
+ # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The resulting string
+ # will be stripped in any case. If the +phrase+ isn't found, nil is returned.
#
# ==== Examples
- # excerpt('This is an example', 'an', 5)
- # # => "...s is an exam..."
+ # excerpt('This is an example', 'an', :radius => 5)
+ # # => ...s is an exam...
#
- # excerpt('This is an example', 'is', 5)
- # # => "This is a..."
+ # excerpt('This is an example', 'is', :radius => 5)
+ # # => This is a...
#
# excerpt('This is an example', 'is')
- # # => "This is an example"
+ # # => This is an example
#
- # excerpt('This next thing is an example', 'ex', 2)
- # # => "...next..."
+ # excerpt('This next thing is an example', 'ex', :radius => 2)
+ # # => ...next...
#
- # excerpt('This is also an example', 'an', 8, '<chop> ')
- # # => "<chop> is also an example"
- def excerpt(text, phrase, radius = 100, excerpt_string = "...")
+ # excerpt('This is also an example', 'an', :radius => 8, :omission => '<chop> ')
+ # # => <chop> is also an example
+ #
+ # You can still use <tt>excerpt</tt> with the old API that accepts the
+ # +radius+ as its optional third and the +ellipsis+ as its
+ # optional forth parameter:
+ # excerpt('This is an example', 'an', 5) # => ...s is an exam...
+ # excerpt('This is also an example', 'an', 8, '<chop> ') # => <chop> is also an example
+ def excerpt(text, phrase, *args)
+ options = args.extract_options!
+ unless args.empty?
+ options[:radius] = args[0] || 100
+ options[:omission] = args[1] || "..."
+ end
+ options.reverse_merge!(:radius => 100, :omission => "...")
+
if text && phrase
phrase = Regexp.escape(phrase)
if found_pos = text.chars =~ /(#{phrase})/i
- start_pos = [ found_pos - radius, 0 ].max
- end_pos = [ [ found_pos + phrase.chars.length + radius - 1, 0].max, text.chars.length ].min
+ start_pos = [ found_pos - options[:radius], 0 ].max
+ end_pos = [ [ found_pos + phrase.chars.length + options[:radius] - 1, 0].max, text.chars.length ].min
- prefix = start_pos > 0 ? excerpt_string : ""
- postfix = end_pos < text.chars.length - 1 ? excerpt_string : ""
+ prefix = start_pos > 0 ? options[:omission] : ""
+ postfix = end_pos < text.chars.length - 1 ? options[:omission] : ""
prefix + text.chars[start_pos..end_pos].strip + postfix
else
@@ -132,16 +184,23 @@ module ActionView
end
end
else
- def excerpt(text, phrase, radius = 100, excerpt_string = "...") #:nodoc:
+ def excerpt(text, phrase, *args) #:nodoc:
+ options = args.extract_options!
+ unless args.empty?
+ options[:radius] = args[0] || 100
+ options[:omission] = args[1] || "..."
+ end
+ options.reverse_merge!(:radius => 100, :omission => "...")
+
if text && phrase
phrase = Regexp.escape(phrase)
if found_pos = text =~ /(#{phrase})/i
- start_pos = [ found_pos - radius, 0 ].max
- end_pos = [ [ found_pos + phrase.length + radius - 1, 0].max, text.length ].min
+ start_pos = [ found_pos - options[:radius], 0 ].max
+ end_pos = [ [ found_pos + phrase.length + options[:radius] - 1, 0].max, text.length ].min
- prefix = start_pos > 0 ? excerpt_string : ""
- postfix = end_pos < text.length - 1 ? excerpt_string : ""
+ prefix = start_pos > 0 ? options[:omission] : ""
+ postfix = end_pos < text.length - 1 ? options[:omission] : ""
prefix + text[start_pos..end_pos].strip + postfix
else
@@ -176,20 +235,31 @@ module ActionView
# (which is 80 by default).
#
# ==== Examples
- # word_wrap('Once upon a time', 4)
- # # => Once\nupon\na\ntime
- #
- # word_wrap('Once upon a time', 8)
- # # => Once upon\na time
#
# word_wrap('Once upon a time')
# # => Once upon a time
#
- # word_wrap('Once upon a time', 1)
+ # word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
+ # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined...
+ #
+ # word_wrap('Once upon a time', :line_width => 8)
+ # # => Once upon\na time
+ #
+ # word_wrap('Once upon a time', :line_width => 1)
# # => Once\nupon\na\ntime
- def word_wrap(text, line_width = 80)
+ #
+ # You can still use <tt>word_wrap</tt> with the old API that accepts the
+ # +line_width+ as its optional second parameter:
+ # word_wrap('Once upon a time', 8) # => Once upon\na time
+ def word_wrap(text, *args)
+ options = args.extract_options!
+ unless args.blank?
+ options[:line_width] = args[0] || 80
+ end
+ options.reverse_merge!(:line_width => 80)
+
text.split("\n").collect do |line|
- line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
+ line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line
end * "\n"
end
@@ -336,12 +406,32 @@ module ActionView
# # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
# Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
#
- def auto_link(text, link = :all, href_options = {}, &block)
+ #
+ # You can still use <tt>auto_link</tt> with the old API that accepts the
+ # +link+ as its optional second parameter and the +html_options+ hash
+ # as its optional third parameter:
+ # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
+ # auto_link(post_body, :urls) # => Once upon\na time
+ # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
+ # Please e-mail me at me@email.com."
+ #
+ # auto_link(post_body, :all, :target => "_blank") # => Once upon\na time
+ # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
+ # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
+ def auto_link(text, *args, &block)#link = :all, href_options = {}, &block)
return '' if text.blank?
- case link
- when :all then auto_link_email_addresses(auto_link_urls(text, href_options, &block), &block)
- when :email_addresses then auto_link_email_addresses(text, &block)
- when :urls then auto_link_urls(text, href_options, &block)
+
+ options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
+ unless args.empty?
+ options[:link] = args[0] || :all
+ options[:html] = args[1] || {}
+ end
+ options.reverse_merge!(:link => :all, :html => {})
+
+ case options[:link].to_sym
+ when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), &block)
+ when :email_addresses then auto_link_email_addresses(text, &block)
+ when :urls then auto_link_urls(text, options[:html], &block)
end
end
@@ -468,7 +558,7 @@ module ActionView
[-\w]+ # subdomain or domain
(?:\.[-\w]+)* # remaining subdomains or domain
(?::\d+)? # port
- (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path
+ (?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path
(?:\?[\w\+@%&=.;-]+)? # query string
(?:\#[\w\-]*)? # trailing anchor
)
@@ -477,8 +567,8 @@ module ActionView
# Turns all urls into clickable links. If a block is given, each url
# is yielded and the result is used as the link text.
- def auto_link_urls(text, href_options = {})
- extra_options = tag_options(href_options.stringify_keys) || ""
+ def auto_link_urls(text, html_options = {})
+ extra_options = tag_options(html_options.stringify_keys) || ""
text.gsub(AUTO_LINK_RE) do
all, a, b, c, d = $&, $1, $2, $3, $4
if a =~ /<a\s/i # don't replace URL's that are already linked
@@ -508,4 +598,4 @@ module ActionView
end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
new file mode 100644
index 0000000000..de4c1d7689
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -0,0 +1,22 @@
+require 'action_view/helpers/tag_helper'
+
+module ActionView
+ module Helpers
+ module TranslationHelper
+ def translate(*args)
+ args << args.extract_options!.merge(:raise => true)
+ I18n.translate *args
+
+ rescue I18n::MissingTranslationData => e
+ keys = I18n.send :normalize_translation_keys, e.locale, e.key, e.options[:scope]
+ content_tag('span', keys.join(', '), :class => 'translation_missing')
+ end
+ alias :t :translate
+
+ def localize(*args)
+ I18n.localize *args
+ end
+ alias :l :localize
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index e5178938fd..7ba42a3b72 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -3,8 +3,8 @@ require 'action_view/helpers/javascript_helper'
module ActionView
module Helpers #:nodoc:
# Provides a set of methods for making links and getting URLs that
- # depend on the routing subsystem (see ActionController::Routing).
- # This allows you to use the same format for links in views
+ # depend on the routing subsystem (see ActionController::Routing).
+ # This allows you to use the same format for links in views
# and controllers.
module UrlHelper
include JavaScriptHelper
@@ -33,8 +33,8 @@ module ActionView
#
# If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter,
# you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing
- # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as
- # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route).
+ # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as
+ # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route).
#
# ==== Examples
# <%= url_for(:action => 'index') %>
@@ -62,19 +62,33 @@ module ActionView
# <%= url_for(@workshop) %>
# # calls @workshop.to_s
# # => /workshops/5
+ #
+ # <%= url_for("http://www.example.com") %>
+ # # => http://www.example.com
+ #
+ # <%= url_for(:back) %>
+ # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
+ # # => http://www.example.com
+ #
+ # <%= url_for(:back) %>
+ # # if request.env["HTTP_REFERER"] is not set or is blank
+ # # => javascript:history.back()
def url_for(options = {})
options ||= {}
- case options
+ url = case options
+ when String
+ escape = true
+ options
when Hash
options = { :only_path => options[:host].nil? }.update(options.symbolize_keys)
escape = options.key?(:escape) ? options.delete(:escape) : true
- url = @controller.send(:url_for, options)
- when String
- escape = true
- url = options
+ @controller.send(:url_for, options)
+ when :back
+ escape = false
+ @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
else
escape = false
- url = polymorphic_path(options)
+ polymorphic_path(options)
end
escape ? escape_once(url) : url
@@ -116,8 +130,8 @@ module ActionView
#
# Note that if the user has JavaScript disabled, the request will fall back
# to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript disabled
- # clicking the link will have no effect. If you are relying on the POST
- # behavior, your should check for it in your controller's action by using the
+ # clicking the link will have no effect. If you are relying on the POST
+ # behavior, your should check for it in your controller's action by using the
# request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
#
# You can mix and match the +html_options+ with the exception of
@@ -141,8 +155,8 @@ module ActionView
#
# link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
# # => <a href="/profiles/show/1">Profile</a>
- #
- # Similarly,
+ #
+ # Similarly,
#
# link_to "Profiles", profiles_path
# # => <a href="/profiles">Profiles</a>
@@ -185,7 +199,7 @@ module ActionView
# link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
- # The three options specfic to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
+ # The three options specific to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
#
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
# # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
@@ -197,9 +211,9 @@ module ActionView
# # => <a href="/images/9" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
#
# link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete
- # # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
+ # # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
# f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
- # var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
+ # var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
# m.setAttribute('value', 'delete'); f.appendChild(m);f.submit(); };return false;">Delete Image</a>
def link_to(*args, &block)
if block_given?
@@ -211,14 +225,7 @@ module ActionView
options = args.second || {}
html_options = args.third
- url = case options
- when String
- options
- when :back
- @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
- else
- self.url_for(options)
- end
+ url = url_for(options)
if html_options
html_options = html_options.stringify_keys
@@ -228,7 +235,7 @@ module ActionView
else
tag_options = nil
end
-
+
href_attr = "href=\"#{url}\"" unless href
"<a #{href_attr}#{tag_options}>#{name || url}</a>"
end
@@ -260,7 +267,7 @@ module ActionView
# * <tt>:confirm</tt> - This will add a JavaScript confirm
# prompt with the question specified. If the user accepts, the link is
# processed normally, otherwise no action is taken.
- #
+ #
# ==== Examples
# <%= button_to "New", :action => "new" %>
# # => "<form method="post" action="/controller/new" class="button-to">
@@ -286,12 +293,12 @@ module ActionView
end
form_method = method.to_s == 'get' ? 'get' : 'post'
-
+
request_token_tag = ''
if form_method == 'post' && protect_against_forgery?
request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
end
-
+
if confirm = html_options.delete("confirm")
html_options["onclick"] = "return #{confirm_javascript_function(confirm)};"
end
@@ -309,7 +316,7 @@ module ActionView
# Creates a link tag of the given +name+ using a URL created by the set of
# +options+ unless the current request URI is the same as the links, in
# which case only the name is returned (or the given block is yielded, if
- # one exists). You can give link_to_unless_current a block which will
+ # one exists). You can give link_to_unless_current a block which will
# specialize the default behavior (e.g., show a "Start Here" link rather
# than the link's text).
#
@@ -336,13 +343,13 @@ module ActionView
# </ul>
#
# The implicit block given to link_to_unless_current is evaluated if the current
- # action is the action given. So, if we had a comments page and wanted to render a
+ # action is the action given. So, if we had a comments page and wanted to render a
# "Go Back" link instead of a link to the comments page, we could do something like this...
- #
- # <%=
+ #
+ # <%=
# link_to_unless_current("Comment", { :controller => 'comments', :action => 'new}) do
- # link_to("Go back", { :controller => 'posts', :action => 'index' })
- # end
+ # link_to("Go back", { :controller => 'posts', :action => 'index' })
+ # end
# %>
def link_to_unless_current(name, options = {}, html_options = {}, &block)
link_to_unless current_page?(options), name, options, html_options, &block
@@ -359,10 +366,10 @@ module ActionView
# # If the user is logged in...
# # => <a href="/controller/reply/">Reply</a>
#
- # <%=
+ # <%=
# link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
# link_to(name, { :controller => "accounts", :action => "signup" })
- # end
+ # end
# %>
# # If the user is logged in...
# # => <a href="/controller/reply/">Reply</a>
@@ -391,10 +398,10 @@ module ActionView
# # If the user isn't logged in...
# # => <a href="/sessions/new/">Login</a>
#
- # <%=
+ # <%=
# link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do
# link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user })
- # end
+ # end
# %>
# # If the user isn't logged in...
# # => <a href="/sessions/new/">Login</a>
@@ -431,20 +438,20 @@ module ActionView
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
#
# ==== Examples
- # mail_to "me@domain.com"
+ # mail_to "me@domain.com"
# # => <a href="mailto:me@domain.com">me@domain.com</a>
#
- # mail_to "me@domain.com", "My email", :encode => "javascript"
- # # => <script type="text/javascript">eval(unescape('%64%6f%63...%6d%65%6e'))</script>
+ # mail_to "me@domain.com", "My email", :encode => "javascript"
+ # # => <script type="text/javascript">eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
#
- # mail_to "me@domain.com", "My email", :encode => "hex"
+ # mail_to "me@domain.com", "My email", :encode => "hex"
# # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
#
- # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
+ # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
# # => <a href="mailto:me@domain.com" class="email">me_at_domain_dot_com</a>
#
# mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
- # :subject => "This is an example email"
+ # :subject => "This is an example email"
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
def mail_to(email_address, name = nil, html_options = {})
html_options = html_options.stringify_keys
@@ -469,7 +476,7 @@ module ActionView
"document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
string << sprintf("%%%x", c)
end
- "<script type=\"#{Mime::JS}\">eval(unescape('#{string}'))</script>"
+ "<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
elsif encode == "hex"
email_address_encoded = ''
email_address_obfuscated.each_byte do |c|
diff --git a/actionpack/lib/action_view/locale/en-US.yml b/actionpack/lib/action_view/locale/en-US.yml
new file mode 100644
index 0000000000..818f2f93bd
--- /dev/null
+++ b/actionpack/lib/action_view/locale/en-US.yml
@@ -0,0 +1,91 @@
+"en-US":
+ number:
+ # Used in number_with_delimiter()
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
+ format:
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
+ separator: "."
+ # Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
+ delimiter: ","
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
+ precision: 3
+
+ # Used in number_to_currency()
+ currency:
+ format:
+ # Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
+ format: "%u%n"
+ unit: "$"
+ # These three are to override number.format and are optional
+ separator: "."
+ delimiter: ","
+ precision: 2
+
+ # Used in number_to_percentage()
+ percentage:
+ format:
+ # These three are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ # precision:
+
+ # Used in number_to_precision()
+ precision:
+ format:
+ # These three are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ # precision:
+
+ # Used in number_to_human_size()
+ human:
+ format:
+ # These three are to override number.format and are optional
+ # separator:
+ delimiter: ""
+ precision: 1
+
+ # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
+ datetime:
+ distance_in_words:
+ half_a_minute: "half a minute"
+ less_than_x_seconds:
+ one: "less than 1 second"
+ other: "less than {{count}} seconds"
+ x_seconds:
+ one: "1 second"
+ other: "{{count}} seconds"
+ less_than_x_minutes:
+ one: "less than a minute"
+ other: "less than {{count}} minutes"
+ x_minutes:
+ one: "1 minute"
+ other: "{{count}} minutes"
+ about_x_hours:
+ one: "about 1 hour"
+ other: "about {{count}} hours"
+ x_days:
+ one: "1 day"
+ other: "{{count}} days"
+ about_x_months:
+ one: "about 1 month"
+ other: "about {{count}} months"
+ x_months:
+ one: "1 month"
+ other: "{{count}} months"
+ about_x_years:
+ one: "about 1 year"
+ other: "about {{count}} years"
+ over_x_years:
+ one: "over 1 year"
+ other: "over {{count}} years"
+
+ activerecord:
+ errors:
+ template:
+ header:
+ one: "1 error prohibited this {{model}} from being saved"
+ other: "{{count}} errors prohibited this {{model}} from being saved"
+ # The variable :count is also available
+ body: "There were problems with the following fields:"
+
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index 5aa4c83009..443c49b870 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -1,14 +1,15 @@
module ActionView
- # There's also a convenience method for rendering sub templates within the current controller that depends on a single object
- # (we call this kind of sub templates for partials). It relies on the fact that partials should follow the naming convention of being
- # prefixed with an underscore -- as to separate them from regular templates that could be rendered on their own.
+ # There's also a convenience method for rendering sub templates within the current controller that depends on a
+ # single object (we call this kind of sub templates for partials). It relies on the fact that partials should
+ # follow the naming convention of being prefixed with an underscore -- as to separate them from regular
+ # templates that could be rendered on their own.
#
# In a template for Advertiser#account:
#
# <%= render :partial => "account" %>
#
- # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable +account+ to
- # the template for display.
+ # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable
+ # +account+ to the template for display.
#
# In another template for Advertiser#buy, we could have:
#
@@ -18,24 +19,24 @@ module ActionView
# <%= render :partial => "ad", :locals => { :ad => ad } %>
# <% end %>
#
- # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then render
- # "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
+ # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then
+ # render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
#
# == Rendering a collection of partials
#
- # The example of partial use describes a familiar pattern where a template needs to iterate over an array and render a sub
- # template for each of the elements. This pattern has been implemented as a single method that accepts an array and renders
- # a partial by the same name as the elements contained within. So the three-lined example in "Using partials" can be rewritten
- # with a single line:
+ # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
+ # render a sub template for each of the elements. This pattern has been implemented as a single method that
+ # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
+ # example in "Using partials" can be rewritten with a single line:
#
# <%= render :partial => "ad", :collection => @advertisements %>
#
- # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An iteration counter
- # will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the
- # example above, the template would be fed +ad_counter+.
+ # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An
+ # iteration counter will automatically be made available to the template with a name of the form
+ # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
#
- # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also just keep domain objects,
- # like Active Records, in there.
+ # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
+ # just keep domain objects, like Active Records, in there.
#
# == Rendering shared partials
#
@@ -47,8 +48,9 @@ module ActionView
#
# == Rendering partials with layouts
#
- # Partials can have their own layouts applied to them. These layouts are different than the ones that are specified globally
- # for the entire action, but they work in a similar fashion. Imagine a list with two types of users:
+ # Partials can have their own layouts applied to them. These layouts are different than the ones that are
+ # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
+ # of users:
#
# <%# app/views/users/index.html.erb &>
# Here's the administrator:
@@ -68,7 +70,7 @@ module ActionView
#
# <%# app/views/users/_editor.html.erb &>
# <div id="editor">
- # Deadline: $<%= user.deadline %>
+ # Deadline: <%= user.deadline %>
# <%= yield %>
# </div>
#
@@ -82,7 +84,7 @@ module ActionView
#
# Here's the editor:
# <div id="editor">
- # Deadline: $<%= user.deadline %>
+ # Deadline: <%= user.deadline %>
# Name: <%= user.name %>
# </div>
#
@@ -101,42 +103,83 @@ module ActionView
# </div>
#
# As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
+ #
+ # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
+ # an array to layout and treat it as an enumerable.
+ #
+ # <%# app/views/users/_user.html.erb &>
+ # <div class="user">
+ # Budget: $<%= user.budget %>
+ # <%= yield user %>
+ # </div>
+ #
+ # <%# app/views/users/index.html.erb &>
+ # <% render :layout => @users do |user| %>
+ # Title: <%= user.title %>
+ # <% end %>
+ #
+ # This will render the layout for each user and yield to the block, passing the user, each time.
+ #
+ # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
+ #
+ # <%# app/views/users/_user.html.erb &>
+ # <div class="user">
+ # <%= yield user, :header %>
+ # Budget: $<%= user.budget %>
+ # <%= yield user, :footer %>
+ # </div>
+ #
+ # <%# app/views/users/index.html.erb &>
+ # <% render :layout => @users do |user, section| %>
+ # <%- case section when :header -%>
+ # Title: <%= user.title %>
+ # <%- when :footer -%>
+ # Deadline: <%= user.deadline %>
+ # <%- end -%>
+ # <% end %>
module Partials
+ extend ActiveSupport::Memoizable
+
private
- def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc:
- local_assigns ||= {}
+ def render_partial(options = {}) #:nodoc:
+ local_assigns = options[:locals] || {}
- case partial_path
+ case partial_path = options[:partial]
when String, Symbol, NilClass
- pick_template(find_partial_path(partial_path)).render_partial(self, object_assigns, local_assigns)
+ if options.has_key?(:collection)
+ render_partial_collection(options)
+ else
+ _pick_partial_template(partial_path).render_partial(self, options[:object], local_assigns)
+ end
when ActionView::Helpers::FormBuilder
builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')
- render_partial(builder_partial_path, object_assigns, (local_assigns || {}).merge(builder_partial_path.to_sym => partial_path))
+ local_assigns.merge!(builder_partial_path.to_sym => partial_path)
+ render_partial(:partial => builder_partial_path, :object => options[:object], :locals => local_assigns)
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
- if partial_path.any?
- collection = partial_path
- render_partial_collection(nil, collection, nil, local_assigns)
- else
- ""
- end
+ render_partial_collection(options.except(:partial).merge(:collection => partial_path))
else
- render_partial(ActionController::RecordIdentifier.partial_path(partial_path, controller.class.controller_path), partial_path, local_assigns)
+ object = partial_path
+ render_partial(
+ :partial => ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path),
+ :object => object,
+ :locals => local_assigns
+ )
end
end
- def render_partial_collection(partial_path, collection, partial_spacer_template = nil, local_assigns = {}, as = nil) #:nodoc:
- return " " if collection.empty?
+ def render_partial_collection(options = {}) #:nodoc:
+ return nil if options[:collection].blank?
- local_assigns = local_assigns ? local_assigns.clone : {}
- spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : ''
- _paths = {}
- _templates = {}
+ partial = options[:partial]
+ spacer = options[:spacer_template] ? render(:partial => options[:spacer_template]) : ''
+ local_assigns = options[:locals] ? options[:locals].clone : {}
+ as = options[:as]
index = 0
- collection.map do |object|
- _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
- path = _paths[_partial_path] ||= find_partial_path(_partial_path)
- template = _templates[path] ||= pick_template(path)
+ options[:collection].map do |object|
+ _partial_path ||= partial ||
+ ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
+ template = _pick_partial_template(_partial_path)
local_assigns[template.counter_name] = index
result = template.render_partial(self, object, local_assigns, as)
index += 1
@@ -144,14 +187,17 @@ module ActionView
end.join(spacer)
end
- def find_partial_path(partial_path)
+ def _pick_partial_template(partial_path) #:nodoc:
if partial_path.include?('/')
- "#{File.dirname(partial_path)}/_#{File.basename(partial_path)}"
- elsif respond_to?(:controller)
- "#{controller.class.controller_path}/_#{partial_path}"
+ path = File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}")
+ elsif controller
+ path = "#{controller.class.controller_path}/_#{partial_path}"
else
- "_#{partial_path}"
+ path = "_#{partial_path}"
end
+
+ pick_template(path)
end
+ memoize :_pick_partial_template
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index b0ab7d0c67..d6bf2137af 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -3,7 +3,7 @@ module ActionView #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
if Base.warn_cache_misses && defined?(Rails) && Rails.initialized?
- Rails.logger.debug "[PERFORMANCE] Processing view path during a " +
+ Base.logger.debug "[PERFORMANCE] Processing view path during a " +
"request. This an expense disk operation that should be done at " +
"boot. You can manually process this view path with " +
"ActionView::Base.process_view_paths(#{obj.inspect}) and set it " +
@@ -15,70 +15,104 @@ module ActionView #:nodoc:
end
end
+ def initialize(*args)
+ super(*args).map! { |obj| self.class.type_cast(obj) }
+ end
+
+ def <<(obj)
+ super(self.class.type_cast(obj))
+ end
+
+ def concat(array)
+ super(array.map! { |obj| self.class.type_cast(obj) })
+ end
+
+ def insert(index, obj)
+ super(index, self.class.type_cast(obj))
+ end
+
+ def push(*objs)
+ super(*objs.map { |obj| self.class.type_cast(obj) })
+ end
+
+ def unshift(*objs)
+ super(*objs.map { |obj| self.class.type_cast(obj) })
+ end
+
class Path #:nodoc:
+ def self.eager_load_templates!
+ @eager_load_templates = true
+ end
+
+ def self.eager_load_templates?
+ @eager_load_templates || false
+ end
+
attr_reader :path, :paths
- delegate :to_s, :to_str, :inspect, :to => :path
+ delegate :to_s, :to_str, :hash, :inspect, :to => :path
- def initialize(path)
+ def initialize(path, load = true)
+ raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
@path = path.freeze
- reload!
+ reload! if load
end
def ==(path)
to_str == path.to_str
end
+ def eql?(path)
+ to_str == path.to_str
+ end
+
def [](path)
+ raise "Unloaded view path! #{@path}" unless @loaded
@paths[path]
end
+ def loaded?
+ @loaded ? true : false
+ end
+
+ def load
+ reload! unless loaded?
+ self
+ end
+
# Rebuild load path directory cache
def reload!
@paths = {}
templates_in_path do |template|
+ # Eager load memoized methods and freeze cached template
+ template.freeze if self.class.eager_load_templates?
+
@paths[template.path] = template
@paths[template.path_without_extension] ||= template
end
@paths.freeze
+ @loaded = true
end
private
def templates_in_path
(Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
unless File.directory?(file)
- template = Template.new(file.split("#{self}/").last, self)
- # Eager load memoized methods and freeze cached template
- template.freeze if Base.cache_template_loading
- yield template
+ yield Template.new(file.split("#{self}/").last, self)
end
end
end
end
- def initialize(*args)
- super(*args).map! { |obj| self.class.type_cast(obj) }
+ def load
+ each { |path| path.load }
end
def reload!
each { |path| path.reload! }
end
- def <<(obj)
- super(self.class.type_cast(obj))
- end
-
- def push(*objs)
- delete_paths!(objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
- end
-
- def unshift(*objs)
- delete_paths!(objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
- end
-
def [](template_path)
each do |path|
if template = path[template_path]
@@ -87,10 +121,5 @@ module ActionView #:nodoc:
end
nil
end
-
- private
- def delete_paths!(paths)
- paths.each { |p1| delete_if { |p2| p1.to_s == p2.to_s } }
- end
end
end
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index f66356c939..c011f21550 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -3,11 +3,15 @@ module ActionView
# NOTE: The template that this mixin is beening include into is frozen
# So you can not set or modify any instance variables
+ extend ActiveSupport::Memoizable
+
def self.included(base)
@@mutex = Mutex.new
end
- include ActiveSupport::Memoizable
+ def filename
+ 'compiled-template'
+ end
def handler
Template.handler_class_for_extension(extension)
@@ -15,19 +19,29 @@ module ActionView
memoize :handler
def compiled_source
- handler.new(nil).compile(self) if handler.compilable?
+ handler.call(self)
end
memoize :compiled_source
def render(view, local_assigns = {})
+ compile(local_assigns)
+
view._first_render ||= self
view._last_render = self
+
view.send(:evaluate_assigns)
- compile(local_assigns) if handler.compilable?
- handler.new(view).render(self, local_assigns)
+ view.send(:set_controller_content_type, mime_type) if respond_to?(:mime_type)
+
+ view.send(method_name(local_assigns), local_assigns) do |*names|
+ if proc = view.instance_variable_get("@_proc_for_layout")
+ view.capture(*names, &proc)
+ else
+ view.instance_variable_get("@content_for_#{names.first || 'layout'}")
+ end
+ end
end
- def method(local_assigns)
+ def method_name(local_assigns)
if local_assigns && local_assigns.any?
local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
end
@@ -35,35 +49,41 @@ module ActionView
end
private
- # Compile and evaluate the template's code
+ # Compile and evaluate the template's code (if necessary)
def compile(local_assigns)
- render_symbol = method(local_assigns)
+ render_symbol = method_name(local_assigns)
@@mutex.synchronize do
- return false unless recompile?(render_symbol)
-
- locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
-
- source = <<-end_src
- def #{render_symbol}(local_assigns)
- old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
- ensure
- self.output_buffer = old_output_buffer
- end
- end_src
-
- begin
- file_name = respond_to?(:filename) ? filename : 'compiled-template'
- ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0)
- rescue Exception => e # errors from template code
- if logger = ActionController::Base.logger
- logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
- logger.debug "Function body: #{source}"
- logger.debug "Backtrace: #{e.backtrace.join("\n")}"
- end
-
- raise ActionView::TemplateError.new(self, {}, e)
+ if recompile?(render_symbol)
+ compile!(render_symbol, local_assigns)
+ end
+ end
+ end
+
+ def compile!(render_symbol, local_assigns)
+ locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+
+ source = <<-end_src
+ def #{render_symbol}(local_assigns)
+ old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
+ ensure
+ self.output_buffer = old_output_buffer
end
+ end_src
+
+ begin
+ logger = Base.logger
+ logger.debug "Compiling template #{render_symbol}" if logger
+
+ ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
+ rescue Exception => e # errors from template code
+ if logger
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
+ logger.debug "Function body: #{source}"
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
+ end
+
+ raise ActionView::TemplateError.new(self, {}, e)
end
end
@@ -71,8 +91,7 @@ module ActionView
# The template will be compiled if the file has not been compiled yet, or
# if local_assigns has a new key, which isn't supported by the compiled code yet.
def recompile?(symbol)
- meth = Base::CompiledTemplates.instance_method(template.method) rescue nil
- !(meth && Base.cache_template_loading)
+ !(ActionView::PathSet::Path.eager_load_templates? && Base::CompiledTemplates.method_defined?(symbol))
end
end
end
diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb
index fdb1a5e6a7..342850f0f0 100644
--- a/actionpack/lib/action_view/renderable_partial.rb
+++ b/actionpack/lib/action_view/renderable_partial.rb
@@ -3,7 +3,7 @@ module ActionView
# NOTE: The template that this mixin is beening include into is frozen
# So you can not set or modify any instance variables
- include ActiveSupport::Memoizable
+ extend ActiveSupport::Memoizable
def variable_name
name.sub(/\A_/, '').to_sym
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 304aec3a4c..5dc6708431 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,7 +1,7 @@
module ActionView #:nodoc:
class Template
extend TemplateHandlers
- include ActiveSupport::Memoizable
+ extend ActiveSupport::Memoizable
include Renderable
attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
@@ -22,6 +22,19 @@ module ActionView #:nodoc:
end
memoize :format_and_extension
+ def multipart?
+ format && format.include?('.')
+ end
+
+ def content_type
+ format.gsub('.', '/')
+ end
+
+ def mime_type
+ Mime::Type.lookup_by_extension(format) if format
+ end
+ memoize :mime_type
+
def path
[base_path, [name, format, extension].compact.join('.')].compact.join('/')
end
@@ -70,7 +83,7 @@ module ActionView #:nodoc:
load_paths = Array(load_paths) + [nil]
load_paths.each do |load_path|
file = [load_path, path].compact.join('/')
- return load_path, file if File.exist?(file)
+ return load_path, file if File.file?(file)
end
raise MissingTemplate.new(load_paths, path)
end
@@ -79,7 +92,7 @@ module ActionView #:nodoc:
# [base_path, name, format, extension]
def split(file)
if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/)
- if m[5] # Mulipart formats
+ if m[5] # Multipart formats
[m[1], m[2], "#{m[3]}.#{m[4]}", m[5]]
elsif m[4] # Single format
[m[1], m[2], m[3], m[4]]
diff --git a/actionpack/lib/action_view/template_error.rb b/actionpack/lib/action_view/template_error.rb
index 35fc07bdb2..2368662f31 100644
--- a/actionpack/lib/action_view/template_error.rb
+++ b/actionpack/lib/action_view/template_error.rb
@@ -7,7 +7,7 @@ module ActionView
attr_reader :original_exception
def initialize(template, assigns, original_exception)
- @base_path = template.base_path
+ @base_path = template.base_path.to_s
@assigns, @source, @original_exception = assigns.dup, template.source, original_exception
@file_path = template.filename
@backtrace = compute_backtrace
diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template_handler.rb
index 1afea21f67..d7e7c9b199 100644
--- a/actionpack/lib/action_view/template_handler.rb
+++ b/actionpack/lib/action_view/template_handler.rb
@@ -1,25 +1,14 @@
-module ActionView
- class TemplateHandler
- def self.compilable?
- false
- end
-
- def initialize(view)
- @view = view
- end
-
- def render(template, local_assigns = {})
- end
-
- def compile(template)
- end
+# Legacy TemplateHandler stub
- def compilable?
- self.class.compilable?
+module ActionView
+ module TemplateHandlers
+ module Compilable
end
+ end
- # Called by CacheHelper#cache
- def cache_fragment(block, name = {}, options = nil)
+ class TemplateHandler
+ def self.call(template)
+ new.compile(template)
end
end
end
diff --git a/actionpack/lib/action_view/template_handlers.rb b/actionpack/lib/action_view/template_handlers.rb
index 1471e99e01..6c8aa4c2a7 100644
--- a/actionpack/lib/action_view/template_handlers.rb
+++ b/actionpack/lib/action_view/template_handlers.rb
@@ -1,5 +1,4 @@
require 'action_view/template_handler'
-require 'action_view/template_handlers/compilable'
require 'action_view/template_handlers/builder'
require 'action_view/template_handlers/erb'
require 'action_view/template_handlers/rjs'
diff --git a/actionpack/lib/action_view/template_handlers/builder.rb b/actionpack/lib/action_view/template_handlers/builder.rb
index cbe53e11d8..7d24a5c423 100644
--- a/actionpack/lib/action_view/template_handlers/builder.rb
+++ b/actionpack/lib/action_view/template_handlers/builder.rb
@@ -6,18 +6,12 @@ module ActionView
include Compilable
def compile(template)
- # ActionMailer does not have a response
- "controller.respond_to?(:response) && controller.response.content_type ||= Mime::XML;" +
+ "set_controller_content_type(Mime::XML);" +
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
+ "self.output_buffer = xml.target!;" +
template.source +
";xml.target!;"
end
-
- def cache_fragment(block, name = {}, options = nil)
- @view.fragment_for(block, name, options) do
- eval('xml.target!', block.binding)
- end
- end
end
end
end
diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb
deleted file mode 100644
index a0ebaefeef..0000000000
--- a/actionpack/lib/action_view/template_handlers/compilable.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-module ActionView
- module TemplateHandlers
- module Compilable
- def self.included(base)
- base.extend ClassMethod
- end
-
- module ClassMethod
- # If a handler is mixin this module, set compilable to true
- def compilable?
- true
- end
- end
-
- def render(template, local_assigns = {})
- @view.send(:execute, template, local_assigns)
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb
index ac715e30c2..3def949f1e 100644
--- a/actionpack/lib/action_view/template_handlers/erb.rb
+++ b/actionpack/lib/action_view/template_handlers/erb.rb
@@ -48,14 +48,11 @@ module ActionView
self.erb_trim_mode = '-'
def compile(template)
- src = ::ERB.new(template.source, nil, erb_trim_mode, '@output_buffer').src
- "__in_erb_template=true;#{src}"
- end
+ src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
- def cache_fragment(block, name = {}, options = nil) #:nodoc:
- @view.fragment_for(block, name, options) do
- @view.response.template.output_buffer
- end
+ # Ruby 1.9 prepends an encoding to the source. However this is
+ # useless because you can only set an encoding on the first line
+ RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src
end
end
end
diff --git a/actionpack/lib/action_view/template_handlers/rjs.rb b/actionpack/lib/action_view/template_handlers/rjs.rb
index 3892bf1d6e..a700655c9a 100644
--- a/actionpack/lib/action_view/template_handlers/rjs.rb
+++ b/actionpack/lib/action_view/template_handlers/rjs.rb
@@ -7,17 +7,6 @@ module ActionView
"controller.response.content_type ||= Mime::JS;" +
"update_page do |page|;#{template.source}\nend"
end
-
- def cache_fragment(block, name = {}, options = nil) #:nodoc:
- @view.fragment_for(block, name, options) do
- begin
- debug_mode, ActionView::Base.debug_rjs = ActionView::Base.debug_rjs, false
- eval('page.to_s', block.binding)
- ensure
- ActionView::Base.debug_rjs = debug_mode
- end
- end
- end
end
end
end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 1a3c93c283..adbb37fd09 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -25,9 +25,7 @@ module ActionView
end
end
- ActionView::Base.helper_modules.each do |helper_module|
- include helper_module
- end
+ include ActionView::Helpers
include ActionController::PolymorphicRoutes
include ActionController::RecordIdentifier
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 0d2e0f273a..9db4cddd6a 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -22,8 +22,8 @@ ActiveSupport::Deprecation.debug = true
ActionController::Base.logger = nil
ActionController::Routing::Routes.reload rescue nil
-ActionView::Base.cache_template_loading = true
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
+ActionView::PathSet::Path.eager_load_templates!
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
# Wrap tests that use Mocha and skip if unavailable.
diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
index a82a1a3023..d75cb2b53a 100644
--- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
+++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
@@ -39,6 +39,11 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base
@developers = Developer.find(:all)
render :partial => @developers
end
+
+ def render_with_record_collection_and_spacer_template
+ @developer = Developer.find(1)
+ render :partial => @developer.projects, :spacer_template => 'test/partial_only'
+ end
end
class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
@@ -81,6 +86,11 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
assert_equal 'DavidJamisfixture_3fixture_4fixture_5fixture_6fixture_7fixture_8fixture_9fixture_10Jamis', @response.body
end
+ def test_render_with_record_collection_and_spacer_template
+ get :render_with_record_collection_and_spacer_template
+ assert_equal 'Active Recordonly partialActive Controller', @response.body
+ end
+
def test_rendering_partial_with_has_one_association
mascot = Company.find(1).mascot
get :render_with_has_one_association
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 5af579f3e3..1531e7c21a 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -17,6 +17,8 @@ unless defined?(ActionMailer)
end
end
+ActionMailer::Base.template_root = FIXTURE_LOAD_PATH
+
class AssertSelectTest < Test::Unit::TestCase
class AssertSelectController < ActionController::Base
def response_with=(content)
@@ -69,11 +71,10 @@ class AssertSelectTest < Test::Unit::TestCase
ActionMailer::Base.deliveries = []
end
-
def teardown
ActionMailer::Base.deliveries.clear
end
-
+
def assert_failure(message, &block)
e = assert_raises(AssertionFailedError, &block)
assert_match(message, e.message) if Regexp === message
@@ -91,7 +92,6 @@ class AssertSelectTest < Test::Unit::TestCase
assert_failure(/Expected at least 1 element matching \"p\", found 0/) { assert_select "p" }
end
-
def test_equality_true_false
render_html %Q{<div id="1"></div><div id="2"></div>}
assert_nothing_raised { assert_select "div" }
@@ -102,7 +102,6 @@ class AssertSelectTest < Test::Unit::TestCase
assert_nothing_raised { assert_select "p", false }
end
-
def test_equality_string_and_regexp
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_nothing_raised { assert_select "div", "foo" }
@@ -116,7 +115,6 @@ class AssertSelectTest < Test::Unit::TestCase
assert_raises(AssertionFailedError) { assert_select "p", :text=>/foobar/ }
end
-
def test_equality_of_html
render_html %Q{<p>\n<em>"This is <strong>not</strong> a big problem,"</em> he said.\n</p>}
text = "\"This is not a big problem,\" he said."
@@ -135,7 +133,6 @@ class AssertSelectTest < Test::Unit::TestCase
assert_raises(AssertionFailedError) { assert_select "pre", :html=>text }
end
-
def test_counts
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_nothing_raised { assert_select "div", 2 }
@@ -166,7 +163,6 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
-
def test_substitution_values
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_select "div#?", /\d+/ do |elements|
@@ -181,7 +177,6 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
-
def test_nested_assert_select
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_select "div" do |elements|
@@ -200,7 +195,7 @@ class AssertSelectTest < Test::Unit::TestCase
assert_select "#3", false
end
end
-
+
assert_failure(/Expected at least 1 element matching \"#4\", found 0\./) do
assert_select "div" do
assert_select "#4"
@@ -208,7 +203,6 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
-
def test_assert_select_text_match
render_html %Q{<div id="1"><span>foo</span></div><div id="2"><span>bar</span></div>}
assert_select "div" do
@@ -225,7 +219,6 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
-
# With single result.
def test_assert_select_from_rjs_with_single_result
render_rjs do |page|
@@ -255,19 +248,16 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
-
#
# Test css_select.
#
-
def test_css_select
render_html %Q{<div id="1"></div><div id="2"></div>}
assert 2, css_select("div").size
assert 0, css_select("p").size
end
-
def test_nested_css_select
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_select "div#?", /\d+/ do |elements|
@@ -286,7 +276,6 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
-
# With one result.
def test_css_select_from_rjs_with_single_result
render_rjs do |page|
@@ -309,12 +298,10 @@ class AssertSelectTest < Test::Unit::TestCase
assert_equal 1, css_select("#2").size
end
-
#
# Test assert_select_rjs.
#
-
# Test that we can pick up all statements in the result.
def test_assert_select_rjs_picks_up_all_statements
render_rjs do |page|
@@ -381,7 +368,6 @@ class AssertSelectTest < Test::Unit::TestCase
assert_raises(AssertionFailedError) { assert_select_rjs "test4" }
end
-
def test_assert_select_rjs_for_replace
render_rjs do |page|
page.replace "test1", "<div id=\"1\">foo</div>"
@@ -479,7 +465,7 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
end
-
+
# Simple hide
def test_assert_select_rjs_for_hide
render_rjs do |page|
@@ -500,7 +486,7 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
end
-
+
# Simple toggle
def test_assert_select_rjs_for_toggle
render_rjs do |page|
@@ -521,7 +507,7 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
end
-
+
# Non-positioned insert.
def test_assert_select_rjs_for_nonpositioned_insert
render_rjs do |page|
@@ -568,7 +554,7 @@ class AssertSelectTest < Test::Unit::TestCase
assert_select "div", 4
end
end
-
+
# Simple selection from a single result.
def test_nested_assert_select_rjs_with_single_result
render_rjs do |page|
@@ -600,7 +586,6 @@ class AssertSelectTest < Test::Unit::TestCase
end
end
-
def test_feed_item_encoded
render_xml <<-EOF
<rss version="2.0">
@@ -654,7 +639,6 @@ EOF
end
end
-
#
# Test assert_select_email
#
@@ -670,7 +654,6 @@ EOF
end
end
-
protected
def render_html(html)
@controller.response_with = html
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 34c0200fe8..d49cc2a9aa 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -7,6 +7,7 @@ module Submodule
end
class ContainedNonEmptyController < ActionController::Base
def public_action
+ render :nothing => true
end
hide_action :hidden_action
@@ -105,6 +106,18 @@ end
class PerformActionTest < Test::Unit::TestCase
+ class MockLogger
+ attr_reader :logged
+
+ def initialize
+ @logged = []
+ end
+
+ def method_missing(method, *args)
+ @logged << args.first
+ end
+ end
+
def use_controller(controller_class)
@controller = controller_class.new
@@ -142,6 +155,13 @@ class PerformActionTest < Test::Unit::TestCase
get :another_hidden_action
assert_response 404
end
+
+ def test_namespaced_action_should_log_module_name
+ use_controller Submodule::ContainedNonEmptyController
+ @controller.logger = MockLogger.new
+ get :public_action
+ assert_match /Processing\sSubmodule::ContainedNonEmptyController#public_action/, @controller.logger.logged[1]
+ end
end
class DefaultUrlOptionsTest < Test::Unit::TestCase
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index c30f7be700..b6cdd116e5 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -109,7 +109,7 @@ class PageCachingTest < Test::Unit::TestCase
uses_mocha("should_cache_ok_at_custom_path") do
def test_should_cache_ok_at_custom_path
- @request.expects(:path).returns("/index.html")
+ @request.stubs(:path).returns("/index.html")
get :ok
assert_response :ok
assert File.exist?("#{FILE_STORE_PATH}/index.html")
@@ -148,7 +148,6 @@ class PageCachingTest < Test::Unit::TestCase
end
end
-
class ActionCachingTestController < ActionController::Base
caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
caches_action :show, :cache_path => 'http://test.host/custom/show'
@@ -489,54 +488,54 @@ class FragmentCachingTest < Test::Unit::TestCase
def test_fragment_cache_key
assert_equal 'views/what a key', @controller.fragment_cache_key('what a key')
- assert_equal( "views/test.host/fragment_caching_test/some_action",
- @controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action'))
+ assert_equal "views/test.host/fragment_caching_test/some_action",
+ @controller.fragment_cache_key(:controller => 'fragment_caching_test',:action => 'some_action')
end
- def test_read_fragment__with_caching_enabled
+ def test_read_fragment_with_caching_enabled
@store.write('views/name', 'value')
assert_equal 'value', @controller.read_fragment('name')
end
- def test_read_fragment__with_caching_disabled
+ def test_read_fragment_with_caching_disabled
ActionController::Base.perform_caching = false
@store.write('views/name', 'value')
assert_nil @controller.read_fragment('name')
end
- def test_fragment_exist__with_caching_enabled
+ def test_fragment_exist_with_caching_enabled
@store.write('views/name', 'value')
assert @controller.fragment_exist?('name')
assert !@controller.fragment_exist?('other_name')
end
- def test_fragment_exist__with_caching_disabled
+ def test_fragment_exist_with_caching_disabled
ActionController::Base.perform_caching = false
@store.write('views/name', 'value')
assert !@controller.fragment_exist?('name')
assert !@controller.fragment_exist?('other_name')
end
- def test_write_fragment__with_caching_enabled
+ def test_write_fragment_with_caching_enabled
assert_nil @store.read('views/name')
assert_equal 'value', @controller.write_fragment('name', 'value')
assert_equal 'value', @store.read('views/name')
end
- def test_write_fragment__with_caching_disabled
+ def test_write_fragment_with_caching_disabled
assert_nil @store.read('views/name')
ActionController::Base.perform_caching = false
assert_equal nil, @controller.write_fragment('name', 'value')
assert_nil @store.read('views/name')
end
- def test_expire_fragment__with_simple_key
+ def test_expire_fragment_with_simple_key
@store.write('views/name', 'value')
@controller.expire_fragment 'name'
assert_nil @store.read('views/name')
end
- def test_expire_fragment__with__regexp
+ def test_expire_fragment_with_regexp
@store.write('views/name', 'value')
@store.write('views/another_name', 'another_value')
@store.write('views/primalgrasp', 'will not expire ;-)')
@@ -548,14 +547,14 @@ class FragmentCachingTest < Test::Unit::TestCase
assert_equal 'will not expire ;-)', @store.read('views/primalgrasp')
end
- def test_fragment_for__with_disabled_caching
+ def test_fragment_for_with_disabled_caching
ActionController::Base.perform_caching = false
@store.write('views/expensive', 'fragment content')
fragment_computed = false
buffer = 'generated till now -> '
- @controller.fragment_for(Proc.new { fragment_computed = true }, 'expensive') { buffer }
+ @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
assert fragment_computed
assert_equal 'generated till now -> ', buffer
@@ -566,53 +565,13 @@ class FragmentCachingTest < Test::Unit::TestCase
fragment_computed = false
buffer = 'generated till now -> '
- @controller.fragment_for(Proc.new { fragment_computed = true }, 'expensive') { buffer}
+ @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
assert !fragment_computed
assert_equal 'generated till now -> fragment content', buffer
end
-
- def test_cache_erb_fragment
- @store.write('views/expensive', 'fragment content')
- @controller.response.template.output_buffer = 'generated till now -> '
-
- assert_equal( 'generated till now -> fragment content',
- ActionView::TemplateHandlers::ERB.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
- end
-
- def test_cache_rxml_fragment
- @store.write('views/expensive', 'fragment content')
- xml = 'generated till now -> '
- class << xml; def target!; to_s; end; end
-
- assert_equal( 'generated till now -> fragment content',
- ActionView::TemplateHandlers::Builder.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
- end
-
- def test_cache_rjs_fragment
- @store.write('views/expensive', 'fragment content')
- page = 'generated till now -> '
-
- assert_equal( 'generated till now -> fragment content',
- ActionView::TemplateHandlers::RJS.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
- end
-
- def test_cache_rjs_fragment_debug_mode_does_not_interfere
- @store.write('views/expensive', 'fragment content')
- page = 'generated till now -> '
-
- begin
- debug_mode, ActionView::Base.debug_rjs = ActionView::Base.debug_rjs, true
- assert_equal( 'generated till now -> fragment content',
- ActionView::TemplateHandlers::RJS.new(@controller).cache_fragment(Proc.new{ }, 'expensive'))
- assert ActionView::Base.debug_rjs
- ensure
- ActionView::Base.debug_rjs = debug_mode
- end
- end
end
-
class FunctionalCachingController < ActionController::Base
def fragment_cached
end
@@ -629,6 +588,13 @@ class FunctionalCachingController < ActionController::Base
end
end
+ def formatted_fragment_cached
+ respond_to do |format|
+ format.html
+ format.xml
+ format.js
+ end
+ end
def rescue_action(e)
raise e
@@ -678,4 +644,35 @@ CACHED
assert_match /Fragment caching in a partial/, @response.body
assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial')
end
+
+ def test_html_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "html"
+ assert_response :success
+ expected_body = "<body>\n<p>ERB</p>\n</body>"
+
+ assert_equal expected_body, @response.body
+
+ assert_equal "<p>ERB</p>", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
+
+ def test_xml_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "xml"
+ assert_response :success
+ expected_body = "<body>\n <p>Builder</p>\n</body>\n"
+
+ assert_equal expected_body, @response.body
+
+ assert_equal " <p>Builder</p>\n", @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
+
+ def test_js_formatted_fragment_caching
+ get :formatted_fragment_cached, :format => "js"
+ assert_response :success
+ expected_body = %(title = "Hey";\n$("element_1").visualEffect("highlight");\n) +
+ %($("element_2").visualEffect("highlight");\nfooter = "Bye";)
+ assert_equal expected_body, @response.body
+
+ assert_equal ['$("element_1").visualEffect("highlight");', '$("element_2").visualEffect("highlight");'],
+ @store.read('views/test.host/functional_caching/formatted_fragment_cached')
+ end
end
diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb
index bf3b8b788e..813171857a 100755..100644
--- a/actionpack/test/controller/cgi_test.rb
+++ b/actionpack/test/controller/cgi_test.rb
@@ -53,6 +53,15 @@ class BaseCgiTest < Test::Unit::TestCase
end
def default_test; end
+
+ private
+
+ def set_content_data(data)
+ @request.env['REQUEST_METHOD'] = 'POST'
+ @request.env['CONTENT_LENGTH'] = data.length
+ @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+ @request.env['RAW_POST_DATA'] = data
+ end
end
class CgiRequestTest < BaseCgiTest
@@ -66,7 +75,7 @@ class CgiRequestTest < BaseCgiTest
assert_equal "rubyonrails.org:8080", @request.host_with_port
@request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
- assert_equal "www.secondhost.org", @request.host
+ assert_equal "www.secondhost.org", @request.host(true)
end
def test_http_host_with_default_port_overrides_server_port
@@ -155,10 +164,8 @@ end
class CgiRequestParamsParsingTest < BaseCgiTest
def test_doesnt_break_when_content_type_has_charset
- data = 'flamenco=love'
- @request.env['CONTENT_LENGTH'] = data.length
- @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
- @request.env['RAW_POST_DATA'] = data
+ set_content_data 'flamenco=love'
+
assert_equal({"flamenco"=> "love"}, @request.request_parameters)
end
@@ -168,6 +175,41 @@ class CgiRequestParamsParsingTest < BaseCgiTest
end
end
+class CgiRequestContentTypeTest < BaseCgiTest
+ def test_html_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::HTML.to_s
+ assert @request.content_type.verify_request?
+ end
+
+ def test_xml_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+ assert !@request.content_type.verify_request?
+ end
+end
+
+class CgiRequestMethodTest < BaseCgiTest
+ def test_get
+ assert_equal :get, @request.request_method
+ end
+
+ def test_post
+ @request.env['REQUEST_METHOD'] = 'POST'
+ assert_equal :post, @request.request_method
+ end
+
+ def test_put
+ set_content_data '_method=put'
+
+ assert_equal :put, @request.request_method
+ end
+
+ def test_delete
+ set_content_data '_method=delete'
+
+ assert_equal :delete, @request.request_method
+ end
+end
+
class CgiRequestNeedsRewoundTest < BaseCgiTest
def test_body_should_be_rewound
data = 'foo'
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index d457d13aef..ae71d62e11 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -19,6 +19,11 @@ class ContentTypeController < ActionController::Base
render :text => "hello world!"
end
+ def render_nil_charset_from_body
+ response.charset = nil
+ render :text => "hello world!"
+ end
+
def render_default_for_rhtml
end
@@ -85,8 +90,23 @@ class ContentTypeTest < Test::Unit::TestCase
def test_charset_from_body
get :render_charset_from_body
+ assert_equal Mime::HTML, @response.content_type
assert_equal "utf-16", @response.charset
+ end
+
+ def test_nil_charset_from_body
+ get :render_nil_charset_from_body
assert_equal Mime::HTML, @response.content_type
+ assert_equal "utf-8", @response.charset, @response.headers.inspect
+ end
+
+ def test_nil_default_for_rhtml
+ ContentTypeController.default_charset = nil
+ get :render_default_for_rhtml
+ assert_equal Mime::HTML, @response.content_type
+ assert_nil @response.charset, @response.headers.inspect
+ ensure
+ ContentTypeController.default_charset = "utf-8"
end
def test_default_for_rhtml
@@ -128,23 +148,23 @@ class AcceptBasedContentTypeTest < ActionController::TestCase
def test_render_default_content_types_for_respond_to
- @request.env["HTTP_ACCEPT"] = Mime::HTML.to_s
+ @request.accept = Mime::HTML.to_s
get :render_default_content_types_for_respond_to
assert_equal Mime::HTML, @response.content_type
- @request.env["HTTP_ACCEPT"] = Mime::JS.to_s
+ @request.accept = Mime::JS.to_s
get :render_default_content_types_for_respond_to
assert_equal Mime::JS, @response.content_type
end
def test_render_default_content_types_for_respond_to_with_template
- @request.env["HTTP_ACCEPT"] = Mime::XML.to_s
+ @request.accept = Mime::XML.to_s
get :render_default_content_types_for_respond_to
assert_equal Mime::XML, @response.content_type
end
def test_render_default_content_types_for_respond_to_with_overwrite
- @request.env["HTTP_ACCEPT"] = Mime::RSS.to_s
+ @request.accept = Mime::RSS.to_s
get :render_default_content_types_for_respond_to
assert_equal Mime::XML, @response.content_type
end
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index b45fbb17d3..5a6fb49861 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -60,7 +60,7 @@ class CookieTest < Test::Unit::TestCase
end
def test_setting_cookie_for_fourteen_days_with_symbols
- get :authenticate_for_fourteen_days
+ get :authenticate_for_fourteen_days_with_symbols
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 475e13897b..c986941140 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -253,7 +253,14 @@ class IntegrationProcessTest < ActionController::IntegrationTest
session :off
def get
- render :text => "OK", :status => 200
+ respond_to do |format|
+ format.html { render :text => "OK", :status => 200 }
+ format.js { render :text => "JS OK", :status => 200 }
+ end
+ end
+
+ def get_with_params
+ render :text => "foo: #{params[:foo]}", :status => 200
end
def post
@@ -265,6 +272,10 @@ class IntegrationProcessTest < ActionController::IntegrationTest
cookies["cookie_3"] = "chocolate"
render :text => "Gone", :status => 410
end
+
+ def redirect
+ redirect_to :action => "get"
+ end
end
def test_get
@@ -274,6 +285,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest
assert_equal "OK", status_message
assert_equal "200 OK", response.headers["Status"]
assert_equal ["200 OK"], headers["status"]
+ assert_response 200
+ assert_response :success
+ assert_response :ok
assert_equal [], response.headers["cookie"]
assert_equal [], headers["cookie"]
assert_equal({}, cookies)
@@ -290,6 +304,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest
assert_equal "Created", status_message
assert_equal "201 Created", response.headers["Status"]
assert_equal ["201 Created"], headers["status"]
+ assert_response 201
+ assert_response :success
+ assert_response :created
assert_equal [], response.headers["cookie"]
assert_equal [], headers["cookie"]
assert_equal({}, cookies)
@@ -308,23 +325,84 @@ class IntegrationProcessTest < ActionController::IntegrationTest
assert_equal "Gone", status_message
assert_equal "410 Gone", response.headers["Status"]
assert_equal ["410 Gone"], headers["status"]
- assert_equal nil, response.headers["Set-Cookie"]
+ assert_response 410
+ assert_response :gone
+ assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], response.headers["Set-Cookie"]
assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], headers['set-cookie']
- assert_equal [[], ["chocolate"]], response.headers["cookie"]
+ assert_equal [
+ CGI::Cookie::new("name" => "cookie_1", "value" => ""),
+ CGI::Cookie::new("name" => "cookie_3", "value" => "chocolate")
+ ], response.headers["cookie"]
assert_equal [], headers["cookie"]
assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies)
assert_equal "Gone", response.body
end
end
+ def test_redirect
+ with_test_route_set do
+ get '/redirect'
+ assert_equal 302, status
+ assert_equal "Found", status_message
+ assert_equal "302 Found", response.headers["Status"]
+ assert_equal ["302 Found"], headers["status"]
+ assert_response 302
+ assert_response :redirect
+ assert_response :found
+ assert_equal "<html><body>You are being <a href=\"http://www.example.com/get\">redirected</a>.</body></html>", response.body
+ assert_kind_of HTML::Document, html_document
+ assert_equal 1, request_count
+ end
+ end
+
+ def test_xml_http_request_get
+ with_test_route_set do
+ xhr :get, '/get'
+ assert_equal 200, status
+ assert_equal "OK", status_message
+ assert_equal "200 OK", response.headers["Status"]
+ assert_equal ["200 OK"], headers["status"]
+ assert_response 200
+ assert_response :success
+ assert_response :ok
+ assert_equal "JS OK", response.body
+ end
+ end
+
+ def test_get_with_query_string
+ with_test_route_set do
+ get '/get_with_params?foo=bar'
+ assert_equal '/get_with_params?foo=bar', request.env["REQUEST_URI"]
+ assert_equal '/get_with_params?foo=bar', request.request_uri
+ assert_equal nil, request.env["QUERY_STRING"]
+ assert_equal 'foo=bar', request.query_string
+ assert_equal 'bar', request.parameters['foo']
+
+ assert_equal 200, status
+ assert_equal "foo: bar", response.body
+ end
+ end
+
+ def test_get_with_parameters
+ with_test_route_set do
+ get '/get_with_params', :foo => "bar"
+ assert_equal '/get_with_params', request.env["REQUEST_URI"]
+ assert_equal '/get_with_params', request.request_uri
+ assert_equal 'foo=bar', request.env["QUERY_STRING"]
+ assert_equal 'foo=bar', request.query_string
+ assert_equal 'bar', request.parameters['foo']
+
+ assert_equal 200, status
+ assert_equal "foo: bar", response.body
+ end
+ end
+
private
def with_test_route_set
with_routing do |set|
set.draw do |map|
map.with_options :controller => "IntegrationProcessTest::Integration" do |c|
- c.connect '/get', :action => "get"
- c.connect '/post', :action => "post"
- c.connect '/cookie_monster', :action => "cookie_monster"
+ c.connect "/:action"
end
end
yield
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 92b6aa4f2f..71f110f241 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -31,16 +31,8 @@ end
class MultipleExtensions < LayoutTest
end
-class MabView < ActionView::TemplateHandler
- def initialize(view)
- end
-
- def render(template, local_assigns)
- template.source
- end
-end
-
-ActionView::Template::register_template_handler :mab, MabView
+ActionView::Template::register_template_handler :mab,
+ lambda { |template| template.source.inspect }
class LayoutAutoDiscoveryTest < Test::Unit::TestCase
def setup
@@ -49,19 +41,19 @@ class LayoutAutoDiscoveryTest < Test::Unit::TestCase
@request.host = "www.nextangle.com"
end
-
+
def test_application_layout_is_default_when_no_controller_match
@controller = ProductController.new
get :hello
assert_equal 'layout_test.rhtml hello.rhtml', @response.body
end
-
+
def test_controller_name_layout_name_match
@controller = ItemController.new
get :hello
assert_equal 'item.rhtml hello.rhtml', @response.body
end
-
+
def test_third_party_template_library_auto_discovers_layout
ThirdPartyTemplateLibraryController.view_paths.reload!
@controller = ThirdPartyTemplateLibraryController.new
@@ -71,14 +63,14 @@ class LayoutAutoDiscoveryTest < Test::Unit::TestCase
assert_response :success
assert_equal 'Mab', @response.body
end
-
+
def test_namespaced_controllers_auto_detect_layouts
@controller = ControllerNameSpace::NestedController.new
get :hello
assert_equal 'layouts/controller_name_space/nested', @controller.active_layout
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
end
-
+
def test_namespaced_controllers_auto_detect_layouts
@controller = MultipleExtensions.new
get :hello
@@ -123,7 +115,7 @@ class ExemptFromLayoutTest < Test::Unit::TestCase
def test_rhtml_exempt_from_layout_status_should_prevent_layout_render
ActionController::Base.exempt_from_layout :rhtml
-
+
assert @controller.send!(:template_exempt_from_layout?, 'test.rhtml')
assert @controller.send!(:template_exempt_from_layout?, 'hello.rhtml')
@@ -164,19 +156,19 @@ class LayoutSetInResponseTest < Test::Unit::TestCase
get :hello
assert_equal 'layouts/layout_test', @response.layout
end
-
+
def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
assert_equal 'layouts/item', @response.layout
end
-
+
def test_layout_set_when_using_render
@controller = SetsLayoutInRenderController.new
get :hello
assert_equal 'layouts/third_party_template_library', @response.layout
end
-
+
def test_layout_is_not_set_when_none_rendered
@controller = RendersNoLayoutController.new
get :hello
@@ -257,4 +249,3 @@ class LayoutSymlinkedIsRenderedTest < Test::Unit::TestCase
assert_equal "layouts/symlinked/symlinked_layout", @response.layout
end
end
- \ No newline at end of file
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 1701431858..0d508eb8df 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -177,7 +177,7 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_html
- @request.env["HTTP_ACCEPT"] = "text/html"
+ @request.accept = "text/html"
get :js_or_html
assert_equal 'HTML', @response.body
@@ -189,7 +189,7 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_all
- @request.env["HTTP_ACCEPT"] = "*/*"
+ @request.accept = "*/*"
get :js_or_html
assert_equal 'HTML', @response.body # js is not part of all
@@ -201,13 +201,13 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_xml
- @request.env["HTTP_ACCEPT"] = "application/xml"
+ @request.accept = "application/xml"
get :html_xml_or_rss
assert_equal 'XML', @response.body
end
def test_js_or_html
- @request.env["HTTP_ACCEPT"] = "text/javascript, text/html"
+ @request.accept = "text/javascript, text/html"
get :js_or_html
assert_equal 'JS', @response.body
@@ -232,7 +232,7 @@ class MimeControllerTest < Test::Unit::TestCase
'JSON' => %w(application/json text/x-json)
}.each do |body, content_types|
content_types.each do |content_type|
- @request.env['HTTP_ACCEPT'] = content_type
+ @request.accept = content_type
get :json_or_yaml
assert_equal body, @response.body
end
@@ -240,7 +240,7 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_js_or_anything
- @request.env["HTTP_ACCEPT"] = "text/javascript, */*"
+ @request.accept = "text/javascript, */*"
get :js_or_html
assert_equal 'JS', @response.body
@@ -252,34 +252,34 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_using_defaults
- @request.env["HTTP_ACCEPT"] = "*/*"
+ @request.accept = "*/*"
get :using_defaults
assert_equal "text/html", @response.content_type
assert_equal 'Hello world!', @response.body
- @request.env["HTTP_ACCEPT"] = "text/javascript"
+ @request.accept = "text/javascript"
get :using_defaults
assert_equal "text/javascript", @response.content_type
assert_equal '$("body").visualEffect("highlight");', @response.body
- @request.env["HTTP_ACCEPT"] = "application/xml"
+ @request.accept = "application/xml"
get :using_defaults
assert_equal "application/xml", @response.content_type
assert_equal "<p>Hello world!</p>\n", @response.body
end
def test_using_defaults_with_type_list
- @request.env["HTTP_ACCEPT"] = "*/*"
+ @request.accept = "*/*"
get :using_defaults_with_type_list
assert_equal "text/html", @response.content_type
assert_equal 'Hello world!', @response.body
- @request.env["HTTP_ACCEPT"] = "text/javascript"
+ @request.accept = "text/javascript"
get :using_defaults_with_type_list
assert_equal "text/javascript", @response.content_type
assert_equal '$("body").visualEffect("highlight");', @response.body
- @request.env["HTTP_ACCEPT"] = "application/xml"
+ @request.accept = "application/xml"
get :using_defaults_with_type_list
assert_equal "application/xml", @response.content_type
assert_equal "<p>Hello world!</p>\n", @response.body
@@ -298,55 +298,55 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_synonyms
- @request.env["HTTP_ACCEPT"] = "application/javascript"
+ @request.accept = "application/javascript"
get :js_or_html
assert_equal 'JS', @response.body
- @request.env["HTTP_ACCEPT"] = "application/x-xml"
+ @request.accept = "application/x-xml"
get :html_xml_or_rss
assert_equal "XML", @response.body
end
def test_custom_types
- @request.env["HTTP_ACCEPT"] = "application/crazy-xml"
+ @request.accept = "application/crazy-xml"
get :custom_type_handling
assert_equal "application/crazy-xml", @response.content_type
assert_equal 'Crazy XML', @response.body
- @request.env["HTTP_ACCEPT"] = "text/html"
+ @request.accept = "text/html"
get :custom_type_handling
assert_equal "text/html", @response.content_type
assert_equal 'HTML', @response.body
end
def test_xhtml_alias
- @request.env["HTTP_ACCEPT"] = "application/xhtml+xml,application/xml"
+ @request.accept = "application/xhtml+xml,application/xml"
get :html_or_xml
assert_equal 'HTML', @response.body
end
def test_firefox_simulation
- @request.env["HTTP_ACCEPT"] = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
+ @request.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
get :html_or_xml
assert_equal 'HTML', @response.body
end
def test_handle_any
- @request.env["HTTP_ACCEPT"] = "*/*"
+ @request.accept = "*/*"
get :handle_any
assert_equal 'HTML', @response.body
- @request.env["HTTP_ACCEPT"] = "text/javascript"
+ @request.accept = "text/javascript"
get :handle_any
assert_equal 'Either JS or XML', @response.body
- @request.env["HTTP_ACCEPT"] = "text/xml"
+ @request.accept = "text/xml"
get :handle_any
assert_equal 'Either JS or XML', @response.body
end
def test_handle_any_any
- @request.env["HTTP_ACCEPT"] = "*/*"
+ @request.accept = "*/*"
get :handle_any_any
assert_equal 'HTML', @response.body
end
@@ -357,31 +357,31 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_handle_any_any_explicit_html
- @request.env["HTTP_ACCEPT"] = "text/html"
+ @request.accept = "text/html"
get :handle_any_any
assert_equal 'HTML', @response.body
end
def test_handle_any_any_javascript
- @request.env["HTTP_ACCEPT"] = "text/javascript"
+ @request.accept = "text/javascript"
get :handle_any_any
assert_equal 'Whatever you ask for, I got it', @response.body
end
def test_handle_any_any_xml
- @request.env["HTTP_ACCEPT"] = "text/xml"
+ @request.accept = "text/xml"
get :handle_any_any
assert_equal 'Whatever you ask for, I got it', @response.body
end
def test_rjs_type_skips_layout
- @request.env["HTTP_ACCEPT"] = "text/javascript"
+ @request.accept = "text/javascript"
get :all_types_with_layout
assert_equal 'RJS for all_types_with_layout', @response.body
end
def test_html_type_with_layout
- @request.env["HTTP_ACCEPT"] = "text/html"
+ @request.accept = "text/html"
get :all_types_with_layout
assert_equal '<html><div id="html">HTML for all_types_with_layout</div></html>', @response.body
end
@@ -460,7 +460,7 @@ class MimeControllerTest < Test::Unit::TestCase
end
def test_format_with_custom_response_type_and_request_headers
- @request.env["HTTP_ACCEPT"] = "text/iphone"
+ @request.accept = "text/iphone"
get :iphone_with_html_response_type
assert_equal '<html><div id="iphone">Hello iPhone future from iPhone!</div></html>', @response.body
assert_equal "text/html", @response.content_type
@@ -470,7 +470,7 @@ class MimeControllerTest < Test::Unit::TestCase
get :iphone_with_html_response_type_without_layout
assert_equal '<html><div id="html_missing">Hello future from Firefox!</div></html>', @response.body
- @request.env["HTTP_ACCEPT"] = "text/iphone"
+ @request.accept = "text/iphone"
assert_raises(ActionView::MissingTemplate) { get :iphone_with_html_response_type_without_layout }
end
end
@@ -522,7 +522,7 @@ class MimeControllerLayoutsTest < Test::Unit::TestCase
get :index
assert_equal '<html><div id="html">Hello Firefox</div></html>', @response.body
- @request.env["HTTP_ACCEPT"] = "text/iphone"
+ @request.accept = "text/iphone"
get :index
assert_equal 'Hello iPhone', @response.body
end
@@ -533,7 +533,7 @@ class MimeControllerLayoutsTest < Test::Unit::TestCase
get :index
assert_equal 'Super Firefox', @response.body
- @request.env["HTTP_ACCEPT"] = "text/iphone"
+ @request.accept = "text/iphone"
get :index
assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
end
diff --git a/actionpack/test/controller/new_render_test.rb b/actionpack/test/controller/new_render_test.rb
index d2a3a2b0b0..82919b7777 100644
--- a/actionpack/test/controller/new_render_test.rb
+++ b/actionpack/test/controller/new_render_test.rb
@@ -136,6 +136,10 @@ class NewRenderTestController < ActionController::Base
render :partial => "partial_only", :layout => true
end
+ def partial_with_counter
+ render :partial => "counter", :locals => { :counter_counter => 5 }
+ end
+
def partial_with_locals
render :partial => "customer", :locals => { :customer => Customer.new("david") }
end
@@ -431,6 +435,10 @@ class NewRenderTestController < ActionController::Base
render :action => "using_layout_around_block"
end
+ def render_using_layout_around_block_with_args
+ render :action => "using_layout_around_block_with_args"
+ end
+
def render_using_layout_around_block_in_main_layout_and_within_content_for_layout
render :action => "using_layout_around_block"
end
@@ -741,6 +749,11 @@ EOS
assert_equal "<title>Talking to the layout</title>\nAction was here!", @response.body
end
+ def test_partial_with_counter
+ get :partial_with_counter
+ assert_equal "5", @response.body
+ end
+
def test_partials_list
get :partials_list
assert_equal "goodbyeHello: davidHello: marygoodbye\n", @response.body
@@ -960,4 +973,9 @@ EOS
get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout
assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body
end
+
+ def test_using_layout_around_block_with_args
+ get :render_using_layout_around_block_with_args
+ assert_equal "Before\narg1arg2\nAfter", @response.body
+ end
end
diff --git a/actionpack/test/controller/polymorphic_routes_test.rb b/actionpack/test/controller/polymorphic_routes_test.rb
index 3f52526f08..6ddf2826cd 100644
--- a/actionpack/test/controller/polymorphic_routes_test.rb
+++ b/actionpack/test/controller/polymorphic_routes_test.rb
@@ -60,6 +60,18 @@ uses_mocha 'polymorphic URL helpers' do
edit_polymorphic_url(@article)
end
+ def test_url_helper_prefixed_with_edit_with_url_options
+ @article.save
+ expects(:edit_article_url).with(@article, :param1 => '10')
+ edit_polymorphic_url(@article, :param1 => '10')
+ end
+
+ def test_url_helper_with_url_options
+ @article.save
+ expects(:article_url).with(@article, :param1 => '10')
+ polymorphic_url(@article, :param1 => '10')
+ end
+
def test_formatted_url_helper
expects(:formatted_article_url).with(@article, :pdf)
formatted_polymorphic_url([@article, :pdf])
@@ -67,10 +79,16 @@ uses_mocha 'polymorphic URL helpers' do
def test_format_option
@article.save
- expects(:article_url).with(@article, :pdf)
+ expects(:formatted_article_url).with(@article, :pdf)
polymorphic_url(@article, :format => :pdf)
end
+ def test_format_option_with_url_options
+ @article.save
+ expects(:formatted_article_url).with(@article, :pdf, :param1 => '10')
+ polymorphic_url(@article, :format => :pdf, :param1 => '10')
+ end
+
def test_id_and_format_option
@article.save
expects(:article_url).with(:id => @article, :format => :pdf)
@@ -147,7 +165,7 @@ uses_mocha 'polymorphic URL helpers' do
def test_nesting_with_array_containing_singleton_resource_and_format_option
@tag = Tag.new
@tag.save
- expects(:article_response_tag_url).with(@article, @tag, :pdf)
+ expects(:formatted_article_response_tag_url).with(@article, @tag, :pdf)
polymorphic_url([@article, :response, @tag], :format => :pdf)
end
diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb
index 486fe49737..d5e56b9584 100644
--- a/actionpack/test/controller/rack_test.rb
+++ b/actionpack/test/controller/rack_test.rb
@@ -51,62 +51,74 @@ class BaseRackTest < Test::Unit::TestCase
end
def default_test; end
+
+ private
+
+ def set_content_data(data)
+ @request.env['REQUEST_METHOD'] = 'POST'
+ @request.env['CONTENT_LENGTH'] = data.length
+ @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
+ @request.env['RAW_POST_DATA'] = data
+ end
end
class RackRequestTest < BaseRackTest
def test_proxy_request
- assert_equal 'glu.ttono.us', @request.host_with_port
+ assert_equal 'glu.ttono.us', @request.host_with_port(true)
end
def test_http_host
@env.delete "HTTP_X_FORWARDED_HOST"
@env['HTTP_HOST'] = "rubyonrails.org:8080"
- assert_equal "rubyonrails.org:8080", @request.host_with_port
+ assert_equal "rubyonrails.org", @request.host(true)
+ assert_equal "rubyonrails.org:8080", @request.host_with_port(true)
@env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
- assert_equal "www.secondhost.org", @request.host
+ assert_equal "www.secondhost.org", @request.host(true)
end
def test_http_host_with_default_port_overrides_server_port
@env.delete "HTTP_X_FORWARDED_HOST"
@env['HTTP_HOST'] = "rubyonrails.org"
- assert_equal "rubyonrails.org", @request.host_with_port
+ assert_equal "rubyonrails.org", @request.host_with_port(true)
end
def test_host_with_port_defaults_to_server_name_if_no_host_headers
@env.delete "HTTP_X_FORWARDED_HOST"
@env.delete "HTTP_HOST"
- assert_equal "glu.ttono.us:8007", @request.host_with_port
+ assert_equal "glu.ttono.us:8007", @request.host_with_port(true)
end
def test_host_with_port_falls_back_to_server_addr_if_necessary
@env.delete "HTTP_X_FORWARDED_HOST"
@env.delete "HTTP_HOST"
@env.delete "SERVER_NAME"
- assert_equal "207.7.108.53:8007", @request.host_with_port
+ assert_equal "207.7.108.53", @request.host(true)
+ assert_equal 8007, @request.port(true)
+ assert_equal "207.7.108.53:8007", @request.host_with_port(true)
end
def test_host_with_port_if_http_standard_port_is_specified
@env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80"
- assert_equal "glu.ttono.us", @request.host_with_port
+ assert_equal "glu.ttono.us", @request.host_with_port(true)
end
def test_host_with_port_if_https_standard_port_is_specified
@env['HTTP_X_FORWARDED_PROTO'] = "https"
@env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443"
- assert_equal "glu.ttono.us", @request.host_with_port
+ assert_equal "glu.ttono.us", @request.host_with_port(true)
end
def test_host_if_ipv6_reference
@env.delete "HTTP_X_FORWARDED_HOST"
@env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]"
- assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+ assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host(true)
end
def test_host_if_ipv6_reference_with_port
@env.delete "HTTP_X_FORWARDED_HOST"
@env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008"
- assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
+ assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host(true)
end
def test_cgi_environment_variables
@@ -153,10 +165,8 @@ end
class RackRequestParamsParsingTest < BaseRackTest
def test_doesnt_break_when_content_type_has_charset
- data = 'flamenco=love'
- @request.env['CONTENT_LENGTH'] = data.length
- @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
- @request.env['RAW_POST_DATA'] = data
+ set_content_data 'flamenco=love'
+
assert_equal({"flamenco"=> "love"}, @request.request_parameters)
end
@@ -166,6 +176,41 @@ class RackRequestParamsParsingTest < BaseRackTest
end
end
+class RackRequestContentTypeTest < BaseRackTest
+ def test_html_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::HTML.to_s
+ assert @request.content_type.verify_request?
+ end
+
+ def test_xml_content_type_verification
+ @request.env['CONTENT_TYPE'] = Mime::XML.to_s
+ assert !@request.content_type.verify_request?
+ end
+end
+
+class RackRequestMethodTest < BaseRackTest
+ def test_get
+ assert_equal :get, @request.request_method
+ end
+
+ def test_post
+ @request.env['REQUEST_METHOD'] = 'POST'
+ assert_equal :post, @request.request_method
+ end
+
+ def test_put
+ set_content_data '_method=put'
+
+ assert_equal :put, @request.request_method
+ end
+
+ def test_delete
+ set_content_data '_method=delete'
+
+ assert_equal :delete, @request.request_method
+ end
+end
+
class RackRequestNeedsRewoundTest < BaseRackTest
def test_body_should_be_rewound
data = 'foo'
@@ -191,10 +236,17 @@ class RackResponseTest < BaseRackTest
def test_simple_output
@response.body = "Hello, World!"
+ @response.prepare!
status, headers, body = @response.out(@output)
assert_equal "200 OK", status
- assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers)
+ assert_equal({
+ "Content-Type" => "text/html; charset=utf-8",
+ "Cache-Control" => "private, max-age=0, must-revalidate",
+ "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
+ "Set-Cookie" => [],
+ "Content-Length" => "13"
+ }, headers)
parts = []
body.each { |part| parts << part }
@@ -205,10 +257,11 @@ class RackResponseTest < BaseRackTest
@response.body = Proc.new do |response, output|
5.times { |n| output.write(n) }
end
+ @response.prepare!
status, headers, body = @response.out(@output)
assert_equal "200 OK", status
- assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers)
+ assert_equal({"Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers)
parts = []
body.each { |part| parts << part }
@@ -220,13 +273,16 @@ class RackResponseTest < BaseRackTest
@request.cgi.send :instance_variable_set, '@output_cookies', [cookie]
@response.body = "Hello, World!"
+ @response.prepare!
status, headers, body = @response.out(@output)
assert_equal "200 OK", status
assert_equal({
- "Content-Type" => "text/html",
- "Cache-Control" => "no-cache",
- "Set-Cookie" => ["name=Josh; path="]
+ "Content-Type" => "text/html; charset=utf-8",
+ "Cache-Control" => "private, max-age=0, must-revalidate",
+ "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
+ "Set-Cookie" => ["name=Josh; path="],
+ "Content-Length" => "13"
}, headers)
parts = []
@@ -234,3 +290,34 @@ class RackResponseTest < BaseRackTest
assert_equal ["Hello, World!"], parts
end
end
+
+class RackResponseHeadersTest < BaseRackTest
+ def setup
+ super
+ @response = ActionController::RackResponse.new(@request)
+ @output = StringIO.new('')
+ @response.headers['Status'] = "200 OK"
+ end
+
+ def test_content_type
+ [204, 304].each do |c|
+ @response.headers['Status'] = c.to_s
+ assert !response_headers.has_key?("Content-Type"), "#{c} should not have Content-Type header"
+ end
+
+ [200, 302, 404, 500].each do |c|
+ @response.headers['Status'] = c.to_s
+ assert response_headers.has_key?("Content-Type"), "#{c} did not have Content-Type header"
+ end
+ end
+
+ def test_status
+ assert !response_headers.has_key?('Status')
+ end
+
+ private
+ def response_headers
+ @response.prepare!
+ @response.out(@output)[1]
+ end
+end
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 28da5c6163..2f8bf7b6ee 100755..100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -9,11 +9,11 @@ class Workshop
def initialize(id, new_record)
@id, @new_record = id, new_record
end
-
+
def new_record?
@new_record
end
-
+
def to_s
id.to_s
end
@@ -24,32 +24,32 @@ class RedirectController < ActionController::Base
redirect_to :action => "hello_world"
end
- def redirect_with_status
+ def redirect_with_status
redirect_to({:action => "hello_world", :status => 301})
- end
+ end
def redirect_with_status_hash
redirect_to({:action => "hello_world"}, {:status => 301})
- end
+ end
- def url_redirect_with_status
+ def url_redirect_with_status
redirect_to("http://www.example.com", :status => :moved_permanently)
- end
-
- def url_redirect_with_status_hash
+ end
+
+ def url_redirect_with_status_hash
redirect_to("http://www.example.com", {:status => 301})
- end
+ end
- def relative_url_redirect_with_status
+ def relative_url_redirect_with_status
redirect_to("/things/stuff", :status => :found)
- end
-
+ end
+
def relative_url_redirect_with_status_hash
redirect_to("/things/stuff", {:status => 301})
- end
-
- def redirect_to_back_with_status
- redirect_to :back, :status => 307
+ end
+
+ def redirect_to_back_with_status
+ redirect_to :back, :status => 307
end
def host_redirect
@@ -90,9 +90,9 @@ class RedirectController < ActionController::Base
end
def rescue_errors(e) raise e end
-
+
def rescue_action(e) raise end
-
+
protected
def dashbord_url(id, message)
url_for :action => "dashboard", :params => { "id" => id, "message" => message }
@@ -118,48 +118,48 @@ class RedirectTest < Test::Unit::TestCase
assert_equal "http://test.host/redirect/hello_world", redirect_to_url
end
- def test_redirect_with_status
- get :redirect_with_status
- assert_response 301
- assert_equal "http://test.host/redirect/hello_world", redirect_to_url
- end
+ def test_redirect_with_status
+ get :redirect_with_status
+ assert_response 301
+ assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+ end
- def test_redirect_with_status_hash
+ def test_redirect_with_status_hash
get :redirect_with_status_hash
- assert_response 301
- assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+ assert_response 301
+ assert_equal "http://test.host/redirect/hello_world", redirect_to_url
+ end
+
+ def test_url_redirect_with_status
+ get :url_redirect_with_status
+ assert_response 301
+ assert_equal "http://www.example.com", redirect_to_url
end
-
- def test_url_redirect_with_status
- get :url_redirect_with_status
- assert_response 301
- assert_equal "http://www.example.com", redirect_to_url
- end
def test_url_redirect_with_status_hash
get :url_redirect_with_status_hash
- assert_response 301
- assert_equal "http://www.example.com", redirect_to_url
- end
+ assert_response 301
+ assert_equal "http://www.example.com", redirect_to_url
+ end
-
- def test_relative_url_redirect_with_status
- get :relative_url_redirect_with_status
+
+ def test_relative_url_redirect_with_status
+ get :relative_url_redirect_with_status
assert_response 302
- assert_equal "http://test.host/things/stuff", redirect_to_url
- end
-
+ assert_equal "http://test.host/things/stuff", redirect_to_url
+ end
+
def test_relative_url_redirect_with_status_hash
get :relative_url_redirect_with_status_hash
- assert_response 301
- assert_equal "http://test.host/things/stuff", redirect_to_url
- end
-
- def test_redirect_to_back_with_status
- @request.env["HTTP_REFERER"] = "http://www.example.com/coming/from"
- get :redirect_to_back_with_status
- assert_response 307
- assert_equal "http://www.example.com/coming/from", redirect_to_url
+ assert_response 301
+ assert_equal "http://test.host/things/stuff", redirect_to_url
+ end
+
+ def test_redirect_to_back_with_status
+ @request.env["HTTP_REFERER"] = "http://www.example.com/coming/from"
+ get :redirect_to_back_with_status
+ assert_response 307
+ assert_equal "http://www.example.com/coming/from", redirect_to_url
end
def test_simple_redirect_using_options
@@ -204,20 +204,20 @@ class RedirectTest < Test::Unit::TestCase
assert_response :redirect
assert_equal "http://www.example.com/coming/from", redirect_to_url
end
-
+
def test_redirect_to_back_with_no_referer
assert_raises(ActionController::RedirectBackError) {
@request.env["HTTP_REFERER"] = nil
get :redirect_to_back
}
end
-
+
def test_redirect_to_record
ActionController::Routing::Routes.draw do |map|
map.resources :workshops
map.connect ':controller/:action/:id'
end
-
+
get :redirect_to_existing_record
assert_equal "http://test.host/workshops/5", redirect_to_url
assert_redirected_to Workshop.new(5, false)
@@ -237,7 +237,6 @@ class RedirectTest < Test::Unit::TestCase
get :redirect_to_nil
end
end
-
end
module ModuleTest
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index a857810b78..3008f5ca03 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -8,14 +8,23 @@ module Fun
end
end
-
-# FIXME: crashes Ruby 1.9
class TestController < ActionController::Base
layout :determine_layout
def hello_world
end
+ def conditional_hello
+ response.last_modified = Time.now.utc.beginning_of_day
+ response.etag = [:foo, 123]
+
+ if request.fresh?(response)
+ head :not_modified
+ else
+ render :action => 'hello_world'
+ end
+ end
+
def render_hello_world
render :template => "test/hello_world"
end
@@ -101,12 +110,7 @@ class TestController < ActionController::Base
end
def render_line_offset
- begin
- render :inline => '<% raise %>', :locals => {:foo => 'bar'}
- rescue RuntimeError => exc
- end
- line = exc.backtrace.first
- render :text => line
+ render :inline => '<% raise %>', :locals => {:foo => 'bar'}
end
def heading
@@ -198,11 +202,11 @@ class TestController < ActionController::Base
def render_alternate_default
# For this test, the method "default_render" is overridden:
- @alternate_default_render = lambda {
- render :update do |page|
- page.replace :foo, :partial => 'partial'
- end
- }
+ @alternate_default_render = lambda do
+ render :update do |page|
+ page.replace :foo, :partial => 'partial'
+ end
+ end
end
def rescue_action(e) raise end
@@ -238,10 +242,15 @@ class RenderTest < Test::Unit::TestCase
end
def test_line_offset
- get :render_line_offset
- line = @response.body
- assert(line =~ %r{:(\d+):})
- assert_equal "1", $1
+ begin
+ get :render_line_offset
+ flunk "the action should have raised an exception"
+ rescue RuntimeError => exc
+ line = exc.backtrace.first
+ assert(line =~ %r{:(\d+):})
+ assert_equal "1", $1,
+ "The line offset is wrong, perhaps the wrong exception has been raised, exception was: #{exc.inspect}"
+ end
end
def test_render_with_forward_slash
@@ -320,7 +329,7 @@ class RenderTest < Test::Unit::TestCase
def test_render_text_with_nil
get :render_text_with_nil
assert_response 200
- assert_equal '', @response.body
+ assert_equal ' ', @response.body
end
def test_render_text_with_false
@@ -408,58 +417,6 @@ class RenderTest < Test::Unit::TestCase
assert_equal "Goodbye, Local David", @response.body
end
- def test_render_200_should_set_etag
- get :render_hello_world_from_variable
- assert_equal etag_for("hello david"), @response.headers['ETag']
- assert_equal "private, max-age=0, must-revalidate", @response.headers['Cache-Control']
- end
-
- def test_render_against_etag_request_should_304_when_match
- @request.headers["HTTP_IF_NONE_MATCH"] = etag_for("hello david")
- get :render_hello_world_from_variable
- assert_equal "304 Not Modified", @response.headers['Status']
- assert @response.body.empty?
- end
-
- def test_render_against_etag_request_should_200_when_no_match
- @request.headers["HTTP_IF_NONE_MATCH"] = etag_for("hello somewhere else")
- get :render_hello_world_from_variable
- assert_equal "200 OK", @response.headers['Status']
- assert !@response.body.empty?
- end
-
- def test_render_with_etag
- get :render_hello_world_from_variable
- expected_etag = etag_for('hello david')
- assert_equal expected_etag, @response.headers['ETag']
-
- @request.headers["HTTP_IF_NONE_MATCH"] = expected_etag
- get :render_hello_world_from_variable
- assert_equal "304 Not Modified", @response.headers['Status']
-
- @request.headers["HTTP_IF_NONE_MATCH"] = "\"diftag\""
- get :render_hello_world_from_variable
- assert_equal "200 OK", @response.headers['Status']
- end
-
- def render_with_404_shouldnt_have_etag
- get :render_custom_code
- assert_nil @response.headers['ETag']
- end
-
- def test_etag_should_not_be_changed_when_already_set
- expected_etag = etag_for("hello somewhere else")
- @response.headers["ETag"] = expected_etag
- get :render_hello_world_from_variable
- assert_equal expected_etag, @response.headers['ETag']
- end
-
- def test_etag_should_govern_renders_with_layouts_too
- get :builder_layout_test
- assert_equal "<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body
- assert_equal etag_for("<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n"), @response.headers['ETag']
- end
-
def test_should_render_formatted_template
get :formatted_html_erb
assert_equal 'formatted html erb', @response.body
@@ -476,7 +433,7 @@ class RenderTest < Test::Unit::TestCase
end
def test_should_render_formatted_html_erb_template_with_faulty_accepts_header
- @request.env["HTTP_ACCEPT"] = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*"
+ @request.accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*"
get :formatted_xml_erb
assert_equal '<test>passed formatted html erb</test>', @response.body
end
@@ -516,8 +473,107 @@ class RenderTest < Test::Unit::TestCase
assert_equal "application/atomsvc+xml", @response.content_type
end
+ def test_should_use_implicit_content_type
+ get :implicit_content_type, :format => 'atom'
+ assert_equal Mime::ATOM, @response.content_type
+ end
+end
+
+class EtagRenderTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = TestController.new
+
+ @request.host = "www.nextangle.com"
+ end
+
+ def test_render_200_should_set_etag
+ get :render_hello_world_from_variable
+ assert_equal etag_for("hello david"), @response.headers['ETag']
+ assert_equal "private, max-age=0, must-revalidate", @response.headers['Cache-Control']
+ end
+
+ def test_render_against_etag_request_should_304_when_match
+ @request.if_none_match = etag_for("hello david")
+ get :render_hello_world_from_variable
+ assert_equal "304 Not Modified", @response.status
+ assert @response.body.empty?
+ end
+
+ def test_render_against_etag_request_should_200_when_no_match
+ @request.if_none_match = etag_for("hello somewhere else")
+ get :render_hello_world_from_variable
+ assert_equal "200 OK", @response.status
+ assert !@response.body.empty?
+ end
+
+ def test_render_with_etag
+ get :render_hello_world_from_variable
+ expected_etag = etag_for('hello david')
+ assert_equal expected_etag, @response.headers['ETag']
+
+ @request.if_none_match = expected_etag
+ get :render_hello_world_from_variable
+ assert_equal "304 Not Modified", @response.status
+
+ @request.if_none_match = "\"diftag\""
+ get :render_hello_world_from_variable
+ assert_equal "200 OK", @response.status
+ end
+
+ def render_with_404_shouldnt_have_etag
+ get :render_custom_code
+ assert_nil @response.headers['ETag']
+ end
+
+ def test_etag_should_not_be_changed_when_already_set
+ expected_etag = etag_for("hello somewhere else")
+ @response.headers["ETag"] = expected_etag
+ get :render_hello_world_from_variable
+ assert_equal expected_etag, @response.headers['ETag']
+ end
+
+ def test_etag_should_govern_renders_with_layouts_too
+ get :builder_layout_test
+ assert_equal "<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body
+ assert_equal etag_for("<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n"), @response.headers['ETag']
+ end
+
protected
def etag_for(text)
%("#{Digest::MD5.hexdigest(text)}")
end
end
+
+class LastModifiedRenderTest < Test::Unit::TestCase
+ def setup
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller = TestController.new
+
+ @request.host = "www.nextangle.com"
+ @last_modified = Time.now.utc.beginning_of_day.httpdate
+ end
+
+ def test_responds_with_last_modified
+ get :conditional_hello
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
+ def test_request_not_modified
+ @request.if_modified_since = @last_modified
+ get :conditional_hello
+ assert_equal "304 Not Modified", @response.status
+ assert @response.body.blank?, @response.body
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+
+ def test_request_modified
+ @request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
+ get :conditional_hello
+ assert_equal "200 OK", @response.status
+ assert !@response.body.blank?
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+end
diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb
index 932c0e21a1..045dab4141 100644
--- a/actionpack/test/controller/request_test.rb
+++ b/actionpack/test/controller/request_test.rb
@@ -3,64 +3,69 @@ require 'action_controller/integration'
class RequestTest < Test::Unit::TestCase
def setup
+ ActionController::Base.relative_url_root = nil
@request = ActionController::TestRequest.new
end
+ def teardown
+ ActionController::Base.relative_url_root = nil
+ end
+
def test_remote_ip
assert_equal '0.0.0.0', @request.remote_ip
@request.remote_addr = '1.2.3.4'
- assert_equal '1.2.3.4', @request.remote_ip
+ assert_equal '1.2.3.4', @request.remote_ip(true)
@request.env['HTTP_CLIENT_IP'] = '2.3.4.5'
- assert_equal '1.2.3.4', @request.remote_ip
+ assert_equal '1.2.3.4', @request.remote_ip(true)
@request.remote_addr = '192.168.0.1'
- assert_equal '2.3.4.5', @request.remote_ip
+ assert_equal '2.3.4.5', @request.remote_ip(true)
@request.env.delete 'HTTP_CLIENT_IP'
@request.remote_addr = '1.2.3.4'
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
- assert_equal '1.2.3.4', @request.remote_ip
+ assert_equal '1.2.3.4', @request.remote_ip(true)
@request.remote_addr = '127.0.0.1'
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
- assert_equal '3.4.5.6', @request.remote_ip
+ assert_equal '3.4.5.6', @request.remote_ip(true)
@request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,3.4.5.6'
- assert_equal '3.4.5.6', @request.remote_ip
+ assert_equal '3.4.5.6', @request.remote_ip(true)
@request.env['HTTP_X_FORWARDED_FOR'] = '172.16.0.1,3.4.5.6'
- assert_equal '3.4.5.6', @request.remote_ip
+ assert_equal '3.4.5.6', @request.remote_ip(true)
@request.env['HTTP_X_FORWARDED_FOR'] = '192.168.0.1,3.4.5.6'
- assert_equal '3.4.5.6', @request.remote_ip
+ assert_equal '3.4.5.6', @request.remote_ip(true)
@request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1,3.4.5.6'
- assert_equal '3.4.5.6', @request.remote_ip
-
+ assert_equal '3.4.5.6', @request.remote_ip(true)
+
@request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1, 10.0.0.1, 3.4.5.6'
- assert_equal '3.4.5.6', @request.remote_ip
+ assert_equal '3.4.5.6', @request.remote_ip(true)
@request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6'
- assert_equal '3.4.5.6', @request.remote_ip
+ assert_equal '3.4.5.6', @request.remote_ip(true)
@request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1'
- assert_equal 'unknown', @request.remote_ip
+ assert_equal 'unknown', @request.remote_ip(true)
@request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
- assert_equal '3.4.5.6', @request.remote_ip
+ assert_equal '3.4.5.6', @request.remote_ip(true)
@request.env['HTTP_CLIENT_IP'] = '8.8.8.8'
e = assert_raises(ActionController::ActionControllerError) {
- @request.remote_ip
+ @request.remote_ip(true)
}
assert_match /IP spoofing attack/, e.message
assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message
assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message
@request.env['HTTP_X_FORWARDED_FOR'] = '8.8.8.8, 9.9.9.9'
- assert_equal '8.8.8.8', @request.remote_ip
+ assert_equal '8.8.8.8', @request.remote_ip(true)
@request.env.delete 'HTTP_CLIENT_IP'
@request.env.delete 'HTTP_X_FORWARDED_FOR'
@@ -120,169 +125,118 @@ class RequestTest < Test::Unit::TestCase
assert_equal ":8080", @request.port_string
end
- def test_relative_url_root
- @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
- @request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
- assert_equal '', @request.relative_url_root, "relative_url_root should be disabled on lighttpd"
-
- @request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text'
-
- @request.env['SCRIPT_NAME'] = nil
- assert_equal "", @request.relative_url_root
-
- @request.env['SCRIPT_NAME'] = "/dispatch.cgi"
- assert_equal "", @request.relative_url_root
-
- @request.env['SCRIPT_NAME'] = "/myapp.rb"
- assert_equal "", @request.relative_url_root
-
- @request.relative_url_root = nil
- @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
- assert_equal "/hieraki", @request.relative_url_root
-
- @request.relative_url_root = nil
- @request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
- assert_equal "/collaboration/hieraki", @request.relative_url_root
-
- # apache/scgi case
- @request.relative_url_root = nil
- @request.env['SCRIPT_NAME'] = "/collaboration/hieraki"
- assert_equal "/collaboration/hieraki", @request.relative_url_root
-
- @request.relative_url_root = nil
- @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
- @request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
- @request.env['RAILS_RELATIVE_URL_ROOT'] = "/hieraki"
- assert_equal "/hieraki", @request.relative_url_root
-
- # @env overrides path guess
- @request.relative_url_root = nil
- @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
- @request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text'
- @request.env['RAILS_RELATIVE_URL_ROOT'] = "/real_url"
- assert_equal "/real_url", @request.relative_url_root
- end
-
def test_request_uri
@request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432'
- @request.relative_url_root = nil
@request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1"
assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
assert_equal "/path/of/some/uri", @request.path
- @request.relative_url_root = nil
@request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri"
assert_equal "/path/of/some/uri", @request.request_uri
assert_equal "/path/of/some/uri", @request.path
- @request.relative_url_root = nil
@request.set_REQUEST_URI "/path/of/some/uri"
assert_equal "/path/of/some/uri", @request.request_uri
assert_equal "/path/of/some/uri", @request.path
- @request.relative_url_root = nil
@request.set_REQUEST_URI "/"
assert_equal "/", @request.request_uri
assert_equal "/", @request.path
- @request.relative_url_root = nil
@request.set_REQUEST_URI "/?m=b"
assert_equal "/?m=b", @request.request_uri
assert_equal "/", @request.path
- @request.relative_url_root = nil
@request.set_REQUEST_URI "/"
@request.env['SCRIPT_NAME'] = "/dispatch.cgi"
assert_equal "/", @request.request_uri
assert_equal "/", @request.path
- @request.relative_url_root = nil
+ ActionController::Base.relative_url_root = "/hieraki"
@request.set_REQUEST_URI "/hieraki/"
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
assert_equal "/hieraki/", @request.request_uri
assert_equal "/", @request.path
+ ActionController::Base.relative_url_root = nil
- @request.relative_url_root = nil
+ ActionController::Base.relative_url_root = "/collaboration/hieraki"
@request.set_REQUEST_URI "/collaboration/hieraki/books/edit/2"
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
assert_equal "/collaboration/hieraki/books/edit/2", @request.request_uri
assert_equal "/books/edit/2", @request.path
+ ActionController::Base.relative_url_root = nil
# The following tests are for when REQUEST_URI is not supplied (as in IIS)
- @request.relative_url_root = nil
- @request.set_REQUEST_URI nil
@request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
@request.env['SCRIPT_NAME'] = nil #"/path/dispatch.rb"
+ @request.set_REQUEST_URI nil
assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
assert_equal "/path/of/some/uri", @request.path
- @request.set_REQUEST_URI nil
- @request.relative_url_root = nil
+ ActionController::Base.relative_url_root = '/path'
@request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1"
@request.env['SCRIPT_NAME'] = "/path/dispatch.rb"
- assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
- assert_equal "/of/some/uri", @request.path
-
@request.set_REQUEST_URI nil
- @request.relative_url_root = nil
+ assert_equal "/path/of/some/uri?mapped=1", @request.request_uri(true)
+ assert_equal "/of/some/uri", @request.path(true)
+ ActionController::Base.relative_url_root = nil
+
@request.env['PATH_INFO'] = "/path/of/some/uri"
@request.env['SCRIPT_NAME'] = nil
+ @request.set_REQUEST_URI nil
assert_equal "/path/of/some/uri", @request.request_uri
assert_equal "/path/of/some/uri", @request.path
- @request.set_REQUEST_URI nil
- @request.relative_url_root = nil
@request.env['PATH_INFO'] = "/"
+ @request.set_REQUEST_URI nil
assert_equal "/", @request.request_uri
assert_equal "/", @request.path
- @request.set_REQUEST_URI nil
- @request.relative_url_root = nil
@request.env['PATH_INFO'] = "/?m=b"
+ @request.set_REQUEST_URI nil
assert_equal "/?m=b", @request.request_uri
assert_equal "/", @request.path
- @request.set_REQUEST_URI nil
- @request.relative_url_root = nil
@request.env['PATH_INFO'] = "/"
@request.env['SCRIPT_NAME'] = "/dispatch.cgi"
+ @request.set_REQUEST_URI nil
assert_equal "/", @request.request_uri
assert_equal "/", @request.path
- @request.set_REQUEST_URI nil
- @request.relative_url_root = nil
+ ActionController::Base.relative_url_root = '/hieraki'
@request.env['PATH_INFO'] = "/hieraki/"
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
+ @request.set_REQUEST_URI nil
assert_equal "/hieraki/", @request.request_uri
assert_equal "/", @request.path
+ ActionController::Base.relative_url_root = nil
@request.set_REQUEST_URI '/hieraki/dispatch.cgi'
- @request.relative_url_root = '/hieraki'
- assert_equal "/dispatch.cgi", @request.path
- @request.relative_url_root = nil
+ ActionController::Base.relative_url_root = '/hieraki'
+ assert_equal "/dispatch.cgi", @request.path(true)
+ ActionController::Base.relative_url_root = nil
@request.set_REQUEST_URI '/hieraki/dispatch.cgi'
- @request.relative_url_root = '/foo'
- assert_equal "/hieraki/dispatch.cgi", @request.path
- @request.relative_url_root = nil
+ ActionController::Base.relative_url_root = '/foo'
+ assert_equal "/hieraki/dispatch.cgi", @request.path(true)
+ ActionController::Base.relative_url_root = nil
# This test ensures that Rails uses REQUEST_URI over PATH_INFO
- @request.relative_url_root = nil
+ ActionController::Base.relative_url_root = nil
@request.env['REQUEST_URI'] = "/some/path"
@request.env['PATH_INFO'] = "/another/path"
@request.env['SCRIPT_NAME'] = "/dispatch.cgi"
- assert_equal "/some/path", @request.request_uri
- assert_equal "/some/path", @request.path
+ assert_equal "/some/path", @request.request_uri(true)
+ assert_equal "/some/path", @request.path(true)
end
-
def test_host_with_default_port
@request.host = "rubyonrails.org"
@request.port = 80
assert_equal "rubyonrails.org", @request.host_with_port
end
-
+
def test_host_with_non_default_port
@request.host = "rubyonrails.org"
@request.port = 81
@@ -290,13 +244,13 @@ class RequestTest < Test::Unit::TestCase
end
def test_server_software
- assert_equal nil, @request.server_software
+ assert_equal nil, @request.server_software(true)
@request.env['SERVER_SOFTWARE'] = 'Apache3.422'
- assert_equal 'apache', @request.server_software
+ assert_equal 'apache', @request.server_software(true)
@request.env['SERVER_SOFTWARE'] = 'lighttpd(1.1.4)'
- assert_equal 'lighttpd', @request.server_software
+ assert_equal 'lighttpd', @request.server_software(true)
end
def test_xml_http_request
@@ -326,44 +280,44 @@ class RequestTest < Test::Unit::TestCase
def test_symbolized_request_methods
[:get, :post, :put, :delete].each do |method|
- set_request_method_to method
+ self.request_method = method
assert_equal method, @request.method
end
end
def test_invalid_http_method_raises_exception
- set_request_method_to :random_method
assert_raises(ActionController::UnknownHttpMethod) do
- @request.method
+ self.request_method = :random_method
end
end
def test_allow_method_hacking_on_post
- set_request_method_to :post
+ self.request_method = :post
[:get, :head, :options, :put, :post, :delete].each do |method|
- @request.instance_eval { @parameters = { :_method => method } ; @request_method = nil }
+ @request.instance_eval { @parameters = { :_method => method.to_s } ; @request_method = nil }
+ @request.request_method(true)
assert_equal(method == :head ? :get : method, @request.method)
end
end
def test_invalid_method_hacking_on_post_raises_exception
- set_request_method_to :post
+ self.request_method = :post
@request.instance_eval { @parameters = { :_method => :random_method } ; @request_method = nil }
assert_raises(ActionController::UnknownHttpMethod) do
- @request.method
+ @request.request_method(true)
end
end
def test_restrict_method_hacking
@request.instance_eval { @parameters = { :_method => 'put' } }
[:get, :put, :delete].each do |method|
- set_request_method_to method
+ self.request_method = method
assert_equal method, @request.method
end
end
- def test_head_masquarading_as_get
- set_request_method_to :head
+ def test_head_masquerading_as_get
+ self.request_method = :head
assert_equal :get, @request.method
assert @request.get?
assert @request.head?
@@ -385,9 +339,16 @@ class RequestTest < Test::Unit::TestCase
end
def test_nil_format
- @request.instance_eval { @parameters = { :format => nil } }
+ ActionController::Base.use_accept_header, old =
+ false, ActionController::Base.use_accept_header
+
+ @request.instance_eval { @parameters = {} }
@request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
+ assert @request.xhr?
assert_equal Mime::JS, @request.format
+
+ ensure
+ ActionController::Base.use_accept_header = old
end
def test_content_type
@@ -415,28 +376,27 @@ class RequestTest < Test::Unit::TestCase
@request.env["CONTENT_TYPE"] = "application/xml; charset=UTF-8"
assert_equal Mime::XML, @request.content_type
end
-
+
def test_user_agent
assert_not_nil @request.user_agent
end
-
+
def test_parameters
@request.instance_eval { @request_parameters = { "foo" => 1 } }
@request.instance_eval { @query_parameters = { "bar" => 2 } }
-
+
assert_equal({"foo" => 1, "bar" => 2}, @request.parameters)
assert_equal({"foo" => 1}, @request.request_parameters)
assert_equal({"bar" => 2}, @request.query_parameters)
end
protected
- def set_request_method_to(method)
+ def request_method=(method)
@request.env['REQUEST_METHOD'] = method.to_s.upcase
- @request.instance_eval { @request_method = nil }
+ @request.request_method(true)
end
end
-
class UrlEncodedRequestParameterParsingTest < Test::Unit::TestCase
def setup
@query_string = "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1"
@@ -548,7 +508,6 @@ class UrlEncodedRequestParameterParsingTest < Test::Unit::TestCase
)
end
-
def test_request_hash_parsing
query = {
"note[viewers][viewer][][type]" => ["User", "Group"],
@@ -560,7 +519,6 @@ class UrlEncodedRequestParameterParsingTest < Test::Unit::TestCase
assert_equal(expected, ActionController::AbstractRequest.parse_request_parameters(query))
end
-
def test_parse_params
input = {
"customers[boston][first][name]" => [ "David" ],
@@ -743,7 +701,6 @@ class UrlEncodedRequestParameterParsingTest < Test::Unit::TestCase
end
end
-
class MultipartRequestParameterParsingTest < Test::Unit::TestCase
FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart'
@@ -774,19 +731,19 @@ class MultipartRequestParameterParsingTest < Test::Unit::TestCase
file = params['file']
foo = params['foo']
-
+
if RUBY_VERSION > '1.9'
assert_kind_of File, file
else
assert_kind_of Tempfile, file
end
-
+
assert_equal 'file.txt', file.original_filename
assert_equal "text/plain", file.content_type
-
+
assert_equal 'bar', foo
end
-
+
def test_large_text_file
params = process('large_text_file')
assert_equal %w(file foo), params.keys.sort
@@ -891,20 +848,20 @@ class XmlParamsParsingTest < Test::Unit::TestCase
private
def parse_body(body)
- env = { 'CONTENT_TYPE' => 'application/xml',
+ env = { 'rack.input' => StringIO.new(body),
+ 'CONTENT_TYPE' => 'application/xml',
'CONTENT_LENGTH' => body.size.to_s }
- cgi = ActionController::Integration::Session::StubCGI.new(env, body)
- ActionController::CgiRequest.new(cgi).request_parameters
+ ActionController::RackRequest.new(env).request_parameters
end
end
class LegacyXmlParamsParsingTest < XmlParamsParsingTest
private
def parse_body(body)
- env = { 'HTTP_X_POST_DATA_FORMAT' => 'xml',
- 'CONTENT_LENGTH' => body.size.to_s }
- cgi = ActionController::Integration::Session::StubCGI.new(env, body)
- ActionController::CgiRequest.new(cgi).request_parameters
+ env = { 'rack.input' => StringIO.new(body),
+ 'HTTP_X_POST_DATA_FORMAT' => 'xml',
+ 'CONTENT_LENGTH' => body.size.to_s }
+ ActionController::RackRequest.new(env).request_parameters
end
end
@@ -923,9 +880,9 @@ class JsonParamsParsingTest < Test::Unit::TestCase
private
def parse_body(body,content_type)
- env = { 'CONTENT_TYPE' => content_type,
+ env = { 'rack.input' => StringIO.new(body),
+ 'CONTENT_TYPE' => content_type,
'CONTENT_LENGTH' => body.size.to_s }
- cgi = ActionController::Integration::Session::StubCGI.new(env, body)
- ActionController::CgiRequest.new(cgi).request_parameters
+ ActionController::RackRequest.new(env).request_parameters
end
end
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index 27fcc5e04c..da076d2090 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -62,6 +62,11 @@ class RescueController < ActionController::Base
render :text => exception.message
end
+ # This is a Dispatcher exception and should be in ApplicationController.
+ rescue_from ActionController::RoutingError do
+ render :text => 'no way'
+ end
+
def raises
render :text => 'already rendered'
raise "don't panic!"
@@ -378,6 +383,10 @@ class RescueTest < Test::Unit::TestCase
assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body
end
+ def test_rescue_dispatcher_exceptions
+ RescueController.process_with_exception(@request, @response, ActionController::RoutingError.new("Route not found"))
+ assert_equal "no way", @response.body
+ end
protected
def with_all_requests_local(local = true)
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 0f7924649a..e153b0cc98 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -516,6 +516,26 @@ class ResourcesTest < Test::Unit::TestCase
end
end
+ def test_should_not_allow_invalid_head_method_for_member_routes
+ with_routing do |set|
+ set.draw do |map|
+ assert_raises(ArgumentError) do
+ map.resources :messages, :member => {:something => :head}
+ end
+ end
+ end
+ end
+
+ def test_should_not_allow_invalid_http_methods_for_member_routes
+ with_routing do |set|
+ set.draw do |map|
+ assert_raises(ArgumentError) do
+ map.resources :messages, :member => {:something => :invalid}
+ end
+ end
+ end
+ end
+
def test_resource_action_separator
with_routing do |set|
set.draw do |map|
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index c5ccb71582..6cf134c26f 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -67,66 +67,56 @@ class SegmentTest < Test::Unit::TestCase
end
def test_interpolation_statement
- s = ROUTING::StaticSegment.new
- s.value = "Hello"
+ s = ROUTING::StaticSegment.new("Hello")
assert_equal "Hello", eval(s.interpolation_statement([]))
assert_equal "HelloHello", eval(s.interpolation_statement([s]))
- s2 = ROUTING::StaticSegment.new
- s2.value = "-"
+ s2 = ROUTING::StaticSegment.new("-")
assert_equal "Hello-Hello", eval(s.interpolation_statement([s, s2]))
- s3 = ROUTING::StaticSegment.new
- s3.value = "World"
+ s3 = ROUTING::StaticSegment.new("World")
assert_equal "Hello-World", eval(s3.interpolation_statement([s, s2]))
end
end
class StaticSegmentTest < Test::Unit::TestCase
def test_interpolation_chunk_should_respect_raw
- s = ROUTING::StaticSegment.new
- s.value = 'Hello World'
- assert ! s.raw?
+ s = ROUTING::StaticSegment.new('Hello World')
+ assert !s.raw?
assert_equal 'Hello%20World', s.interpolation_chunk
- s.raw = true
+ s = ROUTING::StaticSegment.new('Hello World', :raw => true)
assert s.raw?
assert_equal 'Hello World', s.interpolation_chunk
end
def test_regexp_chunk_should_escape_specials
- s = ROUTING::StaticSegment.new
-
- s.value = 'Hello*World'
+ s = ROUTING::StaticSegment.new('Hello*World')
assert_equal 'Hello\*World', s.regexp_chunk
- s.value = 'HelloWorld'
+ s = ROUTING::StaticSegment.new('HelloWorld')
assert_equal 'HelloWorld', s.regexp_chunk
end
def test_regexp_chunk_should_add_question_mark_for_optionals
- s = ROUTING::StaticSegment.new
- s.value = "/"
- s.is_optional = true
+ s = ROUTING::StaticSegment.new("/", :optional => true)
assert_equal "/?", s.regexp_chunk
- s.value = "hello"
+ s = ROUTING::StaticSegment.new("hello", :optional => true)
assert_equal "(?:hello)?", s.regexp_chunk
end
end
class DynamicSegmentTest < Test::Unit::TestCase
- def segment
+ def segment(options = {})
unless @segment
- @segment = ROUTING::DynamicSegment.new
- @segment.key = :a
+ @segment = ROUTING::DynamicSegment.new(:a, options)
end
@segment
end
def test_extract_value
- s = ROUTING::DynamicSegment.new
- s.key = :a
+ s = ROUTING::DynamicSegment.new(:a)
hash = {:a => '10', :b => '20'}
assert_equal '10', eval(s.extract_value)
@@ -149,31 +139,31 @@ class DynamicSegmentTest < Test::Unit::TestCase
end
def test_regexp_value_check_rejects_nil
- segment.regexp = /\d+/
+ segment = segment(:regexp => /\d+/)
+
a_value = nil
- assert ! eval(segment.value_check)
+ assert !eval(segment.value_check)
end
def test_optional_regexp_value_check_should_accept_nil
- segment.regexp = /\d+/
- segment.is_optional = true
+ segment = segment(:regexp => /\d+/, :optional => true)
+
a_value = nil
assert eval(segment.value_check)
end
def test_regexp_value_check_rejects_no_match
- segment.regexp = /\d+/
+ segment = segment(:regexp => /\d+/)
a_value = "Hello20World"
- assert ! eval(segment.value_check)
+ assert !eval(segment.value_check)
a_value = "20Hi"
- assert ! eval(segment.value_check)
+ assert !eval(segment.value_check)
end
def test_regexp_value_check_accepts_match
- segment.regexp = /\d+/
-
+ segment = segment(:regexp => /\d+/)
a_value = "30"
assert eval(segment.value_check)
end
@@ -184,14 +174,14 @@ class DynamicSegmentTest < Test::Unit::TestCase
end
def test_optional_value_needs_no_check
- segment.is_optional = true
+ segment = segment(:optional => true)
+
a_value = nil
assert_equal nil, segment.value_check
end
def test_regexp_value_check_should_accept_match_with_default
- segment.regexp = /\d+/
- segment.default = '200'
+ segment = segment(:regexp => /\d+/, :default => '200')
a_value = '100'
assert eval(segment.value_check)
@@ -234,7 +224,7 @@ class DynamicSegmentTest < Test::Unit::TestCase
end
def test_extraction_code_should_return_on_mismatch
- segment.regexp = /\d+/
+ segment = segment(:regexp => /\d+/)
hash = merged = {:a => 'Hi', :b => '3'}
options = {:b => '3'}
a_value = nil
@@ -292,7 +282,7 @@ class DynamicSegmentTest < Test::Unit::TestCase
end
def test_value_regexp_should_match_exacly
- segment.regexp = /\d+/
+ segment = segment(:regexp => /\d+/)
assert_no_match segment.value_regexp, "Hello 10 World"
assert_no_match segment.value_regexp, "Hello 10"
assert_no_match segment.value_regexp, "10 World"
@@ -300,40 +290,36 @@ class DynamicSegmentTest < Test::Unit::TestCase
end
def test_regexp_chunk_should_return_string
- segment.regexp = /\d+/
+ segment = segment(:regexp => /\d+/)
assert_kind_of String, segment.regexp_chunk
end
def test_build_pattern_non_optional_with_no_captures
# Non optional
- a_segment = ROUTING::DynamicSegment.new
- a_segment.regexp = /\d+/ #number_of_captures is 0
+ a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /\d+/)
assert_equal "(\\d+)stuff", a_segment.build_pattern('stuff')
end
def test_build_pattern_non_optional_with_captures
# Non optional
- a_segment = ROUTING::DynamicSegment.new
- a_segment.regexp = /(\d+)(.*?)/ #number_of_captures is 2
+ a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /(\d+)(.*?)/)
assert_equal "((\\d+)(.*?))stuff", a_segment.build_pattern('stuff')
end
def test_optionality_implied
- a_segment = ROUTING::DynamicSegment.new
- a_segment.key = :id
+ a_segment = ROUTING::DynamicSegment.new(:id)
assert a_segment.optionality_implied?
- a_segment.key = :action
+ a_segment = ROUTING::DynamicSegment.new(:action)
assert a_segment.optionality_implied?
end
def test_modifiers_must_be_handled_sensibly
- a_segment = ROUTING::DynamicSegment.new
- a_segment.regexp = /david|jamis/i
+ a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/i)
assert_equal "((?i-mx:david|jamis))stuff", a_segment.build_pattern('stuff')
- a_segment.regexp = /david|jamis/x
+ a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/x)
assert_equal "((?x-mi:david|jamis))stuff", a_segment.build_pattern('stuff')
- a_segment.regexp = /david|jamis/
+ a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/)
assert_equal "(david|jamis)stuff", a_segment.build_pattern('stuff')
end
end
@@ -560,7 +546,7 @@ class RouteBuilderTest < Test::Unit::TestCase
action = segments[-4]
assert_equal :action, action.key
- action.regexp = /show|in/ # Use 'in' to check partial matches
+ segments[-4] = ROUTING::DynamicSegment.new(:action, :regexp => /show|in/)
builder.assign_default_route_options(segments)
@@ -661,10 +647,10 @@ class RoutingTest < Test::Unit::TestCase
ActionController::Routing.controller_paths = []
assert_equal [], ActionController::Routing.possible_controllers
- ActionController::Routing::Routes.load!
ActionController::Routing.controller_paths = [
root, root + '/app/controllers', root + '/vendor/plugins/bad_plugin/lib'
]
+ ActionController::Routing::Routes.load!
assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort
ensure
@@ -731,15 +717,10 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def request
@request ||= MockRequest.new(:host => "named.route.test", :method => :get)
end
-
- def relative_url_root=(value)
- request.relative_url_root=value
- end
end
class MockRequest
- attr_accessor :path, :path_parameters, :host, :subdomains, :domain,
- :method, :relative_url_root
+ attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method
def initialize(values={})
values.each { |key, value| send("#{key}=", value) }
@@ -839,6 +820,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
puts "#{1 / per_url} url/s\n\n"
end
end
+
def test_time_generation
n = 5000
if RunTimeTests
@@ -920,10 +902,11 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def test_basic_named_route_with_relative_url_root
rs.add_named_route :home, '', :controller => 'content', :action => 'list'
x = setup_for_named_route
- x.relative_url_root="/foo"
+ ActionController::Base.relative_url_root = "/foo"
assert_equal("http://named.route.test/foo/",
x.send(:home_url))
assert_equal "/foo/", x.send(:home_path)
+ ActionController::Base.relative_url_root = nil
end
def test_named_route_with_option
@@ -1377,34 +1360,20 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
def slash_segment(is_optional = false)
- returning ROUTING::DividerSegment.new('/') do |s|
- s.is_optional = is_optional
- end
+ ROUTING::DividerSegment.new('/', :optional => is_optional)
end
def default_route
unless defined?(@default_route)
- @default_route = ROUTING::Route.new
-
- @default_route.segments << (s = ROUTING::StaticSegment.new)
- s.value = '/'
- s.raw = true
-
- @default_route.segments << (s = ROUTING::DynamicSegment.new)
- s.key = :controller
-
- @default_route.segments << slash_segment(:optional)
- @default_route.segments << (s = ROUTING::DynamicSegment.new)
- s.key = :action
- s.default = 'index'
- s.is_optional = true
-
- @default_route.segments << slash_segment(:optional)
- @default_route.segments << (s = ROUTING::DynamicSegment.new)
- s.key = :id
- s.is_optional = true
-
- @default_route.segments << slash_segment(:optional)
+ segments = []
+ segments << ROUTING::StaticSegment.new('/', :raw => true)
+ segments << ROUTING::DynamicSegment.new(:controller)
+ segments << slash_segment(:optional)
+ segments << ROUTING::DynamicSegment.new(:action, :default => 'index', :optional => true)
+ segments << slash_segment(:optional)
+ segments << ROUTING::DynamicSegment.new(:id, :optional => true)
+ segments << slash_segment(:optional)
+ @default_route = ROUTING::Route.new(segments).freeze
end
@default_route
end
@@ -1492,29 +1461,16 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
def test_significant_keys
- user_url = ROUTING::Route.new
- user_url.segments << (s = ROUTING::StaticSegment.new)
- s.value = '/'
- s.raw = true
-
- user_url.segments << (s = ROUTING::StaticSegment.new)
- s.value = 'user'
-
- user_url.segments << (s = ROUTING::StaticSegment.new)
- s.value = '/'
- s.raw = true
- s.is_optional = true
-
- user_url.segments << (s = ROUTING::DynamicSegment.new)
- s.key = :user
-
- user_url.segments << (s = ROUTING::StaticSegment.new)
- s.value = '/'
- s.raw = true
- s.is_optional = true
+ segments = []
+ segments << ROUTING::StaticSegment.new('/', :raw => true)
+ segments << ROUTING::StaticSegment.new('user')
+ segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true)
+ segments << ROUTING::DynamicSegment.new(:user)
+ segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true)
- user_url.requirements = {:controller => 'users', :action => 'show'}
+ requirements = {:controller => 'users', :action => 'show'}
+ user_url = ROUTING::Route.new(segments, requirements)
keys = user_url.significant_keys.sort_by { |k| k.to_s }
assert_equal [:action, :controller, :user], keys
end
@@ -1801,6 +1757,22 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
end
+ def test_route_requirements_with_invalid_http_method_is_invalid
+ assert_raises ArgumentError do
+ set.draw do |map|
+ map.connect 'valid/route', :controller => 'pages', :action => 'show', :conditions => {:method => :invalid}
+ end
+ end
+ end
+
+ def test_route_requirements_with_head_method_condition_is_invalid
+ assert_raises ArgumentError do
+ set.draw do |map|
+ map.connect 'valid/route', :controller => 'pages', :action => 'show', :conditions => {:method => :head}
+ end
+ end
+ end
+
def test_non_path_route_requirements_match_all
set.draw do |map|
map.connect 'page/37s', :controller => 'pages', :action => 'show', :name => /(jamis|david)/
diff --git a/actionpack/test/controller/session/cookie_store_test.rb b/actionpack/test/controller/session/cookie_store_test.rb
index 5adaeaf5c5..5adaeaf5c5 100755..100644
--- a/actionpack/test/controller/session/cookie_store_test.rb
+++ b/actionpack/test/controller/session/cookie_store_test.rb
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index b624005a57..58d9ca537f 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -64,7 +64,7 @@ class TestTest < Test::Unit::TestCase
</html>
HTML
end
-
+
def test_xml_output
response.content_type = "application/xml"
render :text => <<XML
@@ -117,8 +117,8 @@ XML
@controller = TestController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
- ActionController::Routing::Routes.reload
ActionController::Routing.use_controllers! %w(content admin/user test_test/test)
+ ActionController::Routing::Routes.load_routes!
end
def teardown
@@ -366,7 +366,7 @@ XML
:children => { :count => 1,
:only => { :tag => "img" } } } }
end
-
+
def test_should_not_impose_childless_html_tags_in_xml
process :test_xml_output
@@ -412,7 +412,7 @@ XML
def test_assert_routing_with_method
with_routing do |set|
- set.draw { |map| map.resources(:content) }
+ set.draw { |map| map.resources(:content) }
assert_routing({ :method => 'post', :path => 'content' }, { :controller => 'content', :action => 'create' })
end
end
@@ -486,7 +486,7 @@ XML
assert_nil @request.env['HTTP_X_REQUESTED_WITH']
end
- def test_header_properly_reset_after_get_request
+ def test_header_properly_reset_after_get_request
get :test_params
@request.recycle!
assert_nil @request.instance_variable_get("@request_method")
@@ -532,15 +532,15 @@ XML
assert_equal file.path, file.local_path
assert_equal expected, file.read
end
-
+
def test_test_uploaded_file_with_binary
filename = 'mona_lisa.jpg'
path = "#{FILES_DIR}/#{filename}"
content_type = 'image/png'
-
+
binary_uploaded_file = ActionController::TestUploadedFile.new(path, content_type, :binary)
assert_equal File.open(path, READ_BINARY).read, binary_uploaded_file.read
-
+
plain_uploaded_file = ActionController::TestUploadedFile.new(path, content_type)
assert_equal File.open(path, READ_PLAIN).read, plain_uploaded_file.read
end
@@ -549,10 +549,10 @@ XML
filename = 'mona_lisa.jpg'
path = "#{FILES_DIR}/#{filename}"
content_type = 'image/jpg'
-
+
binary_file_upload = fixture_file_upload(path, content_type, :binary)
assert_equal File.open(path, READ_BINARY).read, binary_file_upload.read
-
+
plain_file_upload = fixture_file_upload(path, content_type)
assert_equal File.open(path, READ_PLAIN).read, plain_file_upload.read
end
@@ -584,7 +584,7 @@ XML
get :test_send_file
assert_nothing_raised(NoMethodError) { @response.binary_content }
end
-
+
protected
def with_foo_routing
with_routing do |set|
@@ -597,7 +597,6 @@ XML
end
end
-
class CleanBacktraceTest < Test::Unit::TestCase
def test_should_reraise_the_same_object
exception = Test::Unit::AssertionFailedError.new('message')
@@ -658,7 +657,7 @@ end
class NamedRoutesControllerTest < ActionController::TestCase
tests ContentController
-
+
def test_should_be_able_to_use_named_routes_before_a_request_is_done
with_routing do |set|
set.draw { |map| map.resources :contents }
diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb
index a9974db5d5..64e9a085ca 100644
--- a/actionpack/test/controller/url_rewriter_test.rb
+++ b/actionpack/test/controller/url_rewriter_test.rb
@@ -7,7 +7,7 @@ class UrlRewriterTests < Test::Unit::TestCase
@request = ActionController::TestRequest.new
@params = {}
@rewriter = ActionController::UrlRewriter.new(@request, @params)
- end
+ end
def test_port
assert_equal('http://test.host:1271/c/a/i',
@@ -24,7 +24,7 @@ class UrlRewriterTests < Test::Unit::TestCase
@rewriter.rewrite(:protocol => 'https://', :controller => 'c', :action => 'a', :id => 'i')
)
end
-
+
def test_user_name_and_password
assert_equal(
'http://david:secret@test.host/c/a/i',
@@ -38,12 +38,12 @@ class UrlRewriterTests < Test::Unit::TestCase
@rewriter.rewrite(:user => "openid.aol.com/nextangler", :password => "one two?", :controller => 'c', :action => 'a', :id => 'i')
)
end
-
- def test_anchor
- assert_equal(
- 'http://test.host/c/a/i#anchor',
- @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor')
- )
+
+ def test_anchor
+ assert_equal(
+ 'http://test.host/c/a/i#anchor',
+ @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor')
+ )
end
def test_overwrite_params
@@ -55,12 +55,12 @@ class UrlRewriterTests < Test::Unit::TestCase
u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'})
assert_match %r(/hi/hi/2$), u
end
-
+
def test_overwrite_removes_original
@params[:controller] = 'search'
@params[:action] = 'list'
@params[:list_page] = 1
-
+
assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2})
u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2})
assert_equal 'http://test.host/search/list?list_page=2', u
@@ -86,19 +86,19 @@ class UrlRewriterTests < Test::Unit::TestCase
end
class UrlWriterTests < Test::Unit::TestCase
-
+
class W
include ActionController::UrlWriter
end
-
+
def teardown
W.default_url_options.clear
end
-
+
def add_host!
W.default_url_options[:host] = 'www.basecamphq.com'
end
-
+
def test_exception_is_thrown_without_host
assert_raises RuntimeError do
W.new.url_for :controller => 'c', :action => 'a', :id => 'i'
@@ -110,35 +110,35 @@ class UrlWriterTests < Test::Unit::TestCase
W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => 'anchor')
)
end
-
+
def test_default_host
add_host!
assert_equal('http://www.basecamphq.com/c/a/i',
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i')
)
end
-
+
def test_host_may_be_overridden
add_host!
assert_equal('http://37signals.basecamphq.com/c/a/i',
W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i')
)
end
-
+
def test_port
add_host!
assert_equal('http://www.basecamphq.com:3000/c/a/i',
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000)
)
end
-
+
def test_protocol
add_host!
assert_equal('https://www.basecamphq.com/c/a/i',
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
)
end
-
+
def test_protocol_with_and_without_separator
add_host!
assert_equal('https://www.basecamphq.com/c/a/i',
@@ -184,15 +184,15 @@ class UrlWriterTests < Test::Unit::TestCase
end
def test_relative_url_root_is_respected
- orig_relative_url_root = ActionController::AbstractRequest.relative_url_root
- ActionController::AbstractRequest.relative_url_root = '/subdir'
+ orig_relative_url_root = ActionController::Base.relative_url_root
+ ActionController::Base.relative_url_root = '/subdir'
add_host!
assert_equal('https://www.basecamphq.com/subdir/c/a/i',
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
)
ensure
- ActionController::AbstractRequest.relative_url_root = orig_relative_url_root
+ ActionController::Base.relative_url_root = orig_relative_url_root
end
def test_named_routes
@@ -217,8 +217,8 @@ class UrlWriterTests < Test::Unit::TestCase
end
def test_relative_url_root_is_respected_for_named_routes
- orig_relative_url_root = ActionController::AbstractRequest.relative_url_root
- ActionController::AbstractRequest.relative_url_root = '/subdir'
+ orig_relative_url_root = ActionController::Base.relative_url_root
+ ActionController::Base.relative_url_root = '/subdir'
ActionController::Routing::Routes.draw do |map|
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
@@ -231,7 +231,7 @@ class UrlWriterTests < Test::Unit::TestCase
controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
ensure
ActionController::Routing::Routes.load!
- ActionController::AbstractRequest.relative_url_root = orig_relative_url_root
+ ActionController::Base.relative_url_root = orig_relative_url_root
end
def test_only_path
@@ -239,14 +239,14 @@ class UrlWriterTests < Test::Unit::TestCase
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
map.connect ':controller/:action/:id'
end
-
+
# We need to create a new class in order to install the new named route.
kls = Class.new { include ActionController::UrlWriter }
controller = kls.new
assert controller.respond_to?(:home_url)
assert_equal '/brave/new/world',
controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true)
-
+
assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true))
assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama'))
ensure
@@ -306,5 +306,4 @@ class UrlWriterTests < Test::Unit::TestCase
def extract_params(url)
url.split('?', 2).last.split('&')
end
-
end
diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb
index 85fa58a45b..b859a92cbd 100644
--- a/actionpack/test/controller/view_paths_test.rb
+++ b/actionpack/test/controller/view_paths_test.rb
@@ -54,10 +54,7 @@ class ViewLoadPathsTest < Test::Unit::TestCase
assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths
@controller.append_view_path(FIXTURE_LOAD_PATH)
- assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
-
- @controller.append_view_path([FIXTURE_LOAD_PATH])
- assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
+ assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
end
def test_controller_prepends_view_path_correctly
@@ -68,10 +65,7 @@ class ViewLoadPathsTest < Test::Unit::TestCase
assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
@controller.prepend_view_path(FIXTURE_LOAD_PATH)
- assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths
-
- @controller.prepend_view_path([FIXTURE_LOAD_PATH])
- assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths
+ assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths
end
def test_template_appends_view_path_correctly
diff --git a/actionpack/test/fixtures/_top_level_partial.html.erb b/actionpack/test/fixtures/_top_level_partial.html.erb
new file mode 100644
index 0000000000..0b1c2e46e0
--- /dev/null
+++ b/actionpack/test/fixtures/_top_level_partial.html.erb
@@ -0,0 +1 @@
+top level partial html \ No newline at end of file
diff --git a/actionpack/test/fixtures/_top_level_partial_only.erb b/actionpack/test/fixtures/_top_level_partial_only.erb
new file mode 100644
index 0000000000..44f25b61d0
--- /dev/null
+++ b/actionpack/test/fixtures/_top_level_partial_only.erb
@@ -0,0 +1 @@
+top level partial \ No newline at end of file
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
new file mode 100644
index 0000000000..d7f43ad95e
--- /dev/null
+++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.html.erb
@@ -0,0 +1,3 @@
+<body>
+<% cache do %><p>ERB</p><% end %>
+</body> \ No newline at end of file
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
new file mode 100644
index 0000000000..057f15e62f
--- /dev/null
+++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
@@ -0,0 +1,6 @@
+page.assign 'title', 'Hey'
+cache do
+ page['element_1'].visual_effect :highlight
+ page['element_2'].visual_effect :highlight
+end
+page.assign 'footer', 'Bye'
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder
new file mode 100644
index 0000000000..efdcc28e0f
--- /dev/null
+++ b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder
@@ -0,0 +1,5 @@
+xml.body do
+ cache do
+ xml.p "Builder"
+ end
+end
diff --git a/actionpack/test/fixtures/test/_counter.html.erb b/actionpack/test/fixtures/test/_counter.html.erb
new file mode 100644
index 0000000000..fd245bfc70
--- /dev/null
+++ b/actionpack/test/fixtures/test/_counter.html.erb
@@ -0,0 +1 @@
+<%= counter_counter %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_layout_for_block_with_args.html.erb b/actionpack/test/fixtures/test/_layout_for_block_with_args.html.erb
new file mode 100644
index 0000000000..307533208d
--- /dev/null
+++ b/actionpack/test/fixtures/test/_layout_for_block_with_args.html.erb
@@ -0,0 +1,3 @@
+Before
+<%= yield 'arg1', 'arg2' %>
+After \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/implicit_content_type.atom.builder b/actionpack/test/fixtures/test/implicit_content_type.atom.builder
new file mode 100644
index 0000000000..2fcb32d247
--- /dev/null
+++ b/actionpack/test/fixtures/test/implicit_content_type.atom.builder
@@ -0,0 +1,2 @@
+xml.atom do
+end
diff --git a/actionpack/test/fixtures/test/using_layout_around_block_with_args.html.erb b/actionpack/test/fixtures/test/using_layout_around_block_with_args.html.erb
new file mode 100644
index 0000000000..71b1f30ad0
--- /dev/null
+++ b/actionpack/test/fixtures/test/using_layout_around_block_with_args.html.erb
@@ -0,0 +1 @@
+<% render(:layout => "layout_for_block_with_args") do |*args| %><%= args.join %><% end %> \ No newline at end of file
diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb
new file mode 100644
index 0000000000..7ba9659814
--- /dev/null
+++ b/actionpack/test/template/active_record_helper_i18n_test.rb
@@ -0,0 +1,46 @@
+require 'abstract_unit'
+
+class ActiveRecordHelperI18nTest < Test::Unit::TestCase
+ include ActionView::Helpers::ActiveRecordHelper
+
+ attr_reader :request
+ uses_mocha 'active_record_helper_i18n_test' do
+ def setup
+ @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
+ @object_name = 'book'
+ stubs(:content_tag).returns 'content_tag'
+
+ I18n.stubs(:t).with(:'header', :locale => 'en-US', :scope => [:activerecord, :errors, :template], :count => 1, :model => '').returns "1 error prohibited this from being saved"
+ I18n.stubs(:t).with(:'body', :locale => 'en-US', :scope => [:activerecord, :errors, :template]).returns 'There were problems with the following fields:'
+ end
+
+ def test_error_messages_for_given_a_header_option_it_does_not_translate_header_message
+ I18n.expects(:translate).with(:'header', :locale => 'en-US', :scope => [:activerecord, :errors, :template], :count => 1, :model => '').never
+ error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en-US')
+ end
+
+ def test_error_messages_for_given_no_header_option_it_translates_header_message
+ I18n.expects(:t).with(:'header', :locale => 'en-US', :scope => [:activerecord, :errors, :template], :count => 1, :model => '').returns 'header message'
+ I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
+ error_messages_for(:object => @object, :locale => 'en-US')
+ end
+
+ def test_error_messages_for_given_a_message_option_it_does_not_translate_message
+ I18n.expects(:t).with(:'body', :locale => 'en-US', :scope => [:activerecord, :errors, :template]).never
+ I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
+ error_messages_for(:object => @object, :message => 'message', :locale => 'en-US')
+ end
+
+ def test_error_messages_for_given_no_message_option_it_translates_message
+ I18n.expects(:t).with(:'body', :locale => 'en-US', :scope => [:activerecord, :errors, :template]).returns 'There were problems with the following fields:'
+ I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns ''
+ error_messages_for(:object => @object, :locale => 'en-US')
+ end
+
+ def test_error_messages_for_given_object_name_it_translates_object_name
+ I18n.expects(:t).with(:header, :locale => 'en-US', :scope => [:activerecord, :errors, :template], :count => 1, :model => @object_name).returns "1 error prohibited this #{@object_name} from being saved"
+ I18n.expects(:t).with(@object_name, :default => @object_name, :count => 1, :scope => [:activerecord, :models]).once.returns @object_name
+ error_messages_for(:object => @object, :locale => 'en-US', :object_name => @object_name)
+ end
+ end
+end
diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb
index dfc30e651a..e46f95d18b 100644
--- a/actionpack/test/template/active_record_helper_test.rb
+++ b/actionpack/test/template/active_record_helper_test.rb
@@ -10,17 +10,17 @@ class ActiveRecordHelperTest < ActionView::TestCase
alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast)
alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast)
end
-
+
User = Struct.new("User", :email)
User.class_eval do
alias_method :email_before_type_cast, :email unless respond_to?(:email_before_type_cast)
end
-
+
Column = Struct.new("Column", :type, :name, :human_name)
end
def setup_post
- @post = Post.new
+ @post = Post.new
def @post.errors
Class.new {
def on(field)
@@ -33,12 +33,12 @@ class ActiveRecordHelperTest < ActionView::TestCase
false
end
end
- def empty?() false end
- def count() 1 end
+ def empty?() false end
+ def count() 1 end
def full_messages() [ "Author name can't be empty" ] end
}.new
end
-
+
def @post.new_record?() true end
def @post.to_param() nil end
@@ -58,16 +58,16 @@ class ActiveRecordHelperTest < ActionView::TestCase
end
def setup_user
- @user = User.new
+ @user = User.new
def @user.errors
Class.new {
def on(field) field == "email" end
- def empty?() false end
- def count() 1 end
+ def empty?() false end
+ def count() 1 end
def full_messages() [ "User email can't be empty" ] end
}.new
end
-
+
def @user.new_record?() true end
def @user.to_param() nil end
@@ -81,7 +81,7 @@ class ActiveRecordHelperTest < ActionView::TestCase
@user.email = ""
end
-
+
def protect_against_forgery?
@protect_against_forgery ? true : false
end
@@ -92,7 +92,7 @@ class ActiveRecordHelperTest < ActionView::TestCase
setup_user
@response = ActionController::TestResponse.new
-
+
@controller = Object.new
def @controller.url_for(options)
options = options.symbolize_keys
@@ -111,7 +111,7 @@ class ActiveRecordHelperTest < ActionView::TestCase
assert_dom_equal(
%(<div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div>),
text_area("post", "body")
- )
+ )
end
def test_text_field_with_errors
@@ -140,7 +140,7 @@ class ActiveRecordHelperTest < ActionView::TestCase
form("post")
)
end
-
+
def test_form_with_protect_against_forgery
@protect_against_forgery = true
@request_forgery_protection_token = 'authenticity_token'
@@ -150,7 +150,7 @@ class ActiveRecordHelperTest < ActionView::TestCase
form("post")
)
end
-
+
def test_form_with_method_option
assert_dom_equal(
%(<form action="create" method="get"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
@@ -211,9 +211,9 @@ class ActiveRecordHelperTest < ActionView::TestCase
other_post = @post
assert_dom_equal "<div class=\"formError\">can't be empty</div>", error_message_on(other_post, :author_name)
end
-
- def test_error_message_on_should_use_options
- assert_dom_equal "<div class=\"differentError\">beforecan't be emptyafter</div>", error_message_on(:post, :author_name, "before", "after", "differentError")
+
+ def test_error_message_on_with_options_hash
+ assert_dom_equal "<div class=\"differentError\">beforecan't be emptyafter</div>", error_message_on(:post, :author_name, :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after')
end
def test_error_messages_for_many_objects
@@ -224,10 +224,10 @@ class ActiveRecordHelperTest < ActionView::TestCase
# add the default to put post back in the title
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post", :object_name => "post")
-
+
# symbols work as well
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => :post)
-
+
# any default works too
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this monkey from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => "monkey")
@@ -242,7 +242,7 @@ class ActiveRecordHelperTest < ActionView::TestCase
message = "Please fix the following fields and resubmit:"
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>#{header_message}</h2><p>#{message}</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :header_message => header_message, :message => message)
end
-
+
def test_error_messages_for_non_instance_variable
actual_user = @user
actual_post = @post
@@ -251,14 +251,14 @@ class ActiveRecordHelperTest < ActionView::TestCase
#explicitly set object
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :object => actual_post)
-
+
#multiple objects
assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post", :object => [actual_user, actual_post])
-
+
#nil object
assert_equal '', error_messages_for('user', :object => nil)
end
-
+
def test_form_with_string_multipart
assert_dom_equal(
%(<form action="create" enctype="multipart/form-data" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 020e112fd0..7e40a55dc5 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -30,7 +30,6 @@ class AssetTagHelperTest < ActionView::TestCase
end.new
@request = Class.new do
- def relative_url_root() "" end
def protocol() 'http://' end
def ssl?() false end
def host_with_port() 'localhost' end
@@ -39,8 +38,7 @@ class AssetTagHelperTest < ActionView::TestCase
@controller.request = @request
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
-
- ActionView::Base.computed_public_paths.clear
+ COMPUTED_PUBLIC_PATHS.clear
end
def teardown
@@ -119,7 +117,7 @@ class AssetTagHelperTest < ActionView::TestCase
%(image_path("xml")) => %(/images/xml),
%(image_path("xml.png")) => %(/images/xml.png),
%(image_path("dir/xml.png")) => %(/images/dir/xml.png),
- %(image_path("/dir/xml.png")) => %(/dir/xml.png)
+ %(image_path("/dir/xml.png")) => %(/dir/xml.png)
}
PathToImageToTag = {
@@ -161,7 +159,7 @@ class AssetTagHelperTest < ActionView::TestCase
ENV["RAILS_ASSET_ID"] = ""
JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
- ActionView::Base.computed_public_paths.clear
+ COMPUTED_PUBLIC_PATHS.clear
ENV["RAILS_ASSET_ID"] = "1"
assert_dom_equal(%(<script src="/javascripts/prototype.js?1" type="text/javascript"></script>\n<script src="/javascripts/effects.js?1" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js?1" type="text/javascript"></script>\n<script src="/javascripts/controls.js?1" type="text/javascript"></script>\n<script src="/javascripts/application.js?1" type="text/javascript"></script>), javascript_include_tag(:defaults))
@@ -174,7 +172,7 @@ class AssetTagHelperTest < ActionView::TestCase
ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'lib1', '/elsewhere/blub/lib2'
assert_dom_equal %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/slider.js" type="text/javascript"></script>\n<script src="/javascripts/lib1.js" type="text/javascript"></script>\n<script src="/elsewhere/blub/lib2.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>), javascript_include_tag(:defaults)
end
-
+
def test_custom_javascript_expansions
ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => ["head", "body", "tail"]
assert_dom_equal %(<script src="/javascripts/first.js" type="text/javascript"></script>\n<script src="/javascripts/head.js" type="text/javascript"></script>\n<script src="/javascripts/body.js" type="text/javascript"></script>\n<script src="/javascripts/tail.js" type="text/javascript"></script>\n<script src="/javascripts/last.js" type="text/javascript"></script>), javascript_include_tag('first', :monkey, 'last')
@@ -217,7 +215,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_image_path
ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
-
+
def test_path_to_image_alias_for_image_path
PathToImageToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -225,7 +223,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_image_tag
ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
-
+
def test_timebased_asset_id
expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
@@ -234,7 +232,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_should_skip_asset_id_on_complete_url
assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png")
end
-
+
def test_should_use_preset_asset_id
ENV["RAILS_ASSET_ID"] = "4500"
assert_equal %(<img alt="Rails" src="/images/rails.png?4500" />), image_tag("rails.png")
@@ -256,14 +254,14 @@ class AssetTagHelperTest < ActionView::TestCase
ENV["RAILS_ASSET_ID"] = ""
ActionController::Base.asset_host = 'http://a0.example.com'
ActionController::Base.perform_caching = true
-
+
assert_dom_equal(
%(<script src="http://a0.example.com/javascripts/all.js" type="text/javascript"></script>),
javascript_include_tag(:all, :cache => true)
)
assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
+
assert_dom_equal(
%(<script src="http://a0.example.com/javascripts/money.js" type="text/javascript"></script>),
javascript_include_tag(:all, :cache => "money")
@@ -335,8 +333,9 @@ class AssetTagHelperTest < ActionView::TestCase
ActionController::Base.asset_host = 'http://a%d.example.com'
ActionController::Base.perform_caching = true
+ hash = '/javascripts/cache/money.js'.hash % 4
assert_dom_equal(
- %(<script src="http://a3.example.com/javascripts/cache/money.js" type="text/javascript"></script>),
+ %(<script src="http://a#{hash}.example.com/javascripts/cache/money.js" type="text/javascript"></script>),
javascript_include_tag(:all, :cache => "cache/money")
)
@@ -344,7 +343,7 @@ class AssetTagHelperTest < ActionView::TestCase
ensure
FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))
end
-
+
def test_caching_javascript_include_tag_with_all_and_recursive_puts_defaults_at_the_start_of_the_file
ENV["RAILS_ASSET_ID"] = ""
ActionController::Base.asset_host = 'http://a0.example.com'
@@ -390,7 +389,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_javascript_include_tag_when_caching_off
ENV["RAILS_ASSET_ID"] = ""
ActionController::Base.perform_caching = false
-
+
assert_dom_equal(
%(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
javascript_include_tag(:all, :cache => true)
@@ -402,7 +401,7 @@ class AssetTagHelperTest < ActionView::TestCase
)
assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
+
assert_dom_equal(
%(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),
javascript_include_tag(:all, :cache => "money")
@@ -420,13 +419,14 @@ class AssetTagHelperTest < ActionView::TestCase
ENV["RAILS_ASSET_ID"] = ""
ActionController::Base.asset_host = 'http://a0.example.com'
ActionController::Base.perform_caching = true
-
+
assert_dom_equal(
%(<link href="http://a0.example.com/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />),
stylesheet_link_tag(:all, :cache => true)
)
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+ expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max
+ assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
assert_dom_equal(
%(<link href="http://a0.example.com/stylesheets/money.css" media="screen" rel="stylesheet" type="text/css" />),
@@ -459,7 +459,7 @@ class AssetTagHelperTest < ActionView::TestCase
def test_caching_stylesheet_include_tag_when_caching_off
ENV["RAILS_ASSET_ID"] = ""
ActionController::Base.perform_caching = false
-
+
assert_dom_equal(
%(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
stylesheet_link_tag(:all, :cache => true)
@@ -471,7 +471,7 @@ class AssetTagHelperTest < ActionView::TestCase
)
assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
+
assert_dom_equal(
%(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),
stylesheet_link_tag(:all, :cache => "money")
@@ -490,6 +490,8 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
def setup
+ ActionController::Base.relative_url_root = "/collaboration/hieraki"
+
@controller = Class.new do
attr_accessor :request
@@ -497,22 +499,22 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
"http://www.example.com/collaboration/hieraki"
end
end.new
-
- @request = Class.new do
- def relative_url_root
- "/collaboration/hieraki"
- end
+ @request = Class.new do
def protocol
'gopher://'
end
end.new
-
+
@controller.request = @request
-
+
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
end
+ def teardown
+ ActionController::Base.relative_url_root = nil
+ end
+
def test_should_compute_proper_path
assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
assert_dom_equal(%(/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
@@ -521,7 +523,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse.png'" src="/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse2.png'" src="/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
end
-
+
def test_should_ignore_relative_root_path_on_complete_url
assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png"))
end
diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb
index 9f7e5b4c6c..ef31ab2c76 100644
--- a/actionpack/test/template/atom_feed_helper_test.rb
+++ b/actionpack/test/template/atom_feed_helper_test.rb
@@ -74,12 +74,30 @@ class ScrollsController < ActionController::Base
end
end
EOT
+ FEEDS["feed_with_overridden_ids"] = <<-EOT
+ atom_feed({:id => 'tag:test.rubyonrails.org,2008:test/'}) do |feed|
+ feed.title("My great blog!")
+ feed.updated((@scrolls.first.created_at))
+
+ for scroll in @scrolls
+ feed.entry(scroll, :id => "tag:test.rubyonrails.org,2008:"+scroll.id.to_s) do |entry|
+ entry.title(scroll.title)
+ entry.content(scroll.body, :type => 'html')
+ entry.tag!('app:edited', Time.now)
+
+ entry.author do |author|
+ author.name("DHH")
+ end
+ end
+ end
+ end
+ EOT
def index
@scrolls = [
Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)),
Scroll.new(2, "2", "Hello Two", "Something Boring", Time.utc(2007, 12, 12, 15)),
]
-
+
render :inline => FEEDS[params[:id]], :type => :builder
end
@@ -98,21 +116,21 @@ class AtomFeedTest < Test::Unit::TestCase
@request.host = "www.nextangle.com"
end
-
+
def test_feed_should_use_default_language_if_none_is_given
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
assert_match %r{xml:lang="en-US"}, @response.body
end
end
-
+
def test_feed_should_include_two_entries
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
assert_select "entry", 2
end
end
-
+
def test_entry_should_only_use_published_if_created_at_is_present
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
@@ -167,7 +185,16 @@ class AtomFeedTest < Test::Unit::TestCase
end
end
- private
+ def test_feed_should_allow_overriding_ids
+ with_restful_routing(:scrolls) do
+ get :index, :id => "feed_with_overridden_ids"
+ assert_select "id", :text => "tag:test.rubyonrails.org,2008:test/"
+ assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:1"
+ assert_select "entry id", :text => "tag:test.rubyonrails.org,2008:2"
+ end
+ end
+
+private
def with_restful_routing(resources)
with_routing do |set|
set.draw do |map|
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
new file mode 100644
index 0000000000..e005aa0f03
--- /dev/null
+++ b/actionpack/test/template/compiled_templates_test.rb
@@ -0,0 +1,47 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+
+uses_mocha 'TestTemplateRecompilation' do
+ class CompiledTemplatesTest < Test::Unit::TestCase
+ def setup
+ @compiled_templates = ActionView::Base::CompiledTemplates
+ @compiled_templates.instance_methods.each do |m|
+ @compiled_templates.send(:remove_method, m) if m =~ /^_run_/
+ end
+ end
+
+ def test_template_gets_compiled
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", render("test/hello_world.erb")
+ assert_equal 1, @compiled_templates.instance_methods.size
+ end
+
+ def test_template_gets_recompiled_when_using_different_keys_in_local_assigns
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", render("test/hello_world.erb")
+ assert_equal "Hello world!", render("test/hello_world.erb", {:foo => "bar"})
+ assert_equal 2, @compiled_templates.instance_methods.size
+ end
+
+ def test_compiled_template_will_not_be_recompiled_when_rendered_with_identical_local_assigns
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", render("test/hello_world.erb")
+ ActionView::Template.any_instance.expects(:compile!).never
+ assert_equal "Hello world!", render("test/hello_world.erb")
+ end
+
+ def test_compiled_template_will_always_be_recompiled_when_eager_loaded_templates_is_off
+ ActionView::PathSet::Path.expects(:eager_load_templates?).times(4).returns(false)
+ assert_equal 0, @compiled_templates.instance_methods.size
+ assert_equal "Hello world!", render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb")
+ ActionView::Template.any_instance.expects(:compile!).times(3)
+ 3.times { assert_equal "Hello world!", render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") }
+ assert_equal 1, @compiled_templates.instance_methods.size
+ end
+
+ private
+ def render(*args)
+ ActionView::Base.new(ActionController::Base.view_paths, {}).render(*args)
+ end
+ end
+end
diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb
new file mode 100644
index 0000000000..bf3b2588c8
--- /dev/null
+++ b/actionpack/test/template/date_helper_i18n_test.rb
@@ -0,0 +1,113 @@
+require 'abstract_unit'
+
+class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
+ include ActionView::Helpers::DateHelper
+ attr_reader :request
+
+ def setup
+ @from = Time.mktime(2004, 6, 6, 21, 45, 0)
+ end
+
+ uses_mocha 'date_helper_distance_of_time_in_words_i18n_test' do
+ # distance_of_time_in_words
+
+ def test_distance_of_time_in_words_calls_i18n
+ { # with include_seconds
+ [2.seconds, true] => [:'less_than_x_seconds', 5],
+ [9.seconds, true] => [:'less_than_x_seconds', 10],
+ [19.seconds, true] => [:'less_than_x_seconds', 20],
+ [30.seconds, true] => [:'half_a_minute', nil],
+ [59.seconds, true] => [:'less_than_x_minutes', 1],
+ [60.seconds, true] => [:'x_minutes', 1],
+
+ # without include_seconds
+ [29.seconds, false] => [:'less_than_x_minutes', 1],
+ [60.seconds, false] => [:'x_minutes', 1],
+ [44.minutes, false] => [:'x_minutes', 44],
+ [61.minutes, false] => [:'about_x_hours', 1],
+ [24.hours, false] => [:'x_days', 1],
+ [30.days, false] => [:'about_x_months', 1],
+ [60.days, false] => [:'x_months', 2],
+ [1.year, false] => [:'about_x_years', 1],
+ [3.years, false] => [:'over_x_years', 3]
+
+ }.each do |passed, expected|
+ assert_distance_of_time_in_words_translates_key passed, expected
+ end
+ end
+
+ def assert_distance_of_time_in_words_translates_key(passed, expected)
+ diff, include_seconds = *passed
+ key, count = *expected
+ to = @from + diff
+
+ options = {:locale => 'en-US', :scope => :'datetime.distance_in_words'}
+ options[:count] = count if count
+
+ I18n.expects(:t).with(key, options)
+ distance_of_time_in_words(@from, to, include_seconds, :locale => 'en-US')
+ end
+
+ def test_distance_of_time_pluralizations
+ { [:'less_than_x_seconds', 1] => 'less than 1 second',
+ [:'less_than_x_seconds', 2] => 'less than 2 seconds',
+ [:'less_than_x_minutes', 1] => 'less than a minute',
+ [:'less_than_x_minutes', 2] => 'less than 2 minutes',
+ [:'x_minutes', 1] => '1 minute',
+ [:'x_minutes', 2] => '2 minutes',
+ [:'about_x_hours', 1] => 'about 1 hour',
+ [:'about_x_hours', 2] => 'about 2 hours',
+ [:'x_days', 1] => '1 day',
+ [:'x_days', 2] => '2 days',
+ [:'about_x_years', 1] => 'about 1 year',
+ [:'about_x_years', 2] => 'about 2 years',
+ [:'over_x_years', 1] => 'over 1 year',
+ [:'over_x_years', 2] => 'over 2 years'
+
+ }.each do |args, expected|
+ key, count = *args
+ assert_equal expected, I18n.t(key, :count => count, :scope => 'datetime.distance_in_words')
+ end
+ end
+ end
+end
+
+class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
+ include ActionView::Helpers::DateHelper
+ attr_reader :request
+
+ uses_mocha 'date_helper_select_tags_i18n_tests' do
+ def setup
+ I18n.stubs(:translate).with(:'date.month_names', :locale => 'en-US').returns Date::MONTHNAMES
+ end
+
+ # select_month
+
+ def test_select_month_given_use_month_names_option_does_not_translate_monthnames
+ I18n.expects(:translate).never
+ select_month(8, :locale => 'en-US', :use_month_names => Date::MONTHNAMES)
+ end
+
+ def test_select_month_translates_monthnames
+ I18n.expects(:translate).with(:'date.month_names', :locale => 'en-US').returns Date::MONTHNAMES
+ select_month(8, :locale => 'en-US')
+ end
+
+ def test_select_month_given_use_short_month_option_translates_abbr_monthnames
+ I18n.expects(:translate).with(:'date.abbr_month_names', :locale => 'en-US').returns Date::ABBR_MONTHNAMES
+ select_month(8, :locale => 'en-US', :use_short_month => true)
+ end
+
+ # date_or_time_select
+
+ def test_date_or_time_select_given_an_order_options_does_not_translate_order
+ I18n.expects(:translate).never
+ datetime_select('post', 'updated_at', :order => [:year, :month, :day], :locale => 'en-US')
+ end
+
+ def test_date_or_time_select_given_no_order_options_translates_order
+ I18n.expects(:translate).with(:'date.order', :locale => 'en-US').returns [:year, :month, :day]
+ datetime_select('post', 'updated_at', :locale => 'en-US')
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index 8b4e94c67f..1a645bccc6 100755..100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -17,7 +17,7 @@ class DateHelperTest < ActionView::TestCase
end
end
end
-
+
def assert_distance_of_time_in_words(from, to=nil)
to ||= from
@@ -86,13 +86,13 @@ class DateHelperTest < ActionView::TestCase
from = Time.mktime(2004, 6, 6, 21, 45, 0)
assert_distance_of_time_in_words(from)
end
-
+
def test_distance_in_words_with_time_zones
from = Time.mktime(2004, 6, 6, 21, 45, 0)
assert_distance_of_time_in_words(from.in_time_zone('Alaska'))
assert_distance_of_time_in_words(from.in_time_zone('Hawaii'))
end
-
+
def test_distance_in_words_with_different_time_zones
from = Time.mktime(2004, 6, 6, 21, 45, 0)
assert_distance_of_time_in_words(
@@ -100,13 +100,13 @@ class DateHelperTest < ActionView::TestCase
from.in_time_zone('Hawaii')
)
end
-
+
def test_distance_in_words_with_dates
start_date = Date.new 1975, 1, 31
end_date = Date.new 1977, 1, 31
assert_equal("over 2 years", distance_of_time_in_words(start_date, end_date))
end
-
+
def test_distance_in_words_with_integers
assert_equal "less than a minute", distance_of_time_in_words(59)
assert_equal "about 1 hour", distance_of_time_in_words(60*60)
@@ -557,11 +557,8 @@ class DateHelperTest < ActionView::TestCase
end
def test_select_date_with_incomplete_order
- expected = %(<select id="date_first_day" name="date[first][day]">\n)
- expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
- expected << "</select>\n"
-
- expected << %(<select id="date_first_year" name="date[first][year]">\n)
+ # NOTE: modified this test because of minimal API change
+ expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
expected << "</select>\n"
@@ -569,6 +566,10 @@ class DateHelperTest < ActionView::TestCase
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
expected << "</select>\n"
+ expected << %(<select id="date_first_day" name="date[first][day]">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), :start_year => 2003, :end_year => 2005, :prefix => "date[first]", :order => [:day])
end
@@ -757,6 +758,26 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => "selector")
end
+ def test_select_date_with_separator
+ expected = %(<select id="date_first_year" name="date[first][year]">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << " / "
+
+ expected << %(<select id="date_first_month" name="date[first][month]">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << " / "
+
+ expected << %(<select id="date_first_day" name="date[first][day]">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"})
+ end
+
def test_select_datetime
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -857,6 +878,42 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => 'selector')
end
+ def test_select_datetime_with_all_separators
+ expected = %(<select id="date_first_year" name="date[first][year]" class="selector">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << "/"
+
+ expected << %(<select id="date_first_month" name="date[first][month]" class="selector">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << "/"
+
+ expected << %(<select id="date_first_day" name="date[first][day]" class="selector">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ expected << "&mdash;"
+
+ expected << %(<select id="date_first_hour" name="date[first][hour]" class="selector">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+ expected << "</select>\n"
+
+ expected << ":"
+
+ expected << %(<select id="date_first_minute" name="date[first][minute]" class="selector">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { :datetime_separator => "&mdash;", :date_separator => "/", :time_separator => ":", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => 'selector')
+ end
+
+ def test_select_datetime_should_work_with_date
+ assert_nothing_raised { select_datetime(Date.today) }
+ end
+
def test_select_time
expected = %(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
@@ -933,32 +990,9 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector')
assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {:include_seconds => false}, :class => 'selector')
end
-
- uses_mocha 'TestDatetimeAndTimeSelectUseTimeCurrentAsDefault' do
- def test_select_datetime_uses_time_current_as_default
- time = stub(:year => 2004, :month => 6, :day => 15, :hour => 16, :min => 35, :sec => 0)
- Time.expects(:current).returns time
- expects(:select_date).with(time, anything, anything).returns('')
- expects(:select_time).with(time, anything, anything).returns('')
- select_datetime
- end
-
- def test_select_time_uses_time_current_as_default
- time = stub(:year => 2004, :month => 6, :day => 15, :hour => 16, :min => 35, :sec => 0)
- Time.expects(:current).returns time
- expects(:select_hour).with(time, anything, anything).returns('')
- expects(:select_minute).with(time, anything, anything).returns('')
- select_time
- end
-
- def test_select_date_uses_date_current_as_default
- date = stub(:year => 2004, :month => 6, :day => 15)
- Date.expects(:current).returns date
- expects(:select_year).with(date, anything, anything).returns('')
- expects(:select_month).with(date, anything, anything).returns('')
- expects(:select_day).with(date, anything, anything).returns('')
- select_date
- end
+
+ def test_select_time_should_work_with_date
+ assert_nothing_raised { select_time(Date.today) }
end
def test_date_select
@@ -1179,6 +1213,30 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_date_select_with_separator
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << " / "
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << " / "
+
+ expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]">\n}
+ expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", { :date_separator => " / " })
+ end
+
def test_time_select
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
@@ -1188,11 +1246,11 @@ class DateHelperTest < ActionView::TestCase
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, time_select("post", "written_on")
@@ -1203,11 +1261,11 @@ class DateHelperTest < ActionView::TestCase
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
expected = %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, time_select("post", "written_on", :ignore_date => true)
@@ -1222,15 +1280,15 @@ class DateHelperTest < ActionView::TestCase
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %(<select id="post_written_on_6i" name="post[written_on(6i)]">\n)
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 35}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, time_select("post", "written_on", :include_seconds => true)
@@ -1245,11 +1303,11 @@ class DateHelperTest < ActionView::TestCase
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="selector">\n)
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="selector">\n)
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, time_select("post", "written_on", {}, :class => 'selector')
@@ -1268,16 +1326,43 @@ class DateHelperTest < ActionView::TestCase
expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="selector">\n)
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="selector">\n)
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, output_buffer
end
+ def test_time_select_with_separator
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+ expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " - "
+
+ expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " - "
+
+ expected << %(<select id="post_written_on_6i" name="post[written_on(6i)]">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, time_select("post", "written_on", { :time_separator => " - ", :include_seconds => true })
+ end
+
def test_datetime_select
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
@@ -1306,7 +1391,7 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at")
end
-
+
uses_mocha 'TestDatetimeSelectDefaultsToTimeZoneNowWhenConfigTimeZoneIsSet' do
def test_datetime_select_defaults_to_time_zone_now_when_config_time_zone_is_set
time = stub(:year => 2004, :month => 6, :day => 15, :hour => 16, :min => 35, :sec => 0)
@@ -1360,6 +1445,47 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_datetime_select_with_separators
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << " / "
+
+ expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << " / "
+
+ expected << %{<select id="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+ expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+ expected << "</select>\n"
+
+ expected << " , "
+
+ expected << %(<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n)
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " - "
+
+ expected << %(<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " - "
+
+ expected << %(<select id="post_updated_at_6i" name="post[updated_at(6i)]">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, datetime_select("post", "updated_at", { :date_separator => " / ", :datetime_separator => " , ", :time_separator => " - ", :include_seconds => true })
+ end
+
def test_date_select_with_zero_value_and_no_start_year
expected = %(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
@@ -1370,8 +1496,7 @@ class DateHelperTest < ActionView::TestCase
expected << "</select>\n"
expected << %(<select id="date_first_day" name="date[first][day]">\n)
- expected <<
-%(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
assert_dom_equal expected, select_date(0, :end_year => Date.today.year+1, :prefix => "date[first]")
@@ -1388,8 +1513,7 @@ class DateHelperTest < ActionView::TestCase
expected << "</select>\n"
expected << %(<select id="date_first_day" name="date[first][day]">\n)
- expected <<
-%(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
assert_dom_equal expected, select_date(0, :start_year => 2003, :prefix => "date[first]")
@@ -1405,8 +1529,7 @@ class DateHelperTest < ActionView::TestCase
expected << "</select>\n"
expected << %(<select id="date_first_day" name="date[first][day]">\n)
- expected <<
-%(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
assert_dom_equal expected, select_date(0, :prefix => "date[first]")
@@ -1422,8 +1545,7 @@ class DateHelperTest < ActionView::TestCase
expected << "</select>\n"
expected << %(<select id="date_first_day" name="date[first][day]">\n)
- expected <<
-%(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
expected << "</select>\n"
assert_dom_equal expected, select_date(nil, :prefix => "date[first]")
@@ -1530,15 +1652,15 @@ class DateHelperTest < ActionView::TestCase
expected << " &mdash; "
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_6i" name="post[updated_at(6i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 35}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 35}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :include_seconds => true)
@@ -1559,11 +1681,11 @@ class DateHelperTest < ActionView::TestCase
expected << " &mdash; "
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true)
@@ -1582,11 +1704,11 @@ class DateHelperTest < ActionView::TestCase
expected << " &mdash; "
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :discard_month => true)
@@ -1601,11 +1723,11 @@ class DateHelperTest < ActionView::TestCase
expected << %{<input type="hidden" id="post_updated_at_3i" name="post[updated_at(3i)]" value="15" />\n}
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true)
@@ -1628,11 +1750,11 @@ class DateHelperTest < ActionView::TestCase
expected << " &mdash; "
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :order => [:minute, :day, :hour, :month, :year, :second])
@@ -1653,11 +1775,11 @@ class DateHelperTest < ActionView::TestCase
expected << " &mdash; "
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :order => [:day, :month])
@@ -1680,11 +1802,11 @@ class DateHelperTest < ActionView::TestCase
expected << " &mdash; "
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :default => Time.local(2006, 9, 19, 15, 16, 35))
@@ -1727,11 +1849,11 @@ class DateHelperTest < ActionView::TestCase
expected << " &mdash; "
expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
- 0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 9}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 9}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
expected << " : "
expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
- 0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 42}>#{leading_zero_on_single_digits(i)}</option>\n) }
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 42}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
assert_dom_equal expected, datetime_select("post", "updated_at", :default => { :month => 10, :minute => 42, :hour => 9 })
@@ -1766,33 +1888,158 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", {}, :class => 'selector')
end
- uses_mocha 'TestInstanceTagDefaultTimeFromOptions' do
- def test_instance_tag_default_time_from_options_uses_time_current_as_default_when_hash_passed_as_arg
- dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
- Time.expects(:current).returns Time.now
- dummy_instance_tag.send!(:default_time_from_options, :hour => 2)
- end
-
- def test_instance_tag_default_time_from_options_respects_hash_arg_settings_when_time_falls_in_system_local_dst_spring_gap
- with_env_tz('US/Central') do
- dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
- Time.stubs(:now).returns Time.local(2006, 4, 2, 1)
- assert_equal 2, dummy_instance_tag.send!(:default_time_from_options, :hour => 2).hour
- end
- end
-
- def test_instance_tag_default_time_from_options_handles_far_future_date
- dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3)
- time = dummy_instance_tag.send!(:default_time_from_options, :year => 2050, :month => 2, :day => 10, :hour => 15, :min => 30, :sec => 45)
- assert_equal 2050, time.year
- end
+ def test_date_select_should_not_change_passed_options_hash
+ @post = Post.new
+ @post.updated_at = Time.local(2008, 7, 16, 23, 30)
+
+ options = {
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }
+ date_select(@post, :updated_at, options)
+
+ # note: the literal hash is intentional to show that the actual options hash isn't modified
+ # don't change this!
+ assert_equal({
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }, options)
+ end
+
+ def test_datetime_select_should_not_change_passed_options_hash
+ @post = Post.new
+ @post.updated_at = Time.local(2008, 7, 16, 23, 30)
+
+ options = {
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }
+ datetime_select(@post, :updated_at, options)
+
+ # note: the literal hash is intentional to show that the actual options hash isn't modified
+ # don't change this!
+ assert_equal({
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }, options)
+ end
+
+ def test_time_select_should_not_change_passed_options_hash
+ @post = Post.new
+ @post.updated_at = Time.local(2008, 7, 16, 23, 30)
+
+ options = {
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }
+ time_select(@post, :updated_at, options)
+
+ # note: the literal hash is intentional to show that the actual options hash isn't modified
+ # don't change this!
+ assert_equal({
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }, options)
+ end
+
+ def test_select_date_should_not_change_passed_options_hash
+ options = {
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }
+ select_date(Date.today, options)
+
+ # note: the literal hash is intentional to show that the actual options hash isn't modified
+ # don't change this!
+ assert_equal({
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }, options)
+ end
+
+ def test_select_datetime_should_not_change_passed_options_hash
+ options = {
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }
+ select_datetime(Time.now, options)
+
+ # note: the literal hash is intentional to show that the actual options hash isn't modified
+ # don't change this!
+ assert_equal({
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }, options)
+ end
+
+ def test_select_time_should_not_change_passed_options_hash
+ options = {
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }
+ select_time(Time.now, options)
+
+ # note: the literal hash is intentional to show that the actual options hash isn't modified
+ # don't change this!
+ assert_equal({
+ :order => [ :year, :month, :day ],
+ :default => { :year => 2008, :month => 7, :day => 16, :hour => 23, :minute => 30, :second => 1 },
+ :discard_type => false,
+ :include_blank => false,
+ :ignore_date => false,
+ :include_seconds => true
+ }, options)
end
-
+
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz
yield
ensure
old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
- end
+ end
end
diff --git a/actionpack/test/template/form_country_helper_test.rb b/actionpack/test/template/form_country_helper_test.rb
new file mode 100644
index 0000000000..8862e08222
--- /dev/null
+++ b/actionpack/test/template/form_country_helper_test.rb
@@ -0,0 +1,1549 @@
+require 'abstract_unit'
+
+class FormCountryHelperTest < ActionView::TestCase
+ tests ActionView::Helpers::FormCountryHelper
+
+ silence_warnings do
+ Post = Struct.new('Post', :title, :author_name, :body, :secret, :written_on, :category, :origin)
+ end
+
+ def test_country_select
+ @post = Post.new
+ @post.origin = "Denmark"
+ expected_select = <<-COUNTRIES
+<select id="post_origin" name="post[origin]"><option value="Afghanistan">Afghanistan</option>
+<option value="Aland Islands">Aland Islands</option>
+<option value="Albania">Albania</option>
+<option value="Algeria">Algeria</option>
+<option value="American Samoa">American Samoa</option>
+<option value="Andorra">Andorra</option>
+<option value="Angola">Angola</option>
+<option value="Anguilla">Anguilla</option>
+<option value="Antarctica">Antarctica</option>
+<option value="Antigua And Barbuda">Antigua And Barbuda</option>
+<option value="Argentina">Argentina</option>
+<option value="Armenia">Armenia</option>
+<option value="Aruba">Aruba</option>
+<option value="Australia">Australia</option>
+<option value="Austria">Austria</option>
+<option value="Azerbaijan">Azerbaijan</option>
+<option value="Bahamas">Bahamas</option>
+<option value="Bahrain">Bahrain</option>
+<option value="Bangladesh">Bangladesh</option>
+<option value="Barbados">Barbados</option>
+<option value="Belarus">Belarus</option>
+<option value="Belgium">Belgium</option>
+<option value="Belize">Belize</option>
+<option value="Benin">Benin</option>
+<option value="Bermuda">Bermuda</option>
+<option value="Bhutan">Bhutan</option>
+<option value="Bolivia">Bolivia</option>
+<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
+<option value="Botswana">Botswana</option>
+<option value="Bouvet Island">Bouvet Island</option>
+<option value="Brazil">Brazil</option>
+<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
+<option value="Brunei Darussalam">Brunei Darussalam</option>
+<option value="Bulgaria">Bulgaria</option>
+<option value="Burkina Faso">Burkina Faso</option>
+<option value="Burundi">Burundi</option>
+<option value="Cambodia">Cambodia</option>
+<option value="Cameroon">Cameroon</option>
+<option value="Canada">Canada</option>
+<option value="Cape Verde">Cape Verde</option>
+<option value="Cayman Islands">Cayman Islands</option>
+<option value="Central African Republic">Central African Republic</option>
+<option value="Chad">Chad</option>
+<option value="Chile">Chile</option>
+<option value="China">China</option>
+<option value="Christmas Island">Christmas Island</option>
+<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
+<option value="Colombia">Colombia</option>
+<option value="Comoros">Comoros</option>
+<option value="Congo">Congo</option>
+<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
+<option value="Cook Islands">Cook Islands</option>
+<option value="Costa Rica">Costa Rica</option>
+<option value="Cote d'Ivoire">Cote d'Ivoire</option>
+<option value="Croatia">Croatia</option>
+<option value="Cuba">Cuba</option>
+<option value="Cyprus">Cyprus</option>
+<option value="Czech Republic">Czech Republic</option>
+<option selected="selected" value="Denmark">Denmark</option>
+<option value="Djibouti">Djibouti</option>
+<option value="Dominica">Dominica</option>
+<option value="Dominican Republic">Dominican Republic</option>
+<option value="Ecuador">Ecuador</option>
+<option value="Egypt">Egypt</option>
+<option value="El Salvador">El Salvador</option>
+<option value="Equatorial Guinea">Equatorial Guinea</option>
+<option value="Eritrea">Eritrea</option>
+<option value="Estonia">Estonia</option>
+<option value="Ethiopia">Ethiopia</option>
+<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
+<option value="Faroe Islands">Faroe Islands</option>
+<option value="Fiji">Fiji</option>
+<option value="Finland">Finland</option>
+<option value="France">France</option>
+<option value="French Guiana">French Guiana</option>
+<option value="French Polynesia">French Polynesia</option>
+<option value="French Southern Territories">French Southern Territories</option>
+<option value="Gabon">Gabon</option>
+<option value="Gambia">Gambia</option>
+<option value="Georgia">Georgia</option>
+<option value="Germany">Germany</option>
+<option value="Ghana">Ghana</option>
+<option value="Gibraltar">Gibraltar</option>
+<option value="Greece">Greece</option>
+<option value="Greenland">Greenland</option>
+<option value="Grenada">Grenada</option>
+<option value="Guadeloupe">Guadeloupe</option>
+<option value="Guam">Guam</option>
+<option value="Guatemala">Guatemala</option>
+<option value="Guernsey">Guernsey</option>
+<option value="Guinea">Guinea</option>
+<option value="Guinea-Bissau">Guinea-Bissau</option>
+<option value="Guyana">Guyana</option>
+<option value="Haiti">Haiti</option>
+<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
+<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
+<option value="Honduras">Honduras</option>
+<option value="Hong Kong">Hong Kong</option>
+<option value="Hungary">Hungary</option>
+<option value="Iceland">Iceland</option>
+<option value="India">India</option>
+<option value="Indonesia">Indonesia</option>
+<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
+<option value="Iraq">Iraq</option>
+<option value="Ireland">Ireland</option>
+<option value="Isle of Man">Isle of Man</option>
+<option value="Israel">Israel</option>
+<option value="Italy">Italy</option>
+<option value="Jamaica">Jamaica</option>
+<option value="Japan">Japan</option>
+<option value="Jersey">Jersey</option>
+<option value="Jordan">Jordan</option>
+<option value="Kazakhstan">Kazakhstan</option>
+<option value="Kenya">Kenya</option>
+<option value="Kiribati">Kiribati</option>
+<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
+<option value="Korea, Republic of">Korea, Republic of</option>
+<option value="Kuwait">Kuwait</option>
+<option value="Kyrgyzstan">Kyrgyzstan</option>
+<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
+<option value="Latvia">Latvia</option>
+<option value="Lebanon">Lebanon</option>
+<option value="Lesotho">Lesotho</option>
+<option value="Liberia">Liberia</option>
+<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
+<option value="Liechtenstein">Liechtenstein</option>
+<option value="Lithuania">Lithuania</option>
+<option value="Luxembourg">Luxembourg</option>
+<option value="Macao">Macao</option>
+<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
+<option value="Madagascar">Madagascar</option>
+<option value="Malawi">Malawi</option>
+<option value="Malaysia">Malaysia</option>
+<option value="Maldives">Maldives</option>
+<option value="Mali">Mali</option>
+<option value="Malta">Malta</option>
+<option value="Marshall Islands">Marshall Islands</option>
+<option value="Martinique">Martinique</option>
+<option value="Mauritania">Mauritania</option>
+<option value="Mauritius">Mauritius</option>
+<option value="Mayotte">Mayotte</option>
+<option value="Mexico">Mexico</option>
+<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
+<option value="Moldova, Republic of">Moldova, Republic of</option>
+<option value="Monaco">Monaco</option>
+<option value="Mongolia">Mongolia</option>
+<option value="Montenegro">Montenegro</option>
+<option value="Montserrat">Montserrat</option>
+<option value="Morocco">Morocco</option>
+<option value="Mozambique">Mozambique</option>
+<option value="Myanmar">Myanmar</option>
+<option value="Namibia">Namibia</option>
+<option value="Nauru">Nauru</option>
+<option value="Nepal">Nepal</option>
+<option value="Netherlands">Netherlands</option>
+<option value="Netherlands Antilles">Netherlands Antilles</option>
+<option value="New Caledonia">New Caledonia</option>
+<option value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option>
+<option value="Niger">Niger</option>
+<option value="Nigeria">Nigeria</option>
+<option value="Niue">Niue</option>
+<option value="Norfolk Island">Norfolk Island</option>
+<option value="Northern Mariana Islands">Northern Mariana Islands</option>
+<option value="Norway">Norway</option>
+<option value="Oman">Oman</option>
+<option value="Pakistan">Pakistan</option>
+<option value="Palau">Palau</option>
+<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
+<option value="Panama">Panama</option>
+<option value="Papua New Guinea">Papua New Guinea</option>
+<option value="Paraguay">Paraguay</option>
+<option value="Peru">Peru</option>
+<option value="Philippines">Philippines</option>
+<option value="Pitcairn">Pitcairn</option>
+<option value="Poland">Poland</option>
+<option value="Portugal">Portugal</option>
+<option value="Puerto Rico">Puerto Rico</option>
+<option value="Qatar">Qatar</option>
+<option value="Reunion">Reunion</option>
+<option value="Romania">Romania</option>
+<option value="Russian Federation">Russian Federation</option>
+<option value="Rwanda">Rwanda</option>
+<option value="Saint Barthelemy">Saint Barthelemy</option>
+<option value="Saint Helena">Saint Helena</option>
+<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
+<option value="Saint Lucia">Saint Lucia</option>
+<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
+<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
+<option value="Samoa">Samoa</option>
+<option value="San Marino">San Marino</option>
+<option value="Sao Tome and Principe">Sao Tome and Principe</option>
+<option value="Saudi Arabia">Saudi Arabia</option>
+<option value="Senegal">Senegal</option>
+<option value="Serbia">Serbia</option>
+<option value="Seychelles">Seychelles</option>
+<option value="Sierra Leone">Sierra Leone</option>
+<option value="Singapore">Singapore</option>
+<option value="Slovakia">Slovakia</option>
+<option value="Slovenia">Slovenia</option>
+<option value="Solomon Islands">Solomon Islands</option>
+<option value="Somalia">Somalia</option>
+<option value="South Africa">South Africa</option>
+<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
+<option value="Spain">Spain</option>
+<option value="Sri Lanka">Sri Lanka</option>
+<option value="Sudan">Sudan</option>
+<option value="Suriname">Suriname</option>
+<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
+<option value="Swaziland">Swaziland</option>
+<option value="Sweden">Sweden</option>
+<option value="Switzerland">Switzerland</option>
+<option value="Syrian Arab Republic">Syrian Arab Republic</option>
+<option value="Taiwan, Province of China">Taiwan, Province of China</option>
+<option value="Tajikistan">Tajikistan</option>
+<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
+<option value="Thailand">Thailand</option>
+<option value="Timor-Leste">Timor-Leste</option>
+<option value="Togo">Togo</option>
+<option value="Tokelau">Tokelau</option>
+<option value="Tonga">Tonga</option>
+<option value="Trinidad and Tobago">Trinidad and Tobago</option>
+<option value="Tunisia">Tunisia</option>
+<option value="Turkey">Turkey</option>
+<option value="Turkmenistan">Turkmenistan</option>
+<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
+<option value="Tuvalu">Tuvalu</option>
+<option value="Uganda">Uganda</option>
+<option value="Ukraine">Ukraine</option>
+<option value="United Arab Emirates">United Arab Emirates</option>
+<option value="United Kingdom">United Kingdom</option>
+<option value="United States">United States</option>
+<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
+<option value="Uruguay">Uruguay</option>
+<option value="Uzbekistan">Uzbekistan</option>
+<option value="Vanuatu">Vanuatu</option>
+<option value="Venezuela">Venezuela</option>
+<option value="Viet Nam">Viet Nam</option>
+<option value="Virgin Islands, British">Virgin Islands, British</option>
+<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
+<option value="Wallis and Futuna">Wallis and Futuna</option>
+<option value="Western Sahara">Western Sahara</option>
+<option value="Yemen">Yemen</option>
+<option value="Zambia">Zambia</option>
+<option value="Zimbabwe">Zimbabwe</option></select>
+COUNTRIES
+ assert_dom_equal(expected_select[0..-2], country_select("post", "origin"))
+ end
+
+ def test_country_select_with_priority_countries
+ @post = Post.new
+ @post.origin = "Denmark"
+ expected_select = <<-COUNTRIES
+<select id="post_origin" name="post[origin]"><option value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option><option value="" disabled="disabled">-------------</option>
+<option value="Afghanistan">Afghanistan</option>
+<option value="Aland Islands">Aland Islands</option>
+<option value="Albania">Albania</option>
+<option value="Algeria">Algeria</option>
+<option value="American Samoa">American Samoa</option>
+<option value="Andorra">Andorra</option>
+<option value="Angola">Angola</option>
+<option value="Anguilla">Anguilla</option>
+<option value="Antarctica">Antarctica</option>
+<option value="Antigua And Barbuda">Antigua And Barbuda</option>
+<option value="Argentina">Argentina</option>
+<option value="Armenia">Armenia</option>
+<option value="Aruba">Aruba</option>
+<option value="Australia">Australia</option>
+<option value="Austria">Austria</option>
+<option value="Azerbaijan">Azerbaijan</option>
+<option value="Bahamas">Bahamas</option>
+<option value="Bahrain">Bahrain</option>
+<option value="Bangladesh">Bangladesh</option>
+<option value="Barbados">Barbados</option>
+<option value="Belarus">Belarus</option>
+<option value="Belgium">Belgium</option>
+<option value="Belize">Belize</option>
+<option value="Benin">Benin</option>
+<option value="Bermuda">Bermuda</option>
+<option value="Bhutan">Bhutan</option>
+<option value="Bolivia">Bolivia</option>
+<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
+<option value="Botswana">Botswana</option>
+<option value="Bouvet Island">Bouvet Island</option>
+<option value="Brazil">Brazil</option>
+<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
+<option value="Brunei Darussalam">Brunei Darussalam</option>
+<option value="Bulgaria">Bulgaria</option>
+<option value="Burkina Faso">Burkina Faso</option>
+<option value="Burundi">Burundi</option>
+<option value="Cambodia">Cambodia</option>
+<option value="Cameroon">Cameroon</option>
+<option value="Canada">Canada</option>
+<option value="Cape Verde">Cape Verde</option>
+<option value="Cayman Islands">Cayman Islands</option>
+<option value="Central African Republic">Central African Republic</option>
+<option value="Chad">Chad</option>
+<option value="Chile">Chile</option>
+<option value="China">China</option>
+<option value="Christmas Island">Christmas Island</option>
+<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
+<option value="Colombia">Colombia</option>
+<option value="Comoros">Comoros</option>
+<option value="Congo">Congo</option>
+<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
+<option value="Cook Islands">Cook Islands</option>
+<option value="Costa Rica">Costa Rica</option>
+<option value="Cote d'Ivoire">Cote d'Ivoire</option>
+<option value="Croatia">Croatia</option>
+<option value="Cuba">Cuba</option>
+<option value="Cyprus">Cyprus</option>
+<option value="Czech Republic">Czech Republic</option>
+<option selected="selected" value="Denmark">Denmark</option>
+<option value="Djibouti">Djibouti</option>
+<option value="Dominica">Dominica</option>
+<option value="Dominican Republic">Dominican Republic</option>
+<option value="Ecuador">Ecuador</option>
+<option value="Egypt">Egypt</option>
+<option value="El Salvador">El Salvador</option>
+<option value="Equatorial Guinea">Equatorial Guinea</option>
+<option value="Eritrea">Eritrea</option>
+<option value="Estonia">Estonia</option>
+<option value="Ethiopia">Ethiopia</option>
+<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
+<option value="Faroe Islands">Faroe Islands</option>
+<option value="Fiji">Fiji</option>
+<option value="Finland">Finland</option>
+<option value="France">France</option>
+<option value="French Guiana">French Guiana</option>
+<option value="French Polynesia">French Polynesia</option>
+<option value="French Southern Territories">French Southern Territories</option>
+<option value="Gabon">Gabon</option>
+<option value="Gambia">Gambia</option>
+<option value="Georgia">Georgia</option>
+<option value="Germany">Germany</option>
+<option value="Ghana">Ghana</option>
+<option value="Gibraltar">Gibraltar</option>
+<option value="Greece">Greece</option>
+<option value="Greenland">Greenland</option>
+<option value="Grenada">Grenada</option>
+<option value="Guadeloupe">Guadeloupe</option>
+<option value="Guam">Guam</option>
+<option value="Guatemala">Guatemala</option>
+<option value="Guernsey">Guernsey</option>
+<option value="Guinea">Guinea</option>
+<option value="Guinea-Bissau">Guinea-Bissau</option>
+<option value="Guyana">Guyana</option>
+<option value="Haiti">Haiti</option>
+<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
+<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
+<option value="Honduras">Honduras</option>
+<option value="Hong Kong">Hong Kong</option>
+<option value="Hungary">Hungary</option>
+<option value="Iceland">Iceland</option>
+<option value="India">India</option>
+<option value="Indonesia">Indonesia</option>
+<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
+<option value="Iraq">Iraq</option>
+<option value="Ireland">Ireland</option>
+<option value="Isle of Man">Isle of Man</option>
+<option value="Israel">Israel</option>
+<option value="Italy">Italy</option>
+<option value="Jamaica">Jamaica</option>
+<option value="Japan">Japan</option>
+<option value="Jersey">Jersey</option>
+<option value="Jordan">Jordan</option>
+<option value="Kazakhstan">Kazakhstan</option>
+<option value="Kenya">Kenya</option>
+<option value="Kiribati">Kiribati</option>
+<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
+<option value="Korea, Republic of">Korea, Republic of</option>
+<option value="Kuwait">Kuwait</option>
+<option value="Kyrgyzstan">Kyrgyzstan</option>
+<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
+<option value="Latvia">Latvia</option>
+<option value="Lebanon">Lebanon</option>
+<option value="Lesotho">Lesotho</option>
+<option value="Liberia">Liberia</option>
+<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
+<option value="Liechtenstein">Liechtenstein</option>
+<option value="Lithuania">Lithuania</option>
+<option value="Luxembourg">Luxembourg</option>
+<option value="Macao">Macao</option>
+<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
+<option value="Madagascar">Madagascar</option>
+<option value="Malawi">Malawi</option>
+<option value="Malaysia">Malaysia</option>
+<option value="Maldives">Maldives</option>
+<option value="Mali">Mali</option>
+<option value="Malta">Malta</option>
+<option value="Marshall Islands">Marshall Islands</option>
+<option value="Martinique">Martinique</option>
+<option value="Mauritania">Mauritania</option>
+<option value="Mauritius">Mauritius</option>
+<option value="Mayotte">Mayotte</option>
+<option value="Mexico">Mexico</option>
+<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
+<option value="Moldova, Republic of">Moldova, Republic of</option>
+<option value="Monaco">Monaco</option>
+<option value="Mongolia">Mongolia</option>
+<option value="Montenegro">Montenegro</option>
+<option value="Montserrat">Montserrat</option>
+<option value="Morocco">Morocco</option>
+<option value="Mozambique">Mozambique</option>
+<option value="Myanmar">Myanmar</option>
+<option value="Namibia">Namibia</option>
+<option value="Nauru">Nauru</option>
+<option value="Nepal">Nepal</option>
+<option value="Netherlands">Netherlands</option>
+<option value="Netherlands Antilles">Netherlands Antilles</option>
+<option value="New Caledonia">New Caledonia</option>
+<option value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option>
+<option value="Niger">Niger</option>
+<option value="Nigeria">Nigeria</option>
+<option value="Niue">Niue</option>
+<option value="Norfolk Island">Norfolk Island</option>
+<option value="Northern Mariana Islands">Northern Mariana Islands</option>
+<option value="Norway">Norway</option>
+<option value="Oman">Oman</option>
+<option value="Pakistan">Pakistan</option>
+<option value="Palau">Palau</option>
+<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
+<option value="Panama">Panama</option>
+<option value="Papua New Guinea">Papua New Guinea</option>
+<option value="Paraguay">Paraguay</option>
+<option value="Peru">Peru</option>
+<option value="Philippines">Philippines</option>
+<option value="Pitcairn">Pitcairn</option>
+<option value="Poland">Poland</option>
+<option value="Portugal">Portugal</option>
+<option value="Puerto Rico">Puerto Rico</option>
+<option value="Qatar">Qatar</option>
+<option value="Reunion">Reunion</option>
+<option value="Romania">Romania</option>
+<option value="Russian Federation">Russian Federation</option>
+<option value="Rwanda">Rwanda</option>
+<option value="Saint Barthelemy">Saint Barthelemy</option>
+<option value="Saint Helena">Saint Helena</option>
+<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
+<option value="Saint Lucia">Saint Lucia</option>
+<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
+<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
+<option value="Samoa">Samoa</option>
+<option value="San Marino">San Marino</option>
+<option value="Sao Tome and Principe">Sao Tome and Principe</option>
+<option value="Saudi Arabia">Saudi Arabia</option>
+<option value="Senegal">Senegal</option>
+<option value="Serbia">Serbia</option>
+<option value="Seychelles">Seychelles</option>
+<option value="Sierra Leone">Sierra Leone</option>
+<option value="Singapore">Singapore</option>
+<option value="Slovakia">Slovakia</option>
+<option value="Slovenia">Slovenia</option>
+<option value="Solomon Islands">Solomon Islands</option>
+<option value="Somalia">Somalia</option>
+<option value="South Africa">South Africa</option>
+<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
+<option value="Spain">Spain</option>
+<option value="Sri Lanka">Sri Lanka</option>
+<option value="Sudan">Sudan</option>
+<option value="Suriname">Suriname</option>
+<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
+<option value="Swaziland">Swaziland</option>
+<option value="Sweden">Sweden</option>
+<option value="Switzerland">Switzerland</option>
+<option value="Syrian Arab Republic">Syrian Arab Republic</option>
+<option value="Taiwan, Province of China">Taiwan, Province of China</option>
+<option value="Tajikistan">Tajikistan</option>
+<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
+<option value="Thailand">Thailand</option>
+<option value="Timor-Leste">Timor-Leste</option>
+<option value="Togo">Togo</option>
+<option value="Tokelau">Tokelau</option>
+<option value="Tonga">Tonga</option>
+<option value="Trinidad and Tobago">Trinidad and Tobago</option>
+<option value="Tunisia">Tunisia</option>
+<option value="Turkey">Turkey</option>
+<option value="Turkmenistan">Turkmenistan</option>
+<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
+<option value="Tuvalu">Tuvalu</option>
+<option value="Uganda">Uganda</option>
+<option value="Ukraine">Ukraine</option>
+<option value="United Arab Emirates">United Arab Emirates</option>
+<option value="United Kingdom">United Kingdom</option>
+<option value="United States">United States</option>
+<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
+<option value="Uruguay">Uruguay</option>
+<option value="Uzbekistan">Uzbekistan</option>
+<option value="Vanuatu">Vanuatu</option>
+<option value="Venezuela">Venezuela</option>
+<option value="Viet Nam">Viet Nam</option>
+<option value="Virgin Islands, British">Virgin Islands, British</option>
+<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
+<option value="Wallis and Futuna">Wallis and Futuna</option>
+<option value="Western Sahara">Western Sahara</option>
+<option value="Yemen">Yemen</option>
+<option value="Zambia">Zambia</option>
+<option value="Zimbabwe">Zimbabwe</option></select>
+COUNTRIES
+ assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"]))
+ end
+
+ def test_country_select_with_selected_priority_country
+ @post = Post.new
+ @post.origin = "New Zealand"
+ expected_select = <<-COUNTRIES
+<select id="post_origin" name="post[origin]"><option selected="selected" value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option><option value="" disabled="disabled">-------------</option>
+<option value="Afghanistan">Afghanistan</option>
+<option value="Aland Islands">Aland Islands</option>
+<option value="Albania">Albania</option>
+<option value="Algeria">Algeria</option>
+<option value="American Samoa">American Samoa</option>
+<option value="Andorra">Andorra</option>
+<option value="Angola">Angola</option>
+<option value="Anguilla">Anguilla</option>
+<option value="Antarctica">Antarctica</option>
+<option value="Antigua And Barbuda">Antigua And Barbuda</option>
+<option value="Argentina">Argentina</option>
+<option value="Armenia">Armenia</option>
+<option value="Aruba">Aruba</option>
+<option value="Australia">Australia</option>
+<option value="Austria">Austria</option>
+<option value="Azerbaijan">Azerbaijan</option>
+<option value="Bahamas">Bahamas</option>
+<option value="Bahrain">Bahrain</option>
+<option value="Bangladesh">Bangladesh</option>
+<option value="Barbados">Barbados</option>
+<option value="Belarus">Belarus</option>
+<option value="Belgium">Belgium</option>
+<option value="Belize">Belize</option>
+<option value="Benin">Benin</option>
+<option value="Bermuda">Bermuda</option>
+<option value="Bhutan">Bhutan</option>
+<option value="Bolivia">Bolivia</option>
+<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
+<option value="Botswana">Botswana</option>
+<option value="Bouvet Island">Bouvet Island</option>
+<option value="Brazil">Brazil</option>
+<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
+<option value="Brunei Darussalam">Brunei Darussalam</option>
+<option value="Bulgaria">Bulgaria</option>
+<option value="Burkina Faso">Burkina Faso</option>
+<option value="Burundi">Burundi</option>
+<option value="Cambodia">Cambodia</option>
+<option value="Cameroon">Cameroon</option>
+<option value="Canada">Canada</option>
+<option value="Cape Verde">Cape Verde</option>
+<option value="Cayman Islands">Cayman Islands</option>
+<option value="Central African Republic">Central African Republic</option>
+<option value="Chad">Chad</option>
+<option value="Chile">Chile</option>
+<option value="China">China</option>
+<option value="Christmas Island">Christmas Island</option>
+<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
+<option value="Colombia">Colombia</option>
+<option value="Comoros">Comoros</option>
+<option value="Congo">Congo</option>
+<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
+<option value="Cook Islands">Cook Islands</option>
+<option value="Costa Rica">Costa Rica</option>
+<option value="Cote d'Ivoire">Cote d'Ivoire</option>
+<option value="Croatia">Croatia</option>
+<option value="Cuba">Cuba</option>
+<option value="Cyprus">Cyprus</option>
+<option value="Czech Republic">Czech Republic</option>
+<option value="Denmark">Denmark</option>
+<option value="Djibouti">Djibouti</option>
+<option value="Dominica">Dominica</option>
+<option value="Dominican Republic">Dominican Republic</option>
+<option value="Ecuador">Ecuador</option>
+<option value="Egypt">Egypt</option>
+<option value="El Salvador">El Salvador</option>
+<option value="Equatorial Guinea">Equatorial Guinea</option>
+<option value="Eritrea">Eritrea</option>
+<option value="Estonia">Estonia</option>
+<option value="Ethiopia">Ethiopia</option>
+<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
+<option value="Faroe Islands">Faroe Islands</option>
+<option value="Fiji">Fiji</option>
+<option value="Finland">Finland</option>
+<option value="France">France</option>
+<option value="French Guiana">French Guiana</option>
+<option value="French Polynesia">French Polynesia</option>
+<option value="French Southern Territories">French Southern Territories</option>
+<option value="Gabon">Gabon</option>
+<option value="Gambia">Gambia</option>
+<option value="Georgia">Georgia</option>
+<option value="Germany">Germany</option>
+<option value="Ghana">Ghana</option>
+<option value="Gibraltar">Gibraltar</option>
+<option value="Greece">Greece</option>
+<option value="Greenland">Greenland</option>
+<option value="Grenada">Grenada</option>
+<option value="Guadeloupe">Guadeloupe</option>
+<option value="Guam">Guam</option>
+<option value="Guatemala">Guatemala</option>
+<option value="Guernsey">Guernsey</option>
+<option value="Guinea">Guinea</option>
+<option value="Guinea-Bissau">Guinea-Bissau</option>
+<option value="Guyana">Guyana</option>
+<option value="Haiti">Haiti</option>
+<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
+<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
+<option value="Honduras">Honduras</option>
+<option value="Hong Kong">Hong Kong</option>
+<option value="Hungary">Hungary</option>
+<option value="Iceland">Iceland</option>
+<option value="India">India</option>
+<option value="Indonesia">Indonesia</option>
+<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
+<option value="Iraq">Iraq</option>
+<option value="Ireland">Ireland</option>
+<option value="Isle of Man">Isle of Man</option>
+<option value="Israel">Israel</option>
+<option value="Italy">Italy</option>
+<option value="Jamaica">Jamaica</option>
+<option value="Japan">Japan</option>
+<option value="Jersey">Jersey</option>
+<option value="Jordan">Jordan</option>
+<option value="Kazakhstan">Kazakhstan</option>
+<option value="Kenya">Kenya</option>
+<option value="Kiribati">Kiribati</option>
+<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
+<option value="Korea, Republic of">Korea, Republic of</option>
+<option value="Kuwait">Kuwait</option>
+<option value="Kyrgyzstan">Kyrgyzstan</option>
+<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
+<option value="Latvia">Latvia</option>
+<option value="Lebanon">Lebanon</option>
+<option value="Lesotho">Lesotho</option>
+<option value="Liberia">Liberia</option>
+<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
+<option value="Liechtenstein">Liechtenstein</option>
+<option value="Lithuania">Lithuania</option>
+<option value="Luxembourg">Luxembourg</option>
+<option value="Macao">Macao</option>
+<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
+<option value="Madagascar">Madagascar</option>
+<option value="Malawi">Malawi</option>
+<option value="Malaysia">Malaysia</option>
+<option value="Maldives">Maldives</option>
+<option value="Mali">Mali</option>
+<option value="Malta">Malta</option>
+<option value="Marshall Islands">Marshall Islands</option>
+<option value="Martinique">Martinique</option>
+<option value="Mauritania">Mauritania</option>
+<option value="Mauritius">Mauritius</option>
+<option value="Mayotte">Mayotte</option>
+<option value="Mexico">Mexico</option>
+<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
+<option value="Moldova, Republic of">Moldova, Republic of</option>
+<option value="Monaco">Monaco</option>
+<option value="Mongolia">Mongolia</option>
+<option value="Montenegro">Montenegro</option>
+<option value="Montserrat">Montserrat</option>
+<option value="Morocco">Morocco</option>
+<option value="Mozambique">Mozambique</option>
+<option value="Myanmar">Myanmar</option>
+<option value="Namibia">Namibia</option>
+<option value="Nauru">Nauru</option>
+<option value="Nepal">Nepal</option>
+<option value="Netherlands">Netherlands</option>
+<option value="Netherlands Antilles">Netherlands Antilles</option>
+<option value="New Caledonia">New Caledonia</option>
+<option selected="selected" value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option>
+<option value="Niger">Niger</option>
+<option value="Nigeria">Nigeria</option>
+<option value="Niue">Niue</option>
+<option value="Norfolk Island">Norfolk Island</option>
+<option value="Northern Mariana Islands">Northern Mariana Islands</option>
+<option value="Norway">Norway</option>
+<option value="Oman">Oman</option>
+<option value="Pakistan">Pakistan</option>
+<option value="Palau">Palau</option>
+<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
+<option value="Panama">Panama</option>
+<option value="Papua New Guinea">Papua New Guinea</option>
+<option value="Paraguay">Paraguay</option>
+<option value="Peru">Peru</option>
+<option value="Philippines">Philippines</option>
+<option value="Pitcairn">Pitcairn</option>
+<option value="Poland">Poland</option>
+<option value="Portugal">Portugal</option>
+<option value="Puerto Rico">Puerto Rico</option>
+<option value="Qatar">Qatar</option>
+<option value="Reunion">Reunion</option>
+<option value="Romania">Romania</option>
+<option value="Russian Federation">Russian Federation</option>
+<option value="Rwanda">Rwanda</option>
+<option value="Saint Barthelemy">Saint Barthelemy</option>
+<option value="Saint Helena">Saint Helena</option>
+<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
+<option value="Saint Lucia">Saint Lucia</option>
+<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
+<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
+<option value="Samoa">Samoa</option>
+<option value="San Marino">San Marino</option>
+<option value="Sao Tome and Principe">Sao Tome and Principe</option>
+<option value="Saudi Arabia">Saudi Arabia</option>
+<option value="Senegal">Senegal</option>
+<option value="Serbia">Serbia</option>
+<option value="Seychelles">Seychelles</option>
+<option value="Sierra Leone">Sierra Leone</option>
+<option value="Singapore">Singapore</option>
+<option value="Slovakia">Slovakia</option>
+<option value="Slovenia">Slovenia</option>
+<option value="Solomon Islands">Solomon Islands</option>
+<option value="Somalia">Somalia</option>
+<option value="South Africa">South Africa</option>
+<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
+<option value="Spain">Spain</option>
+<option value="Sri Lanka">Sri Lanka</option>
+<option value="Sudan">Sudan</option>
+<option value="Suriname">Suriname</option>
+<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
+<option value="Swaziland">Swaziland</option>
+<option value="Sweden">Sweden</option>
+<option value="Switzerland">Switzerland</option>
+<option value="Syrian Arab Republic">Syrian Arab Republic</option>
+<option value="Taiwan, Province of China">Taiwan, Province of China</option>
+<option value="Tajikistan">Tajikistan</option>
+<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
+<option value="Thailand">Thailand</option>
+<option value="Timor-Leste">Timor-Leste</option>
+<option value="Togo">Togo</option>
+<option value="Tokelau">Tokelau</option>
+<option value="Tonga">Tonga</option>
+<option value="Trinidad and Tobago">Trinidad and Tobago</option>
+<option value="Tunisia">Tunisia</option>
+<option value="Turkey">Turkey</option>
+<option value="Turkmenistan">Turkmenistan</option>
+<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
+<option value="Tuvalu">Tuvalu</option>
+<option value="Uganda">Uganda</option>
+<option value="Ukraine">Ukraine</option>
+<option value="United Arab Emirates">United Arab Emirates</option>
+<option value="United Kingdom">United Kingdom</option>
+<option value="United States">United States</option>
+<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
+<option value="Uruguay">Uruguay</option>
+<option value="Uzbekistan">Uzbekistan</option>
+<option value="Vanuatu">Vanuatu</option>
+<option value="Venezuela">Venezuela</option>
+<option value="Viet Nam">Viet Nam</option>
+<option value="Virgin Islands, British">Virgin Islands, British</option>
+<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
+<option value="Wallis and Futuna">Wallis and Futuna</option>
+<option value="Western Sahara">Western Sahara</option>
+<option value="Yemen">Yemen</option>
+<option value="Zambia">Zambia</option>
+<option value="Zimbabwe">Zimbabwe</option></select>
+COUNTRIES
+ assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"]))
+ end
+
+ def test_country_select_under_fields_for
+ @post = Post.new
+ @post.origin = "Australia"
+ expected_select = <<-COUNTRIES
+<select id="post_origin" name="post[origin]"><option value="Afghanistan">Afghanistan</option>
+<option value="Aland Islands">Aland Islands</option>
+<option value="Albania">Albania</option>
+<option value="Algeria">Algeria</option>
+<option value="American Samoa">American Samoa</option>
+<option value="Andorra">Andorra</option>
+<option value="Angola">Angola</option>
+<option value="Anguilla">Anguilla</option>
+<option value="Antarctica">Antarctica</option>
+<option value="Antigua And Barbuda">Antigua And Barbuda</option>
+<option value="Argentina">Argentina</option>
+<option value="Armenia">Armenia</option>
+<option value="Aruba">Aruba</option>
+<option selected="selected" value="Australia">Australia</option>
+<option value="Austria">Austria</option>
+<option value="Azerbaijan">Azerbaijan</option>
+<option value="Bahamas">Bahamas</option>
+<option value="Bahrain">Bahrain</option>
+<option value="Bangladesh">Bangladesh</option>
+<option value="Barbados">Barbados</option>
+<option value="Belarus">Belarus</option>
+<option value="Belgium">Belgium</option>
+<option value="Belize">Belize</option>
+<option value="Benin">Benin</option>
+<option value="Bermuda">Bermuda</option>
+<option value="Bhutan">Bhutan</option>
+<option value="Bolivia">Bolivia</option>
+<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
+<option value="Botswana">Botswana</option>
+<option value="Bouvet Island">Bouvet Island</option>
+<option value="Brazil">Brazil</option>
+<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
+<option value="Brunei Darussalam">Brunei Darussalam</option>
+<option value="Bulgaria">Bulgaria</option>
+<option value="Burkina Faso">Burkina Faso</option>
+<option value="Burundi">Burundi</option>
+<option value="Cambodia">Cambodia</option>
+<option value="Cameroon">Cameroon</option>
+<option value="Canada">Canada</option>
+<option value="Cape Verde">Cape Verde</option>
+<option value="Cayman Islands">Cayman Islands</option>
+<option value="Central African Republic">Central African Republic</option>
+<option value="Chad">Chad</option>
+<option value="Chile">Chile</option>
+<option value="China">China</option>
+<option value="Christmas Island">Christmas Island</option>
+<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
+<option value="Colombia">Colombia</option>
+<option value="Comoros">Comoros</option>
+<option value="Congo">Congo</option>
+<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
+<option value="Cook Islands">Cook Islands</option>
+<option value="Costa Rica">Costa Rica</option>
+<option value="Cote d'Ivoire">Cote d'Ivoire</option>
+<option value="Croatia">Croatia</option>
+<option value="Cuba">Cuba</option>
+<option value="Cyprus">Cyprus</option>
+<option value="Czech Republic">Czech Republic</option>
+<option value="Denmark">Denmark</option>
+<option value="Djibouti">Djibouti</option>
+<option value="Dominica">Dominica</option>
+<option value="Dominican Republic">Dominican Republic</option>
+<option value="Ecuador">Ecuador</option>
+<option value="Egypt">Egypt</option>
+<option value="El Salvador">El Salvador</option>
+<option value="Equatorial Guinea">Equatorial Guinea</option>
+<option value="Eritrea">Eritrea</option>
+<option value="Estonia">Estonia</option>
+<option value="Ethiopia">Ethiopia</option>
+<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
+<option value="Faroe Islands">Faroe Islands</option>
+<option value="Fiji">Fiji</option>
+<option value="Finland">Finland</option>
+<option value="France">France</option>
+<option value="French Guiana">French Guiana</option>
+<option value="French Polynesia">French Polynesia</option>
+<option value="French Southern Territories">French Southern Territories</option>
+<option value="Gabon">Gabon</option>
+<option value="Gambia">Gambia</option>
+<option value="Georgia">Georgia</option>
+<option value="Germany">Germany</option>
+<option value="Ghana">Ghana</option>
+<option value="Gibraltar">Gibraltar</option>
+<option value="Greece">Greece</option>
+<option value="Greenland">Greenland</option>
+<option value="Grenada">Grenada</option>
+<option value="Guadeloupe">Guadeloupe</option>
+<option value="Guam">Guam</option>
+<option value="Guatemala">Guatemala</option>
+<option value="Guernsey">Guernsey</option>
+<option value="Guinea">Guinea</option>
+<option value="Guinea-Bissau">Guinea-Bissau</option>
+<option value="Guyana">Guyana</option>
+<option value="Haiti">Haiti</option>
+<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
+<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
+<option value="Honduras">Honduras</option>
+<option value="Hong Kong">Hong Kong</option>
+<option value="Hungary">Hungary</option>
+<option value="Iceland">Iceland</option>
+<option value="India">India</option>
+<option value="Indonesia">Indonesia</option>
+<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
+<option value="Iraq">Iraq</option>
+<option value="Ireland">Ireland</option>
+<option value="Isle of Man">Isle of Man</option>
+<option value="Israel">Israel</option>
+<option value="Italy">Italy</option>
+<option value="Jamaica">Jamaica</option>
+<option value="Japan">Japan</option>
+<option value="Jersey">Jersey</option>
+<option value="Jordan">Jordan</option>
+<option value="Kazakhstan">Kazakhstan</option>
+<option value="Kenya">Kenya</option>
+<option value="Kiribati">Kiribati</option>
+<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
+<option value="Korea, Republic of">Korea, Republic of</option>
+<option value="Kuwait">Kuwait</option>
+<option value="Kyrgyzstan">Kyrgyzstan</option>
+<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
+<option value="Latvia">Latvia</option>
+<option value="Lebanon">Lebanon</option>
+<option value="Lesotho">Lesotho</option>
+<option value="Liberia">Liberia</option>
+<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
+<option value="Liechtenstein">Liechtenstein</option>
+<option value="Lithuania">Lithuania</option>
+<option value="Luxembourg">Luxembourg</option>
+<option value="Macao">Macao</option>
+<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
+<option value="Madagascar">Madagascar</option>
+<option value="Malawi">Malawi</option>
+<option value="Malaysia">Malaysia</option>
+<option value="Maldives">Maldives</option>
+<option value="Mali">Mali</option>
+<option value="Malta">Malta</option>
+<option value="Marshall Islands">Marshall Islands</option>
+<option value="Martinique">Martinique</option>
+<option value="Mauritania">Mauritania</option>
+<option value="Mauritius">Mauritius</option>
+<option value="Mayotte">Mayotte</option>
+<option value="Mexico">Mexico</option>
+<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
+<option value="Moldova, Republic of">Moldova, Republic of</option>
+<option value="Monaco">Monaco</option>
+<option value="Mongolia">Mongolia</option>
+<option value="Montenegro">Montenegro</option>
+<option value="Montserrat">Montserrat</option>
+<option value="Morocco">Morocco</option>
+<option value="Mozambique">Mozambique</option>
+<option value="Myanmar">Myanmar</option>
+<option value="Namibia">Namibia</option>
+<option value="Nauru">Nauru</option>
+<option value="Nepal">Nepal</option>
+<option value="Netherlands">Netherlands</option>
+<option value="Netherlands Antilles">Netherlands Antilles</option>
+<option value="New Caledonia">New Caledonia</option>
+<option value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option>
+<option value="Niger">Niger</option>
+<option value="Nigeria">Nigeria</option>
+<option value="Niue">Niue</option>
+<option value="Norfolk Island">Norfolk Island</option>
+<option value="Northern Mariana Islands">Northern Mariana Islands</option>
+<option value="Norway">Norway</option>
+<option value="Oman">Oman</option>
+<option value="Pakistan">Pakistan</option>
+<option value="Palau">Palau</option>
+<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
+<option value="Panama">Panama</option>
+<option value="Papua New Guinea">Papua New Guinea</option>
+<option value="Paraguay">Paraguay</option>
+<option value="Peru">Peru</option>
+<option value="Philippines">Philippines</option>
+<option value="Pitcairn">Pitcairn</option>
+<option value="Poland">Poland</option>
+<option value="Portugal">Portugal</option>
+<option value="Puerto Rico">Puerto Rico</option>
+<option value="Qatar">Qatar</option>
+<option value="Reunion">Reunion</option>
+<option value="Romania">Romania</option>
+<option value="Russian Federation">Russian Federation</option>
+<option value="Rwanda">Rwanda</option>
+<option value="Saint Barthelemy">Saint Barthelemy</option>
+<option value="Saint Helena">Saint Helena</option>
+<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
+<option value="Saint Lucia">Saint Lucia</option>
+<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
+<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
+<option value="Samoa">Samoa</option>
+<option value="San Marino">San Marino</option>
+<option value="Sao Tome and Principe">Sao Tome and Principe</option>
+<option value="Saudi Arabia">Saudi Arabia</option>
+<option value="Senegal">Senegal</option>
+<option value="Serbia">Serbia</option>
+<option value="Seychelles">Seychelles</option>
+<option value="Sierra Leone">Sierra Leone</option>
+<option value="Singapore">Singapore</option>
+<option value="Slovakia">Slovakia</option>
+<option value="Slovenia">Slovenia</option>
+<option value="Solomon Islands">Solomon Islands</option>
+<option value="Somalia">Somalia</option>
+<option value="South Africa">South Africa</option>
+<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
+<option value="Spain">Spain</option>
+<option value="Sri Lanka">Sri Lanka</option>
+<option value="Sudan">Sudan</option>
+<option value="Suriname">Suriname</option>
+<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
+<option value="Swaziland">Swaziland</option>
+<option value="Sweden">Sweden</option>
+<option value="Switzerland">Switzerland</option>
+<option value="Syrian Arab Republic">Syrian Arab Republic</option>
+<option value="Taiwan, Province of China">Taiwan, Province of China</option>
+<option value="Tajikistan">Tajikistan</option>
+<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
+<option value="Thailand">Thailand</option>
+<option value="Timor-Leste">Timor-Leste</option>
+<option value="Togo">Togo</option>
+<option value="Tokelau">Tokelau</option>
+<option value="Tonga">Tonga</option>
+<option value="Trinidad and Tobago">Trinidad and Tobago</option>
+<option value="Tunisia">Tunisia</option>
+<option value="Turkey">Turkey</option>
+<option value="Turkmenistan">Turkmenistan</option>
+<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
+<option value="Tuvalu">Tuvalu</option>
+<option value="Uganda">Uganda</option>
+<option value="Ukraine">Ukraine</option>
+<option value="United Arab Emirates">United Arab Emirates</option>
+<option value="United Kingdom">United Kingdom</option>
+<option value="United States">United States</option>
+<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
+<option value="Uruguay">Uruguay</option>
+<option value="Uzbekistan">Uzbekistan</option>
+<option value="Vanuatu">Vanuatu</option>
+<option value="Venezuela">Venezuela</option>
+<option value="Viet Nam">Viet Nam</option>
+<option value="Virgin Islands, British">Virgin Islands, British</option>
+<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
+<option value="Wallis and Futuna">Wallis and Futuna</option>
+<option value="Western Sahara">Western Sahara</option>
+<option value="Yemen">Yemen</option>
+<option value="Zambia">Zambia</option>
+<option value="Zimbabwe">Zimbabwe</option></select>
+COUNTRIES
+
+ fields_for :post, @post do |f|
+ concat f.country_select("origin")
+ end
+
+ assert_dom_equal(expected_select[0..-2], output_buffer)
+ end
+
+ def test_country_select_under_fields_for_with_index
+ @post = Post.new
+ @post.origin = "United States"
+ expected_select = <<-COUNTRIES
+<select id="post_325_origin" name="post[325][origin]"><option value="Afghanistan">Afghanistan</option>
+<option value="Aland Islands">Aland Islands</option>
+<option value="Albania">Albania</option>
+<option value="Algeria">Algeria</option>
+<option value="American Samoa">American Samoa</option>
+<option value="Andorra">Andorra</option>
+<option value="Angola">Angola</option>
+<option value="Anguilla">Anguilla</option>
+<option value="Antarctica">Antarctica</option>
+<option value="Antigua And Barbuda">Antigua And Barbuda</option>
+<option value="Argentina">Argentina</option>
+<option value="Armenia">Armenia</option>
+<option value="Aruba">Aruba</option>
+<option value="Australia">Australia</option>
+<option value="Austria">Austria</option>
+<option value="Azerbaijan">Azerbaijan</option>
+<option value="Bahamas">Bahamas</option>
+<option value="Bahrain">Bahrain</option>
+<option value="Bangladesh">Bangladesh</option>
+<option value="Barbados">Barbados</option>
+<option value="Belarus">Belarus</option>
+<option value="Belgium">Belgium</option>
+<option value="Belize">Belize</option>
+<option value="Benin">Benin</option>
+<option value="Bermuda">Bermuda</option>
+<option value="Bhutan">Bhutan</option>
+<option value="Bolivia">Bolivia</option>
+<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
+<option value="Botswana">Botswana</option>
+<option value="Bouvet Island">Bouvet Island</option>
+<option value="Brazil">Brazil</option>
+<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
+<option value="Brunei Darussalam">Brunei Darussalam</option>
+<option value="Bulgaria">Bulgaria</option>
+<option value="Burkina Faso">Burkina Faso</option>
+<option value="Burundi">Burundi</option>
+<option value="Cambodia">Cambodia</option>
+<option value="Cameroon">Cameroon</option>
+<option value="Canada">Canada</option>
+<option value="Cape Verde">Cape Verde</option>
+<option value="Cayman Islands">Cayman Islands</option>
+<option value="Central African Republic">Central African Republic</option>
+<option value="Chad">Chad</option>
+<option value="Chile">Chile</option>
+<option value="China">China</option>
+<option value="Christmas Island">Christmas Island</option>
+<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
+<option value="Colombia">Colombia</option>
+<option value="Comoros">Comoros</option>
+<option value="Congo">Congo</option>
+<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
+<option value="Cook Islands">Cook Islands</option>
+<option value="Costa Rica">Costa Rica</option>
+<option value="Cote d'Ivoire">Cote d'Ivoire</option>
+<option value="Croatia">Croatia</option>
+<option value="Cuba">Cuba</option>
+<option value="Cyprus">Cyprus</option>
+<option value="Czech Republic">Czech Republic</option>
+<option value="Denmark">Denmark</option>
+<option value="Djibouti">Djibouti</option>
+<option value="Dominica">Dominica</option>
+<option value="Dominican Republic">Dominican Republic</option>
+<option value="Ecuador">Ecuador</option>
+<option value="Egypt">Egypt</option>
+<option value="El Salvador">El Salvador</option>
+<option value="Equatorial Guinea">Equatorial Guinea</option>
+<option value="Eritrea">Eritrea</option>
+<option value="Estonia">Estonia</option>
+<option value="Ethiopia">Ethiopia</option>
+<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
+<option value="Faroe Islands">Faroe Islands</option>
+<option value="Fiji">Fiji</option>
+<option value="Finland">Finland</option>
+<option value="France">France</option>
+<option value="French Guiana">French Guiana</option>
+<option value="French Polynesia">French Polynesia</option>
+<option value="French Southern Territories">French Southern Territories</option>
+<option value="Gabon">Gabon</option>
+<option value="Gambia">Gambia</option>
+<option value="Georgia">Georgia</option>
+<option value="Germany">Germany</option>
+<option value="Ghana">Ghana</option>
+<option value="Gibraltar">Gibraltar</option>
+<option value="Greece">Greece</option>
+<option value="Greenland">Greenland</option>
+<option value="Grenada">Grenada</option>
+<option value="Guadeloupe">Guadeloupe</option>
+<option value="Guam">Guam</option>
+<option value="Guatemala">Guatemala</option>
+<option value="Guernsey">Guernsey</option>
+<option value="Guinea">Guinea</option>
+<option value="Guinea-Bissau">Guinea-Bissau</option>
+<option value="Guyana">Guyana</option>
+<option value="Haiti">Haiti</option>
+<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
+<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
+<option value="Honduras">Honduras</option>
+<option value="Hong Kong">Hong Kong</option>
+<option value="Hungary">Hungary</option>
+<option value="Iceland">Iceland</option>
+<option value="India">India</option>
+<option value="Indonesia">Indonesia</option>
+<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
+<option value="Iraq">Iraq</option>
+<option value="Ireland">Ireland</option>
+<option value="Isle of Man">Isle of Man</option>
+<option value="Israel">Israel</option>
+<option value="Italy">Italy</option>
+<option value="Jamaica">Jamaica</option>
+<option value="Japan">Japan</option>
+<option value="Jersey">Jersey</option>
+<option value="Jordan">Jordan</option>
+<option value="Kazakhstan">Kazakhstan</option>
+<option value="Kenya">Kenya</option>
+<option value="Kiribati">Kiribati</option>
+<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
+<option value="Korea, Republic of">Korea, Republic of</option>
+<option value="Kuwait">Kuwait</option>
+<option value="Kyrgyzstan">Kyrgyzstan</option>
+<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
+<option value="Latvia">Latvia</option>
+<option value="Lebanon">Lebanon</option>
+<option value="Lesotho">Lesotho</option>
+<option value="Liberia">Liberia</option>
+<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
+<option value="Liechtenstein">Liechtenstein</option>
+<option value="Lithuania">Lithuania</option>
+<option value="Luxembourg">Luxembourg</option>
+<option value="Macao">Macao</option>
+<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
+<option value="Madagascar">Madagascar</option>
+<option value="Malawi">Malawi</option>
+<option value="Malaysia">Malaysia</option>
+<option value="Maldives">Maldives</option>
+<option value="Mali">Mali</option>
+<option value="Malta">Malta</option>
+<option value="Marshall Islands">Marshall Islands</option>
+<option value="Martinique">Martinique</option>
+<option value="Mauritania">Mauritania</option>
+<option value="Mauritius">Mauritius</option>
+<option value="Mayotte">Mayotte</option>
+<option value="Mexico">Mexico</option>
+<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
+<option value="Moldova, Republic of">Moldova, Republic of</option>
+<option value="Monaco">Monaco</option>
+<option value="Mongolia">Mongolia</option>
+<option value="Montenegro">Montenegro</option>
+<option value="Montserrat">Montserrat</option>
+<option value="Morocco">Morocco</option>
+<option value="Mozambique">Mozambique</option>
+<option value="Myanmar">Myanmar</option>
+<option value="Namibia">Namibia</option>
+<option value="Nauru">Nauru</option>
+<option value="Nepal">Nepal</option>
+<option value="Netherlands">Netherlands</option>
+<option value="Netherlands Antilles">Netherlands Antilles</option>
+<option value="New Caledonia">New Caledonia</option>
+<option value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option>
+<option value="Niger">Niger</option>
+<option value="Nigeria">Nigeria</option>
+<option value="Niue">Niue</option>
+<option value="Norfolk Island">Norfolk Island</option>
+<option value="Northern Mariana Islands">Northern Mariana Islands</option>
+<option value="Norway">Norway</option>
+<option value="Oman">Oman</option>
+<option value="Pakistan">Pakistan</option>
+<option value="Palau">Palau</option>
+<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
+<option value="Panama">Panama</option>
+<option value="Papua New Guinea">Papua New Guinea</option>
+<option value="Paraguay">Paraguay</option>
+<option value="Peru">Peru</option>
+<option value="Philippines">Philippines</option>
+<option value="Pitcairn">Pitcairn</option>
+<option value="Poland">Poland</option>
+<option value="Portugal">Portugal</option>
+<option value="Puerto Rico">Puerto Rico</option>
+<option value="Qatar">Qatar</option>
+<option value="Reunion">Reunion</option>
+<option value="Romania">Romania</option>
+<option value="Russian Federation">Russian Federation</option>
+<option value="Rwanda">Rwanda</option>
+<option value="Saint Barthelemy">Saint Barthelemy</option>
+<option value="Saint Helena">Saint Helena</option>
+<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
+<option value="Saint Lucia">Saint Lucia</option>
+<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
+<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
+<option value="Samoa">Samoa</option>
+<option value="San Marino">San Marino</option>
+<option value="Sao Tome and Principe">Sao Tome and Principe</option>
+<option value="Saudi Arabia">Saudi Arabia</option>
+<option value="Senegal">Senegal</option>
+<option value="Serbia">Serbia</option>
+<option value="Seychelles">Seychelles</option>
+<option value="Sierra Leone">Sierra Leone</option>
+<option value="Singapore">Singapore</option>
+<option value="Slovakia">Slovakia</option>
+<option value="Slovenia">Slovenia</option>
+<option value="Solomon Islands">Solomon Islands</option>
+<option value="Somalia">Somalia</option>
+<option value="South Africa">South Africa</option>
+<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
+<option value="Spain">Spain</option>
+<option value="Sri Lanka">Sri Lanka</option>
+<option value="Sudan">Sudan</option>
+<option value="Suriname">Suriname</option>
+<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
+<option value="Swaziland">Swaziland</option>
+<option value="Sweden">Sweden</option>
+<option value="Switzerland">Switzerland</option>
+<option value="Syrian Arab Republic">Syrian Arab Republic</option>
+<option value="Taiwan, Province of China">Taiwan, Province of China</option>
+<option value="Tajikistan">Tajikistan</option>
+<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
+<option value="Thailand">Thailand</option>
+<option value="Timor-Leste">Timor-Leste</option>
+<option value="Togo">Togo</option>
+<option value="Tokelau">Tokelau</option>
+<option value="Tonga">Tonga</option>
+<option value="Trinidad and Tobago">Trinidad and Tobago</option>
+<option value="Tunisia">Tunisia</option>
+<option value="Turkey">Turkey</option>
+<option value="Turkmenistan">Turkmenistan</option>
+<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
+<option value="Tuvalu">Tuvalu</option>
+<option value="Uganda">Uganda</option>
+<option value="Ukraine">Ukraine</option>
+<option value="United Arab Emirates">United Arab Emirates</option>
+<option value="United Kingdom">United Kingdom</option>
+<option selected="selected" value="United States">United States</option>
+<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
+<option value="Uruguay">Uruguay</option>
+<option value="Uzbekistan">Uzbekistan</option>
+<option value="Vanuatu">Vanuatu</option>
+<option value="Venezuela">Venezuela</option>
+<option value="Viet Nam">Viet Nam</option>
+<option value="Virgin Islands, British">Virgin Islands, British</option>
+<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
+<option value="Wallis and Futuna">Wallis and Futuna</option>
+<option value="Western Sahara">Western Sahara</option>
+<option value="Yemen">Yemen</option>
+<option value="Zambia">Zambia</option>
+<option value="Zimbabwe">Zimbabwe</option></select>
+COUNTRIES
+
+ fields_for :post, @post, :index => 325 do |f|
+ concat f.country_select("origin")
+ end
+
+ assert_dom_equal(expected_select[0..-2], output_buffer)
+ end
+
+ def test_country_select_under_fields_for_with_auto_index
+ @post = Post.new
+ @post.origin = "Iraq"
+ def @post.to_param; 325; end
+
+ expected_select = <<-COUNTRIES
+<select id="post_325_origin" name="post[325][origin]"><option value="Afghanistan">Afghanistan</option>
+<option value="Aland Islands">Aland Islands</option>
+<option value="Albania">Albania</option>
+<option value="Algeria">Algeria</option>
+<option value="American Samoa">American Samoa</option>
+<option value="Andorra">Andorra</option>
+<option value="Angola">Angola</option>
+<option value="Anguilla">Anguilla</option>
+<option value="Antarctica">Antarctica</option>
+<option value="Antigua And Barbuda">Antigua And Barbuda</option>
+<option value="Argentina">Argentina</option>
+<option value="Armenia">Armenia</option>
+<option value="Aruba">Aruba</option>
+<option value="Australia">Australia</option>
+<option value="Austria">Austria</option>
+<option value="Azerbaijan">Azerbaijan</option>
+<option value="Bahamas">Bahamas</option>
+<option value="Bahrain">Bahrain</option>
+<option value="Bangladesh">Bangladesh</option>
+<option value="Barbados">Barbados</option>
+<option value="Belarus">Belarus</option>
+<option value="Belgium">Belgium</option>
+<option value="Belize">Belize</option>
+<option value="Benin">Benin</option>
+<option value="Bermuda">Bermuda</option>
+<option value="Bhutan">Bhutan</option>
+<option value="Bolivia">Bolivia</option>
+<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
+<option value="Botswana">Botswana</option>
+<option value="Bouvet Island">Bouvet Island</option>
+<option value="Brazil">Brazil</option>
+<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
+<option value="Brunei Darussalam">Brunei Darussalam</option>
+<option value="Bulgaria">Bulgaria</option>
+<option value="Burkina Faso">Burkina Faso</option>
+<option value="Burundi">Burundi</option>
+<option value="Cambodia">Cambodia</option>
+<option value="Cameroon">Cameroon</option>
+<option value="Canada">Canada</option>
+<option value="Cape Verde">Cape Verde</option>
+<option value="Cayman Islands">Cayman Islands</option>
+<option value="Central African Republic">Central African Republic</option>
+<option value="Chad">Chad</option>
+<option value="Chile">Chile</option>
+<option value="China">China</option>
+<option value="Christmas Island">Christmas Island</option>
+<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
+<option value="Colombia">Colombia</option>
+<option value="Comoros">Comoros</option>
+<option value="Congo">Congo</option>
+<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
+<option value="Cook Islands">Cook Islands</option>
+<option value="Costa Rica">Costa Rica</option>
+<option value="Cote d'Ivoire">Cote d'Ivoire</option>
+<option value="Croatia">Croatia</option>
+<option value="Cuba">Cuba</option>
+<option value="Cyprus">Cyprus</option>
+<option value="Czech Republic">Czech Republic</option>
+<option value="Denmark">Denmark</option>
+<option value="Djibouti">Djibouti</option>
+<option value="Dominica">Dominica</option>
+<option value="Dominican Republic">Dominican Republic</option>
+<option value="Ecuador">Ecuador</option>
+<option value="Egypt">Egypt</option>
+<option value="El Salvador">El Salvador</option>
+<option value="Equatorial Guinea">Equatorial Guinea</option>
+<option value="Eritrea">Eritrea</option>
+<option value="Estonia">Estonia</option>
+<option value="Ethiopia">Ethiopia</option>
+<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
+<option value="Faroe Islands">Faroe Islands</option>
+<option value="Fiji">Fiji</option>
+<option value="Finland">Finland</option>
+<option value="France">France</option>
+<option value="French Guiana">French Guiana</option>
+<option value="French Polynesia">French Polynesia</option>
+<option value="French Southern Territories">French Southern Territories</option>
+<option value="Gabon">Gabon</option>
+<option value="Gambia">Gambia</option>
+<option value="Georgia">Georgia</option>
+<option value="Germany">Germany</option>
+<option value="Ghana">Ghana</option>
+<option value="Gibraltar">Gibraltar</option>
+<option value="Greece">Greece</option>
+<option value="Greenland">Greenland</option>
+<option value="Grenada">Grenada</option>
+<option value="Guadeloupe">Guadeloupe</option>
+<option value="Guam">Guam</option>
+<option value="Guatemala">Guatemala</option>
+<option value="Guernsey">Guernsey</option>
+<option value="Guinea">Guinea</option>
+<option value="Guinea-Bissau">Guinea-Bissau</option>
+<option value="Guyana">Guyana</option>
+<option value="Haiti">Haiti</option>
+<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
+<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
+<option value="Honduras">Honduras</option>
+<option value="Hong Kong">Hong Kong</option>
+<option value="Hungary">Hungary</option>
+<option value="Iceland">Iceland</option>
+<option value="India">India</option>
+<option value="Indonesia">Indonesia</option>
+<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
+<option selected="selected" value="Iraq">Iraq</option>
+<option value="Ireland">Ireland</option>
+<option value="Isle of Man">Isle of Man</option>
+<option value="Israel">Israel</option>
+<option value="Italy">Italy</option>
+<option value="Jamaica">Jamaica</option>
+<option value="Japan">Japan</option>
+<option value="Jersey">Jersey</option>
+<option value="Jordan">Jordan</option>
+<option value="Kazakhstan">Kazakhstan</option>
+<option value="Kenya">Kenya</option>
+<option value="Kiribati">Kiribati</option>
+<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
+<option value="Korea, Republic of">Korea, Republic of</option>
+<option value="Kuwait">Kuwait</option>
+<option value="Kyrgyzstan">Kyrgyzstan</option>
+<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
+<option value="Latvia">Latvia</option>
+<option value="Lebanon">Lebanon</option>
+<option value="Lesotho">Lesotho</option>
+<option value="Liberia">Liberia</option>
+<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
+<option value="Liechtenstein">Liechtenstein</option>
+<option value="Lithuania">Lithuania</option>
+<option value="Luxembourg">Luxembourg</option>
+<option value="Macao">Macao</option>
+<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
+<option value="Madagascar">Madagascar</option>
+<option value="Malawi">Malawi</option>
+<option value="Malaysia">Malaysia</option>
+<option value="Maldives">Maldives</option>
+<option value="Mali">Mali</option>
+<option value="Malta">Malta</option>
+<option value="Marshall Islands">Marshall Islands</option>
+<option value="Martinique">Martinique</option>
+<option value="Mauritania">Mauritania</option>
+<option value="Mauritius">Mauritius</option>
+<option value="Mayotte">Mayotte</option>
+<option value="Mexico">Mexico</option>
+<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
+<option value="Moldova, Republic of">Moldova, Republic of</option>
+<option value="Monaco">Monaco</option>
+<option value="Mongolia">Mongolia</option>
+<option value="Montenegro">Montenegro</option>
+<option value="Montserrat">Montserrat</option>
+<option value="Morocco">Morocco</option>
+<option value="Mozambique">Mozambique</option>
+<option value="Myanmar">Myanmar</option>
+<option value="Namibia">Namibia</option>
+<option value="Nauru">Nauru</option>
+<option value="Nepal">Nepal</option>
+<option value="Netherlands">Netherlands</option>
+<option value="Netherlands Antilles">Netherlands Antilles</option>
+<option value="New Caledonia">New Caledonia</option>
+<option value="New Zealand">New Zealand</option>
+<option value="Nicaragua">Nicaragua</option>
+<option value="Niger">Niger</option>
+<option value="Nigeria">Nigeria</option>
+<option value="Niue">Niue</option>
+<option value="Norfolk Island">Norfolk Island</option>
+<option value="Northern Mariana Islands">Northern Mariana Islands</option>
+<option value="Norway">Norway</option>
+<option value="Oman">Oman</option>
+<option value="Pakistan">Pakistan</option>
+<option value="Palau">Palau</option>
+<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
+<option value="Panama">Panama</option>
+<option value="Papua New Guinea">Papua New Guinea</option>
+<option value="Paraguay">Paraguay</option>
+<option value="Peru">Peru</option>
+<option value="Philippines">Philippines</option>
+<option value="Pitcairn">Pitcairn</option>
+<option value="Poland">Poland</option>
+<option value="Portugal">Portugal</option>
+<option value="Puerto Rico">Puerto Rico</option>
+<option value="Qatar">Qatar</option>
+<option value="Reunion">Reunion</option>
+<option value="Romania">Romania</option>
+<option value="Russian Federation">Russian Federation</option>
+<option value="Rwanda">Rwanda</option>
+<option value="Saint Barthelemy">Saint Barthelemy</option>
+<option value="Saint Helena">Saint Helena</option>
+<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
+<option value="Saint Lucia">Saint Lucia</option>
+<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
+<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
+<option value="Samoa">Samoa</option>
+<option value="San Marino">San Marino</option>
+<option value="Sao Tome and Principe">Sao Tome and Principe</option>
+<option value="Saudi Arabia">Saudi Arabia</option>
+<option value="Senegal">Senegal</option>
+<option value="Serbia">Serbia</option>
+<option value="Seychelles">Seychelles</option>
+<option value="Sierra Leone">Sierra Leone</option>
+<option value="Singapore">Singapore</option>
+<option value="Slovakia">Slovakia</option>
+<option value="Slovenia">Slovenia</option>
+<option value="Solomon Islands">Solomon Islands</option>
+<option value="Somalia">Somalia</option>
+<option value="South Africa">South Africa</option>
+<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
+<option value="Spain">Spain</option>
+<option value="Sri Lanka">Sri Lanka</option>
+<option value="Sudan">Sudan</option>
+<option value="Suriname">Suriname</option>
+<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
+<option value="Swaziland">Swaziland</option>
+<option value="Sweden">Sweden</option>
+<option value="Switzerland">Switzerland</option>
+<option value="Syrian Arab Republic">Syrian Arab Republic</option>
+<option value="Taiwan, Province of China">Taiwan, Province of China</option>
+<option value="Tajikistan">Tajikistan</option>
+<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
+<option value="Thailand">Thailand</option>
+<option value="Timor-Leste">Timor-Leste</option>
+<option value="Togo">Togo</option>
+<option value="Tokelau">Tokelau</option>
+<option value="Tonga">Tonga</option>
+<option value="Trinidad and Tobago">Trinidad and Tobago</option>
+<option value="Tunisia">Tunisia</option>
+<option value="Turkey">Turkey</option>
+<option value="Turkmenistan">Turkmenistan</option>
+<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
+<option value="Tuvalu">Tuvalu</option>
+<option value="Uganda">Uganda</option>
+<option value="Ukraine">Ukraine</option>
+<option value="United Arab Emirates">United Arab Emirates</option>
+<option value="United Kingdom">United Kingdom</option>
+<option value="United States">United States</option>
+<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
+<option value="Uruguay">Uruguay</option>
+<option value="Uzbekistan">Uzbekistan</option>
+<option value="Vanuatu">Vanuatu</option>
+<option value="Venezuela">Venezuela</option>
+<option value="Viet Nam">Viet Nam</option>
+<option value="Virgin Islands, British">Virgin Islands, British</option>
+<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
+<option value="Wallis and Futuna">Wallis and Futuna</option>
+<option value="Western Sahara">Western Sahara</option>
+<option value="Yemen">Yemen</option>
+<option value="Zambia">Zambia</option>
+<option value="Zimbabwe">Zimbabwe</option></select>
+COUNTRIES
+
+ fields_for "post[]", @post do |f|
+ concat f.country_select("origin")
+ end
+
+ assert_dom_equal(expected_select[0..-2], output_buffer)
+ end
+
+end \ No newline at end of file
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 39649c3622..52e8bf376a 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -6,7 +6,7 @@ silence_warnings do
alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast)
alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast)
alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast)
- alias_method :secret?, :secret
+ alias_method :secret?, :secret
def new_record=(boolean)
@new_record = boolean
@@ -22,6 +22,7 @@ silence_warnings do
attr_reader :post_id
def save; @id = 1; @post_id = 1 end
def new_record?; @id.nil? end
+ def to_param; @id; end
def name
@id.nil? ? 'new comment' : "comment ##{@id}"
end
@@ -30,7 +31,6 @@ end
class Comment::Nested < Comment; end
-
class FormHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormHelper
@@ -447,6 +447,117 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_nested_fields_for_with_nested_collections
+ form_for('post[]', @post) do |f|
+ concat f.text_field(:title)
+ f.fields_for('comment[]', @comment) do |c|
+ concat c.text_field(:name)
+ end
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
+ "<input name='post[123][comment][][name]' size='30' type='text' id='post_123_comment__name' value='new comment' />" +
+ "</form>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_index
+ form_for('post', @post, :index => 1) do |c|
+ concat c.text_field(:title)
+ c.fields_for('comment', @comment, :index => 1) do |r|
+ concat r.text_field(:name)
+ end
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[1][title]' size='30' type='text' id='post_1_title' value='Hello World' />" +
+ "<input name='post[1][comment][1][name]' size='30' type='text' id='post_1_comment_1_name' value='new comment' />" +
+ "</form>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_index
+ form_for(:post, @post, :index => 1) do |f|
+ f.fields_for(:comment, @post) do |c|
+ concat c.text_field(:title)
+ end
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[1][comment][title]' size='30' type='text' id='post_1_comment_title' value='Hello World' />" +
+ "</form>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_index_on_both
+ form_for(:post, @post, :index => 1) do |f|
+ f.fields_for(:comment, @post, :index => 5) do |c|
+ concat c.text_field(:title)
+ end
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[1][comment][5][title]' size='30' type='text' id='post_1_comment_5_title' value='Hello World' />" +
+ "</form>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_auto_index
+ form_for("post[]", @post) do |f|
+ f.fields_for(:comment, @post) do |c|
+ concat c.text_field(:title)
+ end
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[123][comment][title]' size='30' type='text' id='post_123_comment_title' value='Hello World' />" +
+ "</form>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_auto_index_on_both
+ form_for("post[]", @post) do |f|
+ f.fields_for("comment[]", @post) do |c|
+ concat c.text_field(:title)
+ end
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[123][comment][123][title]' size='30' type='text' id='post_123_comment_123_title' value='Hello World' />" +
+ "</form>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_index_and_auto_index
+ form_for("post[]", @post) do |f|
+ f.fields_for(:comment, @post, :index => 5) do |c|
+ concat c.text_field(:title)
+ end
+ end
+
+ form_for(:post, @post, :index => 1) do |f|
+ f.fields_for("comment[]", @post) do |c|
+ concat c.text_field(:title)
+ end
+ end
+
+ expected = "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[123][comment][5][title]' size='30' type='text' id='post_123_comment_5_title' value='Hello World' />" +
+ "</form>" +
+ "<form action='http://www.example.com' method='post'>" +
+ "<input name='post[1][comment][123][title]' size='30' type='text' id='post_1_comment_123_title' value='Hello World' />" +
+ "</form>"
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_fields_for
fields_for(:post, @post) do |f|
concat f.text_field(:title)
@@ -831,7 +942,6 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
-
protected
def comments_path(post)
"/posts/#{post.id}/comments"
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 9dd43d7b4f..a33eb85b66 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -472,1545 +472,6 @@ uses_mocha "FormOptionsHelperTest" do
assert_dom_equal expected, collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true, :name => 'post[author_name][]' }, :multiple => true)
end
- def test_country_select
- @post = Post.new
- @post.origin = "Denmark"
- expected_select = <<-COUNTRIES
-<select id="post_origin" name="post[origin]"><option value="Afghanistan">Afghanistan</option>
-<option value="Aland Islands">Aland Islands</option>
-<option value="Albania">Albania</option>
-<option value="Algeria">Algeria</option>
-<option value="American Samoa">American Samoa</option>
-<option value="Andorra">Andorra</option>
-<option value="Angola">Angola</option>
-<option value="Anguilla">Anguilla</option>
-<option value="Antarctica">Antarctica</option>
-<option value="Antigua And Barbuda">Antigua And Barbuda</option>
-<option value="Argentina">Argentina</option>
-<option value="Armenia">Armenia</option>
-<option value="Aruba">Aruba</option>
-<option value="Australia">Australia</option>
-<option value="Austria">Austria</option>
-<option value="Azerbaijan">Azerbaijan</option>
-<option value="Bahamas">Bahamas</option>
-<option value="Bahrain">Bahrain</option>
-<option value="Bangladesh">Bangladesh</option>
-<option value="Barbados">Barbados</option>
-<option value="Belarus">Belarus</option>
-<option value="Belgium">Belgium</option>
-<option value="Belize">Belize</option>
-<option value="Benin">Benin</option>
-<option value="Bermuda">Bermuda</option>
-<option value="Bhutan">Bhutan</option>
-<option value="Bolivia">Bolivia</option>
-<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
-<option value="Botswana">Botswana</option>
-<option value="Bouvet Island">Bouvet Island</option>
-<option value="Brazil">Brazil</option>
-<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
-<option value="Brunei Darussalam">Brunei Darussalam</option>
-<option value="Bulgaria">Bulgaria</option>
-<option value="Burkina Faso">Burkina Faso</option>
-<option value="Burundi">Burundi</option>
-<option value="Cambodia">Cambodia</option>
-<option value="Cameroon">Cameroon</option>
-<option value="Canada">Canada</option>
-<option value="Cape Verde">Cape Verde</option>
-<option value="Cayman Islands">Cayman Islands</option>
-<option value="Central African Republic">Central African Republic</option>
-<option value="Chad">Chad</option>
-<option value="Chile">Chile</option>
-<option value="China">China</option>
-<option value="Christmas Island">Christmas Island</option>
-<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
-<option value="Colombia">Colombia</option>
-<option value="Comoros">Comoros</option>
-<option value="Congo">Congo</option>
-<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
-<option value="Cook Islands">Cook Islands</option>
-<option value="Costa Rica">Costa Rica</option>
-<option value="Cote d'Ivoire">Cote d'Ivoire</option>
-<option value="Croatia">Croatia</option>
-<option value="Cuba">Cuba</option>
-<option value="Cyprus">Cyprus</option>
-<option value="Czech Republic">Czech Republic</option>
-<option selected="selected" value="Denmark">Denmark</option>
-<option value="Djibouti">Djibouti</option>
-<option value="Dominica">Dominica</option>
-<option value="Dominican Republic">Dominican Republic</option>
-<option value="Ecuador">Ecuador</option>
-<option value="Egypt">Egypt</option>
-<option value="El Salvador">El Salvador</option>
-<option value="Equatorial Guinea">Equatorial Guinea</option>
-<option value="Eritrea">Eritrea</option>
-<option value="Estonia">Estonia</option>
-<option value="Ethiopia">Ethiopia</option>
-<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
-<option value="Faroe Islands">Faroe Islands</option>
-<option value="Fiji">Fiji</option>
-<option value="Finland">Finland</option>
-<option value="France">France</option>
-<option value="French Guiana">French Guiana</option>
-<option value="French Polynesia">French Polynesia</option>
-<option value="French Southern Territories">French Southern Territories</option>
-<option value="Gabon">Gabon</option>
-<option value="Gambia">Gambia</option>
-<option value="Georgia">Georgia</option>
-<option value="Germany">Germany</option>
-<option value="Ghana">Ghana</option>
-<option value="Gibraltar">Gibraltar</option>
-<option value="Greece">Greece</option>
-<option value="Greenland">Greenland</option>
-<option value="Grenada">Grenada</option>
-<option value="Guadeloupe">Guadeloupe</option>
-<option value="Guam">Guam</option>
-<option value="Guatemala">Guatemala</option>
-<option value="Guernsey">Guernsey</option>
-<option value="Guinea">Guinea</option>
-<option value="Guinea-Bissau">Guinea-Bissau</option>
-<option value="Guyana">Guyana</option>
-<option value="Haiti">Haiti</option>
-<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
-<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
-<option value="Honduras">Honduras</option>
-<option value="Hong Kong">Hong Kong</option>
-<option value="Hungary">Hungary</option>
-<option value="Iceland">Iceland</option>
-<option value="India">India</option>
-<option value="Indonesia">Indonesia</option>
-<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
-<option value="Iraq">Iraq</option>
-<option value="Ireland">Ireland</option>
-<option value="Isle of Man">Isle of Man</option>
-<option value="Israel">Israel</option>
-<option value="Italy">Italy</option>
-<option value="Jamaica">Jamaica</option>
-<option value="Japan">Japan</option>
-<option value="Jersey">Jersey</option>
-<option value="Jordan">Jordan</option>
-<option value="Kazakhstan">Kazakhstan</option>
-<option value="Kenya">Kenya</option>
-<option value="Kiribati">Kiribati</option>
-<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
-<option value="Korea, Republic of">Korea, Republic of</option>
-<option value="Kuwait">Kuwait</option>
-<option value="Kyrgyzstan">Kyrgyzstan</option>
-<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
-<option value="Latvia">Latvia</option>
-<option value="Lebanon">Lebanon</option>
-<option value="Lesotho">Lesotho</option>
-<option value="Liberia">Liberia</option>
-<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
-<option value="Liechtenstein">Liechtenstein</option>
-<option value="Lithuania">Lithuania</option>
-<option value="Luxembourg">Luxembourg</option>
-<option value="Macao">Macao</option>
-<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
-<option value="Madagascar">Madagascar</option>
-<option value="Malawi">Malawi</option>
-<option value="Malaysia">Malaysia</option>
-<option value="Maldives">Maldives</option>
-<option value="Mali">Mali</option>
-<option value="Malta">Malta</option>
-<option value="Marshall Islands">Marshall Islands</option>
-<option value="Martinique">Martinique</option>
-<option value="Mauritania">Mauritania</option>
-<option value="Mauritius">Mauritius</option>
-<option value="Mayotte">Mayotte</option>
-<option value="Mexico">Mexico</option>
-<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
-<option value="Moldova, Republic of">Moldova, Republic of</option>
-<option value="Monaco">Monaco</option>
-<option value="Mongolia">Mongolia</option>
-<option value="Montenegro">Montenegro</option>
-<option value="Montserrat">Montserrat</option>
-<option value="Morocco">Morocco</option>
-<option value="Mozambique">Mozambique</option>
-<option value="Myanmar">Myanmar</option>
-<option value="Namibia">Namibia</option>
-<option value="Nauru">Nauru</option>
-<option value="Nepal">Nepal</option>
-<option value="Netherlands">Netherlands</option>
-<option value="Netherlands Antilles">Netherlands Antilles</option>
-<option value="New Caledonia">New Caledonia</option>
-<option value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option>
-<option value="Niger">Niger</option>
-<option value="Nigeria">Nigeria</option>
-<option value="Niue">Niue</option>
-<option value="Norfolk Island">Norfolk Island</option>
-<option value="Northern Mariana Islands">Northern Mariana Islands</option>
-<option value="Norway">Norway</option>
-<option value="Oman">Oman</option>
-<option value="Pakistan">Pakistan</option>
-<option value="Palau">Palau</option>
-<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
-<option value="Panama">Panama</option>
-<option value="Papua New Guinea">Papua New Guinea</option>
-<option value="Paraguay">Paraguay</option>
-<option value="Peru">Peru</option>
-<option value="Philippines">Philippines</option>
-<option value="Pitcairn">Pitcairn</option>
-<option value="Poland">Poland</option>
-<option value="Portugal">Portugal</option>
-<option value="Puerto Rico">Puerto Rico</option>
-<option value="Qatar">Qatar</option>
-<option value="Reunion">Reunion</option>
-<option value="Romania">Romania</option>
-<option value="Russian Federation">Russian Federation</option>
-<option value="Rwanda">Rwanda</option>
-<option value="Saint Barthelemy">Saint Barthelemy</option>
-<option value="Saint Helena">Saint Helena</option>
-<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
-<option value="Saint Lucia">Saint Lucia</option>
-<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
-<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
-<option value="Samoa">Samoa</option>
-<option value="San Marino">San Marino</option>
-<option value="Sao Tome and Principe">Sao Tome and Principe</option>
-<option value="Saudi Arabia">Saudi Arabia</option>
-<option value="Senegal">Senegal</option>
-<option value="Serbia">Serbia</option>
-<option value="Seychelles">Seychelles</option>
-<option value="Sierra Leone">Sierra Leone</option>
-<option value="Singapore">Singapore</option>
-<option value="Slovakia">Slovakia</option>
-<option value="Slovenia">Slovenia</option>
-<option value="Solomon Islands">Solomon Islands</option>
-<option value="Somalia">Somalia</option>
-<option value="South Africa">South Africa</option>
-<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
-<option value="Spain">Spain</option>
-<option value="Sri Lanka">Sri Lanka</option>
-<option value="Sudan">Sudan</option>
-<option value="Suriname">Suriname</option>
-<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
-<option value="Swaziland">Swaziland</option>
-<option value="Sweden">Sweden</option>
-<option value="Switzerland">Switzerland</option>
-<option value="Syrian Arab Republic">Syrian Arab Republic</option>
-<option value="Taiwan, Province of China">Taiwan, Province of China</option>
-<option value="Tajikistan">Tajikistan</option>
-<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
-<option value="Thailand">Thailand</option>
-<option value="Timor-Leste">Timor-Leste</option>
-<option value="Togo">Togo</option>
-<option value="Tokelau">Tokelau</option>
-<option value="Tonga">Tonga</option>
-<option value="Trinidad and Tobago">Trinidad and Tobago</option>
-<option value="Tunisia">Tunisia</option>
-<option value="Turkey">Turkey</option>
-<option value="Turkmenistan">Turkmenistan</option>
-<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
-<option value="Tuvalu">Tuvalu</option>
-<option value="Uganda">Uganda</option>
-<option value="Ukraine">Ukraine</option>
-<option value="United Arab Emirates">United Arab Emirates</option>
-<option value="United Kingdom">United Kingdom</option>
-<option value="United States">United States</option>
-<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
-<option value="Uruguay">Uruguay</option>
-<option value="Uzbekistan">Uzbekistan</option>
-<option value="Vanuatu">Vanuatu</option>
-<option value="Venezuela">Venezuela</option>
-<option value="Viet Nam">Viet Nam</option>
-<option value="Virgin Islands, British">Virgin Islands, British</option>
-<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
-<option value="Wallis and Futuna">Wallis and Futuna</option>
-<option value="Western Sahara">Western Sahara</option>
-<option value="Yemen">Yemen</option>
-<option value="Zambia">Zambia</option>
-<option value="Zimbabwe">Zimbabwe</option></select>
- COUNTRIES
- assert_dom_equal(expected_select[0..-2], country_select("post", "origin"))
- end
-
- def test_country_select_with_priority_countries
- @post = Post.new
- @post.origin = "Denmark"
- expected_select = <<-COUNTRIES
-<select id="post_origin" name="post[origin]"><option value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option><option value="" disabled="disabled">-------------</option>
-<option value="Afghanistan">Afghanistan</option>
-<option value="Aland Islands">Aland Islands</option>
-<option value="Albania">Albania</option>
-<option value="Algeria">Algeria</option>
-<option value="American Samoa">American Samoa</option>
-<option value="Andorra">Andorra</option>
-<option value="Angola">Angola</option>
-<option value="Anguilla">Anguilla</option>
-<option value="Antarctica">Antarctica</option>
-<option value="Antigua And Barbuda">Antigua And Barbuda</option>
-<option value="Argentina">Argentina</option>
-<option value="Armenia">Armenia</option>
-<option value="Aruba">Aruba</option>
-<option value="Australia">Australia</option>
-<option value="Austria">Austria</option>
-<option value="Azerbaijan">Azerbaijan</option>
-<option value="Bahamas">Bahamas</option>
-<option value="Bahrain">Bahrain</option>
-<option value="Bangladesh">Bangladesh</option>
-<option value="Barbados">Barbados</option>
-<option value="Belarus">Belarus</option>
-<option value="Belgium">Belgium</option>
-<option value="Belize">Belize</option>
-<option value="Benin">Benin</option>
-<option value="Bermuda">Bermuda</option>
-<option value="Bhutan">Bhutan</option>
-<option value="Bolivia">Bolivia</option>
-<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
-<option value="Botswana">Botswana</option>
-<option value="Bouvet Island">Bouvet Island</option>
-<option value="Brazil">Brazil</option>
-<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
-<option value="Brunei Darussalam">Brunei Darussalam</option>
-<option value="Bulgaria">Bulgaria</option>
-<option value="Burkina Faso">Burkina Faso</option>
-<option value="Burundi">Burundi</option>
-<option value="Cambodia">Cambodia</option>
-<option value="Cameroon">Cameroon</option>
-<option value="Canada">Canada</option>
-<option value="Cape Verde">Cape Verde</option>
-<option value="Cayman Islands">Cayman Islands</option>
-<option value="Central African Republic">Central African Republic</option>
-<option value="Chad">Chad</option>
-<option value="Chile">Chile</option>
-<option value="China">China</option>
-<option value="Christmas Island">Christmas Island</option>
-<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
-<option value="Colombia">Colombia</option>
-<option value="Comoros">Comoros</option>
-<option value="Congo">Congo</option>
-<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
-<option value="Cook Islands">Cook Islands</option>
-<option value="Costa Rica">Costa Rica</option>
-<option value="Cote d'Ivoire">Cote d'Ivoire</option>
-<option value="Croatia">Croatia</option>
-<option value="Cuba">Cuba</option>
-<option value="Cyprus">Cyprus</option>
-<option value="Czech Republic">Czech Republic</option>
-<option selected="selected" value="Denmark">Denmark</option>
-<option value="Djibouti">Djibouti</option>
-<option value="Dominica">Dominica</option>
-<option value="Dominican Republic">Dominican Republic</option>
-<option value="Ecuador">Ecuador</option>
-<option value="Egypt">Egypt</option>
-<option value="El Salvador">El Salvador</option>
-<option value="Equatorial Guinea">Equatorial Guinea</option>
-<option value="Eritrea">Eritrea</option>
-<option value="Estonia">Estonia</option>
-<option value="Ethiopia">Ethiopia</option>
-<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
-<option value="Faroe Islands">Faroe Islands</option>
-<option value="Fiji">Fiji</option>
-<option value="Finland">Finland</option>
-<option value="France">France</option>
-<option value="French Guiana">French Guiana</option>
-<option value="French Polynesia">French Polynesia</option>
-<option value="French Southern Territories">French Southern Territories</option>
-<option value="Gabon">Gabon</option>
-<option value="Gambia">Gambia</option>
-<option value="Georgia">Georgia</option>
-<option value="Germany">Germany</option>
-<option value="Ghana">Ghana</option>
-<option value="Gibraltar">Gibraltar</option>
-<option value="Greece">Greece</option>
-<option value="Greenland">Greenland</option>
-<option value="Grenada">Grenada</option>
-<option value="Guadeloupe">Guadeloupe</option>
-<option value="Guam">Guam</option>
-<option value="Guatemala">Guatemala</option>
-<option value="Guernsey">Guernsey</option>
-<option value="Guinea">Guinea</option>
-<option value="Guinea-Bissau">Guinea-Bissau</option>
-<option value="Guyana">Guyana</option>
-<option value="Haiti">Haiti</option>
-<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
-<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
-<option value="Honduras">Honduras</option>
-<option value="Hong Kong">Hong Kong</option>
-<option value="Hungary">Hungary</option>
-<option value="Iceland">Iceland</option>
-<option value="India">India</option>
-<option value="Indonesia">Indonesia</option>
-<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
-<option value="Iraq">Iraq</option>
-<option value="Ireland">Ireland</option>
-<option value="Isle of Man">Isle of Man</option>
-<option value="Israel">Israel</option>
-<option value="Italy">Italy</option>
-<option value="Jamaica">Jamaica</option>
-<option value="Japan">Japan</option>
-<option value="Jersey">Jersey</option>
-<option value="Jordan">Jordan</option>
-<option value="Kazakhstan">Kazakhstan</option>
-<option value="Kenya">Kenya</option>
-<option value="Kiribati">Kiribati</option>
-<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
-<option value="Korea, Republic of">Korea, Republic of</option>
-<option value="Kuwait">Kuwait</option>
-<option value="Kyrgyzstan">Kyrgyzstan</option>
-<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
-<option value="Latvia">Latvia</option>
-<option value="Lebanon">Lebanon</option>
-<option value="Lesotho">Lesotho</option>
-<option value="Liberia">Liberia</option>
-<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
-<option value="Liechtenstein">Liechtenstein</option>
-<option value="Lithuania">Lithuania</option>
-<option value="Luxembourg">Luxembourg</option>
-<option value="Macao">Macao</option>
-<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
-<option value="Madagascar">Madagascar</option>
-<option value="Malawi">Malawi</option>
-<option value="Malaysia">Malaysia</option>
-<option value="Maldives">Maldives</option>
-<option value="Mali">Mali</option>
-<option value="Malta">Malta</option>
-<option value="Marshall Islands">Marshall Islands</option>
-<option value="Martinique">Martinique</option>
-<option value="Mauritania">Mauritania</option>
-<option value="Mauritius">Mauritius</option>
-<option value="Mayotte">Mayotte</option>
-<option value="Mexico">Mexico</option>
-<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
-<option value="Moldova, Republic of">Moldova, Republic of</option>
-<option value="Monaco">Monaco</option>
-<option value="Mongolia">Mongolia</option>
-<option value="Montenegro">Montenegro</option>
-<option value="Montserrat">Montserrat</option>
-<option value="Morocco">Morocco</option>
-<option value="Mozambique">Mozambique</option>
-<option value="Myanmar">Myanmar</option>
-<option value="Namibia">Namibia</option>
-<option value="Nauru">Nauru</option>
-<option value="Nepal">Nepal</option>
-<option value="Netherlands">Netherlands</option>
-<option value="Netherlands Antilles">Netherlands Antilles</option>
-<option value="New Caledonia">New Caledonia</option>
-<option value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option>
-<option value="Niger">Niger</option>
-<option value="Nigeria">Nigeria</option>
-<option value="Niue">Niue</option>
-<option value="Norfolk Island">Norfolk Island</option>
-<option value="Northern Mariana Islands">Northern Mariana Islands</option>
-<option value="Norway">Norway</option>
-<option value="Oman">Oman</option>
-<option value="Pakistan">Pakistan</option>
-<option value="Palau">Palau</option>
-<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
-<option value="Panama">Panama</option>
-<option value="Papua New Guinea">Papua New Guinea</option>
-<option value="Paraguay">Paraguay</option>
-<option value="Peru">Peru</option>
-<option value="Philippines">Philippines</option>
-<option value="Pitcairn">Pitcairn</option>
-<option value="Poland">Poland</option>
-<option value="Portugal">Portugal</option>
-<option value="Puerto Rico">Puerto Rico</option>
-<option value="Qatar">Qatar</option>
-<option value="Reunion">Reunion</option>
-<option value="Romania">Romania</option>
-<option value="Russian Federation">Russian Federation</option>
-<option value="Rwanda">Rwanda</option>
-<option value="Saint Barthelemy">Saint Barthelemy</option>
-<option value="Saint Helena">Saint Helena</option>
-<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
-<option value="Saint Lucia">Saint Lucia</option>
-<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
-<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
-<option value="Samoa">Samoa</option>
-<option value="San Marino">San Marino</option>
-<option value="Sao Tome and Principe">Sao Tome and Principe</option>
-<option value="Saudi Arabia">Saudi Arabia</option>
-<option value="Senegal">Senegal</option>
-<option value="Serbia">Serbia</option>
-<option value="Seychelles">Seychelles</option>
-<option value="Sierra Leone">Sierra Leone</option>
-<option value="Singapore">Singapore</option>
-<option value="Slovakia">Slovakia</option>
-<option value="Slovenia">Slovenia</option>
-<option value="Solomon Islands">Solomon Islands</option>
-<option value="Somalia">Somalia</option>
-<option value="South Africa">South Africa</option>
-<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
-<option value="Spain">Spain</option>
-<option value="Sri Lanka">Sri Lanka</option>
-<option value="Sudan">Sudan</option>
-<option value="Suriname">Suriname</option>
-<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
-<option value="Swaziland">Swaziland</option>
-<option value="Sweden">Sweden</option>
-<option value="Switzerland">Switzerland</option>
-<option value="Syrian Arab Republic">Syrian Arab Republic</option>
-<option value="Taiwan, Province of China">Taiwan, Province of China</option>
-<option value="Tajikistan">Tajikistan</option>
-<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
-<option value="Thailand">Thailand</option>
-<option value="Timor-Leste">Timor-Leste</option>
-<option value="Togo">Togo</option>
-<option value="Tokelau">Tokelau</option>
-<option value="Tonga">Tonga</option>
-<option value="Trinidad and Tobago">Trinidad and Tobago</option>
-<option value="Tunisia">Tunisia</option>
-<option value="Turkey">Turkey</option>
-<option value="Turkmenistan">Turkmenistan</option>
-<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
-<option value="Tuvalu">Tuvalu</option>
-<option value="Uganda">Uganda</option>
-<option value="Ukraine">Ukraine</option>
-<option value="United Arab Emirates">United Arab Emirates</option>
-<option value="United Kingdom">United Kingdom</option>
-<option value="United States">United States</option>
-<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
-<option value="Uruguay">Uruguay</option>
-<option value="Uzbekistan">Uzbekistan</option>
-<option value="Vanuatu">Vanuatu</option>
-<option value="Venezuela">Venezuela</option>
-<option value="Viet Nam">Viet Nam</option>
-<option value="Virgin Islands, British">Virgin Islands, British</option>
-<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
-<option value="Wallis and Futuna">Wallis and Futuna</option>
-<option value="Western Sahara">Western Sahara</option>
-<option value="Yemen">Yemen</option>
-<option value="Zambia">Zambia</option>
-<option value="Zimbabwe">Zimbabwe</option></select>
- COUNTRIES
- assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"]))
- end
-
- def test_country_select_with_selected_priority_country
- @post = Post.new
- @post.origin = "New Zealand"
- expected_select = <<-COUNTRIES
-<select id="post_origin" name="post[origin]"><option selected="selected" value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option><option value="" disabled="disabled">-------------</option>
-<option value="Afghanistan">Afghanistan</option>
-<option value="Aland Islands">Aland Islands</option>
-<option value="Albania">Albania</option>
-<option value="Algeria">Algeria</option>
-<option value="American Samoa">American Samoa</option>
-<option value="Andorra">Andorra</option>
-<option value="Angola">Angola</option>
-<option value="Anguilla">Anguilla</option>
-<option value="Antarctica">Antarctica</option>
-<option value="Antigua And Barbuda">Antigua And Barbuda</option>
-<option value="Argentina">Argentina</option>
-<option value="Armenia">Armenia</option>
-<option value="Aruba">Aruba</option>
-<option value="Australia">Australia</option>
-<option value="Austria">Austria</option>
-<option value="Azerbaijan">Azerbaijan</option>
-<option value="Bahamas">Bahamas</option>
-<option value="Bahrain">Bahrain</option>
-<option value="Bangladesh">Bangladesh</option>
-<option value="Barbados">Barbados</option>
-<option value="Belarus">Belarus</option>
-<option value="Belgium">Belgium</option>
-<option value="Belize">Belize</option>
-<option value="Benin">Benin</option>
-<option value="Bermuda">Bermuda</option>
-<option value="Bhutan">Bhutan</option>
-<option value="Bolivia">Bolivia</option>
-<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
-<option value="Botswana">Botswana</option>
-<option value="Bouvet Island">Bouvet Island</option>
-<option value="Brazil">Brazil</option>
-<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
-<option value="Brunei Darussalam">Brunei Darussalam</option>
-<option value="Bulgaria">Bulgaria</option>
-<option value="Burkina Faso">Burkina Faso</option>
-<option value="Burundi">Burundi</option>
-<option value="Cambodia">Cambodia</option>
-<option value="Cameroon">Cameroon</option>
-<option value="Canada">Canada</option>
-<option value="Cape Verde">Cape Verde</option>
-<option value="Cayman Islands">Cayman Islands</option>
-<option value="Central African Republic">Central African Republic</option>
-<option value="Chad">Chad</option>
-<option value="Chile">Chile</option>
-<option value="China">China</option>
-<option value="Christmas Island">Christmas Island</option>
-<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
-<option value="Colombia">Colombia</option>
-<option value="Comoros">Comoros</option>
-<option value="Congo">Congo</option>
-<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
-<option value="Cook Islands">Cook Islands</option>
-<option value="Costa Rica">Costa Rica</option>
-<option value="Cote d'Ivoire">Cote d'Ivoire</option>
-<option value="Croatia">Croatia</option>
-<option value="Cuba">Cuba</option>
-<option value="Cyprus">Cyprus</option>
-<option value="Czech Republic">Czech Republic</option>
-<option value="Denmark">Denmark</option>
-<option value="Djibouti">Djibouti</option>
-<option value="Dominica">Dominica</option>
-<option value="Dominican Republic">Dominican Republic</option>
-<option value="Ecuador">Ecuador</option>
-<option value="Egypt">Egypt</option>
-<option value="El Salvador">El Salvador</option>
-<option value="Equatorial Guinea">Equatorial Guinea</option>
-<option value="Eritrea">Eritrea</option>
-<option value="Estonia">Estonia</option>
-<option value="Ethiopia">Ethiopia</option>
-<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
-<option value="Faroe Islands">Faroe Islands</option>
-<option value="Fiji">Fiji</option>
-<option value="Finland">Finland</option>
-<option value="France">France</option>
-<option value="French Guiana">French Guiana</option>
-<option value="French Polynesia">French Polynesia</option>
-<option value="French Southern Territories">French Southern Territories</option>
-<option value="Gabon">Gabon</option>
-<option value="Gambia">Gambia</option>
-<option value="Georgia">Georgia</option>
-<option value="Germany">Germany</option>
-<option value="Ghana">Ghana</option>
-<option value="Gibraltar">Gibraltar</option>
-<option value="Greece">Greece</option>
-<option value="Greenland">Greenland</option>
-<option value="Grenada">Grenada</option>
-<option value="Guadeloupe">Guadeloupe</option>
-<option value="Guam">Guam</option>
-<option value="Guatemala">Guatemala</option>
-<option value="Guernsey">Guernsey</option>
-<option value="Guinea">Guinea</option>
-<option value="Guinea-Bissau">Guinea-Bissau</option>
-<option value="Guyana">Guyana</option>
-<option value="Haiti">Haiti</option>
-<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
-<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
-<option value="Honduras">Honduras</option>
-<option value="Hong Kong">Hong Kong</option>
-<option value="Hungary">Hungary</option>
-<option value="Iceland">Iceland</option>
-<option value="India">India</option>
-<option value="Indonesia">Indonesia</option>
-<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
-<option value="Iraq">Iraq</option>
-<option value="Ireland">Ireland</option>
-<option value="Isle of Man">Isle of Man</option>
-<option value="Israel">Israel</option>
-<option value="Italy">Italy</option>
-<option value="Jamaica">Jamaica</option>
-<option value="Japan">Japan</option>
-<option value="Jersey">Jersey</option>
-<option value="Jordan">Jordan</option>
-<option value="Kazakhstan">Kazakhstan</option>
-<option value="Kenya">Kenya</option>
-<option value="Kiribati">Kiribati</option>
-<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
-<option value="Korea, Republic of">Korea, Republic of</option>
-<option value="Kuwait">Kuwait</option>
-<option value="Kyrgyzstan">Kyrgyzstan</option>
-<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
-<option value="Latvia">Latvia</option>
-<option value="Lebanon">Lebanon</option>
-<option value="Lesotho">Lesotho</option>
-<option value="Liberia">Liberia</option>
-<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
-<option value="Liechtenstein">Liechtenstein</option>
-<option value="Lithuania">Lithuania</option>
-<option value="Luxembourg">Luxembourg</option>
-<option value="Macao">Macao</option>
-<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
-<option value="Madagascar">Madagascar</option>
-<option value="Malawi">Malawi</option>
-<option value="Malaysia">Malaysia</option>
-<option value="Maldives">Maldives</option>
-<option value="Mali">Mali</option>
-<option value="Malta">Malta</option>
-<option value="Marshall Islands">Marshall Islands</option>
-<option value="Martinique">Martinique</option>
-<option value="Mauritania">Mauritania</option>
-<option value="Mauritius">Mauritius</option>
-<option value="Mayotte">Mayotte</option>
-<option value="Mexico">Mexico</option>
-<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
-<option value="Moldova, Republic of">Moldova, Republic of</option>
-<option value="Monaco">Monaco</option>
-<option value="Mongolia">Mongolia</option>
-<option value="Montenegro">Montenegro</option>
-<option value="Montserrat">Montserrat</option>
-<option value="Morocco">Morocco</option>
-<option value="Mozambique">Mozambique</option>
-<option value="Myanmar">Myanmar</option>
-<option value="Namibia">Namibia</option>
-<option value="Nauru">Nauru</option>
-<option value="Nepal">Nepal</option>
-<option value="Netherlands">Netherlands</option>
-<option value="Netherlands Antilles">Netherlands Antilles</option>
-<option value="New Caledonia">New Caledonia</option>
-<option selected="selected" value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option>
-<option value="Niger">Niger</option>
-<option value="Nigeria">Nigeria</option>
-<option value="Niue">Niue</option>
-<option value="Norfolk Island">Norfolk Island</option>
-<option value="Northern Mariana Islands">Northern Mariana Islands</option>
-<option value="Norway">Norway</option>
-<option value="Oman">Oman</option>
-<option value="Pakistan">Pakistan</option>
-<option value="Palau">Palau</option>
-<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
-<option value="Panama">Panama</option>
-<option value="Papua New Guinea">Papua New Guinea</option>
-<option value="Paraguay">Paraguay</option>
-<option value="Peru">Peru</option>
-<option value="Philippines">Philippines</option>
-<option value="Pitcairn">Pitcairn</option>
-<option value="Poland">Poland</option>
-<option value="Portugal">Portugal</option>
-<option value="Puerto Rico">Puerto Rico</option>
-<option value="Qatar">Qatar</option>
-<option value="Reunion">Reunion</option>
-<option value="Romania">Romania</option>
-<option value="Russian Federation">Russian Federation</option>
-<option value="Rwanda">Rwanda</option>
-<option value="Saint Barthelemy">Saint Barthelemy</option>
-<option value="Saint Helena">Saint Helena</option>
-<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
-<option value="Saint Lucia">Saint Lucia</option>
-<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
-<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
-<option value="Samoa">Samoa</option>
-<option value="San Marino">San Marino</option>
-<option value="Sao Tome and Principe">Sao Tome and Principe</option>
-<option value="Saudi Arabia">Saudi Arabia</option>
-<option value="Senegal">Senegal</option>
-<option value="Serbia">Serbia</option>
-<option value="Seychelles">Seychelles</option>
-<option value="Sierra Leone">Sierra Leone</option>
-<option value="Singapore">Singapore</option>
-<option value="Slovakia">Slovakia</option>
-<option value="Slovenia">Slovenia</option>
-<option value="Solomon Islands">Solomon Islands</option>
-<option value="Somalia">Somalia</option>
-<option value="South Africa">South Africa</option>
-<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
-<option value="Spain">Spain</option>
-<option value="Sri Lanka">Sri Lanka</option>
-<option value="Sudan">Sudan</option>
-<option value="Suriname">Suriname</option>
-<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
-<option value="Swaziland">Swaziland</option>
-<option value="Sweden">Sweden</option>
-<option value="Switzerland">Switzerland</option>
-<option value="Syrian Arab Republic">Syrian Arab Republic</option>
-<option value="Taiwan, Province of China">Taiwan, Province of China</option>
-<option value="Tajikistan">Tajikistan</option>
-<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
-<option value="Thailand">Thailand</option>
-<option value="Timor-Leste">Timor-Leste</option>
-<option value="Togo">Togo</option>
-<option value="Tokelau">Tokelau</option>
-<option value="Tonga">Tonga</option>
-<option value="Trinidad and Tobago">Trinidad and Tobago</option>
-<option value="Tunisia">Tunisia</option>
-<option value="Turkey">Turkey</option>
-<option value="Turkmenistan">Turkmenistan</option>
-<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
-<option value="Tuvalu">Tuvalu</option>
-<option value="Uganda">Uganda</option>
-<option value="Ukraine">Ukraine</option>
-<option value="United Arab Emirates">United Arab Emirates</option>
-<option value="United Kingdom">United Kingdom</option>
-<option value="United States">United States</option>
-<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
-<option value="Uruguay">Uruguay</option>
-<option value="Uzbekistan">Uzbekistan</option>
-<option value="Vanuatu">Vanuatu</option>
-<option value="Venezuela">Venezuela</option>
-<option value="Viet Nam">Viet Nam</option>
-<option value="Virgin Islands, British">Virgin Islands, British</option>
-<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
-<option value="Wallis and Futuna">Wallis and Futuna</option>
-<option value="Western Sahara">Western Sahara</option>
-<option value="Yemen">Yemen</option>
-<option value="Zambia">Zambia</option>
-<option value="Zimbabwe">Zimbabwe</option></select>
- COUNTRIES
- assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"]))
- end
-
- def test_country_select_under_fields_for
- @post = Post.new
- @post.origin = "Australia"
- expected_select = <<-COUNTRIES
-<select id="post_origin" name="post[origin]"><option value="Afghanistan">Afghanistan</option>
-<option value="Aland Islands">Aland Islands</option>
-<option value="Albania">Albania</option>
-<option value="Algeria">Algeria</option>
-<option value="American Samoa">American Samoa</option>
-<option value="Andorra">Andorra</option>
-<option value="Angola">Angola</option>
-<option value="Anguilla">Anguilla</option>
-<option value="Antarctica">Antarctica</option>
-<option value="Antigua And Barbuda">Antigua And Barbuda</option>
-<option value="Argentina">Argentina</option>
-<option value="Armenia">Armenia</option>
-<option value="Aruba">Aruba</option>
-<option selected="selected" value="Australia">Australia</option>
-<option value="Austria">Austria</option>
-<option value="Azerbaijan">Azerbaijan</option>
-<option value="Bahamas">Bahamas</option>
-<option value="Bahrain">Bahrain</option>
-<option value="Bangladesh">Bangladesh</option>
-<option value="Barbados">Barbados</option>
-<option value="Belarus">Belarus</option>
-<option value="Belgium">Belgium</option>
-<option value="Belize">Belize</option>
-<option value="Benin">Benin</option>
-<option value="Bermuda">Bermuda</option>
-<option value="Bhutan">Bhutan</option>
-<option value="Bolivia">Bolivia</option>
-<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
-<option value="Botswana">Botswana</option>
-<option value="Bouvet Island">Bouvet Island</option>
-<option value="Brazil">Brazil</option>
-<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
-<option value="Brunei Darussalam">Brunei Darussalam</option>
-<option value="Bulgaria">Bulgaria</option>
-<option value="Burkina Faso">Burkina Faso</option>
-<option value="Burundi">Burundi</option>
-<option value="Cambodia">Cambodia</option>
-<option value="Cameroon">Cameroon</option>
-<option value="Canada">Canada</option>
-<option value="Cape Verde">Cape Verde</option>
-<option value="Cayman Islands">Cayman Islands</option>
-<option value="Central African Republic">Central African Republic</option>
-<option value="Chad">Chad</option>
-<option value="Chile">Chile</option>
-<option value="China">China</option>
-<option value="Christmas Island">Christmas Island</option>
-<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
-<option value="Colombia">Colombia</option>
-<option value="Comoros">Comoros</option>
-<option value="Congo">Congo</option>
-<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
-<option value="Cook Islands">Cook Islands</option>
-<option value="Costa Rica">Costa Rica</option>
-<option value="Cote d'Ivoire">Cote d'Ivoire</option>
-<option value="Croatia">Croatia</option>
-<option value="Cuba">Cuba</option>
-<option value="Cyprus">Cyprus</option>
-<option value="Czech Republic">Czech Republic</option>
-<option value="Denmark">Denmark</option>
-<option value="Djibouti">Djibouti</option>
-<option value="Dominica">Dominica</option>
-<option value="Dominican Republic">Dominican Republic</option>
-<option value="Ecuador">Ecuador</option>
-<option value="Egypt">Egypt</option>
-<option value="El Salvador">El Salvador</option>
-<option value="Equatorial Guinea">Equatorial Guinea</option>
-<option value="Eritrea">Eritrea</option>
-<option value="Estonia">Estonia</option>
-<option value="Ethiopia">Ethiopia</option>
-<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
-<option value="Faroe Islands">Faroe Islands</option>
-<option value="Fiji">Fiji</option>
-<option value="Finland">Finland</option>
-<option value="France">France</option>
-<option value="French Guiana">French Guiana</option>
-<option value="French Polynesia">French Polynesia</option>
-<option value="French Southern Territories">French Southern Territories</option>
-<option value="Gabon">Gabon</option>
-<option value="Gambia">Gambia</option>
-<option value="Georgia">Georgia</option>
-<option value="Germany">Germany</option>
-<option value="Ghana">Ghana</option>
-<option value="Gibraltar">Gibraltar</option>
-<option value="Greece">Greece</option>
-<option value="Greenland">Greenland</option>
-<option value="Grenada">Grenada</option>
-<option value="Guadeloupe">Guadeloupe</option>
-<option value="Guam">Guam</option>
-<option value="Guatemala">Guatemala</option>
-<option value="Guernsey">Guernsey</option>
-<option value="Guinea">Guinea</option>
-<option value="Guinea-Bissau">Guinea-Bissau</option>
-<option value="Guyana">Guyana</option>
-<option value="Haiti">Haiti</option>
-<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
-<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
-<option value="Honduras">Honduras</option>
-<option value="Hong Kong">Hong Kong</option>
-<option value="Hungary">Hungary</option>
-<option value="Iceland">Iceland</option>
-<option value="India">India</option>
-<option value="Indonesia">Indonesia</option>
-<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
-<option value="Iraq">Iraq</option>
-<option value="Ireland">Ireland</option>
-<option value="Isle of Man">Isle of Man</option>
-<option value="Israel">Israel</option>
-<option value="Italy">Italy</option>
-<option value="Jamaica">Jamaica</option>
-<option value="Japan">Japan</option>
-<option value="Jersey">Jersey</option>
-<option value="Jordan">Jordan</option>
-<option value="Kazakhstan">Kazakhstan</option>
-<option value="Kenya">Kenya</option>
-<option value="Kiribati">Kiribati</option>
-<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
-<option value="Korea, Republic of">Korea, Republic of</option>
-<option value="Kuwait">Kuwait</option>
-<option value="Kyrgyzstan">Kyrgyzstan</option>
-<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
-<option value="Latvia">Latvia</option>
-<option value="Lebanon">Lebanon</option>
-<option value="Lesotho">Lesotho</option>
-<option value="Liberia">Liberia</option>
-<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
-<option value="Liechtenstein">Liechtenstein</option>
-<option value="Lithuania">Lithuania</option>
-<option value="Luxembourg">Luxembourg</option>
-<option value="Macao">Macao</option>
-<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
-<option value="Madagascar">Madagascar</option>
-<option value="Malawi">Malawi</option>
-<option value="Malaysia">Malaysia</option>
-<option value="Maldives">Maldives</option>
-<option value="Mali">Mali</option>
-<option value="Malta">Malta</option>
-<option value="Marshall Islands">Marshall Islands</option>
-<option value="Martinique">Martinique</option>
-<option value="Mauritania">Mauritania</option>
-<option value="Mauritius">Mauritius</option>
-<option value="Mayotte">Mayotte</option>
-<option value="Mexico">Mexico</option>
-<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
-<option value="Moldova, Republic of">Moldova, Republic of</option>
-<option value="Monaco">Monaco</option>
-<option value="Mongolia">Mongolia</option>
-<option value="Montenegro">Montenegro</option>
-<option value="Montserrat">Montserrat</option>
-<option value="Morocco">Morocco</option>
-<option value="Mozambique">Mozambique</option>
-<option value="Myanmar">Myanmar</option>
-<option value="Namibia">Namibia</option>
-<option value="Nauru">Nauru</option>
-<option value="Nepal">Nepal</option>
-<option value="Netherlands">Netherlands</option>
-<option value="Netherlands Antilles">Netherlands Antilles</option>
-<option value="New Caledonia">New Caledonia</option>
-<option value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option>
-<option value="Niger">Niger</option>
-<option value="Nigeria">Nigeria</option>
-<option value="Niue">Niue</option>
-<option value="Norfolk Island">Norfolk Island</option>
-<option value="Northern Mariana Islands">Northern Mariana Islands</option>
-<option value="Norway">Norway</option>
-<option value="Oman">Oman</option>
-<option value="Pakistan">Pakistan</option>
-<option value="Palau">Palau</option>
-<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
-<option value="Panama">Panama</option>
-<option value="Papua New Guinea">Papua New Guinea</option>
-<option value="Paraguay">Paraguay</option>
-<option value="Peru">Peru</option>
-<option value="Philippines">Philippines</option>
-<option value="Pitcairn">Pitcairn</option>
-<option value="Poland">Poland</option>
-<option value="Portugal">Portugal</option>
-<option value="Puerto Rico">Puerto Rico</option>
-<option value="Qatar">Qatar</option>
-<option value="Reunion">Reunion</option>
-<option value="Romania">Romania</option>
-<option value="Russian Federation">Russian Federation</option>
-<option value="Rwanda">Rwanda</option>
-<option value="Saint Barthelemy">Saint Barthelemy</option>
-<option value="Saint Helena">Saint Helena</option>
-<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
-<option value="Saint Lucia">Saint Lucia</option>
-<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
-<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
-<option value="Samoa">Samoa</option>
-<option value="San Marino">San Marino</option>
-<option value="Sao Tome and Principe">Sao Tome and Principe</option>
-<option value="Saudi Arabia">Saudi Arabia</option>
-<option value="Senegal">Senegal</option>
-<option value="Serbia">Serbia</option>
-<option value="Seychelles">Seychelles</option>
-<option value="Sierra Leone">Sierra Leone</option>
-<option value="Singapore">Singapore</option>
-<option value="Slovakia">Slovakia</option>
-<option value="Slovenia">Slovenia</option>
-<option value="Solomon Islands">Solomon Islands</option>
-<option value="Somalia">Somalia</option>
-<option value="South Africa">South Africa</option>
-<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
-<option value="Spain">Spain</option>
-<option value="Sri Lanka">Sri Lanka</option>
-<option value="Sudan">Sudan</option>
-<option value="Suriname">Suriname</option>
-<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
-<option value="Swaziland">Swaziland</option>
-<option value="Sweden">Sweden</option>
-<option value="Switzerland">Switzerland</option>
-<option value="Syrian Arab Republic">Syrian Arab Republic</option>
-<option value="Taiwan, Province of China">Taiwan, Province of China</option>
-<option value="Tajikistan">Tajikistan</option>
-<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
-<option value="Thailand">Thailand</option>
-<option value="Timor-Leste">Timor-Leste</option>
-<option value="Togo">Togo</option>
-<option value="Tokelau">Tokelau</option>
-<option value="Tonga">Tonga</option>
-<option value="Trinidad and Tobago">Trinidad and Tobago</option>
-<option value="Tunisia">Tunisia</option>
-<option value="Turkey">Turkey</option>
-<option value="Turkmenistan">Turkmenistan</option>
-<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
-<option value="Tuvalu">Tuvalu</option>
-<option value="Uganda">Uganda</option>
-<option value="Ukraine">Ukraine</option>
-<option value="United Arab Emirates">United Arab Emirates</option>
-<option value="United Kingdom">United Kingdom</option>
-<option value="United States">United States</option>
-<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
-<option value="Uruguay">Uruguay</option>
-<option value="Uzbekistan">Uzbekistan</option>
-<option value="Vanuatu">Vanuatu</option>
-<option value="Venezuela">Venezuela</option>
-<option value="Viet Nam">Viet Nam</option>
-<option value="Virgin Islands, British">Virgin Islands, British</option>
-<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
-<option value="Wallis and Futuna">Wallis and Futuna</option>
-<option value="Western Sahara">Western Sahara</option>
-<option value="Yemen">Yemen</option>
-<option value="Zambia">Zambia</option>
-<option value="Zimbabwe">Zimbabwe</option></select>
- COUNTRIES
-
- fields_for :post, @post do |f|
- concat f.country_select("origin")
- end
-
- assert_dom_equal(expected_select[0..-2], output_buffer)
- end
-
- def test_country_select_under_fields_for_with_index
- @post = Post.new
- @post.origin = "United States"
- expected_select = <<-COUNTRIES
-<select id="post_325_origin" name="post[325][origin]"><option value="Afghanistan">Afghanistan</option>
-<option value="Aland Islands">Aland Islands</option>
-<option value="Albania">Albania</option>
-<option value="Algeria">Algeria</option>
-<option value="American Samoa">American Samoa</option>
-<option value="Andorra">Andorra</option>
-<option value="Angola">Angola</option>
-<option value="Anguilla">Anguilla</option>
-<option value="Antarctica">Antarctica</option>
-<option value="Antigua And Barbuda">Antigua And Barbuda</option>
-<option value="Argentina">Argentina</option>
-<option value="Armenia">Armenia</option>
-<option value="Aruba">Aruba</option>
-<option value="Australia">Australia</option>
-<option value="Austria">Austria</option>
-<option value="Azerbaijan">Azerbaijan</option>
-<option value="Bahamas">Bahamas</option>
-<option value="Bahrain">Bahrain</option>
-<option value="Bangladesh">Bangladesh</option>
-<option value="Barbados">Barbados</option>
-<option value="Belarus">Belarus</option>
-<option value="Belgium">Belgium</option>
-<option value="Belize">Belize</option>
-<option value="Benin">Benin</option>
-<option value="Bermuda">Bermuda</option>
-<option value="Bhutan">Bhutan</option>
-<option value="Bolivia">Bolivia</option>
-<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
-<option value="Botswana">Botswana</option>
-<option value="Bouvet Island">Bouvet Island</option>
-<option value="Brazil">Brazil</option>
-<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
-<option value="Brunei Darussalam">Brunei Darussalam</option>
-<option value="Bulgaria">Bulgaria</option>
-<option value="Burkina Faso">Burkina Faso</option>
-<option value="Burundi">Burundi</option>
-<option value="Cambodia">Cambodia</option>
-<option value="Cameroon">Cameroon</option>
-<option value="Canada">Canada</option>
-<option value="Cape Verde">Cape Verde</option>
-<option value="Cayman Islands">Cayman Islands</option>
-<option value="Central African Republic">Central African Republic</option>
-<option value="Chad">Chad</option>
-<option value="Chile">Chile</option>
-<option value="China">China</option>
-<option value="Christmas Island">Christmas Island</option>
-<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
-<option value="Colombia">Colombia</option>
-<option value="Comoros">Comoros</option>
-<option value="Congo">Congo</option>
-<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
-<option value="Cook Islands">Cook Islands</option>
-<option value="Costa Rica">Costa Rica</option>
-<option value="Cote d'Ivoire">Cote d'Ivoire</option>
-<option value="Croatia">Croatia</option>
-<option value="Cuba">Cuba</option>
-<option value="Cyprus">Cyprus</option>
-<option value="Czech Republic">Czech Republic</option>
-<option value="Denmark">Denmark</option>
-<option value="Djibouti">Djibouti</option>
-<option value="Dominica">Dominica</option>
-<option value="Dominican Republic">Dominican Republic</option>
-<option value="Ecuador">Ecuador</option>
-<option value="Egypt">Egypt</option>
-<option value="El Salvador">El Salvador</option>
-<option value="Equatorial Guinea">Equatorial Guinea</option>
-<option value="Eritrea">Eritrea</option>
-<option value="Estonia">Estonia</option>
-<option value="Ethiopia">Ethiopia</option>
-<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
-<option value="Faroe Islands">Faroe Islands</option>
-<option value="Fiji">Fiji</option>
-<option value="Finland">Finland</option>
-<option value="France">France</option>
-<option value="French Guiana">French Guiana</option>
-<option value="French Polynesia">French Polynesia</option>
-<option value="French Southern Territories">French Southern Territories</option>
-<option value="Gabon">Gabon</option>
-<option value="Gambia">Gambia</option>
-<option value="Georgia">Georgia</option>
-<option value="Germany">Germany</option>
-<option value="Ghana">Ghana</option>
-<option value="Gibraltar">Gibraltar</option>
-<option value="Greece">Greece</option>
-<option value="Greenland">Greenland</option>
-<option value="Grenada">Grenada</option>
-<option value="Guadeloupe">Guadeloupe</option>
-<option value="Guam">Guam</option>
-<option value="Guatemala">Guatemala</option>
-<option value="Guernsey">Guernsey</option>
-<option value="Guinea">Guinea</option>
-<option value="Guinea-Bissau">Guinea-Bissau</option>
-<option value="Guyana">Guyana</option>
-<option value="Haiti">Haiti</option>
-<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
-<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
-<option value="Honduras">Honduras</option>
-<option value="Hong Kong">Hong Kong</option>
-<option value="Hungary">Hungary</option>
-<option value="Iceland">Iceland</option>
-<option value="India">India</option>
-<option value="Indonesia">Indonesia</option>
-<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
-<option value="Iraq">Iraq</option>
-<option value="Ireland">Ireland</option>
-<option value="Isle of Man">Isle of Man</option>
-<option value="Israel">Israel</option>
-<option value="Italy">Italy</option>
-<option value="Jamaica">Jamaica</option>
-<option value="Japan">Japan</option>
-<option value="Jersey">Jersey</option>
-<option value="Jordan">Jordan</option>
-<option value="Kazakhstan">Kazakhstan</option>
-<option value="Kenya">Kenya</option>
-<option value="Kiribati">Kiribati</option>
-<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
-<option value="Korea, Republic of">Korea, Republic of</option>
-<option value="Kuwait">Kuwait</option>
-<option value="Kyrgyzstan">Kyrgyzstan</option>
-<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
-<option value="Latvia">Latvia</option>
-<option value="Lebanon">Lebanon</option>
-<option value="Lesotho">Lesotho</option>
-<option value="Liberia">Liberia</option>
-<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
-<option value="Liechtenstein">Liechtenstein</option>
-<option value="Lithuania">Lithuania</option>
-<option value="Luxembourg">Luxembourg</option>
-<option value="Macao">Macao</option>
-<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
-<option value="Madagascar">Madagascar</option>
-<option value="Malawi">Malawi</option>
-<option value="Malaysia">Malaysia</option>
-<option value="Maldives">Maldives</option>
-<option value="Mali">Mali</option>
-<option value="Malta">Malta</option>
-<option value="Marshall Islands">Marshall Islands</option>
-<option value="Martinique">Martinique</option>
-<option value="Mauritania">Mauritania</option>
-<option value="Mauritius">Mauritius</option>
-<option value="Mayotte">Mayotte</option>
-<option value="Mexico">Mexico</option>
-<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
-<option value="Moldova, Republic of">Moldova, Republic of</option>
-<option value="Monaco">Monaco</option>
-<option value="Mongolia">Mongolia</option>
-<option value="Montenegro">Montenegro</option>
-<option value="Montserrat">Montserrat</option>
-<option value="Morocco">Morocco</option>
-<option value="Mozambique">Mozambique</option>
-<option value="Myanmar">Myanmar</option>
-<option value="Namibia">Namibia</option>
-<option value="Nauru">Nauru</option>
-<option value="Nepal">Nepal</option>
-<option value="Netherlands">Netherlands</option>
-<option value="Netherlands Antilles">Netherlands Antilles</option>
-<option value="New Caledonia">New Caledonia</option>
-<option value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option>
-<option value="Niger">Niger</option>
-<option value="Nigeria">Nigeria</option>
-<option value="Niue">Niue</option>
-<option value="Norfolk Island">Norfolk Island</option>
-<option value="Northern Mariana Islands">Northern Mariana Islands</option>
-<option value="Norway">Norway</option>
-<option value="Oman">Oman</option>
-<option value="Pakistan">Pakistan</option>
-<option value="Palau">Palau</option>
-<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
-<option value="Panama">Panama</option>
-<option value="Papua New Guinea">Papua New Guinea</option>
-<option value="Paraguay">Paraguay</option>
-<option value="Peru">Peru</option>
-<option value="Philippines">Philippines</option>
-<option value="Pitcairn">Pitcairn</option>
-<option value="Poland">Poland</option>
-<option value="Portugal">Portugal</option>
-<option value="Puerto Rico">Puerto Rico</option>
-<option value="Qatar">Qatar</option>
-<option value="Reunion">Reunion</option>
-<option value="Romania">Romania</option>
-<option value="Russian Federation">Russian Federation</option>
-<option value="Rwanda">Rwanda</option>
-<option value="Saint Barthelemy">Saint Barthelemy</option>
-<option value="Saint Helena">Saint Helena</option>
-<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
-<option value="Saint Lucia">Saint Lucia</option>
-<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
-<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
-<option value="Samoa">Samoa</option>
-<option value="San Marino">San Marino</option>
-<option value="Sao Tome and Principe">Sao Tome and Principe</option>
-<option value="Saudi Arabia">Saudi Arabia</option>
-<option value="Senegal">Senegal</option>
-<option value="Serbia">Serbia</option>
-<option value="Seychelles">Seychelles</option>
-<option value="Sierra Leone">Sierra Leone</option>
-<option value="Singapore">Singapore</option>
-<option value="Slovakia">Slovakia</option>
-<option value="Slovenia">Slovenia</option>
-<option value="Solomon Islands">Solomon Islands</option>
-<option value="Somalia">Somalia</option>
-<option value="South Africa">South Africa</option>
-<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
-<option value="Spain">Spain</option>
-<option value="Sri Lanka">Sri Lanka</option>
-<option value="Sudan">Sudan</option>
-<option value="Suriname">Suriname</option>
-<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
-<option value="Swaziland">Swaziland</option>
-<option value="Sweden">Sweden</option>
-<option value="Switzerland">Switzerland</option>
-<option value="Syrian Arab Republic">Syrian Arab Republic</option>
-<option value="Taiwan, Province of China">Taiwan, Province of China</option>
-<option value="Tajikistan">Tajikistan</option>
-<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
-<option value="Thailand">Thailand</option>
-<option value="Timor-Leste">Timor-Leste</option>
-<option value="Togo">Togo</option>
-<option value="Tokelau">Tokelau</option>
-<option value="Tonga">Tonga</option>
-<option value="Trinidad and Tobago">Trinidad and Tobago</option>
-<option value="Tunisia">Tunisia</option>
-<option value="Turkey">Turkey</option>
-<option value="Turkmenistan">Turkmenistan</option>
-<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
-<option value="Tuvalu">Tuvalu</option>
-<option value="Uganda">Uganda</option>
-<option value="Ukraine">Ukraine</option>
-<option value="United Arab Emirates">United Arab Emirates</option>
-<option value="United Kingdom">United Kingdom</option>
-<option selected="selected" value="United States">United States</option>
-<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
-<option value="Uruguay">Uruguay</option>
-<option value="Uzbekistan">Uzbekistan</option>
-<option value="Vanuatu">Vanuatu</option>
-<option value="Venezuela">Venezuela</option>
-<option value="Viet Nam">Viet Nam</option>
-<option value="Virgin Islands, British">Virgin Islands, British</option>
-<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
-<option value="Wallis and Futuna">Wallis and Futuna</option>
-<option value="Western Sahara">Western Sahara</option>
-<option value="Yemen">Yemen</option>
-<option value="Zambia">Zambia</option>
-<option value="Zimbabwe">Zimbabwe</option></select>
- COUNTRIES
-
- fields_for :post, @post, :index => 325 do |f|
- concat f.country_select("origin")
- end
-
- assert_dom_equal(expected_select[0..-2], output_buffer)
- end
-
- def test_country_select_under_fields_for_with_auto_index
- @post = Post.new
- @post.origin = "Iraq"
- def @post.to_param; 325; end
-
- expected_select = <<-COUNTRIES
-<select id="post_325_origin" name="post[325][origin]"><option value="Afghanistan">Afghanistan</option>
-<option value="Aland Islands">Aland Islands</option>
-<option value="Albania">Albania</option>
-<option value="Algeria">Algeria</option>
-<option value="American Samoa">American Samoa</option>
-<option value="Andorra">Andorra</option>
-<option value="Angola">Angola</option>
-<option value="Anguilla">Anguilla</option>
-<option value="Antarctica">Antarctica</option>
-<option value="Antigua And Barbuda">Antigua And Barbuda</option>
-<option value="Argentina">Argentina</option>
-<option value="Armenia">Armenia</option>
-<option value="Aruba">Aruba</option>
-<option value="Australia">Australia</option>
-<option value="Austria">Austria</option>
-<option value="Azerbaijan">Azerbaijan</option>
-<option value="Bahamas">Bahamas</option>
-<option value="Bahrain">Bahrain</option>
-<option value="Bangladesh">Bangladesh</option>
-<option value="Barbados">Barbados</option>
-<option value="Belarus">Belarus</option>
-<option value="Belgium">Belgium</option>
-<option value="Belize">Belize</option>
-<option value="Benin">Benin</option>
-<option value="Bermuda">Bermuda</option>
-<option value="Bhutan">Bhutan</option>
-<option value="Bolivia">Bolivia</option>
-<option value="Bosnia and Herzegowina">Bosnia and Herzegowina</option>
-<option value="Botswana">Botswana</option>
-<option value="Bouvet Island">Bouvet Island</option>
-<option value="Brazil">Brazil</option>
-<option value="British Indian Ocean Territory">British Indian Ocean Territory</option>
-<option value="Brunei Darussalam">Brunei Darussalam</option>
-<option value="Bulgaria">Bulgaria</option>
-<option value="Burkina Faso">Burkina Faso</option>
-<option value="Burundi">Burundi</option>
-<option value="Cambodia">Cambodia</option>
-<option value="Cameroon">Cameroon</option>
-<option value="Canada">Canada</option>
-<option value="Cape Verde">Cape Verde</option>
-<option value="Cayman Islands">Cayman Islands</option>
-<option value="Central African Republic">Central African Republic</option>
-<option value="Chad">Chad</option>
-<option value="Chile">Chile</option>
-<option value="China">China</option>
-<option value="Christmas Island">Christmas Island</option>
-<option value="Cocos (Keeling) Islands">Cocos (Keeling) Islands</option>
-<option value="Colombia">Colombia</option>
-<option value="Comoros">Comoros</option>
-<option value="Congo">Congo</option>
-<option value="Congo, the Democratic Republic of the">Congo, the Democratic Republic of the</option>
-<option value="Cook Islands">Cook Islands</option>
-<option value="Costa Rica">Costa Rica</option>
-<option value="Cote d'Ivoire">Cote d'Ivoire</option>
-<option value="Croatia">Croatia</option>
-<option value="Cuba">Cuba</option>
-<option value="Cyprus">Cyprus</option>
-<option value="Czech Republic">Czech Republic</option>
-<option value="Denmark">Denmark</option>
-<option value="Djibouti">Djibouti</option>
-<option value="Dominica">Dominica</option>
-<option value="Dominican Republic">Dominican Republic</option>
-<option value="Ecuador">Ecuador</option>
-<option value="Egypt">Egypt</option>
-<option value="El Salvador">El Salvador</option>
-<option value="Equatorial Guinea">Equatorial Guinea</option>
-<option value="Eritrea">Eritrea</option>
-<option value="Estonia">Estonia</option>
-<option value="Ethiopia">Ethiopia</option>
-<option value="Falkland Islands (Malvinas)">Falkland Islands (Malvinas)</option>
-<option value="Faroe Islands">Faroe Islands</option>
-<option value="Fiji">Fiji</option>
-<option value="Finland">Finland</option>
-<option value="France">France</option>
-<option value="French Guiana">French Guiana</option>
-<option value="French Polynesia">French Polynesia</option>
-<option value="French Southern Territories">French Southern Territories</option>
-<option value="Gabon">Gabon</option>
-<option value="Gambia">Gambia</option>
-<option value="Georgia">Georgia</option>
-<option value="Germany">Germany</option>
-<option value="Ghana">Ghana</option>
-<option value="Gibraltar">Gibraltar</option>
-<option value="Greece">Greece</option>
-<option value="Greenland">Greenland</option>
-<option value="Grenada">Grenada</option>
-<option value="Guadeloupe">Guadeloupe</option>
-<option value="Guam">Guam</option>
-<option value="Guatemala">Guatemala</option>
-<option value="Guernsey">Guernsey</option>
-<option value="Guinea">Guinea</option>
-<option value="Guinea-Bissau">Guinea-Bissau</option>
-<option value="Guyana">Guyana</option>
-<option value="Haiti">Haiti</option>
-<option value="Heard and McDonald Islands">Heard and McDonald Islands</option>
-<option value="Holy See (Vatican City State)">Holy See (Vatican City State)</option>
-<option value="Honduras">Honduras</option>
-<option value="Hong Kong">Hong Kong</option>
-<option value="Hungary">Hungary</option>
-<option value="Iceland">Iceland</option>
-<option value="India">India</option>
-<option value="Indonesia">Indonesia</option>
-<option value="Iran, Islamic Republic of">Iran, Islamic Republic of</option>
-<option selected="selected" value="Iraq">Iraq</option>
-<option value="Ireland">Ireland</option>
-<option value="Isle of Man">Isle of Man</option>
-<option value="Israel">Israel</option>
-<option value="Italy">Italy</option>
-<option value="Jamaica">Jamaica</option>
-<option value="Japan">Japan</option>
-<option value="Jersey">Jersey</option>
-<option value="Jordan">Jordan</option>
-<option value="Kazakhstan">Kazakhstan</option>
-<option value="Kenya">Kenya</option>
-<option value="Kiribati">Kiribati</option>
-<option value="Korea, Democratic People's Republic of">Korea, Democratic People's Republic of</option>
-<option value="Korea, Republic of">Korea, Republic of</option>
-<option value="Kuwait">Kuwait</option>
-<option value="Kyrgyzstan">Kyrgyzstan</option>
-<option value="Lao People's Democratic Republic">Lao People's Democratic Republic</option>
-<option value="Latvia">Latvia</option>
-<option value="Lebanon">Lebanon</option>
-<option value="Lesotho">Lesotho</option>
-<option value="Liberia">Liberia</option>
-<option value="Libyan Arab Jamahiriya">Libyan Arab Jamahiriya</option>
-<option value="Liechtenstein">Liechtenstein</option>
-<option value="Lithuania">Lithuania</option>
-<option value="Luxembourg">Luxembourg</option>
-<option value="Macao">Macao</option>
-<option value="Macedonia, The Former Yugoslav Republic Of">Macedonia, The Former Yugoslav Republic Of</option>
-<option value="Madagascar">Madagascar</option>
-<option value="Malawi">Malawi</option>
-<option value="Malaysia">Malaysia</option>
-<option value="Maldives">Maldives</option>
-<option value="Mali">Mali</option>
-<option value="Malta">Malta</option>
-<option value="Marshall Islands">Marshall Islands</option>
-<option value="Martinique">Martinique</option>
-<option value="Mauritania">Mauritania</option>
-<option value="Mauritius">Mauritius</option>
-<option value="Mayotte">Mayotte</option>
-<option value="Mexico">Mexico</option>
-<option value="Micronesia, Federated States of">Micronesia, Federated States of</option>
-<option value="Moldova, Republic of">Moldova, Republic of</option>
-<option value="Monaco">Monaco</option>
-<option value="Mongolia">Mongolia</option>
-<option value="Montenegro">Montenegro</option>
-<option value="Montserrat">Montserrat</option>
-<option value="Morocco">Morocco</option>
-<option value="Mozambique">Mozambique</option>
-<option value="Myanmar">Myanmar</option>
-<option value="Namibia">Namibia</option>
-<option value="Nauru">Nauru</option>
-<option value="Nepal">Nepal</option>
-<option value="Netherlands">Netherlands</option>
-<option value="Netherlands Antilles">Netherlands Antilles</option>
-<option value="New Caledonia">New Caledonia</option>
-<option value="New Zealand">New Zealand</option>
-<option value="Nicaragua">Nicaragua</option>
-<option value="Niger">Niger</option>
-<option value="Nigeria">Nigeria</option>
-<option value="Niue">Niue</option>
-<option value="Norfolk Island">Norfolk Island</option>
-<option value="Northern Mariana Islands">Northern Mariana Islands</option>
-<option value="Norway">Norway</option>
-<option value="Oman">Oman</option>
-<option value="Pakistan">Pakistan</option>
-<option value="Palau">Palau</option>
-<option value="Palestinian Territory, Occupied">Palestinian Territory, Occupied</option>
-<option value="Panama">Panama</option>
-<option value="Papua New Guinea">Papua New Guinea</option>
-<option value="Paraguay">Paraguay</option>
-<option value="Peru">Peru</option>
-<option value="Philippines">Philippines</option>
-<option value="Pitcairn">Pitcairn</option>
-<option value="Poland">Poland</option>
-<option value="Portugal">Portugal</option>
-<option value="Puerto Rico">Puerto Rico</option>
-<option value="Qatar">Qatar</option>
-<option value="Reunion">Reunion</option>
-<option value="Romania">Romania</option>
-<option value="Russian Federation">Russian Federation</option>
-<option value="Rwanda">Rwanda</option>
-<option value="Saint Barthelemy">Saint Barthelemy</option>
-<option value="Saint Helena">Saint Helena</option>
-<option value="Saint Kitts and Nevis">Saint Kitts and Nevis</option>
-<option value="Saint Lucia">Saint Lucia</option>
-<option value="Saint Pierre and Miquelon">Saint Pierre and Miquelon</option>
-<option value="Saint Vincent and the Grenadines">Saint Vincent and the Grenadines</option>
-<option value="Samoa">Samoa</option>
-<option value="San Marino">San Marino</option>
-<option value="Sao Tome and Principe">Sao Tome and Principe</option>
-<option value="Saudi Arabia">Saudi Arabia</option>
-<option value="Senegal">Senegal</option>
-<option value="Serbia">Serbia</option>
-<option value="Seychelles">Seychelles</option>
-<option value="Sierra Leone">Sierra Leone</option>
-<option value="Singapore">Singapore</option>
-<option value="Slovakia">Slovakia</option>
-<option value="Slovenia">Slovenia</option>
-<option value="Solomon Islands">Solomon Islands</option>
-<option value="Somalia">Somalia</option>
-<option value="South Africa">South Africa</option>
-<option value="South Georgia and the South Sandwich Islands">South Georgia and the South Sandwich Islands</option>
-<option value="Spain">Spain</option>
-<option value="Sri Lanka">Sri Lanka</option>
-<option value="Sudan">Sudan</option>
-<option value="Suriname">Suriname</option>
-<option value="Svalbard and Jan Mayen">Svalbard and Jan Mayen</option>
-<option value="Swaziland">Swaziland</option>
-<option value="Sweden">Sweden</option>
-<option value="Switzerland">Switzerland</option>
-<option value="Syrian Arab Republic">Syrian Arab Republic</option>
-<option value="Taiwan, Province of China">Taiwan, Province of China</option>
-<option value="Tajikistan">Tajikistan</option>
-<option value="Tanzania, United Republic of">Tanzania, United Republic of</option>
-<option value="Thailand">Thailand</option>
-<option value="Timor-Leste">Timor-Leste</option>
-<option value="Togo">Togo</option>
-<option value="Tokelau">Tokelau</option>
-<option value="Tonga">Tonga</option>
-<option value="Trinidad and Tobago">Trinidad and Tobago</option>
-<option value="Tunisia">Tunisia</option>
-<option value="Turkey">Turkey</option>
-<option value="Turkmenistan">Turkmenistan</option>
-<option value="Turks and Caicos Islands">Turks and Caicos Islands</option>
-<option value="Tuvalu">Tuvalu</option>
-<option value="Uganda">Uganda</option>
-<option value="Ukraine">Ukraine</option>
-<option value="United Arab Emirates">United Arab Emirates</option>
-<option value="United Kingdom">United Kingdom</option>
-<option value="United States">United States</option>
-<option value="United States Minor Outlying Islands">United States Minor Outlying Islands</option>
-<option value="Uruguay">Uruguay</option>
-<option value="Uzbekistan">Uzbekistan</option>
-<option value="Vanuatu">Vanuatu</option>
-<option value="Venezuela">Venezuela</option>
-<option value="Viet Nam">Viet Nam</option>
-<option value="Virgin Islands, British">Virgin Islands, British</option>
-<option value="Virgin Islands, U.S.">Virgin Islands, U.S.</option>
-<option value="Wallis and Futuna">Wallis and Futuna</option>
-<option value="Western Sahara">Western Sahara</option>
-<option value="Yemen">Yemen</option>
-<option value="Zambia">Zambia</option>
-<option value="Zimbabwe">Zimbabwe</option></select>
- COUNTRIES
-
- fields_for "post[]", @post do |f|
- concat f.country_select("origin")
- end
-
- assert_dom_equal(expected_select[0..-2], output_buffer)
- end
-
def test_time_zone_select
@firm = Firm.new("D")
html = time_zone_select( "firm", "time_zone" )
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 4e4102aec7..9b41ff8179 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -190,6 +190,12 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
+ def test_label_tag_with_symbol
+ actual = label_tag :title
+ expected = %(<label for="title">Title</label>)
+ assert_dom_equal expected, actual
+ end
+
def test_label_tag_with_text
actual = label_tag "title", "My Title"
expected = %(<label for="title">My Title</label>)
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index b2c4a130c8..d41111127b 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -3,6 +3,12 @@ require 'abstract_unit'
class JavaScriptHelperTest < ActionView::TestCase
tests ActionView::Helpers::JavaScriptHelper
+ attr_accessor :template_format, :output_buffer
+
+ def setup
+ @template = self
+ end
+
def test_escape_javascript
assert_equal '', escape_javascript(nil)
assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos'))
diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb
new file mode 100644
index 0000000000..2ee7f43a65
--- /dev/null
+++ b/actionpack/test/template/number_helper_i18n_test.rb
@@ -0,0 +1,54 @@
+require 'abstract_unit'
+
+class NumberHelperI18nTests < Test::Unit::TestCase
+ include ActionView::Helpers::NumberHelper
+
+ attr_reader :request
+
+ uses_mocha 'number_helper_i18n_tests' do
+ def setup
+ @number_defaults = { :precision => 3, :delimiter => ',', :separator => '.' }
+ @currency_defaults = { :unit => '$', :format => '%u%n', :precision => 2 }
+ @human_defaults = { :precision => 1 }
+ @percentage_defaults = { :delimiter => '' }
+ @precision_defaults = { :delimiter => '' }
+
+ I18n.backend.store_translations 'en-US', :number => { :format => @number_defaults,
+ :currency => { :format => @currency_defaults }, :human => @human_defaults }
+ end
+
+ def test_number_to_currency_translates_currency_formats
+ I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
+ I18n.expects(:translate).with(:'number.currency.format', :locale => 'en-US',
+ :raise => true).returns(@currency_defaults)
+ number_to_currency(1, :locale => 'en-US')
+ end
+
+ def test_number_with_precision_translates_number_formats
+ I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
+ I18n.expects(:translate).with(:'number.precision.format', :locale => 'en-US',
+ :raise => true).returns(@precision_defaults)
+ number_with_precision(1, :locale => 'en-US')
+ end
+
+ def test_number_with_delimiter_translates_number_formats
+ I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
+ number_with_delimiter(1, :locale => 'en-US')
+ end
+
+ def test_number_to_percentage_translates_number_formats
+ I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
+ I18n.expects(:translate).with(:'number.percentage.format', :locale => 'en-US',
+ :raise => true).returns(@percentage_defaults)
+ number_to_percentage(1, :locale => 'en-US')
+ end
+
+ def test_number_to_human_size_translates_human_formats
+ I18n.expects(:translate).with(:'number.format', :locale => 'en-US', :raise => true).returns(@number_defaults)
+ I18n.expects(:translate).with(:'number.human.format', :locale => 'en-US',
+ :raise => true).returns(@human_defaults)
+ # can't be called with 1 because this directly returns without calling I18n.translate
+ number_to_human_size(1025, :locale => 'en-US')
+ end
+ end
+end
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index 4a8d09b544..9c9f54936c 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -26,7 +26,8 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("&pound;1234567890,50", number_to_currency(1234567890.50, {:unit => "&pound;", :separator => ",", :delimiter => ""}))
assert_equal("$1,234,567,890.50", number_to_currency("1234567890.50"))
assert_equal("1,234,567,890.50 K&#269;", number_to_currency("1234567890.50", {:unit => "K&#269;", :format => "%n %u"}))
- assert_equal("$x.", number_to_currency("x"))
+ #assert_equal("$x.", number_to_currency("x")) # fails due to API consolidation
+ assert_equal("$x", number_to_currency("x"))
assert_nil number_to_currency(nil)
end
@@ -35,7 +36,9 @@ class NumberHelperTest < ActionView::TestCase
assert_equal("100%", number_to_percentage(100, {:precision => 0}))
assert_equal("302.06%", number_to_percentage(302.0574, {:precision => 2}))
assert_equal("100.000%", number_to_percentage("100"))
+ assert_equal("1000.000%", number_to_percentage("1000"))
assert_equal("x%", number_to_percentage("x"))
+ assert_equal("1.000,000%", number_to_percentage(1000, :delimiter => '.', :separator => ','))
assert_nil number_to_percentage(nil)
end
@@ -54,21 +57,33 @@ class NumberHelperTest < ActionView::TestCase
assert_nil number_with_delimiter(nil)
end
+ def test_number_with_delimiter_with_options_hash
+ assert_equal '12 345 678', number_with_delimiter(12345678, :delimiter => ' ')
+ assert_equal '12,345,678-05', number_with_delimiter(12345678.05, :separator => '-')
+ assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :separator => ',', :delimiter => '.')
+ assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :delimiter => '.', :separator => ',')
+ end
+
def test_number_with_precision
assert_equal("111.235", number_with_precision(111.2346))
- assert_equal("31.83", number_with_precision(31.825, 2))
- assert_equal("111.23", number_with_precision(111.2346, 2))
- assert_equal("111.00", number_with_precision(111, 2))
+ assert_equal("31.83", number_with_precision(31.825, :precision => 2))
+ assert_equal("111.23", number_with_precision(111.2346, :precision => 2))
+ assert_equal("111.00", number_with_precision(111, :precision => 2))
assert_equal("111.235", number_with_precision("111.2346"))
- assert_equal("31.83", number_with_precision("31.825", 2))
- assert_equal("112", number_with_precision(111.50, 0))
- assert_equal("1234567892", number_with_precision(1234567891.50, 0))
+ assert_equal("31.83", number_with_precision("31.825", :precision => 2))
+ assert_equal("112", number_with_precision(111.50, :precision => 0))
+ assert_equal("1234567892", number_with_precision(1234567891.50, :precision => 0))
# Return non-numeric params unchanged.
assert_equal("x", number_with_precision("x"))
assert_nil number_with_precision(nil)
end
+ def test_number_with_precision_with_custom_delimiter_and_separator
+ assert_equal '31,83', number_with_precision(31.825, :precision => 2, :separator => ',')
+ assert_equal '1.231,83', number_with_precision(1231.825, :precision => 2, :separator => ',', :delimiter => '.')
+ end
+
def test_number_to_human_size
assert_equal '0 Bytes', number_to_human_size(0)
assert_equal '1 Byte', number_to_human_size(1)
@@ -80,18 +95,33 @@ class NumberHelperTest < ActionView::TestCase
assert_equal '1.2 MB', number_to_human_size(1234567)
assert_equal '1.1 GB', number_to_human_size(1234567890)
assert_equal '1.1 TB', number_to_human_size(1234567890123)
+ assert_equal '1025 TB', number_to_human_size(1025.terabytes)
assert_equal '444 KB', number_to_human_size(444.kilobytes)
assert_equal '1023 MB', number_to_human_size(1023.megabytes)
assert_equal '3 TB', number_to_human_size(3.terabytes)
- assert_equal '1.18 MB', number_to_human_size(1234567, 2)
- assert_equal '3 Bytes', number_to_human_size(3.14159265, 4)
+ assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2)
+ assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4)
assert_equal("123 Bytes", number_to_human_size("123"))
- assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, 2)
- assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, 4)
- assert_equal '10 KB', number_to_human_size(10.000.kilobytes, 4)
+ assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2)
+ assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4)
+ assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4)
assert_equal '1 Byte', number_to_human_size(1.1)
assert_equal '10 Bytes', number_to_human_size(10)
- assert_nil number_to_human_size('x')
+ #assert_nil number_to_human_size('x') # fails due to API consolidation
assert_nil number_to_human_size(nil)
end
+
+ def test_number_to_human_size_with_options_hash
+ assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2)
+ assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4)
+ assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2)
+ assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4)
+ assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4)
+ end
+
+ def test_number_to_human_size_with_custom_delimiter_and_separator
+ assert_equal '1,01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2, :separator => ',')
+ assert_equal '1,01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4, :separator => ',')
+ assert_equal '1.000,1 TB', number_to_human_size(1000.1.terabytes, :delimiter => '.', :separator => ',')
+ end
end
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index 1d9bc5eb9b..abc9f930dd 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -25,10 +25,10 @@ class Author::Nested < Author; end
class PrototypeHelperBaseTest < ActionView::TestCase
- attr_accessor :template_format
+ attr_accessor :template_format, :output_buffer
def setup
- @template = nil
+ @template = self
@controller = Class.new do
def url_for(options)
if options.is_a?(String)
@@ -77,6 +77,8 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" })
assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&amp;b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.responseText)}}); return false;\">Remote outauthor</a>),
link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })
+ assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:false, evalScripts:true}); return false;\">Remote outauthor</a>),
+ link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous)
end
def test_link_to_remote_html_options
@@ -243,8 +245,12 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
end
def test_update_page
+ old_output_buffer = output_buffer
+
block = Proc.new { |page| page.replace_html('foo', 'bar') }
assert_equal create_generator(&block).to_s, update_page(&block)
+
+ assert_equal old_output_buffer, output_buffer
end
def test_update_page_tag
@@ -283,13 +289,13 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
end
def test_insert_html_with_string
- assert_equal 'new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");',
+ assert_equal 'Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });',
@generator.insert_html(:top, 'element', '<p>This is a test</p>')
- assert_equal 'new Insertion.Bottom("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");',
+ assert_equal 'Element.insert("element", { bottom: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
@generator.insert_html(:bottom, 'element', '<p>This is a test</p>')
- assert_equal 'new Insertion.Before("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");',
+ assert_equal 'Element.insert("element", { before: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
@generator.insert_html(:before, 'element', '<p>This is a test</p>')
- assert_equal 'new Insertion.After("element", "\\u003Cp\u003EThis is a test\\u003C/p\u003E");',
+ assert_equal 'Element.insert("element", { after: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
@generator.insert_html(:after, 'element', '<p>This is a test</p>')
end
@@ -362,8 +368,8 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest
@generator.replace_html('baz', '<p>This is a test</p>')
assert_equal <<-EOS.chomp, @generator.to_s
-new Insertion.Top("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
-new Insertion.Bottom("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
+Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
+Element.insert("element", { bottom: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
["foo", "bar"].each(Element.remove);
Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
EOS
@@ -425,6 +431,8 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
def test_sortable
assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
@generator.sortable('blah', :url => { :action => "order" })
+ assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
+ @generator.sortable('blah', :url => { :action => "order" }, :type => :synchronous)
end
def test_draggable
@@ -435,6 +443,8 @@ Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
def test_drop_receiving
assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
@generator.drop_receiving('blah', :url => { :action => "order" })
+ assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
+ @generator.drop_receiving('blah', :url => { :action => "order" }, :type => :synchronous)
end
def test_collection_first_and_last
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index cc5b4900dc..f3c8dbcae9 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -19,6 +19,10 @@ class ViewRenderTest < Test::Unit::TestCase
assert_equal "Hello world!", @view.render("test/hello_world")
end
+ def test_render_file_at_top_level
+ assert_equal 'Elastica', @view.render('/shared')
+ end
+
def test_render_file_with_full_path
template_path = File.join(File.dirname(__FILE__), '../fixtures/test/hello_world.erb')
assert_equal "Hello world!", @view.render(:file => template_path)
@@ -47,6 +51,24 @@ class ViewRenderTest < Test::Unit::TestCase
assert_equal "only partial", @view.render(:partial => "test/partial_only")
end
+ def test_render_partial_with_format
+ assert_equal 'partial html', @view.render(:partial => 'test/partial')
+ end
+
+ def test_render_partial_at_top_level
+ # file fixtures/_top_level_partial_only.erb (not fixtures/test)
+ assert_equal 'top level partial', @view.render(:partial => '/top_level_partial_only')
+ end
+
+ def test_render_partial_with_format_at_top_level
+ # file fixtures/_top_level_partial.html.erb (not fixtures/test, with format extension)
+ assert_equal 'top level partial html', @view.render(:partial => '/top_level_partial')
+ end
+
+ def test_render_partial_with_locals
+ assert_equal "5", @view.render(:partial => "test/counter", :locals => { :counter_counter => 5 })
+ end
+
def test_render_partial_with_errors
assert_raise(ActionView::TemplateError) { @view.render(:partial => "test/raise") }
end
@@ -54,17 +76,29 @@ class ViewRenderTest < Test::Unit::TestCase
def test_render_partial_collection
assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ])
end
-
+
def test_render_partial_collection_as
- assert_equal "david david davidmary mary mary",
+ assert_equal "david david davidmary mary mary",
@view.render(:partial => "test/customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :customer)
end
-
+
def test_render_partial_collection_without_as
- assert_equal "local_inspector,local_inspector_counter,object",
+ assert_equal "local_inspector,local_inspector_counter,object",
@view.render(:partial => "test/local_inspector", :collection => [ Customer.new("mary") ])
end
+ def test_render_partial_with_empty_collection_should_return_nil
+ assert_nil @view.render(:partial => "test/customer", :collection => [])
+ end
+
+ def test_render_partial_with_nil_collection_should_return_nil
+ assert_nil @view.render(:partial => "test/customer", :collection => nil)
+ end
+
+ def test_render_partial_with_empty_array_should_return_nil
+ assert_nil @view.render(:partial => [])
+ end
+
# TODO: The reason for this test is unclear, improve documentation
def test_render_partial_and_fallback_to_layout
assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" })
@@ -94,38 +128,18 @@ class ViewRenderTest < Test::Unit::TestCase
assert_equal "Hello, World!", @view.render(:inline => "Hello, World!", :type => :foo)
end
- class CustomHandler < ActionView::TemplateHandler
- def render(template, local_assigns)
- [template.source, local_assigns].inspect
- end
- end
-
- def test_render_inline_with_custom_type
- ActionView::Template.register_template_handler :foo, CustomHandler
- assert_equal '["Hello, World!", {}]', @view.render(:inline => "Hello, World!", :type => :foo)
- end
-
- def test_render_inline_with_locals_and_custom_type
- ActionView::Template.register_template_handler :foo, CustomHandler
- assert_equal '["Hello, <%= name %>!", {:name=>"Josh"}]', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
- end
-
- class CompilableCustomHandler < ActionView::TemplateHandler
- include ActionView::TemplateHandlers::Compilable
-
- def compile(template)
- "@output_buffer = ''\n" +
- "@output_buffer << 'source: #{template.source.inspect}'\n"
- end
+ CustomHandler = lambda do |template|
+ "@output_buffer = ''\n" +
+ "@output_buffer << 'source: #{template.source.inspect}'\n"
end
def test_render_inline_with_compilable_custom_type
- ActionView::Template.register_template_handler :foo, CompilableCustomHandler
+ ActionView::Template.register_template_handler :foo, CustomHandler
assert_equal 'source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo)
end
def test_render_inline_with_locals_and_compilable_custom_type
- ActionView::Template.register_template_handler :foo, CompilableCustomHandler
+ ActionView::Template.register_template_handler :foo, CustomHandler
assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
end
end
diff --git a/actionpack/test/template/sanitize_helper_test.rb b/actionpack/test/template/sanitize_helper_test.rb
index e5427d9dc1..f715071bbc 100644
--- a/actionpack/test/template/sanitize_helper_test.rb
+++ b/actionpack/test/template/sanitize_helper_test.rb
@@ -11,9 +11,9 @@ class SanitizeHelperTest < ActionView::TestCase
assert_equal "Dont touch me", strip_links("Dont touch me")
assert_equal "<a<a", strip_links("<a<a")
assert_equal "on my mind\nall day long", strip_links("<a href='almost'>on my mind</a>\n<A href='almost'>all day long</A>")
- assert_equal "0wn3d", strip_links("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>")
- assert_equal "Magic", strip_links("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic")
- assert_equal "FrrFox", strip_links("<href onlclick='steal()'>FrrFox</a></href>")
+ assert_equal "0wn3d", strip_links("<a href='http://www.rubyonrails.com/'><a href='http://www.rubyonrails.com/' onlclick='steal()'>0wn3d</a></a>")
+ assert_equal "Magic", strip_links("<a href='http://www.rubyonrails.com/'>Mag<a href='http://www.ruby-lang.org/'>ic")
+ assert_equal "FrrFox", strip_links("<href onlclick='steal()'>FrrFox</a></href>")
assert_equal "My mind\nall <b>day</b> long", strip_links("<a href='almost'>My mind</a>\n<A href='almost'>all <b>day</b> long</A>")
assert_equal "all <b>day</b> long", strip_links("<<a>a href='hello'>all <b>day</b> long<</A>/a>")
end
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 4999525939..a31d532567 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -35,8 +35,8 @@ class TextHelperTest < ActionView::TestCase
end
def test_truncate
- assert_equal "Hello World!", truncate("Hello World!", 12)
- assert_equal "Hello Wor...", truncate("Hello World!!", 12)
+ assert_equal "Hello World!", truncate("Hello World!", :length => 12)
+ assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12)
end
def test_truncate_should_use_default_length_of_30
@@ -44,23 +44,29 @@ class TextHelperTest < ActionView::TestCase
assert_equal str[0...27] + "...", truncate(str)
end
+ def test_truncate_with_options_hash
+ assert_equal "This is a string that wil[...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]")
+ assert_equal "Hello W...", truncate("Hello World!", :length => 10)
+ assert_equal "Hello[...]", truncate("Hello World!", :omission => "[...]", :length => 10)
+ end
+
if RUBY_VERSION < '1.9.0'
def test_truncate_multibyte
with_kcode 'none' do
- assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", 10)
+ assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10)
end
with_kcode 'u' do
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...",
- truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244", 10)
+ truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244", :length => 10)
end
end
else
def test_truncate_multibyte
assert_equal "\354\225\210\353\205\225\355...",
- truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", 10)
+ truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10)
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
- truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), 10)
+ truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10)
end
end
@@ -88,7 +94,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal ' ', highlight(' ', 'blank text is returned verbatim')
end
- def test_highlighter_with_regexp
+ def test_highlight_with_regexp
assert_equal(
"This is a <strong class=\"highlight\">beautiful!</strong> morning",
highlight("This is a beautiful! morning", "beautiful!")
@@ -105,10 +111,17 @@ class TextHelperTest < ActionView::TestCase
)
end
- def test_highlighting_multiple_phrases_in_one_pass
+ def test_highlight_with_multiple_phrases_in_one_pass
assert_equal %(<em>wow</em> <em>em</em>), highlight('wow em', %w(wow em), '<em>\1</em>')
end
+ def test_highlight_with_options_hash
+ assert_equal(
+ "This is a <b>beautiful</b> morning, but also a <b>beautiful</b> day",
+ highlight("This is a beautiful morning, but also a beautiful day", "beautiful", :highlighter => '<b>\1</b>')
+ )
+ end
+
def test_excerpt
assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", 5))
assert_equal("This is a...", excerpt("This is a beautiful morning", "this", 5))
@@ -138,6 +151,16 @@ class TextHelperTest < ActionView::TestCase
assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', 'beautiful', 5))
end
+ def test_excerpt_with_options_hash
+ assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", :radius => 5))
+ assert_equal("[...]is a beautiful morn[...]", excerpt("This is a beautiful morning", "beautiful", :omission => "[...]",:radius => 5))
+ assert_equal(
+ "This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome tempera[...]",
+ excerpt("This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome temperatures. So what are you gonna do about it?", "very",
+ :omission => "[...]")
+ )
+ end
+
if RUBY_VERSION < '1.9'
def test_excerpt_with_utf8
with_kcode('u') do
@@ -162,6 +185,10 @@ class TextHelperTest < ActionView::TestCase
assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", 15))
end
+ def test_word_wrap_with_options_hash
+ assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", :line_width => 15))
+ end
+
def test_pluralization
assert_equal("1 count", pluralize(1, "count"))
assert_equal("2 counts", pluralize(2, "count"))
@@ -285,7 +312,13 @@ class TextHelperTest < ActionView::TestCase
url = "http://api.rubyonrails.com/Foo.html"
email = "fantabulous@shiznadel.ic"
- assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), auto_link("<p>#{url}<br />#{email}<br /></p>") { |url| truncate(url, 10) }
+ assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), auto_link("<p>#{url}<br />#{email}<br /></p>") { |url| truncate(url, :length => 10) }
+ end
+
+ def test_auto_link_with_options_hash
+ assert_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me@email.com">me@email.com</a>.',
+ auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.",
+ :link => :all, :html => { :class => "menu", :target => "_blank" })
end
def test_cycle_class
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
new file mode 100644
index 0000000000..7b94221ec0
--- /dev/null
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -0,0 +1,28 @@
+require 'abstract_unit'
+
+class TranslationHelperTest < Test::Unit::TestCase
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::TranslationHelper
+
+ attr_reader :request
+ uses_mocha 'translation_helper_test' do
+ def setup
+ end
+
+ def test_delegates_to_i18n_setting_the_raise_option
+ I18n.expects(:translate).with(:foo, 'en-US', :raise => true)
+ translate :foo, 'en-US'
+ end
+
+ def test_returns_missing_translation_message_wrapped_into_span
+ expected = '<span class="translation_missing">en-US, foo</span>'
+ assert_equal expected, translate(:foo)
+ end
+
+ def test_delegates_localize_to_i18n
+ @time = Time.utc(2008, 7, 8, 12, 18, 38)
+ I18n.expects(:localize).with(@time)
+ localize @time
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 91d5c6ffb5..85e967ac1c 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require 'abstract_unit'
RequestMock = Struct.new("Request", :request_uri, :protocol, :host_with_port, :env)
@@ -28,6 +29,16 @@ class UrlHelperTest < ActionView::TestCase
assert_equal "http://www.example.com?a=b&amp;c=d", url_for("http://www.example.com?a=b&amp;c=d")
end
+ def test_url_for_with_back
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'})
+ assert_equal 'http://www.example.com/referer', url_for(:back)
+ end
+
+ def test_url_for_with_back_and_no_referer
+ @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {})
+ assert_equal 'javascript:history.back()', url_for(:back)
+ end
+
# todo: missing test cases
def test_button_to_with_straight_url
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
@@ -267,7 +278,11 @@ class UrlHelperTest < ActionView::TestCase
end
def test_mail_to_with_javascript
- assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
+ end
+
+ def test_mail_to_with_javascript_unicode
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%22%3e%c3%ba%6e%69%63%6f%64%65%3c%2f%61%3e%27%29%3b'))</script>", mail_to("unicode@example.com", "Ășnicode", :encode => "javascript")
end
def test_mail_with_options
@@ -291,8 +306,8 @@ class UrlHelperTest < ActionView::TestCase
assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
+ assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
end
def protect_against_forgery?
@@ -419,7 +434,6 @@ class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase
end
end
-
class Workshop
attr_accessor :id, :new_record