activesupport 3.1.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (276) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +798 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +13 -7
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +38 -34
  7. data/lib/active_support/benchmarkable.rb +17 -28
  8. data/lib/active_support/cache/file_store.rb +85 -70
  9. data/lib/active_support/cache/mem_cache_store.rb +75 -66
  10. data/lib/active_support/cache/memory_store.rb +31 -23
  11. data/lib/active_support/cache/null_store.rb +41 -0
  12. data/lib/active_support/cache/strategy/local_cache.rb +73 -70
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  14. data/lib/active_support/cache.rb +360 -294
  15. data/lib/active_support/callbacks.rb +563 -393
  16. data/lib/active_support/concern.rb +42 -34
  17. data/lib/active_support/concurrency/latch.rb +19 -0
  18. data/lib/active_support/concurrency/share_lock.rb +186 -0
  19. data/lib/active_support/configurable.rb +70 -12
  20. data/lib/active_support/core_ext/array/access.rb +53 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +109 -62
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +39 -32
  24. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  25. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  26. data/lib/active_support/core_ext/array/wrap.rb +16 -18
  27. data/lib/active_support/core_ext/array.rb +2 -2
  28. data/lib/active_support/core_ext/benchmark.rb +7 -0
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -36
  30. data/lib/active_support/core_ext/class/attribute.rb +47 -34
  31. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -79
  32. data/lib/active_support/core_ext/class/subclasses.rb +12 -7
  33. data/lib/active_support/core_ext/class.rb +0 -3
  34. data/lib/active_support/core_ext/date/blank.rb +12 -0
  35. data/lib/active_support/core_ext/date/calculations.rb +57 -167
  36. data/lib/active_support/core_ext/date/conversions.rb +31 -42
  37. data/lib/active_support/core_ext/date/zones.rb +2 -10
  38. data/lib/active_support/core_ext/date.rb +5 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  42. data/lib/active_support/core_ext/date_time/acts_like.rb +1 -0
  43. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  44. data/lib/active_support/core_ext/date_time/calculations.rb +132 -65
  45. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  46. data/lib/active_support/core_ext/date_time/conversions.rb +36 -34
  47. data/lib/active_support/core_ext/date_time.rb +5 -0
  48. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  49. data/lib/active_support/core_ext/enumerable.rb +81 -74
  50. data/lib/active_support/core_ext/file/atomic.rb +53 -26
  51. data/lib/active_support/core_ext/file.rb +0 -1
  52. data/lib/active_support/core_ext/hash/compact.rb +20 -0
  53. data/lib/active_support/core_ext/hash/conversions.rb +175 -70
  54. data/lib/active_support/core_ext/hash/deep_merge.rb +30 -8
  55. data/lib/active_support/core_ext/hash/except.rb +11 -12
  56. data/lib/active_support/core_ext/hash/indifferent_access.rb +7 -8
  57. data/lib/active_support/core_ext/hash/keys.rb +147 -24
  58. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  59. data/lib/active_support/core_ext/hash/slice.rb +22 -14
  60. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  61. data/lib/active_support/core_ext/hash.rb +2 -2
  62. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  63. data/lib/active_support/core_ext/integer/multiple.rb +4 -0
  64. data/lib/active_support/core_ext/integer/time.rb +12 -22
  65. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
  66. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  67. data/lib/active_support/core_ext/kernel/debugger.rb +2 -15
  68. data/lib/active_support/core_ext/kernel/reporting.rb +12 -62
  69. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  70. data/lib/active_support/core_ext/kernel.rb +2 -3
  71. data/lib/active_support/core_ext/load_error.rb +14 -7
  72. data/lib/active_support/core_ext/marshal.rb +22 -0
  73. data/lib/active_support/core_ext/module/aliasing.rb +16 -12
  74. data/lib/active_support/core_ext/module/anonymous.rb +12 -8
  75. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  76. data/lib/active_support/core_ext/module/attribute_accessors.rb +165 -13
  77. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  78. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  79. data/lib/active_support/core_ext/module/delegation.rb +141 -68
  80. data/lib/active_support/core_ext/module/deprecation.rb +17 -3
  81. data/lib/active_support/core_ext/module/introspection.rb +9 -31
  82. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  83. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  84. data/lib/active_support/core_ext/module/reachable.rb +1 -3
  85. data/lib/active_support/core_ext/module/remove_method.rb +24 -5
  86. data/lib/active_support/core_ext/module.rb +3 -3
  87. data/lib/active_support/core_ext/name_error.rb +15 -2
  88. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  89. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  90. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  91. data/lib/active_support/core_ext/numeric/time.rb +31 -36
  92. data/lib/active_support/core_ext/numeric.rb +2 -0
  93. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  94. data/lib/active_support/core_ext/object/blank.rb +52 -18
  95. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  96. data/lib/active_support/core_ext/object/duplicable.rb +12 -20
  97. data/lib/active_support/core_ext/object/inclusion.rb +13 -1
  98. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  99. data/lib/active_support/core_ext/object/json.rb +205 -0
  100. data/lib/active_support/core_ext/object/to_param.rb +1 -55
  101. data/lib/active_support/core_ext/object/to_query.rb +66 -9
  102. data/lib/active_support/core_ext/object/try.rb +124 -33
  103. data/lib/active_support/core_ext/object/with_options.rb +37 -11
  104. data/lib/active_support/core_ext/object.rb +2 -1
  105. data/lib/active_support/core_ext/range/conversions.rb +17 -7
  106. data/lib/active_support/core_ext/range/each.rb +21 -0
  107. data/lib/active_support/core_ext/range/include_range.rb +20 -18
  108. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  109. data/lib/active_support/core_ext/range.rb +1 -2
  110. data/lib/active_support/core_ext/securerandom.rb +23 -0
  111. data/lib/active_support/core_ext/string/access.rb +95 -90
  112. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  113. data/lib/active_support/core_ext/string/conversions.rb +41 -38
  114. data/lib/active_support/core_ext/string/exclude.rb +6 -1
  115. data/lib/active_support/core_ext/string/filters.rb +70 -17
  116. data/lib/active_support/core_ext/string/indent.rb +43 -0
  117. data/lib/active_support/core_ext/string/inflections.rb +139 -59
  118. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  119. data/lib/active_support/core_ext/string/multibyte.rb +46 -65
  120. data/lib/active_support/core_ext/string/output_safety.rb +153 -56
  121. data/lib/active_support/core_ext/string/strip.rb +3 -6
  122. data/lib/active_support/core_ext/string/zones.rb +14 -0
  123. data/lib/active_support/core_ext/string.rb +2 -3
  124. data/lib/active_support/core_ext/struct.rb +3 -0
  125. data/lib/active_support/core_ext/time/calculations.rb +173 -173
  126. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  127. data/lib/active_support/core_ext/time/conversions.rb +33 -29
  128. data/lib/active_support/core_ext/time/marshal.rb +2 -56
  129. data/lib/active_support/core_ext/time/zones.rb +57 -32
  130. data/lib/active_support/core_ext/time.rb +5 -0
  131. data/lib/active_support/core_ext/uri.rb +13 -19
  132. data/lib/active_support/core_ext.rb +3 -2
  133. data/lib/active_support/dependencies/autoload.rb +47 -20
  134. data/lib/active_support/dependencies/interlock.rb +51 -0
  135. data/lib/active_support/dependencies.rb +315 -265
  136. data/lib/active_support/deprecation/behaviors.rb +71 -30
  137. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  138. data/lib/active_support/deprecation/method_wrappers.rb +59 -18
  139. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -14
  140. data/lib/active_support/deprecation/reporting.rb +61 -14
  141. data/lib/active_support/deprecation.rb +38 -13
  142. data/lib/active_support/descendants_tracker.rb +34 -19
  143. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  144. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  145. data/lib/active_support/duration.rb +85 -14
  146. data/lib/active_support/evented_file_update_checker.rb +194 -0
  147. data/lib/active_support/execution_wrapper.rb +117 -0
  148. data/lib/active_support/executor.rb +6 -0
  149. data/lib/active_support/file_update_checker.rb +138 -17
  150. data/lib/active_support/gem_version.rb +15 -0
  151. data/lib/active_support/gzip.rb +11 -5
  152. data/lib/active_support/hash_with_indifferent_access.rb +199 -49
  153. data/lib/active_support/i18n.rb +6 -2
  154. data/lib/active_support/i18n_railtie.rb +40 -21
  155. data/lib/active_support/inflections.rb +22 -13
  156. data/lib/active_support/inflector/inflections.rb +175 -144
  157. data/lib/active_support/inflector/methods.rb +328 -91
  158. data/lib/active_support/inflector/transliterate.rb +51 -37
  159. data/lib/active_support/json/decoding.rb +31 -22
  160. data/lib/active_support/json/encoding.rb +88 -248
  161. data/lib/active_support/key_generator.rb +71 -0
  162. data/lib/active_support/lazy_load_hooks.rb +27 -25
  163. data/lib/active_support/locale/en.yml +102 -3
  164. data/lib/active_support/log_subscriber/test_helper.rb +24 -21
  165. data/lib/active_support/log_subscriber.rb +36 -49
  166. data/lib/active_support/logger.rb +106 -0
  167. data/lib/active_support/logger_silence.rb +28 -0
  168. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  169. data/lib/active_support/message_encryptor.rb +72 -36
  170. data/lib/active_support/message_verifier.rb +96 -24
  171. data/lib/active_support/multibyte/chars.rb +88 -333
  172. data/lib/active_support/multibyte/unicode.rb +156 -136
  173. data/lib/active_support/multibyte.rb +5 -28
  174. data/lib/active_support/notifications/fanout.rb +115 -19
  175. data/lib/active_support/notifications/instrumenter.rb +52 -15
  176. data/lib/active_support/notifications.rb +168 -33
  177. data/lib/active_support/number_helper/number_converter.rb +182 -0
  178. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  179. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  180. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  181. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  182. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  183. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  184. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  185. data/lib/active_support/number_helper.rb +368 -0
  186. data/lib/active_support/option_merger.rb +1 -1
  187. data/lib/active_support/ordered_hash.rb +18 -183
  188. data/lib/active_support/ordered_options.rb +44 -24
  189. data/lib/active_support/per_thread_registry.rb +58 -0
  190. data/lib/active_support/proxy_object.rb +13 -0
  191. data/lib/active_support/rails.rb +27 -0
  192. data/lib/active_support/railtie.rb +25 -34
  193. data/lib/active_support/reloader.rb +129 -0
  194. data/lib/active_support/rescuable.rb +98 -48
  195. data/lib/active_support/security_utils.rb +27 -0
  196. data/lib/active_support/string_inquirer.rb +14 -9
  197. data/lib/active_support/subscriber.rb +120 -0
  198. data/lib/active_support/tagged_logging.rb +78 -0
  199. data/lib/active_support/test_case.rb +69 -17
  200. data/lib/active_support/testing/assertions.rb +43 -41
  201. data/lib/active_support/testing/autorun.rb +12 -0
  202. data/lib/active_support/testing/constant_lookup.rb +50 -0
  203. data/lib/active_support/testing/declarative.rb +7 -21
  204. data/lib/active_support/testing/deprecation.rb +14 -33
  205. data/lib/active_support/testing/file_fixtures.rb +34 -0
  206. data/lib/active_support/testing/isolation.rb +53 -95
  207. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  208. data/lib/active_support/testing/setup_and_teardown.rb +21 -82
  209. data/lib/active_support/testing/stream.rb +42 -0
  210. data/lib/active_support/testing/tagged_logging.rb +25 -0
  211. data/lib/active_support/testing/time_helpers.rb +134 -0
  212. data/lib/active_support/time.rb +6 -23
  213. data/lib/active_support/time_with_zone.rb +239 -92
  214. data/lib/active_support/values/time_zone.rb +236 -160
  215. data/lib/active_support/values/unicode_tables.dat +0 -0
  216. data/lib/active_support/version.rb +5 -7
  217. data/lib/active_support/xml_mini/jdom.rb +19 -13
  218. data/lib/active_support/xml_mini/libxml.rb +3 -4
  219. data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
  220. data/lib/active_support/xml_mini/nokogiri.rb +3 -4
  221. data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
  222. data/lib/active_support/xml_mini/rexml.rb +8 -10
  223. data/lib/active_support/xml_mini.rb +66 -34
  224. data/lib/active_support.rb +40 -23
  225. metadata +185 -134
  226. data/CHANGELOG +0 -1534
  227. data/lib/active_support/base64.rb +0 -42
  228. data/lib/active_support/basic_object.rb +0 -21
  229. data/lib/active_support/buffered_logger.rb +0 -137
  230. data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
  231. data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
  232. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  233. data/lib/active_support/core_ext/array/uniq_by.rb +0 -16
  234. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -44
  235. data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
  236. data/lib/active_support/core_ext/date/freeze.rb +0 -31
  237. data/lib/active_support/core_ext/date_time/zones.rb +0 -21
  238. data/lib/active_support/core_ext/exception.rb +0 -3
  239. data/lib/active_support/core_ext/file/path.rb +0 -5
  240. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  241. data/lib/active_support/core_ext/float.rb +0 -1
  242. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -11
  243. data/lib/active_support/core_ext/hash/diff.rb +0 -13
  244. data/lib/active_support/core_ext/kernel/requires.rb +0 -28
  245. data/lib/active_support/core_ext/logger.rb +0 -81
  246. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
  247. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  248. data/lib/active_support/core_ext/module/synchronization.rb +0 -43
  249. data/lib/active_support/core_ext/object/to_json.rb +0 -19
  250. data/lib/active_support/core_ext/proc.rb +0 -14
  251. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  252. data/lib/active_support/core_ext/process.rb +0 -1
  253. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  254. data/lib/active_support/core_ext/range/cover.rb +0 -3
  255. data/lib/active_support/core_ext/rexml.rb +0 -46
  256. data/lib/active_support/core_ext/string/encoding.rb +0 -11
  257. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  258. data/lib/active_support/core_ext/string/xchar.rb +0 -18
  259. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  260. data/lib/active_support/file_watcher.rb +0 -36
  261. data/lib/active_support/json/variable.rb +0 -9
  262. data/lib/active_support/memoizable.rb +0 -105
  263. data/lib/active_support/multibyte/exceptions.rb +0 -8
  264. data/lib/active_support/multibyte/utils.rb +0 -60
  265. data/lib/active_support/ruby/shim.rb +0 -22
  266. data/lib/active_support/secure_random.rb +0 -6
  267. data/lib/active_support/testing/mochaing.rb +0 -7
  268. data/lib/active_support/testing/pending.rb +0 -52
  269. data/lib/active_support/testing/performance/jruby.rb +0 -115
  270. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  271. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  272. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  273. data/lib/active_support/testing/performance/ruby.rb +0 -152
  274. data/lib/active_support/testing/performance.rb +0 -317
  275. data/lib/active_support/time/autoload.rb +0 -5
  276. data/lib/active_support/whiny_nil.rb +0 -60
