diff options
Diffstat (limited to 'actionpack')
24 files changed, 296 insertions, 344 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 5197d944b1..3fc3e06160 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,44 @@ +## Rails 4.0.0 (unreleased) ## + +* Fixed `ActionView::Helpers::CaptureHelper#content_for` regression when trying to use it in + a boolean statement. + Fixes #9360. + + *Nikolay Shebanov* + +* `format: true` does not override existing format constraints. + Fixes #9466. + + Example: + + # This will force the .json extension. + get '/json_only', to: ok, format: true, constraints: { format: /json/ } + + *Yves Senn* + +* Skip valid encoding checks for non-String parameters that come + from the matched route's defaults. + Fixes #9435. + + Example: + + root to: 'main#posts', page: 1 + + *Yves Senn* + +* Don't verify Regexp requirements for non-Regexp `:constraints`. + Fixes #9432. + + Example: + + get '/photos.:format' => 'feeds#photos', constraints: {format: 'xml'} + + *Yves Senn* + +* Make `ActionDispatch::Journey::Path::Pattern#new` raise more meaningful exception message. + + *Thierry Zires* + ## Rails 4.0.0.beta1 (February 25, 2013) ## * Fix `respond_to` not using formats that have no block if all is present. *Michael Grosser* diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 4b984d0558..f8e4cb4384 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -97,15 +97,23 @@ module AbstractController self.response_body = render_to_body(options) end - # Raw rendering of a template to a string. Just convert the results of - # render_response into a String. + # Raw rendering of a template to a string. + # + # It is similar to render, except that it does not + # set the response_body and it should be guaranteed + # to always return a string. + # + # If a component extends the semantics of response_body + # (as Action Controller extends it to be anything that + # responds to the method each), this method needs to + # overriden in order to still return a string. # :api: plugin def render_to_string(*args, &block) options = _normalize_render(*args, &block) render_to_body(options) end - # Raw rendering of a template to a Rack-compatible body. + # Raw rendering of a template. # :api: plugin def render_to_body(options = {}) _process_options(options) diff --git a/actionpack/lib/abstract_controller/translation.rb b/actionpack/lib/abstract_controller/translation.rb index db48022b9f..02028d8e05 100644 --- a/actionpack/lib/abstract_controller/translation.rb +++ b/actionpack/lib/abstract_controller/translation.rb @@ -11,7 +11,7 @@ module AbstractController def translate(*args) key = args.first if key.is_a?(String) && (key[0] == '.') - key = "#{ controller_path.gsub('/', '.') }.#{ action_name }#{ key }" + key = "#{ controller_path.tr('/', '.') }.#{ action_name }#{ key }" args[0] = key end diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 89a7b12818..40bb060d52 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -121,7 +121,7 @@ module ActionDispatch BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ def valid_accept_header - (xhr? && (accept || content_mime_type)) || + (xhr? && (accept.present? || content_mime_type)) || (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS) end diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb index 4a571ec546..d37aa1fbe5 100644 --- a/actionpack/lib/action_dispatch/journey/path/pattern.rb +++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb @@ -20,7 +20,7 @@ module ActionDispatch @separators = strexp.separators.join @anchored = strexp.anchor else - raise "wtf bro: #{strexp}" + raise ArgumentError, "Bad expression: #{strexp}" end @names = nil diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb index 063302e0f2..6fda085681 100644 --- a/actionpack/lib/action_dispatch/journey/route.rb +++ b/actionpack/lib/action_dispatch/journey/route.rb @@ -71,6 +71,10 @@ module ActionDispatch Visitors::Formatter.new(path_options).accept(path.spec) end + def optimized_path + Visitors::OptimizedPath.new.accept(path.spec) + end + def optional_parts path.optional_names.map { |n| n.to_sym } end diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb index 46bd58c178..2964d80d9f 100644 --- a/actionpack/lib/action_dispatch/journey/visitors.rb +++ b/actionpack/lib/action_dispatch/journey/visitors.rb @@ -74,6 +74,14 @@ module ActionDispatch end end + class OptimizedPath < String # :nodoc: + private + + def visit_GROUP(node) + "" + end + end + # Used for formatting urls (url_for) class Formatter < Visitor # :nodoc: attr_reader :options, :consumed diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index ee29e5c59c..36a0db6e61 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -205,7 +205,7 @@ module ActionDispatch end # Removes the cookie on the client machine by setting the value to an empty string - # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in + # and the expiration date in the past. Like <tt>[]=</tt>, you can pass in # an options hash to delete cookies with extra data such as a <tt>:path</tt>. def delete(key, options = {}) return unless @cookies.has_key? key.to_s diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 6de9be63c5..93a2b52996 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -9,7 +9,7 @@ module ActionDispatch # at GetIp#calculate_ip. # # Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2] - # requires. Some Rack servers simply drop preceeding headers, and only report + # requires. Some Rack servers simply drop preceding headers, and only report # the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers]. # If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn) # then you should test your Rack server to make sure your data is good. diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb index 61690d3e50..cd3daff065 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb @@ -8,7 +8,7 @@ <h2>Failure reasons:</h2> <ol> <% @exception.failures.each do |route, reason| %> - <li><code><%= route.inspect.gsub('\\', '') %></code> failed because <%= reason.downcase %></li> + <li><code><%= route.inspect.delete('\\') %></code> failed because <%= reason.downcase %></li> <% end %> </ol> </p> diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index a8e225d61c..c5f2b33602 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -113,31 +113,15 @@ module ActionDispatch @options.merge!(default_controller_and_action) end - def normalize_format! - if options[:format] == true - options[:format] = /.+/ - elsif options[:format] == false - options.delete(:format) - end - end - def normalize_requirements! constraints.each do |key, requirement| next unless segment_keys.include?(key) || key == :controller - - if requirement.source =~ ANCHOR_CHARACTERS_REGEX - raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}" - end - - if requirement.multiline? - raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}" - end - + verify_regexp_requirement(requirement) if requirement.is_a?(Regexp) @requirements[key] = requirement end if options[:format] == true - @requirements[:format] = /.+/ + @requirements[:format] ||= /.+/ elsif Regexp === options[:format] @requirements[:format] = options[:format] elsif String === options[:format] @@ -145,6 +129,16 @@ module ActionDispatch end end + def verify_regexp_requirement(requirement) + if requirement.source =~ ANCHOR_CHARACTERS_REGEX + raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}" + end + + if requirement.multiline? + raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}" + end + end + def normalize_defaults! @defaults.merge!(scope[:defaults]) if scope[:defaults] @defaults.merge!(options[:defaults]) if options[:defaults] @@ -187,7 +181,8 @@ module ActionDispatch if !via_all && options[:via].blank? msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \ - "If you want to expose your action to GET, use `get` in the router:\n\n" \ + "If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.\n" \ + "If you want to expose your action to GET, use `get` in the router:\n" \ " Instead of: match \"controller#action\"\n" \ " Do: get \"controller#action\"" raise msg @@ -329,7 +324,6 @@ module ActionDispatch # because this means it will be matched first. As this is the most popular route # of most Rails applications, this is beneficial. def root(options = {}) - options = { :to => options } if options.is_a?(String) match '/', { :as => :root, :via => :get }.merge!(options) end @@ -426,11 +420,15 @@ module ActionDispatch # end # # [:constraints] - # Constrains parameters with a hash of regular expressions or an - # object that responds to <tt>matches?</tt> + # Constrains parameters with a hash of regular expressions + # or an object that responds to <tt>matches?</tt>. In addition, constraints + # other than path can also be specified with any object + # that responds to <tt>===</tt> (eg. String, Array, Range, etc.). # # match 'path/:id', constraints: { id: /[A-Z]\d{5}/ } # + # match 'json_only', constraints: { format: 'json' } + # # class Blacklist # def matches?(request) request.remote_ip == '1.2.3.4' end # end @@ -1427,7 +1425,15 @@ module ActionDispatch @set.add_route(app, conditions, requirements, defaults, as, anchor) end - def root(options={}) + def root(path, options={}) + if path.is_a?(String) + options[:to] = path + elsif path.is_a?(Hash) and options.empty? + options = path + else + raise ArgumentError, "must be called with a path and/or options" + end + if @scope[:scope_level] == :resources with_scope_level(:root) do scope(parent_resource.path) do diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index ff86f87d49..619dd22ec1 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -31,6 +31,8 @@ module ActionDispatch # If any of the path parameters has a invalid encoding then # raise since it's likely to trigger errors further on. params.each do |key, value| + next unless value.respond_to?(:valid_encoding?) + unless value.valid_encoding? raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}" end @@ -163,7 +165,7 @@ module ActionDispatch super @path_parts = @route.required_parts @arg_size = @path_parts.size - @string_route = string_route(route) + @string_route = @route.optimized_path end def call(t, args) @@ -178,14 +180,6 @@ module ActionDispatch private - def string_route(route) - string_route = route.ast.to_s.dup - while string_route.gsub!(/\([^\)]*\)/, "") - true - end - string_route - end - def optimized_helper(args) path = @string_route.dup klass = Journey::Router::Utils diff --git a/actionpack/lib/action_view/dependency_tracker.rb b/actionpack/lib/action_view/dependency_tracker.rb index 3de5cd150b..a2a555dfcb 100644 --- a/actionpack/lib/action_view/dependency_tracker.rb +++ b/actionpack/lib/action_view/dependency_tracker.rb @@ -54,8 +54,10 @@ module ActionView render_dependencies + explicit_dependencies end + attr_reader :name, :template + private :name, :template + private - attr_reader :name, :template def source template.source diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 4ec860d69a..1bad82159a 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -156,7 +156,7 @@ module ActionView end nil else - @view_flow.get(name) + @view_flow.get(name).presence end end diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 1adc8225f1..8abd5d6e9c 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -778,7 +778,7 @@ module ActionView # see http://www.w3.org/TR/html4/types.html#type-name def sanitize_to_id(name) - name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_") + name.to_s.delete(']').gsub(/[^-a-zA-Z0-9:.]/, "_") end end end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 1a1083bf00..47bd011d5e 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -109,7 +109,7 @@ module ActionView @cache.clear end - # Normalizes the arguments and passes it on to find_template. + # Normalizes the arguments and passes it on to find_templates. def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[]) cached(key, [name, prefix, partial], details, locals) do find_templates(name, prefix, partial, details) diff --git a/actionpack/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb index 1df826fe2b..368bec1c70 100644 --- a/actionpack/test/activerecord/controller_runtime_test.rb +++ b/actionpack/test/activerecord/controller_runtime_test.rb @@ -8,6 +8,8 @@ ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime class ControllerRuntimeLogSubscriberTest < ActionController::TestCase class LogSubscriberController < ActionController::Base + respond_to :html + def show render :inline => "<%= Project.all %>" end @@ -16,6 +18,12 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase render :inline => "Zero DB runtime" end + def create + ActiveRecord::LogSubscriber.runtime += 100 + project = Project.last + respond_with(project, location: url_for(action: :show)) + end + def redirect Project.all redirect_to :action => 'show' @@ -64,6 +72,12 @@ class ControllerRuntimeLogSubscriberTest < ActionController::TestCase assert_match(/\(Views: [\d.]+ms \| ActiveRecord: 0.0ms\)/, @logger.logged(:info)[1]) end + def test_log_with_active_record_when_post + post :create + wait + assert_match(/ActiveRecord: ([1-9][\d.]+)ms\)/, @logger.logged(:info)[2]) + end + def test_log_with_active_record_when_redirecting get :redirect wait diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 5e821046db..93e94f0f48 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -456,6 +456,22 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal("/", routes.send(:root_path)) end + def test_named_route_root_with_hash + rs.draw do + root "hello#index", as: :index + end + + routes = setup_for_named_route + assert_equal("http://test.host/", routes.send(:index_url)) + assert_equal("/", routes.send(:index_path)) + end + + def test_root_without_path_raises_argument_error + assert_raises ArgumentError do + rs.draw { root nil } + end + end + def test_named_route_root_with_trailing_slash rs.draw do root "hello#index" diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 91810864d5..f6de9748ca 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -594,6 +594,11 @@ class RequestTest < ActiveSupport::TestCase request.expects(:parameters).at_least_once.returns({}) assert_equal [Mime::HTML], request.formats + request = stub_request 'HTTP_ACCEPT' => '', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" + request.expects(:parameters).at_least_once.returns({}) + assert_equal [Mime::JS], request.formats + request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" request.expects(:parameters).at_least_once.returns({}) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 37ad9ddb6b..2bf7056ff7 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1346,7 +1346,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'en', @request.params[:locale] end - def test_default_params + def test_default_string_params draw do get 'inline_pages/(:id)', :to => 'pages#show', :id => 'home' get 'default_pages/(:id)', :to => 'pages#show', :defaults => { :id => 'home' } @@ -1366,6 +1366,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'home', @request.params[:id] end + def test_default_integer_params + draw do + get 'inline_pages/(:page)', to: 'pages#show', page: 1 + get 'default_pages/(:page)', to: 'pages#show', defaults: { page: 1 } + + defaults page: 1 do + get 'scoped_pages/(:page)', to: 'pages#show' + end + end + + get '/inline_pages' + assert_equal 1, @request.params[:page] + + get '/default_pages' + assert_equal 1, @request.params[:page] + + get '/scoped_pages' + assert_equal 1, @request.params[:page] + end + def test_resource_constraints draw do resources :products, :constraints => { :id => /\d{4}/ } do @@ -3178,6 +3198,7 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest app.draw do ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } get '/foo' => ok, as: :foo + get '/post(/:action(/:id))' => ok, as: :posts end end @@ -3195,6 +3216,11 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest test 'named route called on included module' do assert_equal '/foo', foo_path end + + test 'nested optional segments are removed' do + assert_equal '/post', Routes.url_helpers.posts_path + assert_equal '/post', posts_path + end end class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest @@ -3380,6 +3406,66 @@ class TestPortConstraints < ActionDispatch::IntegrationTest end end +class TestFormatConstraints < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } + + get '/string', to: ok, constraints: { format: 'json' } + get '/regexp', to: ok, constraints: { format: /json/ } + get '/json_only', to: ok, format: true, constraints: { format: /json/ } + get '/xml_only', to: ok, format: 'xml' + end + end + + include Routes.url_helpers + def app; Routes end + + def test_string_format_constraints + get 'http://www.example.com/string' + assert_response :success + + get 'http://www.example.com/string.json' + assert_response :success + + get 'http://www.example.com/string.html' + assert_response :not_found + end + + def test_regexp_format_constraints + get 'http://www.example.com/regexp' + assert_response :success + + get 'http://www.example.com/regexp.json' + assert_response :success + + get 'http://www.example.com/regexp.html' + assert_response :not_found + end + + def test_enforce_with_format_true_with_constraint + get 'http://www.example.com/json_only.json' + assert_response :success + + get 'http://www.example.com/json_only.html' + assert_response :not_found + + get 'http://www.example.com/json_only' + assert_response :not_found + end + + def test_enforce_with_string + get 'http://www.example.com/xml_only.xml' + assert_response :success + + get 'http://www.example.com/xml_only' + assert_response :success + + get 'http://www.example.com/xml_only.json' + assert_response :not_found + end +end + class TestRouteDefaults < ActionDispatch::IntegrationTest stub_controllers do |routes| Routes = routes diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb index 2b7227cd0d..ce02104181 100644 --- a/actionpack/test/journey/path/pattern_test.rb +++ b/actionpack/test/journey/path/pattern_test.rb @@ -75,6 +75,10 @@ module ActionDispatch end end + def test_to_raise_exception_with_bad_expression + assert_raise(ArgumentError, "Bad expression: []") { Pattern.new [] } + end + def test_to_regexp_with_extended_group strexp = Router::Strexp.new( '/page/:name', diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb index 234ac3252d..938f1c3e54 100644 --- a/actionpack/test/template/capture_helper_test.rb +++ b/actionpack/test/template/capture_helper_test.rb @@ -137,6 +137,10 @@ class CaptureHelperTest < ActionView::TestCase assert_equal 'bar', content_for(:title) end + def test_content_for_returns_nil_when_content_missing + assert_equal nil, content_for(:some_missing_key) + end + def test_content_for_question_mark assert ! content_for?(:title) content_for :title, 'title' diff --git a/actionpack/test/template/debug_helper_test.rb b/actionpack/test/template/debug_helper_test.rb new file mode 100644 index 0000000000..42d06bd9ff --- /dev/null +++ b/actionpack/test/template/debug_helper_test.rb @@ -0,0 +1,8 @@ +require 'active_record_unit' + +class DebugHelperTest < ActionView::TestCase + def test_debug + company = Company.new(name: "firebase") + assert_match " name: firebase", debug(company) + end +end diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index d8fffe75ed..6e640889d2 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -1,323 +1,75 @@ -require 'abstract_unit' +require "abstract_unit" class NumberHelperTest < ActionView::TestCase tests ActionView::Helpers::NumberHelper - def kilobytes(number) - number * 1024 - end - - def megabytes(number) - kilobytes(number) * 1024 - end - - def gigabytes(number) - megabytes(number) * 1024 - end - - def terabytes(number) - gigabytes(number) * 1024 - end - def test_number_to_phone - assert_equal("555-1234", number_to_phone(5551234)) - assert_equal("800-555-1212", number_to_phone(8005551212)) - assert_equal("(800) 555-1212", number_to_phone(8005551212, {:area_code => true})) - assert_equal("", number_to_phone("", {:area_code => true})) - assert_equal("800 555 1212", number_to_phone(8005551212, {:delimiter => " "})) - assert_equal("(800) 555-1212 x 123", number_to_phone(8005551212, {:area_code => true, :extension => 123})) - assert_equal("800-555-1212", number_to_phone(8005551212, :extension => " ")) - assert_equal("555.1212", number_to_phone(5551212, :delimiter => '.')) - assert_equal("800-555-1212", number_to_phone("8005551212")) - assert_equal("+1-800-555-1212", number_to_phone(8005551212, :country_code => 1)) - assert_equal("+18005551212", number_to_phone(8005551212, :country_code => 1, :delimiter => '')) - assert_equal("22-555-1212", number_to_phone(225551212)) - assert_equal("+45-22-555-1212", number_to_phone(225551212, :country_code => 45)) - assert_equal '111<script></script>111<script></script>1111', number_to_phone(1111111111, :delimiter => "<script></script>") + assert_equal nil, number_to_phone(nil) + assert_equal "555-1234", number_to_phone(5551234) + assert_equal "(800) 555-1212 x 123", number_to_phone(8005551212, area_code: true, extension: 123) + assert_equal "+18005551212", number_to_phone(8005551212, country_code: 1, delimiter: "") end def test_number_to_currency - assert_equal("$1,234,567,890.50", number_to_currency(1234567890.50)) - assert_equal("$1,234,567,890.51", number_to_currency(1234567890.506)) - assert_equal("-$1,234,567,890.50", number_to_currency(-1234567890.50)) - assert_equal("-$ 1,234,567,890.50", number_to_currency(-1234567890.50, {:format => "%u %n"})) - assert_equal("($1,234,567,890.50)", number_to_currency(-1234567890.50, {:negative_format => "(%u%n)"})) - assert_equal("$1,234,567,892", number_to_currency(1234567891.50, {:precision => 0})) - assert_equal("$1,234,567,890.5", number_to_currency(1234567890.50, {:precision => 1})) - assert_equal("£1234567890,50", number_to_currency(1234567890.50, {:unit => "£", :separator => ",", :delimiter => ""})) - assert_equal("$1,234,567,890.50", number_to_currency("1234567890.50")) - assert_equal("1,234,567,890.50 Kč", number_to_currency("1234567890.50", {:unit => "Kč", :format => "%n %u"})) - assert_equal("1,234,567,890.50 - Kč", number_to_currency("-1234567890.50", {:unit => "Kč", :format => "%n %u", :negative_format => "%n - %u"})) - assert_equal '$1<script></script>01', number_to_currency(1.01, :separator => "<script></script>") - assert_equal '$1<script></script>000.00', number_to_currency(1000, :delimiter => "<script></script>") + assert_equal nil, number_to_currency(nil) + assert_equal "$1,234,567,890.50", number_to_currency(1234567890.50) + assert_equal "$1,234,567,892", number_to_currency(1234567891.50, precision: 0) + assert_equal "1,234,567,890.50 - Kč", number_to_currency("-1234567890.50", unit: "Kč", format: "%n %u", negative_format: "%n - %u") end def test_number_to_percentage - assert_equal("100.000%", number_to_percentage(100)) - 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("123.4%", number_to_percentage(123.400, :precision => 3, :strip_insignificant_zeros => true)) - assert_equal("1.000,000%", number_to_percentage(1000, :delimiter => '.', :separator => ',')) - assert_equal("1000.000 %", number_to_percentage(1000, :format => "%n %")) - assert_equal '1<script></script>010%', number_to_percentage(1.01, :separator => "<script></script>") - assert_equal '1<script></script>000.000%', number_to_percentage(1000, :delimiter => "<script></script>") + assert_equal nil, number_to_percentage(nil) + assert_equal "100.000%", number_to_percentage(100) + assert_equal "100%", number_to_percentage(100, precision: 0) + assert_equal "123.4%", number_to_percentage(123.400, precision: 3, strip_insignificant_zeros: true) + assert_equal "1.000,000%", number_to_percentage(1000, delimiter: ".", separator: ",") end def test_number_with_delimiter - assert_equal("12,345,678", number_with_delimiter(12345678)) - assert_equal("0", number_with_delimiter(0)) - assert_equal("123", number_with_delimiter(123)) - assert_equal("123,456", number_with_delimiter(123456)) - assert_equal("123,456.78", number_with_delimiter(123456.78)) - assert_equal("123,456.789", number_with_delimiter(123456.789)) - assert_equal("123,456.78901", number_with_delimiter(123456.78901)) - assert_equal("123,456,789.78901", number_with_delimiter(123456789.78901)) - assert_equal("0.78901", number_with_delimiter(0.78901)) - assert_equal("123,456.78", number_with_delimiter("123456.78")) - 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 => ',') - assert_equal '1<script></script>01', number_with_delimiter(1.01, :separator => "<script></script>") - assert_equal '1<script></script>000', number_with_delimiter(1000, :delimiter => "<script></script>") + assert_equal nil, number_with_delimiter(nil) + assert_equal "12,345,678", number_with_delimiter(12345678) + assert_equal "0", number_with_delimiter(0) end def test_number_with_precision - assert_equal("-111.235", number_with_precision(-111.2346)) - assert_equal("111.235", number_with_precision(111.2346)) - 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", :precision => 2)) - assert_equal("3268", number_with_precision((32.6751 * 100.00), :precision => 0)) - assert_equal("112", number_with_precision(111.50, :precision => 0)) - assert_equal("1234567892", number_with_precision(1234567891.50, :precision => 0)) - assert_equal("0", number_with_precision(0, :precision => 0)) - assert_equal("0.00100", number_with_precision(0.001, :precision => 5)) - assert_equal("0.001", number_with_precision(0.00111, :precision => 3)) - assert_equal("10.00", number_with_precision(9.995, :precision => 2)) - assert_equal("11.00", number_with_precision(10.995, :precision => 2)) - assert_equal("0.00", number_with_precision(-0.001, :precision => 2)) - 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 => '.') - assert_equal '1<script></script>010', number_with_precision(1.01, :separator => "<script></script>") - assert_equal '1<script></script>000.000', number_with_precision(1000, :delimiter => "<script></script>") - end - - def test_number_with_precision_with_significant_digits - assert_equal "124000", number_with_precision(123987, :precision => 3, :significant => true) - assert_equal "120000000", number_with_precision(123987876, :precision => 2, :significant => true ) - assert_equal "40000", number_with_precision("43523", :precision => 1, :significant => true ) - assert_equal "9775", number_with_precision(9775, :precision => 4, :significant => true ) - assert_equal "5.4", number_with_precision(5.3923, :precision => 2, :significant => true ) - assert_equal "5", number_with_precision(5.3923, :precision => 1, :significant => true ) - assert_equal "1", number_with_precision(1.232, :precision => 1, :significant => true ) - assert_equal "7", number_with_precision(7, :precision => 1, :significant => true ) - assert_equal "1", number_with_precision(1, :precision => 1, :significant => true ) - assert_equal "53", number_with_precision(52.7923, :precision => 2, :significant => true ) - assert_equal "9775.00", number_with_precision(9775, :precision => 6, :significant => true ) - assert_equal "5.392900", number_with_precision(5.3929, :precision => 7, :significant => true ) - assert_equal "0.0", number_with_precision(0, :precision => 2, :significant => true ) - assert_equal "0", number_with_precision(0, :precision => 1, :significant => true ) - assert_equal "0.0001", number_with_precision(0.0001, :precision => 1, :significant => true ) - assert_equal "0.000100", number_with_precision(0.0001, :precision => 3, :significant => true ) - assert_equal "0.0001", number_with_precision(0.0001111, :precision => 1, :significant => true ) - assert_equal "10.0", number_with_precision(9.995, :precision => 3, :significant => true) - assert_equal "9.99", number_with_precision(9.994, :precision => 3, :significant => true) - assert_equal "11.0", number_with_precision(10.995, :precision => 3, :significant => true) - end - - def test_number_with_precision_with_strip_insignificant_zeros - assert_equal "9775.43", number_with_precision(9775.43, :precision => 4, :strip_insignificant_zeros => true ) - assert_equal "9775.2", number_with_precision(9775.2, :precision => 6, :significant => true, :strip_insignificant_zeros => true ) - assert_equal "0", number_with_precision(0, :precision => 6, :significant => true, :strip_insignificant_zeros => true ) - end - - def test_number_with_precision_with_significant_true_and_zero_precision - # Zero precision with significant is a mistake (would always return zero), - # so we treat it as if significant was false (increases backwards compatibility for number_to_human_size) - assert_equal "124", number_with_precision(123.987, :precision => 0, :significant => true) - assert_equal "12", number_with_precision(12, :precision => 0, :significant => true ) - assert_equal "12", number_with_precision("12.3", :precision => 0, :significant => true ) + assert_equal nil, number_with_precision(nil) + assert_equal "-111.235", number_with_precision(-111.2346) + assert_equal "111.00", number_with_precision(111, precision: 2) + assert_equal "0.00100", number_with_precision(0.001, precision: 5) 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) - assert_equal '3 Bytes', number_to_human_size(3.14159265) - assert_equal '123 Bytes', number_to_human_size(123.0) - assert_equal '123 Bytes', number_to_human_size(123) - assert_equal '1.21 KB', number_to_human_size(1234) - assert_equal '12.1 KB', number_to_human_size(12345) - assert_equal '1.18 MB', number_to_human_size(1234567) - assert_equal '1.15 GB', number_to_human_size(1234567890) - assert_equal '1.12 TB', number_to_human_size(1234567890123) - assert_equal '1030 TB', number_to_human_size(terabytes(1026)) - assert_equal '444 KB', number_to_human_size(kilobytes(444)) - assert_equal '1020 MB', number_to_human_size(megabytes(1023)) - assert_equal '3 TB', number_to_human_size(terabytes(3)) - assert_equal '1.2 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 KB', number_to_human_size(kilobytes(1.0123), :precision => 2) - assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4) - assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4) - assert_equal '1 Byte', number_to_human_size(1.1) - assert_equal '10 Bytes', number_to_human_size(10) - end - - def test_number_to_human_size_with_si_prefix - assert_equal '3 Bytes', number_to_human_size(3.14159265, :prefix => :si) - assert_equal '123 Bytes', number_to_human_size(123.0, :prefix => :si) - assert_equal '123 Bytes', number_to_human_size(123, :prefix => :si) - assert_equal '1.23 KB', number_to_human_size(1234, :prefix => :si) - assert_equal '12.3 KB', number_to_human_size(12345, :prefix => :si) - assert_equal '1.23 MB', number_to_human_size(1234567, :prefix => :si) - assert_equal '1.23 GB', number_to_human_size(1234567890, :prefix => :si) - assert_equal '1.23 TB', number_to_human_size(1234567890123, :prefix => :si) - end - - def test_number_to_human_size_with_options_hash - assert_equal '1.2 MB', number_to_human_size(1234567, :precision => 2) - assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4) - assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 2) - assert_equal '1.01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4) - assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4) - assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 1) - assert_equal '500 MB', number_to_human_size(524288000, :precision=>3) - assert_equal '10 MB', number_to_human_size(9961472, :precision=>0) - assert_equal '40 KB', number_to_human_size(41010, :precision => 1) - assert_equal '40 KB', number_to_human_size(41100, :precision => 2) - assert_equal '1.0 KB', number_to_human_size(kilobytes(1.0123), :precision => 2, :strip_insignificant_zeros => false) - assert_equal '1.012 KB', number_to_human_size(kilobytes(1.0123), :precision => 3, :significant => false) - assert_equal '1 KB', number_to_human_size(kilobytes(1.0123), :precision => 0, :significant => true) #ignores significant it precision is 0 - assert_equal '9<script></script>86 KB', number_to_human_size(10100, :separator => "<script></script>") - end - - def test_number_to_human_size_with_custom_delimiter_and_separator - assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0123), :precision => 3, :separator => ',') - assert_equal '1,01 KB', number_to_human_size(kilobytes(1.0100), :precision => 4, :separator => ',') - assert_equal '1.000,1 TB', number_to_human_size(terabytes(1000.1), :precision => 5, :delimiter => '.', :separator => ',') + assert_equal nil, number_to_human_size(nil) + assert_equal "3 Bytes", number_to_human_size(3.14159265) + assert_equal "1.2 MB", number_to_human_size(1234567, precision: 2) end def test_number_to_human - assert_equal '-123', number_to_human(-123) - assert_equal '-0.5', number_to_human(-0.5) - assert_equal '0', number_to_human(0) - assert_equal '0.5', number_to_human(0.5) - assert_equal '123', number_to_human(123) - assert_equal '1.23 Thousand', number_to_human(1234) - assert_equal '12.3 Thousand', number_to_human(12345) - assert_equal '1.23 Million', number_to_human(1234567) - assert_equal '1.23 Billion', number_to_human(1234567890) - assert_equal '1.23 Trillion', number_to_human(1234567890123) - assert_equal '1.23 Quadrillion', number_to_human(1234567890123456) - assert_equal '1230 Quadrillion', number_to_human(1234567890123456789) - assert_equal '490 Thousand', number_to_human(489939, :precision => 2) - assert_equal '489.9 Thousand', number_to_human(489939, :precision => 4) - assert_equal '489 Thousand', number_to_human(489000, :precision => 4) - assert_equal '489.0 Thousand', number_to_human(489000, :precision => 4, :strip_insignificant_zeros => false) - assert_equal '1.2346 Million', number_to_human(1234567, :precision => 4, :significant => false) - assert_equal '1,2 Million', number_to_human(1234567, :precision => 1, :significant => false, :separator => ',') - assert_equal '1 Million', number_to_human(1234567, :precision => 0, :significant => true, :separator => ',') #significant forced to false - end - - def test_number_to_human_with_custom_units - #Only integers - volume = {:unit => "ml", :thousand => "lt", :million => "m3"} - assert_equal '123 lt', number_to_human(123456, :units => volume) - assert_equal '12 ml', number_to_human(12, :units => volume) - assert_equal '1.23 m3', number_to_human(1234567, :units => volume) - - #Including fractionals - distance = {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"} - assert_equal '1.23 mm', number_to_human(0.00123, :units => distance) - assert_equal '1.23 cm', number_to_human(0.0123, :units => distance) - assert_equal '1.23 dm', number_to_human(0.123, :units => distance) - assert_equal '1.23 m', number_to_human(1.23, :units => distance) - assert_equal '1.23 dam', number_to_human(12.3, :units => distance) - assert_equal '1.23 hm', number_to_human(123, :units => distance) - assert_equal '1.23 km', number_to_human(1230, :units => distance) - assert_equal '1.23 km', number_to_human(1230, :units => distance) - assert_equal '1.23 km', number_to_human(1230, :units => distance) - assert_equal '12.3 km', number_to_human(12300, :units => distance) - - #The quantifiers don't need to be a continuous sequence - gangster = {:hundred => "hundred bucks", :million => "thousand quids"} - assert_equal '1 hundred bucks', number_to_human(100, :units => gangster) - assert_equal '25 hundred bucks', number_to_human(2500, :units => gangster) - assert_equal '25 thousand quids', number_to_human(25000000, :units => gangster) - assert_equal '12300 thousand quids', number_to_human(12345000000, :units => gangster) - - #Spaces are stripped from the resulting string - assert_equal '4', number_to_human(4, :units => {:unit => "", :ten => 'tens '}) - assert_equal '4.5 tens', number_to_human(45, :units => {:unit => "", :ten => ' tens '}) - - assert_equal '1<script></script>01', number_to_human(1.01, :separator => "<script></script>") - assert_equal '100<script></script>000 Quadrillion', number_to_human(10**20, :delimiter => "<script></script>") + assert_equal nil, number_to_human(nil) + assert_equal "0", number_to_human(0) + assert_equal "1.23 Thousand", number_to_human(1234) + assert_equal "489.0 Thousand", number_to_human(489000, precision: 4, strip_insignificant_zeros: false) end - def test_number_to_human_with_custom_format - assert_equal '123 times Thousand', number_to_human(123456, :format => "%n times %u") - volume = {:unit => "ml", :thousand => "lt", :million => "m3"} - assert_equal '123.lt', number_to_human(123456, :units => volume, :format => "%n.%u") - end - - def test_number_helpers_should_return_nil_when_given_nil - assert_nil number_to_phone(nil) - assert_nil number_to_currency(nil) - assert_nil number_to_percentage(nil) - assert_nil number_with_delimiter(nil) - assert_nil number_with_precision(nil) - assert_nil number_to_human_size(nil) - assert_nil number_to_human(nil) - end + def test_number_helpers_escape_delimiter_and_separator + assert_equal "111<script></script>111<script></script>1111", number_to_phone(1111111111, delimiter: "<script></script>") - def test_number_helpers_do_not_mutate_options_hash - options = { 'raise' => true } + assert_equal "$1<script></script>01", number_to_currency(1.01, separator: "<script></script>") + assert_equal "$1<script></script>000.00", number_to_currency(1000, delimiter: "<script></script>") - number_to_phone(1, options) - assert_equal({ 'raise' => true }, options) + assert_equal "1<script></script>010%", number_to_percentage(1.01, separator: "<script></script>") + assert_equal "1<script></script>000.000%", number_to_percentage(1000, delimiter: "<script></script>") - number_to_currency(1, options) - assert_equal({ 'raise' => true }, options) + assert_equal "1<script></script>01", number_with_delimiter(1.01, separator: "<script></script>") + assert_equal "1<script></script>000", number_with_delimiter(1000, delimiter: "<script></script>") - number_to_percentage(1, options) - assert_equal({ 'raise' => true }, options) + assert_equal "1<script></script>010", number_with_precision(1.01, separator: "<script></script>") + assert_equal "1<script></script>000.000", number_with_precision(1000, delimiter: "<script></script>") - number_with_delimiter(1, options) - assert_equal({ 'raise' => true }, options) - - number_with_precision(1, options) - assert_equal({ 'raise' => true }, options) - - number_to_human_size(1, options) - assert_equal({ 'raise' => true }, options) - - number_to_human(1, options) - assert_equal({ 'raise' => true }, options) - end + assert_equal "9<script></script>86 KB", number_to_human_size(10100, separator: "<script></script>") - def test_number_helpers_should_return_non_numeric_param_unchanged - assert_equal("+1-x x 123", number_to_phone("x", :country_code => 1, :extension => 123)) - assert_equal("x", number_to_phone("x")) - assert_equal("$x.", number_to_currency("x.")) - assert_equal("$x", number_to_currency("x")) - assert_equal("x%", number_to_percentage("x")) - assert_equal("x", number_with_delimiter("x")) - assert_equal("x.", number_with_precision("x.")) - assert_equal("x", number_with_precision("x")) - assert_equal "x", number_to_human_size('x') - assert_equal "x", number_to_human('x') + assert_equal "1<script></script>01", number_to_human(1.01, separator: "<script></script>") + assert_equal "100<script></script>000 Quadrillion", number_to_human(10**20, delimiter: "<script></script>") end def test_number_helpers_outputs_are_html_safe @@ -332,8 +84,8 @@ class NumberHelperTest < ActionView::TestCase assert number_to_human_size("asdf".html_safe).html_safe? assert number_to_human_size("1".html_safe).html_safe? - assert number_with_precision(1, :strip_insignificant_zeros => false).html_safe? - assert number_with_precision(1, :strip_insignificant_zeros => true).html_safe? + assert number_with_precision(1, strip_insignificant_zeros: false).html_safe? + assert number_with_precision(1, strip_insignificant_zeros: true).html_safe? assert !number_with_precision("<script></script>").html_safe? assert number_with_precision("asdf".html_safe).html_safe? assert number_with_precision("1".html_safe).html_safe? @@ -362,37 +114,37 @@ class NumberHelperTest < ActionView::TestCase def test_number_helpers_should_raise_error_if_invalid_when_specified exception = assert_raise InvalidNumberError do - number_to_human("x", :raise => true) + number_to_human("x", raise: true) end assert_equal "x", exception.number exception = assert_raise InvalidNumberError do - number_to_human_size("x", :raise => true) + number_to_human_size("x", raise: true) end assert_equal "x", exception.number exception = assert_raise InvalidNumberError do - number_with_precision("x", :raise => true) + number_with_precision("x", raise: true) end assert_equal "x", exception.number exception = assert_raise InvalidNumberError do - number_to_currency("x", :raise => true) + number_to_currency("x", raise: true) end assert_equal "x", exception.number exception = assert_raise InvalidNumberError do - number_to_percentage("x", :raise => true) + number_to_percentage("x", raise: true) end assert_equal "x", exception.number exception = assert_raise InvalidNumberError do - number_with_delimiter("x", :raise => true) + number_with_delimiter("x", raise: true) end assert_equal "x", exception.number exception = assert_raise InvalidNumberError do - number_to_phone("x", :raise => true) + number_to_phone("x", raise: true) end assert_equal "x", exception.number end |