halorgium-actionpack 3.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,208 @@
1
+ require 'action_view/helpers/tag_helper'
2
+ require 'action_view/helpers/prototype_helper'
3
+
4
+ module ActionView
5
+ module Helpers
6
+ # Provides functionality for working with JavaScript in your views.
7
+ #
8
+ # == Ajax, controls and visual effects
9
+ #
10
+ # * For information on using Ajax, see
11
+ # ActionView::Helpers::PrototypeHelper.
12
+ # * For information on using controls and visual effects, see
13
+ # ActionView::Helpers::ScriptaculousHelper.
14
+ #
15
+ # == Including the JavaScript libraries into your pages
16
+ #
17
+ # Rails includes the Prototype JavaScript framework and the Scriptaculous
18
+ # JavaScript controls and visual effects library. If you wish to use
19
+ # these libraries and their helpers (ActionView::Helpers::PrototypeHelper
20
+ # and ActionView::Helpers::ScriptaculousHelper), you must do one of the
21
+ # following:
22
+ #
23
+ # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD
24
+ # section of your page (recommended): This function will return
25
+ # references to the JavaScript files created by the +rails+ command in
26
+ # your <tt>public/javascripts</tt> directory. Using it is recommended as
27
+ # the browser can then cache the libraries instead of fetching all the
28
+ # functions anew on every request.
29
+ # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but
30
+ # will only include the Prototype core library, which means you are able
31
+ # to use all basic AJAX functionality. For the Scriptaculous-based
32
+ # JavaScript helpers, like visual effects, autocompletion, drag and drop
33
+ # and so on, you should use the method described above.
34
+ #
35
+ # For documentation on +javascript_include_tag+ see
36
+ # ActionView::Helpers::AssetTagHelper.
37
+ module JavaScriptHelper
38
+ unless const_defined? :JAVASCRIPT_PATH
39
+ JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts')
40
+ end
41
+
42
+ include PrototypeHelper
43
+
44
+ # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
45
+ # onclick handler and return false after the fact.
46
+ #
47
+ # The first argument +name+ is used as the link text.
48
+ #
49
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
50
+ #
51
+ # The +function+ argument can be omitted in favor of an +update_page+
52
+ # block, which evaluates to a string when the template is rendered
53
+ # (instead of making an Ajax request first).
54
+ #
55
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
56
+ #
57
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
58
+ #
59
+ #
60
+ # Examples:
61
+ # link_to_function "Greeting", "alert('Hello world!')"
62
+ # Produces:
63
+ # <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
64
+ #
65
+ # link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()")
66
+ # Produces:
67
+ # <a onclick="if (confirm('Really?')) do_delete(); return false;" href="#">
68
+ # <img src="/images/delete.png?" alt="Delete"/>
69
+ # </a>
70
+ #
71
+ # link_to_function("Show me more", nil, :id => "more_link") do |page|
72
+ # page[:details].visual_effect :toggle_blind
73
+ # page[:more_link].replace_html "Show me less"
74
+ # end
75
+ # Produces:
76
+ # <a href="#" id="more_link" onclick="try {
77
+ # $(&quot;details&quot;).visualEffect(&quot;toggle_blind&quot;);
78
+ # $(&quot;more_link&quot;).update(&quot;Show me less&quot;);
79
+ # }
80
+ # catch (e) {
81
+ # alert('RJS error:\n\n' + e.toString());
82
+ # alert('$(\&quot;details\&quot;).visualEffect(\&quot;toggle_blind\&quot;);
83
+ # \n$(\&quot;more_link\&quot;).update(\&quot;Show me less\&quot;);');
84
+ # throw e
85
+ # };
86
+ # return false;">Show me more</a>
87
+ #
88
+ def link_to_function(name, *args, &block)
89
+ html_options = args.extract_options!.symbolize_keys
90
+
91
+ function = block_given? ? update_page(&block) : args[0] || ''
92
+ onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
93
+ href = html_options[:href] || '#'
94
+
95
+ content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
96
+ end
97
+
98
+ # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
99
+ # onclick handler.
100
+ #
101
+ # The first argument +name+ is used as the button's value or display text.
102
+ #
103
+ # The next arguments are optional and may include the javascript function definition and a hash of html_options.
104
+ #
105
+ # The +function+ argument can be omitted in favor of an +update_page+
106
+ # block, which evaluates to a string when the template is rendered
107
+ # (instead of making an Ajax request first).
108
+ #
109
+ # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
110
+ #
111
+ # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
112
+ #
113
+ # Examples:
114
+ # button_to_function "Greeting", "alert('Hello world!')"
115
+ # button_to_function "Delete", "if (confirm('Really?')) do_delete()"
116
+ # button_to_function "Details" do |page|
117
+ # page[:details].visual_effect :toggle_slide
118
+ # end
119
+ # button_to_function "Details", :class => "details_button" do |page|
120
+ # page[:details].visual_effect :toggle_slide
121
+ # end
122
+ def button_to_function(name, *args, &block)
123
+ html_options = args.extract_options!.symbolize_keys
124
+
125
+ function = block_given? ? update_page(&block) : args[0] || ''
126
+ onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
127
+
128
+ tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
129
+ end
130
+
131
+ JS_ESCAPE_MAP = {
132
+ '\\' => '\\\\',
133
+ '</' => '<\/',
134
+ "\r\n" => '\n',
135
+ "\n" => '\n',
136
+ "\r" => '\n',
137
+ '"' => '\\"',
138
+ "'" => "\\'" }
139
+
140
+ # Escape carrier returns and single and double quotes for JavaScript segments.
141
+ def escape_javascript(javascript)
142
+ if javascript
143
+ javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
144
+ else
145
+ ''
146
+ end
147
+ end
148
+
149
+ # Returns a JavaScript tag with the +content+ inside. Example:
150
+ # javascript_tag "alert('All is good')"
151
+ #
152
+ # Returns:
153
+ # <script type="text/javascript">
154
+ # //<![CDATA[
155
+ # alert('All is good')
156
+ # //]]>
157
+ # </script>
158
+ #
159
+ # +html_options+ may be a hash of attributes for the <script> tag. Example:
160
+ # javascript_tag "alert('All is good')", :defer => 'defer'
161
+ # # => <script defer="defer" type="text/javascript">alert('All is good')</script>
162
+ #
163
+ # Instead of passing the content as an argument, you can also use a block
164
+ # in which case, you pass your +html_options+ as the first parameter.
165
+ # <% javascript_tag :defer => 'defer' do -%>
166
+ # alert('All is good')
167
+ # <% end -%>
168
+ def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
169
+ content =
170
+ if block_given?
171
+ html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
172
+ capture(&block)
173
+ else
174
+ content_or_options_with_block
175
+ end
176
+
177
+ tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
178
+
179
+ if block_called_from_erb?(block)
180
+ concat(tag)
181
+ else
182
+ tag
183
+ end
184
+ end
185
+
186
+ def javascript_cdata_section(content) #:nodoc:
187
+ "\n//#{cdata_section("\n#{content}\n//")}\n"
188
+ end
189
+
190
+ protected
191
+ def options_for_javascript(options)
192
+ if options.empty?
193
+ '{}'
194
+ else
195
+ "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
196
+ end
197
+ end
198
+
199
+ def array_or_string_for_javascript(option)
200
+ if option.kind_of?(Array)
201
+ "['#{option.join('\',\'')}']"
202
+ elsif !option.nil?
203
+ "'#{option}'"
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,311 @@
1
+ require 'active_support/core_ext/big_decimal/conversions'
2
+ require 'active_support/core_ext/float/rounding'
3
+
4
+ module ActionView
5
+ module Helpers #:nodoc:
6
+ # Provides methods for converting numbers into formatted strings.
7
+ # Methods are provided for phone numbers, currency, percentage,
8
+ # precision, positional notation, and file size.
9
+ module NumberHelper
10
+ # Formats a +number+ into a US phone number (e.g., (555) 123-9876). You can customize the format
11
+ # in the +options+ hash.
12
+ #
13
+ # ==== Options
14
+ # * <tt>:area_code</tt> - Adds parentheses around the area code.
15
+ # * <tt>:delimiter</tt> - Specifies the delimiter to use (defaults to "-").
16
+ # * <tt>:extension</tt> - Specifies an extension to add to the end of the
17
+ # generated number.
18
+ # * <tt>:country_code</tt> - Sets the country code for the phone number.
19
+ #
20
+ # ==== Examples
21
+ # number_to_phone(5551234) # => 555-1234
22
+ # number_to_phone(1235551234) # => 123-555-1234
23
+ # number_to_phone(1235551234, :area_code => true) # => (123) 555-1234
24
+ # number_to_phone(1235551234, :delimiter => " ") # => 123 555 1234
25
+ # number_to_phone(1235551234, :area_code => true, :extension => 555) # => (123) 555-1234 x 555
26
+ # number_to_phone(1235551234, :country_code => 1) # => +1-123-555-1234
27
+ #
28
+ # number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
29
+ # => +1.123.555.1234 x 1343
30
+ def number_to_phone(number, options = {})
31
+ number = number.to_s.strip unless number.nil?
32
+ options = options.symbolize_keys
33
+ area_code = options[:area_code] || nil
34
+ delimiter = options[:delimiter] || "-"
35
+ extension = options[:extension].to_s.strip || nil
36
+ country_code = options[:country_code] || nil
37
+
38
+ begin
39
+ str = ""
40
+ str << "+#{country_code}#{delimiter}" unless country_code.blank?
41
+ str << if area_code
42
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
43
+ else
44
+ number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
45
+ number.starts_with?('-') ? number.slice!(1..-1) : number
46
+ end
47
+ str << " x #{extension}" unless extension.blank?
48
+ str
49
+ rescue
50
+ number
51
+ end
52
+ end
53
+
54
+ # Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
55
+ # in the +options+ hash.
56
+ #
57
+ # ==== Options
58
+ # * <tt>:precision</tt> - Sets the level of precision (defaults to 2).
59
+ # * <tt>:unit</tt> - Sets the denomination of the currency (defaults to "$").
60
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
61
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
62
+ # * <tt>:format</tt> - Sets the format of the output string (defaults to "%u%n"). The field types are:
63
+ #
64
+ # %u The currency unit
65
+ # %n The number
66
+ #
67
+ # ==== Examples
68
+ # number_to_currency(1234567890.50) # => $1,234,567,890.50
69
+ # number_to_currency(1234567890.506) # => $1,234,567,890.51
70
+ # number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506
71
+ #
72
+ # number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "")
73
+ # # => &pound;1234567890,50
74
+ # number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "", :format => "%n %u")
75
+ # # => 1234567890,50 &pound;
76
+ def number_to_currency(number, options = {})
77
+ options.symbolize_keys!
78
+
79
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
80
+ currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
81
+ defaults = defaults.merge(currency)
82
+
83
+ precision = options[:precision] || defaults[:precision]
84
+ unit = options[:unit] || defaults[:unit]
85
+ separator = options[:separator] || defaults[:separator]
86
+ delimiter = options[:delimiter] || defaults[:delimiter]
87
+ format = options[:format] || defaults[:format]
88
+ separator = '' if precision == 0
89
+
90
+ begin
91
+ format.gsub(/%n/, number_with_precision(number,
92
+ :precision => precision,
93
+ :delimiter => delimiter,
94
+ :separator => separator)
95
+ ).gsub(/%u/, unit)
96
+ rescue
97
+ number
98
+ end
99
+ end
100
+
101
+ # Formats a +number+ as a percentage string (e.g., 65%). You can customize the
102
+ # format in the +options+ hash.
103
+ #
104
+ # ==== Options
105
+ # * <tt>:precision</tt> - Sets the level of precision (defaults to 3).
106
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
107
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
108
+ #
109
+ # ==== Examples
110
+ # number_to_percentage(100) # => 100.000%
111
+ # number_to_percentage(100, :precision => 0) # => 100%
112
+ # number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
113
+ # number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
114
+ def number_to_percentage(number, options = {})
115
+ options.symbolize_keys!
116
+
117
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
118
+ percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :raise => true) rescue {}
119
+ defaults = defaults.merge(percentage)
120
+
121
+ precision = options[:precision] || defaults[:precision]
122
+ separator = options[:separator] || defaults[:separator]
123
+ delimiter = options[:delimiter] || defaults[:delimiter]
124
+
125
+ begin
126
+ number_with_precision(number,
127
+ :precision => precision,
128
+ :separator => separator,
129
+ :delimiter => delimiter) + "%"
130
+ rescue
131
+ number
132
+ end
133
+ end
134
+
135
+ # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
136
+ # customize the format in the +options+ hash.
137
+ #
138
+ # ==== Options
139
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ",").
140
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
141
+ #
142
+ # ==== Examples
143
+ # number_with_delimiter(12345678) # => 12,345,678
144
+ # number_with_delimiter(12345678.05) # => 12,345,678.05
145
+ # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
146
+ # number_with_delimiter(12345678, :separator => ",") # => 12,345,678
147
+ # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
148
+ # # => 98 765 432,98
149
+ #
150
+ # You can still use <tt>number_with_delimiter</tt> with the old API that accepts the
151
+ # +delimiter+ as its optional second and the +separator+ as its
152
+ # optional third parameter:
153
+ # number_with_delimiter(12345678, " ") # => 12 345.678
154
+ # number_with_delimiter(12345678.05, ".", ",") # => 12.345.678,05
155
+ def number_with_delimiter(number, *args)
156
+ options = args.extract_options!
157
+ options.symbolize_keys!
158
+
159
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
160
+
161
+ unless args.empty?
162
+ ActiveSupport::Deprecation.warn('number_with_delimiter takes an option hash ' +
163
+ 'instead of separate delimiter and precision arguments.', caller)
164
+ delimiter = args[0] || defaults[:delimiter]
165
+ separator = args[1] || defaults[:separator]
166
+ end
167
+
168
+ delimiter ||= (options[:delimiter] || defaults[:delimiter])
169
+ separator ||= (options[:separator] || defaults[:separator])
170
+
171
+ begin
172
+ parts = number.to_s.split('.')
173
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
174
+ parts.join(separator)
175
+ rescue
176
+ number
177
+ end
178
+ end
179
+
180
+ # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision of 2).
181
+ # You can customize the format in the +options+ hash.
182
+ #
183
+ # ==== Options
184
+ # * <tt>:precision</tt> - Sets the level of precision (defaults to 3).
185
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
186
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
187
+ #
188
+ # ==== Examples
189
+ # number_with_precision(111.2345) # => 111.235
190
+ # number_with_precision(111.2345, :precision => 2) # => 111.23
191
+ # number_with_precision(13, :precision => 5) # => 13.00000
192
+ # number_with_precision(389.32314, :precision => 0) # => 389
193
+ # number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
194
+ # # => 1.111,23
195
+ #
196
+ # You can still use <tt>number_with_precision</tt> with the old API that accepts the
197
+ # +precision+ as its optional second parameter:
198
+ # number_with_precision(number_with_precision(111.2345, 2) # => 111.23
199
+ def number_with_precision(number, *args)
200
+ options = args.extract_options!
201
+ options.symbolize_keys!
202
+
203
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
204
+ precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale],
205
+ :raise => true) rescue {}
206
+ defaults = defaults.merge(precision_defaults)
207
+
208
+ unless args.empty?
209
+ ActiveSupport::Deprecation.warn('number_with_precision takes an option hash ' +
210
+ 'instead of a separate precision argument.', caller)
211
+ precision = args[0] || defaults[:precision]
212
+ end
213
+
214
+ precision ||= (options[:precision] || defaults[:precision])
215
+ separator ||= (options[:separator] || defaults[:separator])
216
+ delimiter ||= (options[:delimiter] || defaults[:delimiter])
217
+
218
+ begin
219
+ rounded_number = BigDecimal.new((Float(number) * (10 ** precision)).to_s).round.to_f / 10 ** precision
220
+ number_with_delimiter("%01.#{precision}f" % rounded_number,
221
+ :separator => separator,
222
+ :delimiter => delimiter)
223
+ rescue
224
+ number
225
+ end
226
+ end
227
+
228
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
229
+
230
+ # Formats the bytes in +size+ into a more understandable representation
231
+ # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
232
+ # reporting file sizes to users. This method returns nil if
233
+ # +size+ cannot be converted into a number. You can customize the
234
+ # format in the +options+ hash.
235
+ #
236
+ # ==== Options
237
+ # * <tt>:precision</tt> - Sets the level of precision (defaults to 1).
238
+ # * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
239
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
240
+ #
241
+ # ==== Examples
242
+ # number_to_human_size(123) # => 123 Bytes
243
+ # number_to_human_size(1234) # => 1.2 KB
244
+ # number_to_human_size(12345) # => 12.1 KB
245
+ # number_to_human_size(1234567) # => 1.2 MB
246
+ # number_to_human_size(1234567890) # => 1.1 GB
247
+ # number_to_human_size(1234567890123) # => 1.1 TB
248
+ # number_to_human_size(1234567, :precision => 2) # => 1.18 MB
249
+ # number_to_human_size(483989, :precision => 0) # => 473 KB
250
+ # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
251
+ #
252
+ # Zeros after the decimal point are always stripped out, regardless of the
253
+ # specified precision:
254
+ # helper.number_to_human_size(1234567890123, :precision => 5) # => "1.12283 TB"
255
+ # helper.number_to_human_size(524288000, :precision=>5) # => "500 MB"
256
+ #
257
+ # You can still use <tt>number_to_human_size</tt> with the old API that accepts the
258
+ # +precision+ as its optional second parameter:
259
+ # number_to_human_size(1234567, 2) # => 1.18 MB
260
+ # number_to_human_size(483989, 0) # => 473 KB
261
+ def number_to_human_size(number, *args)
262
+ return nil if number.nil?
263
+
264
+ options = args.extract_options!
265
+ options.symbolize_keys!
266
+
267
+ defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
268
+ human = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {}
269
+ defaults = defaults.merge(human)
270
+
271
+ unless args.empty?
272
+ ActiveSupport::Deprecation.warn('number_to_human_size takes an option hash ' +
273
+ 'instead of a separate precision argument.', caller)
274
+ precision = args[0] || defaults[:precision]
275
+ end
276
+
277
+ precision ||= (options[:precision] || defaults[:precision])
278
+ separator ||= (options[:separator] || defaults[:separator])
279
+ delimiter ||= (options[:delimiter] || defaults[:delimiter])
280
+
281
+ storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
282
+
283
+ if number.to_i < 1024
284
+ unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
285
+ storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
286
+ else
287
+ max_exp = STORAGE_UNITS.size - 1
288
+ number = Float(number)
289
+ exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
290
+ exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
291
+ number /= 1024 ** exponent
292
+
293
+ unit_key = STORAGE_UNITS[exponent]
294
+ unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
295
+
296
+ begin
297
+ escaped_separator = Regexp.escape(separator)
298
+ formatted_number = number_with_precision(number,
299
+ :precision => precision,
300
+ :separator => separator,
301
+ :delimiter => delimiter
302
+ ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
303
+ storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
304
+ rescue
305
+ number
306
+ end
307
+ end
308
+ end
309
+ end
310
+ end
311
+ end