@@ -0,0 +1,62 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToHumanSizeConverter < NumberConverter #:nodoc:
4
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb]
5
+
6
+ self.namespace = :human
7
+ self.validate_float = true
8
+
9
+ def convert
10
+ if opts.key?(:prefix)
11
+ ActiveSupport::Deprecation.warn('The :prefix option of `number_to_human_size` is deprecated and will be removed in Rails 5.1 with no replacement.')
12
+ end
13
+
14
+ @number = Float(number)
15
+
16
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
17
+ unless options.key?(:strip_insignificant_zeros)
18
+ options[:strip_insignificant_zeros] = true
19
+ end
20
+
21
+ if smaller_than_base?
22
+ number_to_format = number.to_i.to_s
23
+ else
24
+ human_size = number / (base ** exponent)
25
+ number_to_format = NumberToRoundedConverter.convert(human_size, options)
26
+ end
27
+ conversion_format.gsub('%n'.freeze, number_to_format).gsub('%u'.freeze, unit)
28
+ end
29
+
30
+ private
31
+
32
+ def conversion_format
33
+ translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
34
+ end
35
+
36
+ def unit
37
+ translate_number_value_with_default(storage_unit_key, :locale => options[:locale], :count => number.to_i, :raise => true)
38
+ end
39
+
40
+ def storage_unit_key
41
+ key_end = smaller_than_base? ? 'byte' : STORAGE_UNITS[exponent]
42
+ "human.storage_units.units.#{key_end}"
43
+ end
44
+
45
+ def exponent
46
+ max = STORAGE_UNITS.size - 1
47
+ exp = (Math.log(number) / Math.log(base)).to_i
48
+ exp = max if exp > max # avoid overflow for the highest unit
49
+ exp
50
+ end
51
+
52
+ def smaller_than_base?
53
+ number.to_i < base
54
+ end
55
+
56
+ def base
57
+ opts[:prefix] == :si ? 1000 : 1024
58
+ end
59
+ end
60
+ end
61
+ end
62
+
@@ -0,0 +1,12 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToPercentageConverter < NumberConverter # :nodoc:
4
+ self.namespace = :percentage
5
+
6
+ def convert
7
+ rounded_number = NumberToRoundedConverter.convert(number, options)
8
+ options[:format].gsub('%n'.freeze, rounded_number)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,58 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToPhoneConverter < NumberConverter #:nodoc:
4
+ def convert
5
+ str = country_code(opts[:country_code])
6
+ str << convert_to_phone_number(number.to_s.strip)
7
+ str << phone_ext(opts[:extension])
8
+ end
9
+
10
+ private
11
+
12
+ def convert_to_phone_number(number)
13
+ if opts[:area_code]
14
+ convert_with_area_code(number)
15
+ else
16
+ convert_without_area_code(number)
17
+ end
18
+ end
19
+
20
+ def convert_with_area_code(number)
21
+ default_pattern = /(\d{1,3})(\d{3})(\d{4}$)/
22
+ number.gsub!(regexp_pattern(default_pattern),
23
+ "(\\1) \\2#{delimiter}\\3")
24
+ number
25
+ end
26
+
27
+ def convert_without_area_code(number)
28
+ default_pattern = /(\d{0,3})(\d{3})(\d{4})$/
29
+ number.gsub!(regexp_pattern(default_pattern),
30
+ "\\1#{delimiter}\\2#{delimiter}\\3")
31
+ number.slice!(0, 1) if start_with_delimiter?(number)
32
+ number
33
+ end
34
+
35
+ def start_with_delimiter?(number)
36
+ delimiter.present? && number.start_with?(delimiter)
37
+ end
38
+
39
+ def delimiter
40
+ opts[:delimiter] || "-"
41
+ end
42
+
43
+ def country_code(code)
44
+ code.blank? ? "" : "+#{code}#{delimiter}"
45
+ end
46
+
47
+ def phone_ext(ext)
48
+ ext.blank? ? "" : " x #{ext}"
49
+ end
50
+
51
+ def regexp_pattern(default_pattern)
52
+ opts.fetch :pattern, default_pattern
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,92 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToRoundedConverter < NumberConverter # :nodoc:
4
+ self.namespace = :precision
5
+ self.validate_float = true
6
+
7
+ def convert
8
+ precision = options.delete :precision
9
+
10
+ if precision
11
+ case number
12
+ when Float, String
13
+ @number = BigDecimal(number.to_s)
14
+ when Rational
15
+ @number = BigDecimal(number, digit_count(number.to_i) + precision)
16
+ else
17
+ @number = number.to_d
18
+ end
19
+
20
+ if options.delete(:significant) && precision > 0
21
+ digits, rounded_number = digits_and_rounded_number(precision)
22
+ precision -= digits
23
+ precision = 0 if precision < 0 # don't let it be negative
24
+ else
25
+ rounded_number = number.round(precision)
26
+ rounded_number = rounded_number.to_i if precision == 0 && rounded_number.finite?
27
+ rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
28
+ end
29
+
30
+ formatted_string =
31
+ if BigDecimal === rounded_number && rounded_number.finite?
32
+ s = rounded_number.to_s('F')
33
+ s << '0'.freeze * precision
34
+ a, b = s.split('.'.freeze, 2)
35
+ a << '.'.freeze
36
+ a << b[0, precision]
37
+ else
38
+ "%00.#{precision}f" % rounded_number
39
+ end
40
+ else
41
+ formatted_string = number
42
+ end
43
+
44
+ delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
45
+ format_number(delimited_number)
46
+ end
47
+
48
+ private
49
+
50
+ def digits_and_rounded_number(precision)
51
+ if zero?
52
+ [1, 0]
53
+ else
54
+ digits = digit_count(number)
55
+ multiplier = 10 ** (digits - precision)
56
+ rounded_number = calculate_rounded_number(multiplier)
57
+ digits = digit_count(rounded_number) # After rounding, the number of digits may have changed
58
+ [digits, rounded_number]
59
+ end
60
+ end
61
+
62
+ def calculate_rounded_number(multiplier)
63
+ (number / BigDecimal.new(multiplier.to_f.to_s)).round * multiplier
64
+ end
65
+
66
+ def digit_count(number)
67
+ number.zero? ? 1 : (Math.log10(absolute_number(number)) + 1).floor
68
+ end
69
+
70
+ def strip_insignificant_zeros
71
+ options[:strip_insignificant_zeros]
72
+ end
73
+
74
+ def format_number(number)
75
+ if strip_insignificant_zeros
76
+ escaped_separator = Regexp.escape(options[:separator])
77
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
78
+ else
79
+ number
80
+ end
81
+ end
82
+
83
+ def absolute_number(number)
84
+ number.respond_to?(:abs) ? number.abs : number.to_d.abs
85
+ end
86
+
87
+ def zero?
88
+ number.respond_to?(:zero?) ? number.zero? : number.to_d.zero?
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,368 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ extend ActiveSupport::Autoload
4
+
5
+ eager_autoload do
6
+ autoload :NumberConverter
7
+ autoload :NumberToRoundedConverter
8
+ autoload :NumberToDelimitedConverter
9
+ autoload :NumberToHumanConverter
10
+ autoload :NumberToHumanSizeConverter
11
+ autoload :NumberToPhoneConverter
12
+ autoload :NumberToCurrencyConverter
13
+ autoload :NumberToPercentageConverter
14
+ end
15
+
16
+ extend self
17
+
18
+ # Formats a +number+ into a phone number (US by default e.g., (555)
19
+ # 123-9876). You can customize the format in the +options+ hash.
20
+ #
21
+ # ==== Options
22
+ #
23
+ # * <tt>:area_code</tt> - Adds parentheses around the area code.
24
+ # * <tt>:delimiter</tt> - Specifies the delimiter to use
25
+ # (defaults to "-").
26
+ # * <tt>:extension</tt> - Specifies an extension to add to the
27
+ # end of the generated number.
28
+ # * <tt>:country_code</tt> - Sets the country code for the phone
29
+ # number.
30
+ # * <tt>:pattern</tt> - Specifies how the number is divided into three
31
+ # groups with the custom regexp to override the default format.
32
+ # ==== Examples
33
+ #
34
+ # number_to_phone(5551234) # => "555-1234"
35
+ # number_to_phone('5551234') # => "555-1234"
36
+ # number_to_phone(1235551234) # => "123-555-1234"
37
+ # number_to_phone(1235551234, area_code: true) # => "(123) 555-1234"
38
+ # number_to_phone(1235551234, delimiter: ' ') # => "123 555 1234"
39
+ # number_to_phone(1235551234, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
40
+ # number_to_phone(1235551234, country_code: 1) # => "+1-123-555-1234"
41
+ # number_to_phone('123a456') # => "123a456"
42
+ #
43
+ # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
44
+ # # => "+1.123.555.1234 x 1343"
45
+ #
46
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
47
+ # # => "(755) 6123-4567"
48
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/))
49
+ # # => "133-1234-5678"
50
+ def number_to_phone(number, options = {})
51
+ NumberToPhoneConverter.convert(number, options)
52
+ end
53
+
54
+ # Formats a +number+ into a currency string (e.g., $13.65). You
55
+ # can customize the format in the +options+ hash.
56
+ #
57
+ # The currency unit and number formatting of the current locale will be used
58
+ # unless otherwise specified in the provided options. No currency conversion
59
+ # is performed. If the user is given a way to change their locale, they will
60
+ # also be able to change the relative value of the currency displayed with
61
+ # this helper. If your application will ever support multiple locales, you
62
+ # may want to specify a constant <tt>:locale</tt> option or consider
63
+ # using a library capable of currency conversion.
64
+ #
65
+ # ==== Options
66
+ #
67
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
68
+ # (defaults to current locale).
69
+ # * <tt>:precision</tt> - Sets the level of precision (defaults
70
+ # to 2).
71
+ # * <tt>:unit</tt> - Sets the denomination of the currency
72
+ # (defaults to "$").
73
+ # * <tt>:separator</tt> - Sets the separator between the units
74
+ # (defaults to ".").
75
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
76
+ # to ",").
77
+ # * <tt>:format</tt> - Sets the format for non-negative numbers
78
+ # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
79
+ # currency, and <tt>%n</tt> for the number.
80
+ # * <tt>:negative_format</tt> - Sets the format for negative
81
+ # numbers (defaults to prepending an hyphen to the formatted
82
+ # number given by <tt>:format</tt>). Accepts the same fields
83
+ # than <tt>:format</tt>, except <tt>%n</tt> is here the
84
+ # absolute value of the number.
85
+ #
86
+ # ==== Examples
87
+ #
88
+ # number_to_currency(1234567890.50) # => "$1,234,567,890.50"
89
+ # number_to_currency(1234567890.506) # => "$1,234,567,890.51"
90
+ # number_to_currency(1234567890.506, precision: 3) # => "$1,234,567,890.506"
91
+ # number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €"
92
+ # number_to_currency('123a456') # => "$123a456"
93
+ #
94
+ # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
95
+ # # => "($1,234,567,890.50)"
96
+ # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '')
97
+ # # => "&pound;1234567890,50"
98
+ # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
99
+ # # => "1234567890,50 &pound;"
100
+ def number_to_currency(number, options = {})
101
+ NumberToCurrencyConverter.convert(number, options)
102
+ end
103
+
104
+ # Formats a +number+ as a percentage string (e.g., 65%). You can
105
+ # customize the format in the +options+ hash.
106
+ #
107
+ # ==== Options
108
+ #
109
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
110
+ # (defaults to current locale).
111
+ # * <tt>:precision</tt> - Sets the precision of the number
112
+ # (defaults to 3). Keeps the number's precision if nil.
113
+ # * <tt>:significant</tt> - If +true+, precision will be the number
114
+ # of significant_digits. If +false+, the number of fractional
115
+ # digits (defaults to +false+).
116
+ # * <tt>:separator</tt> - Sets the separator between the
117
+ # fractional and integer digits (defaults to ".").
118
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
119
+ # to "").
120
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
121
+ # insignificant zeros after the decimal separator (defaults to
122
+ # +false+).
123
+ # * <tt>:format</tt> - Specifies the format of the percentage
124
+ # string The number field is <tt>%n</tt> (defaults to "%n%").
125
+ #
126
+ # ==== Examples
127
+ #
128
+ # number_to_percentage(100) # => "100.000%"
129
+ # number_to_percentage('98') # => "98.000%"
130
+ # number_to_percentage(100, precision: 0) # => "100%"
131
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
132
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
133
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
134
+ # number_to_percentage(1000, precision: nil) # => "1000%"
135
+ # number_to_percentage('98a') # => "98a%"
136
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
137
+ def number_to_percentage(number, options = {})
138
+ NumberToPercentageConverter.convert(number, options)
139
+ end
140
+
141
+ # Formats a +number+ with grouped thousands using +delimiter+
142
+ # (e.g., 12,324). You can customize the format in the +options+
143
+ # hash.
144
+ #
145
+ # ==== Options
146
+ #
147
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
148
+ # (defaults to current locale).
149
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
150
+ # to ",").
151
+ # * <tt>:separator</tt> - Sets the separator between the
152
+ # fractional and integer digits (defaults to ".").
153
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
154
+ # deriving the placement of delimiter. Helpful when using currency formats
155
+ # like INR.
156
+ #
157
+ # ==== Examples
158
+ #
159
+ # number_to_delimited(12345678) # => "12,345,678"
160
+ # number_to_delimited('123456') # => "123,456"
161
+ # number_to_delimited(12345678.05) # => "12,345,678.05"
162
+ # number_to_delimited(12345678, delimiter: '.') # => "12.345.678"
163
+ # number_to_delimited(12345678, delimiter: ',') # => "12,345,678"
164
+ # number_to_delimited(12345678.05, separator: ' ') # => "12,345,678 05"
165
+ # number_to_delimited(12345678.05, locale: :fr) # => "12 345 678,05"
166
+ # number_to_delimited('112a') # => "112a"
167
+ # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
168
+ # # => "98 765 432,98"
169
+ # number_to_delimited("123456.78",
170
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
171
+ # # => "1,23,456.78"
172
+ def number_to_delimited(number, options = {})
173
+ NumberToDelimitedConverter.convert(number, options)
174
+ end
175
+
176
+ # Formats a +number+ with the specified level of
177
+ # <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
178
+ # +:significant+ is +false+, and 5 if +:significant+ is +true+).
179
+ # You can customize the format in the +options+ hash.
180
+ #
181
+ # ==== Options
182
+ #
183
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
184
+ # (defaults to current locale).
185
+ # * <tt>:precision</tt> - Sets the precision of the number
186
+ # (defaults to 3). Keeps the number's precision if nil.
187
+ # * <tt>:significant</tt> - If +true+, precision will be the number
188
+ # of significant_digits. If +false+, the number of fractional
189
+ # digits (defaults to +false+).
190
+ # * <tt>:separator</tt> - Sets the separator between the
191
+ # fractional and integer digits (defaults to ".").
192
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
193
+ # to "").
194
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
195
+ # insignificant zeros after the decimal separator (defaults to
196
+ # +false+).
197
+ #
198
+ # ==== Examples
199
+ #
200
+ # number_to_rounded(111.2345) # => "111.235"
201
+ # number_to_rounded(111.2345, precision: 2) # => "111.23"
202
+ # number_to_rounded(13, precision: 5) # => "13.00000"
203
+ # number_to_rounded(389.32314, precision: 0) # => "389"
204
+ # number_to_rounded(111.2345, significant: true) # => "111"
205
+ # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
206
+ # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
207
+ # number_to_rounded(13, precision: nil) # => "13"
208
+ # number_to_rounded(111.234, locale: :fr) # => "111,234"
209
+ #
210
+ # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
211
+ # # => "13"
212
+ #
213
+ # number_to_rounded(389.32314, precision: 4, significant: true) # => "389.3"
214
+ # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
215
+ # # => "1.111,23"
216
+ def number_to_rounded(number, options = {})
217
+ NumberToRoundedConverter.convert(number, options)
218
+ end
219
+
220
+ # Formats the bytes in +number+ into a more understandable
221
+ # representation (e.g., giving it 1500 yields 1.5 KB). This
222
+ # method is useful for reporting file sizes to users. You can
223
+ # customize the format in the +options+ hash.
224
+ #
225
+ # See <tt>number_to_human</tt> if you want to pretty-print a
226
+ # generic number.
227
+ #
228
+ # ==== Options
229
+ #
230
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
231
+ # (defaults to current locale).
232
+ # * <tt>:precision</tt> - Sets the precision of the number
233
+ # (defaults to 3).
234
+ # * <tt>:significant</tt> - If +true+, precision will be the number
235
+ # of significant_digits. If +false+, the number of fractional
236
+ # digits (defaults to +true+)
237
+ # * <tt>:separator</tt> - Sets the separator between the
238
+ # fractional and integer digits (defaults to ".").
239
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
240
+ # to "").
241
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
242
+ # insignificant zeros after the decimal separator (defaults to
243
+ # +true+)
244
+ #
245
+ # ==== Examples
246
+ #
247
+ # number_to_human_size(123) # => "123 Bytes"
248
+ # number_to_human_size(1234) # => "1.21 KB"
249
+ # number_to_human_size(12345) # => "12.1 KB"
250
+ # number_to_human_size(1234567) # => "1.18 MB"
251
+ # number_to_human_size(1234567890) # => "1.15 GB"
252
+ # number_to_human_size(1234567890123) # => "1.12 TB"
253
+ # number_to_human_size(1234567890123456) # => "1.1 PB"
254
+ # number_to_human_size(1234567890123456789) # => "1.07 EB"
255
+ # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
256
+ # number_to_human_size(483989, precision: 2) # => "470 KB"
257
+ # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
258
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
259
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
260
+ def number_to_human_size(number, options = {})
261
+ NumberToHumanSizeConverter.convert(number, options)
262
+ end
263
+
264
+ # Pretty prints (formats and approximates) a number in a way it
265
+ # is more readable by humans (eg.: 1200000000 becomes "1.2
266
+ # Billion"). This is useful for numbers that can get very large
267
+ # (and too hard to read).
268
+ #
269
+ # See <tt>number_to_human_size</tt> if you want to print a file
270
+ # size.
271
+ #
272
+ # You can also define your own unit-quantifier names if you want
273
+ # to use other decimal units (eg.: 1500 becomes "1.5
274
+ # kilometers", 0.150 becomes "150 milliliters", etc). You may
275
+ # define a wide range of unit quantifiers, even fractional ones
276
+ # (centi, deci, mili, etc).
277
+ #
278
+ # ==== Options
279
+ #
280
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
281
+ # (defaults to current locale).
282
+ # * <tt>:precision</tt> - Sets the precision of the number
283
+ # (defaults to 3).
284
+ # * <tt>:significant</tt> - If +true+, precision will be the number
285
+ # of significant_digits. If +false+, the number of fractional
286
+ # digits (defaults to +true+)
287
+ # * <tt>:separator</tt> - Sets the separator between the
288
+ # fractional and integer digits (defaults to ".").
289
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
290
+ # to "").
291
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
292
+ # insignificant zeros after the decimal separator (defaults to
293
+ # +true+)
294
+ # * <tt>:units</tt> - A Hash of unit quantifier names. Or a
295
+ # string containing an i18n scope where to find this hash. It
296
+ # might have the following keys:
297
+ # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
298
+ # <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
299
+ # <tt>:billion</tt>, <tt>:trillion</tt>,
300
+ # <tt>:quadrillion</tt>
301
+ # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
302
+ # <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
303
+ # <tt>:pico</tt>, <tt>:femto</tt>
304
+ # * <tt>:format</tt> - Sets the format of the output string
305
+ # (defaults to "%n %u"). The field types are:
306
+ # * %u - The quantifier (ex.: 'thousand')
307
+ # * %n - The number
308
+ #
309
+ # ==== Examples
310
+ #
311
+ # number_to_human(123) # => "123"
312
+ # number_to_human(1234) # => "1.23 Thousand"
313
+ # number_to_human(12345) # => "12.3 Thousand"
314
+ # number_to_human(1234567) # => "1.23 Million"
315
+ # number_to_human(1234567890) # => "1.23 Billion"
316
+ # number_to_human(1234567890123) # => "1.23 Trillion"
317
+ # number_to_human(1234567890123456) # => "1.23 Quadrillion"
318
+ # number_to_human(1234567890123456789) # => "1230 Quadrillion"
319
+ # number_to_human(489939, precision: 2) # => "490 Thousand"
320
+ # number_to_human(489939, precision: 4) # => "489.9 Thousand"
321
+ # number_to_human(1234567, precision: 4,
322
+ # significant: false) # => "1.2346 Million"
323
+ # number_to_human(1234567, precision: 1,
324
+ # separator: ',',
325
+ # significant: false) # => "1,2 Million"
326
+ #
327
+ # number_to_human(500000000, precision: 5) # => "500 Million"
328
+ # number_to_human(12345012345, significant: false) # => "12.345 Billion"
329
+ #
330
+ # Non-significant zeros after the decimal separator are stripped
331
+ # out by default (set <tt>:strip_insignificant_zeros</tt> to
332
+ # +false+ to change that):
333
+ #
334
+ # number_to_human(12.00001) # => "12"
335
+ # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
336
+ #
337
+ # ==== Custom Unit Quantifiers
338
+ #
339
+ # You can also use your own custom unit quantifiers:
340
+ # number_to_human(500000, units: { unit: 'ml', thousand: 'lt' }) # => "500 lt"
341
+ #
342
+ # If in your I18n locale you have:
343
+ #
344
+ # distance:
345
+ # centi:
346
+ # one: "centimeter"
347
+ # other: "centimeters"
348
+ # unit:
349
+ # one: "meter"
350
+ # other: "meters"
351
+ # thousand:
352
+ # one: "kilometer"
353
+ # other: "kilometers"
354
+ # billion: "gazillion-distance"
355
+ #
356
+ # Then you could do:
357
+ #
358
+ # number_to_human(543934, units: :distance) # => "544 kilometers"
359
+ # number_to_human(54393498, units: :distance) # => "54400 kilometers"
360
+ # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
361
+ # number_to_human(343, units: :distance, precision: 1) # => "300 meters"
362
+ # number_to_human(1, units: :distance) # => "1 meter"
363
+ # number_to_human(0.34, units: :distance) # => "34 centimeters"
364
+ def number_to_human(number, options = {})
365
+ NumberToHumanConverter.convert(number, options)
366
+ end
367
+ end
368
+ end
@@ -12,7 +12,7 @@ module ActiveSupport
12
12
 
13
13
  private
14
14
  def method_missing(method, *arguments, &block)
15
- if arguments.last.is_a?(Proc)
15
+ if arguments.first.is_a?(Proc)
16
16
  proc = arguments.pop
17
17
  arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
18
18
  else