actionpack 3.2.22.5 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +641 -418
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -288
- data/lib/abstract_controller.rb +1 -8
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +39 -37
- data/lib/abstract_controller/callbacks.rb +101 -82
- data/lib/abstract_controller/collector.rb +7 -3
- data/lib/abstract_controller/helpers.rb +23 -11
- data/lib/abstract_controller/layouts.rb +68 -73
- data/lib/abstract_controller/logger.rb +1 -2
- data/lib/abstract_controller/rendering.rb +22 -13
- data/lib/abstract_controller/translation.rb +16 -1
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller/view_paths.rb +1 -1
- data/lib/action_controller.rb +15 -6
- data/lib/action_controller/base.rb +46 -22
- data/lib/action_controller/caching.rb +46 -33
- data/lib/action_controller/caching/fragments.rb +23 -53
- data/lib/action_controller/deprecated.rb +5 -1
- data/lib/action_controller/deprecated/integration_test.rb +3 -0
- data/lib/action_controller/log_subscriber.rb +11 -8
- data/lib/action_controller/metal.rb +16 -30
- data/lib/action_controller/metal/conditional_get.rb +76 -32
- data/lib/action_controller/metal/data_streaming.rb +20 -26
- data/lib/action_controller/metal/exceptions.rb +19 -6
- data/lib/action_controller/metal/flash.rb +24 -9
- data/lib/action_controller/metal/force_ssl.rb +32 -9
- data/lib/action_controller/metal/head.rb +25 -4
- data/lib/action_controller/metal/helpers.rb +6 -9
- data/lib/action_controller/metal/hide_actions.rb +1 -2
- data/lib/action_controller/metal/http_authentication.rb +105 -87
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +2 -1
- data/lib/action_controller/metal/live.rb +141 -0
- data/lib/action_controller/metal/mime_responds.rb +161 -47
- data/lib/action_controller/metal/params_wrapper.rb +112 -74
- data/lib/action_controller/metal/rack_delegation.rb +9 -3
- data/lib/action_controller/metal/redirecting.rb +15 -20
- data/lib/action_controller/metal/renderers.rb +11 -9
- data/lib/action_controller/metal/rendering.rb +8 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
- data/lib/action_controller/metal/responder.rb +20 -19
- data/lib/action_controller/metal/streaming.rb +12 -18
- data/lib/action_controller/metal/strong_parameters.rb +516 -0
- data/lib/action_controller/metal/testing.rb +13 -18
- data/lib/action_controller/metal/url_for.rb +27 -25
- data/lib/action_controller/model_naming.rb +12 -0
- data/lib/action_controller/railtie.rb +33 -17
- data/lib/action_controller/railties/helpers.rb +22 -0
- data/lib/action_controller/record_identifier.rb +18 -72
- data/lib/action_controller/test_case.rb +215 -123
- data/lib/action_controller/vendor/html-scanner.rb +4 -19
- data/lib/action_dispatch.rb +27 -19
- data/lib/action_dispatch/http/cache.rb +63 -11
- data/lib/action_dispatch/http/filter_parameters.rb +18 -8
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +27 -19
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -2
- data/lib/action_dispatch/http/mime_type.rb +145 -113
- data/lib/action_dispatch/http/mime_types.rb +1 -1
- data/lib/action_dispatch/http/parameter_filter.rb +44 -46
- data/lib/action_dispatch/http/parameters.rb +12 -5
- data/lib/action_dispatch/http/rack_cache.rb +2 -3
- data/lib/action_dispatch/http/request.rb +49 -18
- data/lib/action_dispatch/http/response.rb +129 -35
- data/lib/action_dispatch/http/upload.rb +60 -17
- data/lib/action_dispatch/http/url.rb +53 -31
- data/lib/action_dispatch/journey.rb +5 -0
- data/lib/action_dispatch/journey/backwards.rb +5 -0
- data/lib/action_dispatch/journey/formatter.rb +146 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
- data/lib/action_dispatch/journey/nodes/node.rb +124 -0
- data/lib/action_dispatch/journey/parser.rb +206 -0
- data/lib/action_dispatch/journey/parser.y +47 -0
- data/lib/action_dispatch/journey/parser_extras.rb +23 -0
- data/lib/action_dispatch/journey/path/pattern.rb +196 -0
- data/lib/action_dispatch/journey/route.rb +116 -0
- data/lib/action_dispatch/journey/router.rb +164 -0
- data/lib/action_dispatch/journey/router/strexp.rb +24 -0
- data/lib/action_dispatch/journey/router/utils.rb +54 -0
- data/lib/action_dispatch/journey/routes.rb +75 -0
- data/lib/action_dispatch/journey/scanner.rb +61 -0
- data/lib/action_dispatch/journey/visitors.rb +189 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/middleware/callbacks.rb +9 -4
- data/lib/action_dispatch/middleware/cookies.rb +168 -57
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
- data/lib/action_dispatch/middleware/exception_wrapper.rb +27 -3
- data/lib/action_dispatch/middleware/flash.rb +58 -58
- data/lib/action_dispatch/middleware/params_parser.rb +14 -29
- data/lib/action_dispatch/middleware/public_exceptions.rb +31 -14
- data/lib/action_dispatch/middleware/reloader.rb +6 -6
- data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
- data/lib/action_dispatch/middleware/request_id.rb +2 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
- data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +81 -7
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
- data/lib/action_dispatch/middleware/ssl.rb +70 -0
- data/lib/action_dispatch/middleware/stack.rb +6 -1
- data/lib/action_dispatch/middleware/static.rb +5 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -5
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
- data/lib/action_dispatch/railtie.rb +16 -6
- data/lib/action_dispatch/request/session.rb +181 -0
- data/lib/action_dispatch/routing.rb +41 -40
- data/lib/action_dispatch/routing/inspector.rb +240 -0
- data/lib/action_dispatch/routing/mapper.rb +501 -273
- data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
- data/lib/action_dispatch/routing/redirection.rb +46 -29
- data/lib/action_dispatch/routing/route_set.rb +203 -164
- data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
- data/lib/action_dispatch/routing/url_for.rb +48 -33
- data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
- data/lib/action_dispatch/testing/assertions/response.rb +32 -40
- data/lib/action_dispatch/testing/assertions/routing.rb +40 -39
- data/lib/action_dispatch/testing/assertions/selector.rb +15 -20
- data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
- data/lib/action_dispatch/testing/integration.rb +41 -22
- data/lib/action_dispatch/testing/test_process.rb +9 -6
- data/lib/action_dispatch/testing/test_request.rb +7 -3
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +4 -4
- data/lib/action_view.rb +17 -8
- data/lib/action_view/base.rb +15 -34
- data/lib/action_view/buffers.rb +1 -1
- data/lib/action_view/context.rb +4 -4
- data/lib/action_view/dependency_tracker.rb +91 -0
- data/lib/action_view/digestor.rb +85 -0
- data/lib/action_view/flows.rb +1 -4
- data/lib/action_view/helpers.rb +2 -4
- data/lib/action_view/helpers/active_model_helper.rb +3 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +211 -353
- data/lib/action_view/helpers/asset_url_helper.rb +354 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
- data/lib/action_view/helpers/cache_helper.rb +150 -18
- data/lib/action_view/helpers/capture_helper.rb +42 -29
- data/lib/action_view/helpers/csrf_helper.rb +0 -2
- data/lib/action_view/helpers/date_helper.rb +268 -247
- data/lib/action_view/helpers/debug_helper.rb +10 -11
- data/lib/action_view/helpers/form_helper.rb +904 -547
- data/lib/action_view/helpers/form_options_helper.rb +341 -166
- data/lib/action_view/helpers/form_tag_helper.rb +188 -88
- data/lib/action_view/helpers/javascript_helper.rb +23 -16
- data/lib/action_view/helpers/number_helper.rb +148 -354
- data/lib/action_view/helpers/output_safety_helper.rb +3 -3
- data/lib/action_view/helpers/record_tag_helper.rb +17 -22
- data/lib/action_view/helpers/rendering_helper.rb +2 -4
- data/lib/action_view/helpers/sanitize_helper.rb +3 -6
- data/lib/action_view/helpers/tag_helper.rb +43 -37
- data/lib/action_view/helpers/tags.rb +39 -0
- data/lib/action_view/helpers/tags/base.rb +148 -0
- data/lib/action_view/helpers/tags/check_box.rb +64 -0
- data/lib/action_view/helpers/tags/checkable.rb +16 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
- data/lib/action_view/helpers/tags/collection_select.rb +28 -0
- data/lib/action_view/helpers/tags/color_field.rb +25 -0
- data/lib/action_view/helpers/tags/date_field.rb +13 -0
- data/lib/action_view/helpers/tags/date_select.rb +72 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
- data/lib/action_view/helpers/tags/email_field.rb +8 -0
- data/lib/action_view/helpers/tags/file_field.rb +8 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
- data/lib/action_view/helpers/tags/label.rb +65 -0
- data/lib/action_view/helpers/tags/month_field.rb +13 -0
- data/lib/action_view/helpers/tags/number_field.rb +18 -0
- data/lib/action_view/helpers/tags/password_field.rb +12 -0
- data/lib/action_view/helpers/tags/radio_button.rb +31 -0
- data/lib/action_view/helpers/tags/range_field.rb +8 -0
- data/lib/action_view/helpers/tags/search_field.rb +24 -0
- data/lib/action_view/helpers/tags/select.rb +41 -0
- data/lib/action_view/helpers/tags/tel_field.rb +8 -0
- data/lib/action_view/helpers/tags/text_area.rb +18 -0
- data/lib/action_view/helpers/tags/text_field.rb +29 -0
- data/lib/action_view/helpers/tags/time_field.rb +13 -0
- data/lib/action_view/helpers/tags/time_select.rb +8 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
- data/lib/action_view/helpers/tags/url_field.rb +8 -0
- data/lib/action_view/helpers/tags/week_field.rb +13 -0
- data/lib/action_view/helpers/text_helper.rb +126 -113
- data/lib/action_view/helpers/translation_helper.rb +32 -16
- data/lib/action_view/helpers/url_helper.rb +200 -271
- data/lib/action_view/locale/en.yml +1 -105
- data/lib/action_view/log_subscriber.rb +6 -4
- data/lib/action_view/lookup_context.rb +15 -39
- data/lib/action_view/model_naming.rb +12 -0
- data/lib/action_view/path_set.rb +9 -39
- data/lib/action_view/railtie.rb +6 -22
- data/lib/action_view/record_identifier.rb +84 -0
- data/lib/action_view/renderer/abstract_renderer.rb +10 -19
- data/lib/action_view/renderer/partial_renderer.rb +144 -81
- data/lib/action_view/renderer/renderer.rb +2 -19
- data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
- data/lib/action_view/renderer/template_renderer.rb +14 -13
- data/lib/action_view/routing_url_for.rb +107 -0
- data/lib/action_view/template.rb +22 -21
- data/lib/action_view/template/error.rb +22 -12
- data/lib/action_view/template/handlers.rb +12 -9
- data/lib/action_view/template/handlers/builder.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +11 -16
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/resolver.rb +111 -83
- data/lib/action_view/template/text.rb +12 -8
- data/lib/action_view/template/types.rb +57 -0
- data/lib/action_view/test_case.rb +66 -43
- data/lib/action_view/testing/resolvers.rb +3 -2
- data/lib/action_view/vendor/html-scanner.rb +20 -0
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +18 -7
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +1 -1
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
- metadata +135 -125
- data/lib/action_controller/caching/actions.rb +0 -185
- data/lib/action_controller/caching/pages.rb +0 -187
- data/lib/action_controller/caching/sweeping.rb +0 -97
- data/lib/action_controller/deprecated/performance_test.rb +0 -1
- data/lib/action_controller/metal/compatibility.rb +0 -65
- data/lib/action_controller/metal/session_management.rb +0 -14
- data/lib/action_controller/railties/paths.rb +0 -25
- data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
- data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
- data/lib/action_dispatch/middleware/head.rb +0 -18
- data/lib/action_dispatch/middleware/rescue.rb +0 -26
- data/lib/action_dispatch/testing/performance_test.rb +0 -10
- data/lib/action_view/asset_paths.rb +0 -142
- data/lib/action_view/helpers/asset_paths.rb +0 -7
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
- data/lib/sprockets/assets.rake +0 -99
- data/lib/sprockets/bootstrap.rb +0 -37
- data/lib/sprockets/compressors.rb +0 -83
- data/lib/sprockets/helpers.rb +0 -6
- data/lib/sprockets/helpers/isolated_helper.rb +0 -13
- data/lib/sprockets/helpers/rails_helper.rb +0 -182
- data/lib/sprockets/railtie.rb +0 -62
- data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'action_view/helpers/tag_helper'
|
2
|
-
require 'active_support/core_ext/string/encoding'
|
3
2
|
|
4
3
|
module ActionView
|
5
4
|
module Helpers
|
@@ -14,20 +13,18 @@ module ActionView
|
|
14
13
|
"'" => "\\'"
|
15
14
|
}
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
else
|
20
|
-
JS_ESCAPE_MAP["\342\200\250"] = '
'
|
21
|
-
end
|
16
|
+
JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '
'
|
17
|
+
JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '
'
|
22
18
|
|
23
19
|
# Escapes carriage returns and single and double quotes for JavaScript segments.
|
24
20
|
#
|
25
|
-
# Also available through the alias j(). This is particularly helpful in JavaScript
|
21
|
+
# Also available through the alias j(). This is particularly helpful in JavaScript
|
22
|
+
# responses, like:
|
26
23
|
#
|
27
24
|
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
|
28
25
|
def escape_javascript(javascript)
|
29
26
|
if javascript
|
30
|
-
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
|
27
|
+
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
|
31
28
|
javascript.html_safe? ? result.html_safe : result
|
32
29
|
else
|
33
30
|
''
|
@@ -40,20 +37,22 @@ module ActionView
|
|
40
37
|
# javascript_tag "alert('All is good')"
|
41
38
|
#
|
42
39
|
# Returns:
|
43
|
-
# <script
|
40
|
+
# <script>
|
44
41
|
# //<![CDATA[
|
45
42
|
# alert('All is good')
|
46
43
|
# //]]>
|
47
44
|
# </script>
|
48
45
|
#
|
49
46
|
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
|
50
|
-
# tag.
|
51
|
-
#
|
52
|
-
#
|
47
|
+
# tag.
|
48
|
+
#
|
49
|
+
# javascript_tag "alert('All is good')", defer: 'defer'
|
50
|
+
# # => <script defer="defer">alert('All is good')</script>
|
53
51
|
#
|
54
52
|
# Instead of passing the content as an argument, you can also use a block
|
55
53
|
# in which case, you pass your +html_options+ as the first parameter.
|
56
|
-
#
|
54
|
+
#
|
55
|
+
# <%= javascript_tag defer: 'defer' do -%>
|
57
56
|
# alert('All is good')
|
58
57
|
# <% end -%>
|
59
58
|
def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
|
@@ -65,7 +64,7 @@ module ActionView
|
|
65
64
|
content_or_options_with_block
|
66
65
|
end
|
67
66
|
|
68
|
-
content_tag(:script, javascript_cdata_section(content), html_options
|
67
|
+
content_tag(:script, javascript_cdata_section(content), html_options)
|
69
68
|
end
|
70
69
|
|
71
70
|
def javascript_cdata_section(content) #:nodoc:
|
@@ -78,10 +77,14 @@ module ActionView
|
|
78
77
|
# name is used as button label and the JavaScript code goes into its +onclick+ attribute.
|
79
78
|
# If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+.
|
80
79
|
#
|
81
|
-
# button_to_function "Greeting", "alert('Hello world!')", :
|
80
|
+
# button_to_function "Greeting", "alert('Hello world!')", class: "ok"
|
82
81
|
# # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
|
83
82
|
#
|
84
83
|
def button_to_function(name, function=nil, html_options={})
|
84
|
+
message = "button_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " +
|
85
|
+
"See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
|
86
|
+
ActiveSupport::Deprecation.warn message
|
87
|
+
|
85
88
|
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
|
86
89
|
|
87
90
|
tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
|
@@ -96,10 +99,14 @@ module ActionView
|
|
96
99
|
#
|
97
100
|
# The +href+ attribute of the tag is set to "#" unless +html_options+ has one.
|
98
101
|
#
|
99
|
-
# link_to_function "Greeting", "alert('Hello world!')", :
|
102
|
+
# link_to_function "Greeting", "alert('Hello world!')", class: "nav_link"
|
100
103
|
# # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
|
101
104
|
#
|
102
105
|
def link_to_function(name, function, html_options={})
|
106
|
+
message = "link_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " +
|
107
|
+
"See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
|
108
|
+
ActiveSupport::Deprecation.warn message
|
109
|
+
|
103
110
|
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
|
104
111
|
href = html_options[:href] || '#'
|
105
112
|
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'active_support/core_ext/hash/keys'
|
4
|
-
require 'active_support/core_ext/hash/reverse_merge'
|
5
|
-
require 'active_support/core_ext/big_decimal/conversions'
|
6
|
-
require 'active_support/core_ext/float/rounding'
|
7
|
-
require 'active_support/core_ext/object/blank'
|
8
4
|
require 'active_support/core_ext/string/output_safety'
|
5
|
+
require 'active_support/number_helper'
|
9
6
|
|
10
7
|
module ActionView
|
11
8
|
# = Action View Number Helpers
|
@@ -19,9 +16,6 @@ module ActionView
|
|
19
16
|
# unchanged if can't be converted into a valid number.
|
20
17
|
module NumberHelper
|
21
18
|
|
22
|
-
DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :negative_format => "-%u%n", :unit => "$", :separator => ".", :delimiter => ",",
|
23
|
-
:precision => 2, :significant => false, :strip_insignificant_zeros => false }
|
24
|
-
|
25
19
|
# Raised when argument +number+ param given to the helpers is invalid and
|
26
20
|
# the option :raise is set to +true+.
|
27
21
|
class InvalidNumberError < StandardError
|
@@ -34,8 +28,6 @@ module ActionView
|
|
34
28
|
# Formats a +number+ into a US phone number (e.g., (555)
|
35
29
|
# 123-9876). You can customize the format in the +options+ hash.
|
36
30
|
#
|
37
|
-
# ==== Options
|
38
|
-
#
|
39
31
|
# * <tt>:area_code</tt> - Adds parentheses around the area code.
|
40
32
|
# * <tt>:delimiter</tt> - Specifies the delimiter to use
|
41
33
|
# (defaults to "-").
|
@@ -46,56 +38,29 @@ module ActionView
|
|
46
38
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
47
39
|
# the argument is invalid.
|
48
40
|
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
|
63
|
-
# # => +1.123.555.1234 x 1343
|
41
|
+
# number_to_phone(5551234) # => 555-1234
|
42
|
+
# number_to_phone("5551234") # => 555-1234
|
43
|
+
# number_to_phone(1235551234) # => 123-555-1234
|
44
|
+
# number_to_phone(1235551234, area_code: true) # => (123) 555-1234
|
45
|
+
# number_to_phone(1235551234, delimiter: " ") # => 123 555 1234
|
46
|
+
# number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
|
47
|
+
# number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
|
48
|
+
# number_to_phone("123a456") # => 123a456
|
49
|
+
# number_to_phone("1234a567", raise: true) # => InvalidNumberError
|
50
|
+
#
|
51
|
+
# number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
|
52
|
+
# # => +1.123.555.1234 x 1343
|
64
53
|
def number_to_phone(number, options = {})
|
65
54
|
return unless number
|
55
|
+
options = options.symbolize_keys
|
66
56
|
|
67
|
-
|
68
|
-
|
69
|
-
rescue ArgumentError, TypeError
|
70
|
-
raise InvalidNumberError, number
|
71
|
-
end if options[:raise]
|
72
|
-
|
73
|
-
number = number.to_s.strip
|
74
|
-
options = options.symbolize_keys
|
75
|
-
area_code = options[:area_code]
|
76
|
-
delimiter = options[:delimiter] || "-"
|
77
|
-
extension = options[:extension]
|
78
|
-
country_code = options[:country_code]
|
79
|
-
|
80
|
-
if area_code
|
81
|
-
number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
|
82
|
-
else
|
83
|
-
number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
|
84
|
-
number.slice!(0, 1) if number.starts_with?(delimiter) && !delimiter.blank?
|
85
|
-
end
|
86
|
-
|
87
|
-
str = []
|
88
|
-
str << "+#{country_code}#{delimiter}" unless country_code.blank?
|
89
|
-
str << number
|
90
|
-
str << " x #{extension}" unless extension.blank?
|
91
|
-
ERB::Util.html_escape(str.join)
|
57
|
+
parse_float(number, true) if options.delete(:raise)
|
58
|
+
ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
|
92
59
|
end
|
93
60
|
|
94
61
|
# Formats a +number+ into a currency string (e.g., $13.65). You
|
95
62
|
# can customize the format in the +options+ hash.
|
96
63
|
#
|
97
|
-
# ==== Options
|
98
|
-
#
|
99
64
|
# * <tt>:locale</tt> - Sets the locale to be used for formatting
|
100
65
|
# (defaults to current locale).
|
101
66
|
# * <tt>:precision</tt> - Sets the level of precision (defaults
|
@@ -117,67 +82,32 @@ module ActionView
|
|
117
82
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
118
83
|
# the argument is invalid.
|
119
84
|
#
|
120
|
-
#
|
85
|
+
# number_to_currency(1234567890.50) # => $1,234,567,890.50
|
86
|
+
# number_to_currency(1234567890.506) # => $1,234,567,890.51
|
87
|
+
# number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
|
88
|
+
# number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
|
89
|
+
# number_to_currency("123a456") # => $123a456
|
121
90
|
#
|
122
|
-
#
|
123
|
-
# number_to_currency(1234567890.506) # => $1,234,567,890.51
|
124
|
-
# number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506
|
125
|
-
# number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,51 €
|
126
|
-
# number_to_currency("123a456") # => $123a456
|
91
|
+
# number_to_currency("123a456", raise: true) # => InvalidNumberError
|
127
92
|
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
# number_to_currency(1234567890.50, :unit => "R$", :separator => ",", :delimiter => "", :format => "%n %u")
|
135
|
-
# # => 1234567890,50 R$
|
93
|
+
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
|
94
|
+
# # => ($1,234,567,890.50)
|
95
|
+
# number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "")
|
96
|
+
# # => £1234567890,50
|
97
|
+
# number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "", format: "%n %u")
|
98
|
+
# # => 1234567890,50 £
|
136
99
|
def number_to_currency(number, options = {})
|
137
100
|
return unless number
|
101
|
+
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
|
138
102
|
|
139
|
-
options.
|
140
|
-
|
141
|
-
|
142
|
-
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
|
143
|
-
options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
|
144
|
-
options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
|
145
|
-
|
146
|
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
|
147
|
-
currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :default => {})
|
148
|
-
currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
|
149
|
-
|
150
|
-
defaults = DEFAULT_CURRENCY_VALUES.merge(defaults).merge!(currency)
|
151
|
-
defaults[:negative_format] = "-" + options[:format] if options[:format]
|
152
|
-
|
153
|
-
options = defaults.merge!(options)
|
154
|
-
|
155
|
-
unit = options.delete(:unit)
|
156
|
-
format = options.delete(:format)
|
157
|
-
|
158
|
-
if number.to_f < 0
|
159
|
-
format = options.delete(:negative_format)
|
160
|
-
number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
|
161
|
-
end
|
162
|
-
|
163
|
-
begin
|
164
|
-
value = number_with_precision(number, options.merge(:raise => true))
|
165
|
-
format.gsub(/%n/, ERB::Util.html_escape(value)).gsub(/%u/, ERB::Util.html_escape(unit)).html_safe
|
166
|
-
rescue InvalidNumberError => e
|
167
|
-
if options[:raise]
|
168
|
-
raise
|
169
|
-
else
|
170
|
-
formatted_number = format.gsub(/%n/, e.number).gsub(/%u/, unit)
|
171
|
-
e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
103
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
104
|
+
ActiveSupport::NumberHelper.number_to_currency(number, options)
|
105
|
+
}
|
175
106
|
end
|
176
107
|
|
177
108
|
# Formats a +number+ as a percentage string (e.g., 65%). You can
|
178
109
|
# customize the format in the +options+ hash.
|
179
110
|
#
|
180
|
-
# ==== Options
|
181
111
|
#
|
182
112
|
# * <tt>:locale</tt> - Sets the locale to be used for formatting
|
183
113
|
# (defaults to current locale).
|
@@ -193,51 +123,34 @@ module ActionView
|
|
193
123
|
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
|
194
124
|
# insignificant zeros after the decimal separator (defaults to
|
195
125
|
# +false+).
|
126
|
+
# * <tt>:format</tt> - Specifies the format of the percentage
|
127
|
+
# string The number field is <tt>%n</tt> (defaults to "%n%").
|
196
128
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
197
129
|
# the argument is invalid.
|
198
130
|
#
|
199
|
-
#
|
131
|
+
# number_to_percentage(100) # => 100.000%
|
132
|
+
# number_to_percentage("98") # => 98.000%
|
133
|
+
# number_to_percentage(100, precision: 0) # => 100%
|
134
|
+
# number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
|
135
|
+
# number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
|
136
|
+
# number_to_percentage(1000, locale: :fr) # => 1 000,000%
|
137
|
+
# number_to_percentage("98a") # => 98a%
|
138
|
+
# number_to_percentage(100, format: "%n %") # => 100 %
|
200
139
|
#
|
201
|
-
#
|
202
|
-
# number_to_percentage("98") # => 98.000%
|
203
|
-
# number_to_percentage(100, :precision => 0) # => 100%
|
204
|
-
# number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
|
205
|
-
# number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
|
206
|
-
# number_to_percentage(1000, :locale => :fr) # => 1 000,000%
|
207
|
-
# number_to_percentage("98a") # => 98a%
|
208
|
-
#
|
209
|
-
# number_to_percentage("98a", :raise => true) # => InvalidNumberError
|
140
|
+
# number_to_percentage("98a", raise: true) # => InvalidNumberError
|
210
141
|
def number_to_percentage(number, options = {})
|
211
142
|
return unless number
|
143
|
+
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
|
212
144
|
|
213
|
-
options.
|
214
|
-
|
215
|
-
|
216
|
-
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
|
217
|
-
|
218
|
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
|
219
|
-
percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :default => {})
|
220
|
-
defaults = defaults.merge(percentage)
|
221
|
-
|
222
|
-
options = options.reverse_merge(defaults)
|
223
|
-
|
224
|
-
begin
|
225
|
-
"#{number_with_precision(number, options.merge(:raise => true))}%".html_safe
|
226
|
-
rescue InvalidNumberError => e
|
227
|
-
if options[:raise]
|
228
|
-
raise
|
229
|
-
else
|
230
|
-
e.number.to_s.html_safe? ? "#{e.number}%".html_safe : "#{e.number}%"
|
231
|
-
end
|
232
|
-
end
|
145
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
146
|
+
ActiveSupport::NumberHelper.number_to_percentage(number, options)
|
147
|
+
}
|
233
148
|
end
|
234
149
|
|
235
150
|
# Formats a +number+ with grouped thousands using +delimiter+
|
236
151
|
# (e.g., 12,324). You can customize the format in the +options+
|
237
152
|
# hash.
|
238
153
|
#
|
239
|
-
# ==== Options
|
240
|
-
#
|
241
154
|
# * <tt>:locale</tt> - Sets the locale to be used for formatting
|
242
155
|
# (defaults to current locale).
|
243
156
|
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
|
@@ -247,43 +160,24 @@ module ActionView
|
|
247
160
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
248
161
|
# the argument is invalid.
|
249
162
|
#
|
250
|
-
#
|
251
|
-
#
|
252
|
-
#
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
260
|
-
#
|
261
|
-
# # =>
|
262
|
-
#
|
263
|
-
# number_with_delimiter("112a", :raise => true) # => raise InvalidNumberError
|
163
|
+
# number_with_delimiter(12345678) # => 12,345,678
|
164
|
+
# number_with_delimiter("123456") # => 123,456
|
165
|
+
# number_with_delimiter(12345678.05) # => 12,345,678.05
|
166
|
+
# number_with_delimiter(12345678, delimiter: ".") # => 12.345.678
|
167
|
+
# number_with_delimiter(12345678, delimiter: ",") # => 12,345,678
|
168
|
+
# number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05
|
169
|
+
# number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05
|
170
|
+
# number_with_delimiter("112a") # => 112a
|
171
|
+
# number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
|
172
|
+
# # => 98 765 432,98
|
173
|
+
#
|
174
|
+
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
|
264
175
|
def number_with_delimiter(number, options = {})
|
265
|
-
options.symbolize_keys
|
266
|
-
|
267
|
-
options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
|
268
|
-
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
|
269
|
-
|
270
|
-
begin
|
271
|
-
Float(number)
|
272
|
-
rescue ArgumentError, TypeError
|
273
|
-
if options[:raise]
|
274
|
-
raise InvalidNumberError, number
|
275
|
-
else
|
276
|
-
return number
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
|
281
|
-
options = options.reverse_merge(defaults)
|
282
|
-
|
283
|
-
parts = number.to_s.to_str.split('.')
|
284
|
-
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
285
|
-
parts.join(options[:separator]).html_safe
|
176
|
+
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
|
286
177
|
|
178
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
179
|
+
ActiveSupport::NumberHelper.number_to_delimited(number, options)
|
180
|
+
}
|
287
181
|
end
|
288
182
|
|
289
183
|
# Formats a +number+ with the specified level of
|
@@ -291,8 +185,6 @@ module ActionView
|
|
291
185
|
# +:significant+ is +false+, and 5 if +:significant+ is +true+).
|
292
186
|
# You can customize the format in the +options+ hash.
|
293
187
|
#
|
294
|
-
# ==== Options
|
295
|
-
#
|
296
188
|
# * <tt>:locale</tt> - Sets the locale to be used for formatting
|
297
189
|
# (defaults to current locale).
|
298
190
|
# * <tt>:precision</tt> - Sets the precision of the number
|
@@ -310,70 +202,29 @@ module ActionView
|
|
310
202
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
311
203
|
# the argument is invalid.
|
312
204
|
#
|
313
|
-
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
#
|
322
|
-
#
|
323
|
-
#
|
324
|
-
#
|
325
|
-
#
|
326
|
-
#
|
327
|
-
#
|
328
|
-
# number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
|
329
|
-
# # => 1.111,23
|
205
|
+
# number_with_precision(111.2345) # => 111.235
|
206
|
+
# number_with_precision(111.2345, precision: 2) # => 111.23
|
207
|
+
# number_with_precision(13, precision: 5) # => 13.00000
|
208
|
+
# number_with_precision(389.32314, precision: 0) # => 389
|
209
|
+
# number_with_precision(111.2345, significant: true) # => 111
|
210
|
+
# number_with_precision(111.2345, precision: 1, significant: true) # => 100
|
211
|
+
# number_with_precision(13, precision: 5, significant: true) # => 13.000
|
212
|
+
# number_with_precision(111.234, locale: :fr) # => 111,234
|
213
|
+
#
|
214
|
+
# number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
|
215
|
+
# # => 13
|
216
|
+
#
|
217
|
+
# number_with_precision(389.32314, precision: 4, significant: true) # => 389.3
|
218
|
+
# number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
|
219
|
+
# # => 1.111,23
|
330
220
|
def number_with_precision(number, options = {})
|
331
|
-
options.symbolize_keys
|
332
|
-
|
333
|
-
number = begin
|
334
|
-
Float(number)
|
335
|
-
rescue ArgumentError, TypeError
|
336
|
-
if options[:raise]
|
337
|
-
raise InvalidNumberError, number
|
338
|
-
else
|
339
|
-
return number
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
|
344
|
-
precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale], :default => {})
|
345
|
-
defaults = defaults.merge(precision_defaults)
|
346
|
-
|
347
|
-
options = options.reverse_merge(defaults) # Allow the user to unset default values: Eg.: :significant => false
|
348
|
-
precision = options.delete :precision
|
349
|
-
significant = options.delete :significant
|
350
|
-
strip_insignificant_zeros = options.delete :strip_insignificant_zeros
|
351
|
-
|
352
|
-
if significant and precision > 0
|
353
|
-
if number == 0
|
354
|
-
digits, rounded_number = 1, 0
|
355
|
-
else
|
356
|
-
digits = (Math.log10(number.abs) + 1).floor
|
357
|
-
rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
|
358
|
-
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
|
359
|
-
end
|
360
|
-
precision -= digits
|
361
|
-
precision = precision > 0 ? precision : 0 #don't let it be negative
|
362
|
-
else
|
363
|
-
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
|
364
|
-
end
|
365
|
-
formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
|
366
|
-
if strip_insignificant_zeros
|
367
|
-
escaped_separator = Regexp.escape(options[:separator])
|
368
|
-
formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '').html_safe
|
369
|
-
else
|
370
|
-
formatted_number
|
371
|
-
end
|
221
|
+
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
|
372
222
|
|
223
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
224
|
+
ActiveSupport::NumberHelper.number_to_rounded(number, options)
|
225
|
+
}
|
373
226
|
end
|
374
227
|
|
375
|
-
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
|
376
|
-
|
377
228
|
# Formats the bytes in +number+ into a more understandable
|
378
229
|
# representation (e.g., giving it 1500 yields 1.5 KB). This
|
379
230
|
# method is useful for reporting file sizes to users. You can
|
@@ -382,8 +233,6 @@ module ActionView
|
|
382
233
|
# See <tt>number_to_human</tt> if you want to pretty-print a
|
383
234
|
# generic number.
|
384
235
|
#
|
385
|
-
# ==== Options
|
386
|
-
#
|
387
236
|
# * <tt>:locale</tt> - Sets the locale to be used for formatting
|
388
237
|
# (defaults to current locale).
|
389
238
|
# * <tt>:precision</tt> - Sets the precision of the number
|
@@ -403,69 +252,31 @@ module ActionView
|
|
403
252
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
404
253
|
# the argument is invalid.
|
405
254
|
#
|
406
|
-
#
|
407
|
-
#
|
408
|
-
#
|
409
|
-
#
|
410
|
-
#
|
411
|
-
#
|
412
|
-
#
|
413
|
-
#
|
414
|
-
#
|
415
|
-
# number_to_human_size(483989, :precision => 2) # => 470 KB
|
416
|
-
# number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,2 MB
|
255
|
+
# number_to_human_size(123) # => 123 Bytes
|
256
|
+
# number_to_human_size(1234) # => 1.21 KB
|
257
|
+
# number_to_human_size(12345) # => 12.1 KB
|
258
|
+
# number_to_human_size(1234567) # => 1.18 MB
|
259
|
+
# number_to_human_size(1234567890) # => 1.15 GB
|
260
|
+
# number_to_human_size(1234567890123) # => 1.12 TB
|
261
|
+
# number_to_human_size(1234567, precision: 2) # => 1.2 MB
|
262
|
+
# number_to_human_size(483989, precision: 2) # => 470 KB
|
263
|
+
# number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
|
417
264
|
#
|
418
265
|
# Non-significant zeros after the fractional separator are
|
419
266
|
# stripped out by default (set
|
420
267
|
# <tt>:strip_insignificant_zeros</tt> to +false+ to change
|
421
268
|
# that):
|
422
|
-
#
|
423
|
-
#
|
269
|
+
#
|
270
|
+
# number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
|
271
|
+
# number_to_human_size(524288000, precision: 5) # => "500 MB"
|
424
272
|
def number_to_human_size(number, options = {})
|
425
|
-
options.symbolize_keys
|
273
|
+
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
|
426
274
|
|
427
|
-
number
|
428
|
-
|
429
|
-
|
430
|
-
if options[:raise]
|
431
|
-
raise InvalidNumberError, number
|
432
|
-
else
|
433
|
-
return number
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {})
|
438
|
-
human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
|
439
|
-
defaults = defaults.merge(human)
|
440
|
-
|
441
|
-
options = options.reverse_merge(defaults)
|
442
|
-
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
|
443
|
-
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
|
444
|
-
|
445
|
-
storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
|
446
|
-
|
447
|
-
base = options[:prefix] == :si ? 1000 : 1024
|
448
|
-
|
449
|
-
if number.to_i < base
|
450
|
-
unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
|
451
|
-
storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit).html_safe
|
452
|
-
else
|
453
|
-
max_exp = STORAGE_UNITS.size - 1
|
454
|
-
exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
|
455
|
-
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
|
456
|
-
number /= base ** exponent
|
457
|
-
|
458
|
-
unit_key = STORAGE_UNITS[exponent]
|
459
|
-
unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
|
460
|
-
|
461
|
-
formatted_number = number_with_precision(number, options)
|
462
|
-
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).html_safe
|
463
|
-
end
|
275
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
276
|
+
ActiveSupport::NumberHelper.number_to_human_size(number, options)
|
277
|
+
}
|
464
278
|
end
|
465
279
|
|
466
|
-
DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
|
467
|
-
-1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}
|
468
|
-
|
469
280
|
# Pretty prints (formats and approximates) a number in a way it
|
470
281
|
# is more readable by humans (eg.: 1200000000 becomes "1.2
|
471
282
|
# Billion"). This is useful for numbers that can get very large
|
@@ -513,34 +324,32 @@ module ActionView
|
|
513
324
|
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
|
514
325
|
# the argument is invalid.
|
515
326
|
#
|
516
|
-
#
|
517
|
-
#
|
518
|
-
#
|
519
|
-
#
|
520
|
-
#
|
521
|
-
#
|
522
|
-
#
|
523
|
-
#
|
524
|
-
#
|
525
|
-
#
|
526
|
-
#
|
527
|
-
#
|
528
|
-
#
|
529
|
-
# :
|
530
|
-
#
|
531
|
-
# :separator => ',',
|
532
|
-
# :significant => false) # => "1,2 Million"
|
327
|
+
# number_to_human(123) # => "123"
|
328
|
+
# number_to_human(1234) # => "1.23 Thousand"
|
329
|
+
# number_to_human(12345) # => "12.3 Thousand"
|
330
|
+
# number_to_human(1234567) # => "1.23 Million"
|
331
|
+
# number_to_human(1234567890) # => "1.23 Billion"
|
332
|
+
# number_to_human(1234567890123) # => "1.23 Trillion"
|
333
|
+
# number_to_human(1234567890123456) # => "1.23 Quadrillion"
|
334
|
+
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
|
335
|
+
# number_to_human(489939, precision: 2) # => "490 Thousand"
|
336
|
+
# number_to_human(489939, precision: 4) # => "489.9 Thousand"
|
337
|
+
# number_to_human(1234567, precision: 4,
|
338
|
+
# significant: false) # => "1.2346 Million"
|
339
|
+
# number_to_human(1234567, precision: 1,
|
340
|
+
# separator: ',',
|
341
|
+
# significant: false) # => "1,2 Million"
|
533
342
|
#
|
534
343
|
# Non-significant zeros after the decimal separator are stripped
|
535
344
|
# out by default (set <tt>:strip_insignificant_zeros</tt> to
|
536
345
|
# +false+ to change that):
|
537
|
-
#
|
538
|
-
#
|
346
|
+
# number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
|
347
|
+
# number_to_human(500000000, precision: 5) # => "500 Million"
|
539
348
|
#
|
540
349
|
# ==== Custom Unit Quantifiers
|
541
350
|
#
|
542
351
|
# You can also use your own custom unit quantifiers:
|
543
|
-
# number_to_human(500000, :
|
352
|
+
# number_to_human(500000, units: {unit: "ml", thousand: "lt"}) # => "500 lt"
|
544
353
|
#
|
545
354
|
# If in your I18n locale you have:
|
546
355
|
# distance:
|
@@ -557,66 +366,51 @@ module ActionView
|
|
557
366
|
#
|
558
367
|
# Then you could do:
|
559
368
|
#
|
560
|
-
# number_to_human(543934, :
|
561
|
-
# number_to_human(54393498, :
|
562
|
-
# number_to_human(54393498000, :
|
563
|
-
# number_to_human(343, :
|
564
|
-
# number_to_human(1, :
|
565
|
-
# number_to_human(0.34, :
|
369
|
+
# number_to_human(543934, units: :distance) # => "544 kilometers"
|
370
|
+
# number_to_human(54393498, units: :distance) # => "54400 kilometers"
|
371
|
+
# number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
|
372
|
+
# number_to_human(343, units: :distance, precision: 1) # => "300 meters"
|
373
|
+
# number_to_human(1, units: :distance) # => "1 meter"
|
374
|
+
# number_to_human(0.34, units: :distance) # => "34 centimeters"
|
566
375
|
#
|
567
376
|
def number_to_human(number, options = {})
|
568
|
-
options.symbolize_keys
|
377
|
+
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
|
569
378
|
|
570
|
-
number
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
raise InvalidNumberError, number
|
575
|
-
else
|
576
|
-
return number
|
577
|
-
end
|
578
|
-
end
|
379
|
+
wrap_with_output_safety_handling(number, options.delete(:raise)) {
|
380
|
+
ActiveSupport::NumberHelper.number_to_human(number, options)
|
381
|
+
}
|
382
|
+
end
|
579
383
|
|
580
|
-
|
581
|
-
human = I18n.translate(:'number.human.format', :locale => options[:locale], :default => {})
|
582
|
-
defaults = defaults.merge(human)
|
384
|
+
private
|
583
385
|
|
584
|
-
|
585
|
-
|
586
|
-
options[:
|
386
|
+
def escape_unsafe_delimiters_and_separators(options)
|
387
|
+
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator] && !options[:separator].html_safe?
|
388
|
+
options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter] && !options[:delimiter].html_safe?
|
389
|
+
options
|
390
|
+
end
|
587
391
|
|
588
|
-
|
392
|
+
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
|
393
|
+
valid_float = valid_float?(number)
|
394
|
+
raise InvalidNumberError, number if raise_on_invalid && !valid_float
|
589
395
|
|
590
|
-
|
591
|
-
unit_exponents = case units
|
592
|
-
when Hash
|
593
|
-
units = Hash[units.map { |k, v| [k, ERB::Util.html_escape(v)] }]
|
594
|
-
when String, Symbol
|
595
|
-
I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
|
596
|
-
when nil
|
597
|
-
I18n.translate(:"number.human.decimal_units.units", :locale => options[:locale], :raise => true)
|
598
|
-
else
|
599
|
-
raise ArgumentError, ":units must be a Hash or String translation scope."
|
600
|
-
end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
|
396
|
+
formatted_number = yield
|
601
397
|
|
602
|
-
|
603
|
-
|
604
|
-
number /= 10 ** display_exponent
|
605
|
-
|
606
|
-
unit = case units
|
607
|
-
when Hash
|
608
|
-
units[DECIMAL_UNITS[display_exponent]] || ''
|
609
|
-
when String, Symbol
|
610
|
-
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
|
398
|
+
if valid_float || number.html_safe?
|
399
|
+
formatted_number.html_safe
|
611
400
|
else
|
612
|
-
|
401
|
+
formatted_number
|
613
402
|
end
|
403
|
+
end
|
614
404
|
|
615
|
-
|
616
|
-
|
617
|
-
decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip.html_safe
|
405
|
+
def valid_float?(number)
|
406
|
+
!parse_float(number, false).nil?
|
618
407
|
end
|
619
408
|
|
409
|
+
def parse_float(number, raise_error)
|
410
|
+
Float(number)
|
411
|
+
rescue ArgumentError, TypeError
|
412
|
+
raise InvalidNumberError, number if raise_error
|
413
|
+
end
|
620
414
|
end
|
621
415
|
end
|
622
416
|
end
|