activesupport 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 activesupport might be problematic. Click here for more details.

Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +325 -136
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/active_support.rb +8 -21
  6. data/lib/active_support/backtrace_cleaner.rb +33 -25
  7. data/lib/active_support/basic_object.rb +7 -17
  8. data/lib/active_support/benchmarkable.rb +19 -15
  9. data/lib/active_support/buffered_logger.rb +9 -113
  10. data/lib/active_support/cache.rb +203 -171
  11. data/lib/active_support/cache/file_store.rb +12 -12
  12. data/lib/active_support/cache/mem_cache_store.rb +24 -30
  13. data/lib/active_support/cache/memory_store.rb +2 -0
  14. data/lib/active_support/callbacks.rb +195 -247
  15. data/lib/active_support/concern.rb +16 -23
  16. data/lib/active_support/concurrency/latch.rb +27 -0
  17. data/lib/active_support/configurable.rb +69 -12
  18. data/lib/active_support/core_ext.rb +1 -0
  19. data/lib/active_support/core_ext/array.rb +0 -1
  20. data/lib/active_support/core_ext/array/access.rb +17 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +113 -55
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +21 -22
  24. data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
  25. data/lib/active_support/core_ext/array/wrap.rb +11 -14
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +7 -24
  27. data/lib/active_support/core_ext/class/attribute.rb +12 -8
  28. data/lib/active_support/core_ext/class/attribute_accessors.rb +14 -12
  29. data/lib/active_support/core_ext/class/delegating_attributes.rb +15 -19
  30. data/lib/active_support/core_ext/class/subclasses.rb +11 -5
  31. data/lib/active_support/core_ext/date.rb +6 -0
  32. data/lib/active_support/core_ext/date/calculations.rb +34 -188
  33. data/lib/active_support/core_ext/date/conversions.rb +16 -38
  34. data/lib/active_support/core_ext/date/infinite_comparable.rb +5 -0
  35. data/lib/active_support/core_ext/date/zones.rb +25 -2
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
  37. data/lib/active_support/core_ext/date_time.rb +5 -0
  38. data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
  39. data/lib/active_support/core_ext/date_time/calculations.rb +73 -65
  40. data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
  41. data/lib/active_support/core_ext/date_time/infinite_comparable.rb +5 -0
  42. data/lib/active_support/core_ext/date_time/zones.rb +11 -8
  43. data/lib/active_support/core_ext/enumerable.rb +26 -73
  44. data/lib/active_support/core_ext/file.rb +0 -1
  45. data/lib/active_support/core_ext/file/atomic.rb +27 -11
  46. data/lib/active_support/core_ext/hash.rb +0 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +145 -79
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
  49. data/lib/active_support/core_ext/hash/diff.rb +5 -4
  50. data/lib/active_support/core_ext/hash/except.rb +1 -9
  51. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
  52. data/lib/active_support/core_ext/hash/keys.rb +108 -24
  53. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  54. data/lib/active_support/core_ext/hash/slice.rb +12 -12
  55. data/lib/active_support/core_ext/infinite_comparable.rb +35 -0
  56. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  57. data/lib/active_support/core_ext/integer/time.rb +17 -12
  58. data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
  59. data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
  60. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  61. data/lib/active_support/core_ext/load_error.rb +7 -5
  62. data/lib/active_support/core_ext/logger.rb +7 -23
  63. data/lib/active_support/core_ext/marshal.rb +19 -0
  64. data/lib/active_support/core_ext/module.rb +1 -3
  65. data/lib/active_support/core_ext/module/aliasing.rb +8 -9
  66. data/lib/active_support/core_ext/module/anonymous.rb +2 -7
  67. data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
  68. data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
  69. data/lib/active_support/core_ext/module/delegation.rb +57 -40
  70. data/lib/active_support/core_ext/module/deprecation.rb +19 -3
  71. data/lib/active_support/core_ext/module/introspection.rb +17 -27
  72. data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
  73. data/lib/active_support/core_ext/module/remove_method.rb +1 -5
  74. data/lib/active_support/core_ext/numeric.rb +2 -0
  75. data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
  76. data/lib/active_support/core_ext/numeric/infinite_comparable.rb +9 -0
  77. data/lib/active_support/core_ext/numeric/time.rb +6 -6
  78. data/lib/active_support/core_ext/object.rb +1 -0
  79. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  80. data/lib/active_support/core_ext/object/blank.rb +7 -23
  81. data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
  82. data/lib/active_support/core_ext/object/duplicable.rb +1 -30
  83. data/lib/active_support/core_ext/object/inclusion.rb +6 -6
  84. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  85. data/lib/active_support/core_ext/object/to_json.rb +8 -0
  86. data/lib/active_support/core_ext/object/to_param.rb +5 -2
  87. data/lib/active_support/core_ext/object/try.rb +46 -25
  88. data/lib/active_support/core_ext/object/with_options.rb +7 -8
  89. data/lib/active_support/core_ext/proc.rb +3 -0
  90. data/lib/active_support/core_ext/range.rb +0 -2
  91. data/lib/active_support/core_ext/range/conversions.rb +0 -2
  92. data/lib/active_support/core_ext/range/include_range.rb +1 -1
  93. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  94. data/lib/active_support/core_ext/string.rb +2 -2
  95. data/lib/active_support/core_ext/string/access.rb +95 -90
  96. data/lib/active_support/core_ext/string/conversions.rb +29 -38
  97. data/lib/active_support/core_ext/string/encoding.rb +6 -9
  98. data/lib/active_support/core_ext/string/filters.rb +24 -18
  99. data/lib/active_support/core_ext/string/indent.rb +43 -0
  100. data/lib/active_support/core_ext/string/inflections.rb +70 -60
  101. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  102. data/lib/active_support/core_ext/string/multibyte.rb +41 -64
  103. data/lib/active_support/core_ext/string/output_safety.rb +59 -51
  104. data/lib/active_support/core_ext/string/zones.rb +13 -0
  105. data/lib/active_support/core_ext/struct.rb +6 -0
  106. data/lib/active_support/core_ext/thread.rb +74 -0
  107. data/lib/active_support/core_ext/time.rb +6 -0
  108. data/lib/active_support/core_ext/time/calculations.rb +105 -193
  109. data/lib/active_support/core_ext/time/conversions.rb +27 -51
  110. data/lib/active_support/core_ext/time/infinite_comparable.rb +5 -0
  111. data/lib/active_support/core_ext/time/marshal.rb +0 -27
  112. data/lib/active_support/core_ext/time/zones.rb +27 -17
  113. data/lib/active_support/core_ext/uri.rb +13 -17
  114. data/lib/active_support/dependencies.rb +160 -141
  115. data/lib/active_support/dependencies/autoload.rb +47 -20
  116. data/lib/active_support/deprecation.rb +39 -14
  117. data/lib/active_support/deprecation/behaviors.rb +44 -30
  118. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  119. data/lib/active_support/deprecation/method_wrappers.rb +33 -18
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
  121. data/lib/active_support/deprecation/reporting.rb +40 -11
  122. data/lib/active_support/descendants_tracker.rb +34 -19
  123. data/lib/active_support/duration.rb +6 -8
  124. data/lib/active_support/file_update_checker.rb +63 -47
  125. data/lib/active_support/gzip.rb +11 -5
  126. data/lib/active_support/hash_with_indifferent_access.rb +112 -37
  127. data/lib/active_support/i18n.rb +4 -0
  128. data/lib/active_support/i18n_railtie.rb +5 -22
  129. data/lib/active_support/inflections.rb +14 -12
  130. data/lib/active_support/inflector/inflections.rb +108 -71
  131. data/lib/active_support/inflector/methods.rb +181 -160
  132. data/lib/active_support/inflector/transliterate.rb +16 -17
  133. data/lib/active_support/json/decoding.rb +18 -17
  134. data/lib/active_support/json/encoding.rb +93 -39
  135. data/lib/active_support/json/variable.rb +10 -1
  136. data/lib/active_support/key_generator.rb +75 -0
  137. data/lib/active_support/lazy_load_hooks.rb +21 -19
  138. data/lib/active_support/locale/en.yml +100 -3
  139. data/lib/active_support/log_subscriber.rb +56 -36
  140. data/lib/active_support/log_subscriber/test_helper.rb +18 -15
  141. data/lib/active_support/logger.rb +57 -0
  142. data/lib/active_support/logger_silence.rb +24 -0
  143. data/lib/active_support/message_encryptor.rb +32 -29
  144. data/lib/active_support/message_verifier.rb +8 -14
  145. data/lib/active_support/multibyte.rb +5 -28
  146. data/lib/active_support/multibyte/chars.rb +80 -333
  147. data/lib/active_support/multibyte/unicode.rb +74 -64
  148. data/lib/active_support/notifications.rb +57 -25
  149. data/lib/active_support/notifications/fanout.rb +105 -18
  150. data/lib/active_support/notifications/instrumenter.rb +32 -13
  151. data/lib/active_support/number_helper.rb +636 -0
  152. data/lib/active_support/ordered_hash.rb +8 -190
  153. data/lib/active_support/ordered_options.rb +21 -23
  154. data/lib/active_support/proxy_object.rb +13 -0
  155. data/lib/active_support/rails.rb +27 -0
  156. data/lib/active_support/railtie.rb +12 -32
  157. data/lib/active_support/rescuable.rb +9 -4
  158. data/lib/active_support/string_inquirer.rb +13 -8
  159. data/lib/active_support/tagged_logging.rb +51 -73
  160. data/lib/active_support/test_case.rb +46 -17
  161. data/lib/active_support/testing/assertions.rb +56 -26
  162. data/lib/active_support/testing/autorun.rb +5 -0
  163. data/lib/active_support/testing/constant_lookup.rb +52 -0
  164. data/lib/active_support/testing/declarative.rb +1 -1
  165. data/lib/active_support/testing/deprecation.rb +0 -19
  166. data/lib/active_support/testing/isolation.rb +25 -58
  167. data/lib/active_support/testing/pending.rb +5 -43
  168. data/lib/active_support/testing/setup_and_teardown.rb +6 -92
  169. data/lib/active_support/testing/tagged_logging.rb +25 -0
  170. data/lib/active_support/time.rb +6 -21
  171. data/lib/active_support/time_with_zone.rb +78 -43
  172. data/lib/active_support/values/time_zone.rb +77 -58
  173. data/lib/active_support/values/unicode_tables.dat +0 -0
  174. data/lib/active_support/version.rb +4 -4
  175. data/lib/active_support/xml_mini.rb +35 -17
  176. data/lib/active_support/xml_mini/jdom.rb +9 -17
  177. data/lib/active_support/xml_mini/libxml.rb +1 -2
  178. data/lib/active_support/xml_mini/libxmlsax.rb +1 -2
  179. data/lib/active_support/xml_mini/nokogiri.rb +1 -2
  180. data/lib/active_support/xml_mini/nokogirisax.rb +1 -2
  181. data/lib/active_support/xml_mini/rexml.rb +6 -8
  182. metadata +107 -77
  183. data/lib/active_support/base64.rb +0 -54
  184. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  185. data/lib/active_support/core_ext/date/freeze.rb +0 -33
  186. data/lib/active_support/core_ext/exception.rb +0 -3
  187. data/lib/active_support/core_ext/file/path.rb +0 -5
  188. data/lib/active_support/core_ext/float.rb +0 -1
  189. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  190. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
  191. data/lib/active_support/core_ext/io.rb +0 -15
  192. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  193. data/lib/active_support/core_ext/module/synchronization.rb +0 -45
  194. data/lib/active_support/core_ext/process.rb +0 -1
  195. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  196. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  197. data/lib/active_support/core_ext/range/cover.rb +0 -3
  198. data/lib/active_support/core_ext/rexml.rb +0 -46
  199. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  200. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  201. data/lib/active_support/memoizable.rb +0 -116
  202. data/lib/active_support/multibyte/exceptions.rb +0 -8
  203. data/lib/active_support/multibyte/utils.rb +0 -60
  204. data/lib/active_support/ruby/shim.rb +0 -22
  205. data/lib/active_support/security_utils.rb +0 -27
  206. data/lib/active_support/testing/mochaing.rb +0 -7
  207. data/lib/active_support/testing/performance.rb +0 -317
  208. data/lib/active_support/testing/performance/jruby.rb +0 -115
  209. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  210. data/lib/active_support/testing/performance/ruby.rb +0 -152
  211. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  212. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  213. data/lib/active_support/time/autoload.rb +0 -5
  214. data/lib/active_support/whiny_nil.rb +0 -24
@@ -1,39 +1,51 @@
1
- require 'active_support/core_ext/module/delegation'
1
+ require 'securerandom'
2
2
 
3
3
  module ActiveSupport
4
4
  module Notifications
5
+ # Instrumentors are stored in a thread local.
5
6
  class Instrumenter
6
7
  attr_reader :id
7
8
 
8
9
  def initialize(notifier)
9
- @id = unique_id
10
+ @id = unique_id
10
11
  @notifier = notifier
11
12
  end
12
13
 
13
14
  # Instrument the given block by measuring the time taken to execute it
14
15
  # and publish it. Notice that events get sent even if an error occurs
15
- # in the passed-in block
16
+ # in the passed-in block.
16
17
  def instrument(name, payload={})
17
- started = Time.now
18
-
18
+ start name, payload
19
19
  begin
20
20
  yield
21
21
  rescue Exception => e
22
22
  payload[:exception] = [e.class.name, e.message]
23
23
  raise e
24
24
  ensure
25
- @notifier.publish(name, started, Time.now, @id, payload)
25
+ finish name, payload
26
26
  end
27
27
  end
28
28
 
29
+ # Send a start notification with +name+ and +payload+.
30
+ def start(name, payload)
31
+ @notifier.start name, @id, payload
32
+ end
33
+
34
+ # Send a finish notification with +name+ and +payload+.
35
+ def finish(name, payload)
36
+ @notifier.finish name, @id, payload
37
+ end
38
+
29
39
  private
30
- def unique_id
31
- SecureRandom.hex(10)
32
- end
40
+
41
+ def unique_id
42
+ SecureRandom.hex(10)
43
+ end
33
44
  end
34
45
 
35
46
  class Event
36
- attr_reader :name, :time, :end, :transaction_id, :payload, :duration
47
+ attr_reader :name, :time, :transaction_id, :payload, :children
48
+ attr_accessor :end
37
49
 
38
50
  def initialize(name, start, ending, transaction_id, payload)
39
51
  @name = name
@@ -41,12 +53,19 @@ module ActiveSupport
41
53
  @time = start
42
54
  @transaction_id = transaction_id
43
55
  @end = ending
44
- @duration = 1000.0 * (@end - @time)
56
+ @children = []
57
+ end
58
+
59
+ def duration
60
+ 1000.0 * (self.end - time)
61
+ end
62
+
63
+ def <<(event)
64
+ @children << event
45
65
  end
46
66
 
47
67
  def parent_of?(event)
48
- start = (time - event.time) * 1000
49
- start <= 0 && (start + duration >= event.duration)
68
+ @children.include? event
50
69
  end
51
70
  end
52
71
  end
@@ -0,0 +1,636 @@
1
+ require 'active_support/core_ext/big_decimal/conversions'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/core_ext/hash/keys'
4
+ require 'active_support/i18n'
5
+
6
+ module ActiveSupport
7
+ module NumberHelper
8
+ extend self
9
+
10
+ DEFAULTS = {
11
+ # Used in number_to_delimited
12
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
13
+ format: {
14
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
15
+ separator: ".",
16
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
17
+ delimiter: ",",
18
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
19
+ precision: 3,
20
+ # If set to true, precision will mean the number of significant digits instead
21
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
22
+ significant: false,
23
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
24
+ strip_insignificant_zeros: false
25
+ },
26
+
27
+ # Used in number_to_currency
28
+ currency: {
29
+ format: {
30
+ format: "%u%n",
31
+ negative_format: "-%u%n",
32
+ unit: "$",
33
+ # These five are to override number.format and are optional
34
+ separator: ".",
35
+ delimiter: ",",
36
+ precision: 2,
37
+ significant: false,
38
+ strip_insignificant_zeros: false
39
+ }
40
+ },
41
+
42
+ # Used in number_to_percentage
43
+ percentage: {
44
+ format: {
45
+ delimiter: "",
46
+ format: "%n%"
47
+ }
48
+ },
49
+
50
+ # Used in number_to_rounded
51
+ precision: {
52
+ format: {
53
+ delimiter: ""
54
+ }
55
+ },
56
+
57
+ # Used in number_to_human_size and number_to_human
58
+ human: {
59
+ format: {
60
+ # These five are to override number.format and are optional
61
+ delimiter: "",
62
+ precision: 3,
63
+ significant: true,
64
+ strip_insignificant_zeros: true
65
+ },
66
+ # Used in number_to_human_size
67
+ storage_units: {
68
+ # Storage units output formatting.
69
+ # %u is the storage unit, %n is the number (default: 2 MB)
70
+ format: "%n %u",
71
+ units: {
72
+ byte: "Bytes",
73
+ kb: "KB",
74
+ mb: "MB",
75
+ gb: "GB",
76
+ tb: "TB"
77
+ }
78
+ },
79
+ # Used in number_to_human
80
+ decimal_units: {
81
+ format: "%n %u",
82
+ # Decimal units output formatting
83
+ # By default we will only quantify some of the exponents
84
+ # but the commented ones might be defined or overridden
85
+ # by the user.
86
+ units: {
87
+ # femto: Quadrillionth
88
+ # pico: Trillionth
89
+ # nano: Billionth
90
+ # micro: Millionth
91
+ # mili: Thousandth
92
+ # centi: Hundredth
93
+ # deci: Tenth
94
+ unit: "",
95
+ # ten:
96
+ # one: Ten
97
+ # other: Tens
98
+ # hundred: Hundred
99
+ thousand: "Thousand",
100
+ million: "Million",
101
+ billion: "Billion",
102
+ trillion: "Trillion",
103
+ quadrillion: "Quadrillion"
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
110
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
111
+
112
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
113
+
114
+ # Formats a +number+ into a US phone number (e.g., (555)
115
+ # 123-9876). You can customize the format in the +options+ hash.
116
+ #
117
+ # ==== Options
118
+ #
119
+ # * <tt>:area_code</tt> - Adds parentheses around the area code.
120
+ # * <tt>:delimiter</tt> - Specifies the delimiter to use
121
+ # (defaults to "-").
122
+ # * <tt>:extension</tt> - Specifies an extension to add to the
123
+ # end of the generated number.
124
+ # * <tt>:country_code</tt> - Sets the country code for the phone
125
+ # number.
126
+ # ==== Examples
127
+ #
128
+ # number_to_phone(5551234) # => 555-1234
129
+ # number_to_phone('5551234') # => 555-1234
130
+ # number_to_phone(1235551234) # => 123-555-1234
131
+ # number_to_phone(1235551234, area_code: true) # => (123) 555-1234
132
+ # number_to_phone(1235551234, delimiter: ' ') # => 123 555 1234
133
+ # number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
134
+ # number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
135
+ # number_to_phone('123a456') # => 123a456
136
+ #
137
+ # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
138
+ # # => +1.123.555.1234 x 1343
139
+ def number_to_phone(number, options = {})
140
+ return unless number
141
+ options = options.symbolize_keys
142
+
143
+ number = number.to_s.strip
144
+ area_code = options[:area_code]
145
+ delimiter = options[:delimiter] || "-"
146
+ extension = options[:extension]
147
+ country_code = options[:country_code]
148
+
149
+ if area_code
150
+ number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
151
+ else
152
+ number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
153
+ number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
154
+ end
155
+
156
+ str = ''
157
+ str << "+#{country_code}#{delimiter}" unless country_code.blank?
158
+ str << number
159
+ str << " x #{extension}" unless extension.blank?
160
+ str
161
+ end
162
+
163
+ # Formats a +number+ into a currency string (e.g., $13.65). You
164
+ # can customize the format in the +options+ hash.
165
+ #
166
+ # ==== Options
167
+ #
168
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
169
+ # (defaults to current locale).
170
+ # * <tt>:precision</tt> - Sets the level of precision (defaults
171
+ # to 2).
172
+ # * <tt>:unit</tt> - Sets the denomination of the currency
173
+ # (defaults to "$").
174
+ # * <tt>:separator</tt> - Sets the separator between the units
175
+ # (defaults to ".").
176
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
177
+ # to ",").
178
+ # * <tt>:format</tt> - Sets the format for non-negative numbers
179
+ # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
180
+ # currency, and <tt>%n</tt> for the number.
181
+ # * <tt>:negative_format</tt> - Sets the format for negative
182
+ # numbers (defaults to prepending an hyphen to the formatted
183
+ # number given by <tt>:format</tt>). Accepts the same fields
184
+ # than <tt>:format</tt>, except <tt>%n</tt> is here the
185
+ # absolute value of the number.
186
+ #
187
+ # ==== Examples
188
+ #
189
+ # number_to_currency(1234567890.50) # => $1,234,567,890.50
190
+ # number_to_currency(1234567890.506) # => $1,234,567,890.51
191
+ # number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
192
+ # number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
193
+ # number_to_currency('123a456') # => $123a456
194
+ #
195
+ # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
196
+ # # => ($1,234,567,890.50)
197
+ # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '')
198
+ # # => &pound;1234567890,50
199
+ # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
200
+ # # => 1234567890,50 &pound;
201
+ def number_to_currency(number, options = {})
202
+ return unless number
203
+ options = options.symbolize_keys
204
+
205
+ currency = i18n_format_options(options[:locale], :currency)
206
+ currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
207
+
208
+ defaults = default_format_options(:currency).merge!(currency)
209
+ defaults[:negative_format] = "-" + options[:format] if options[:format]
210
+ options = defaults.merge!(options)
211
+
212
+ unit = options.delete(:unit)
213
+ format = options.delete(:format)
214
+
215
+ if number.to_f.phase != 0
216
+ format = options.delete(:negative_format)
217
+ number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
218
+ end
219
+
220
+ format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
221
+ end
222
+
223
+ # Formats a +number+ as a percentage string (e.g., 65%). You can
224
+ # customize the format in the +options+ hash.
225
+ #
226
+ # ==== Options
227
+ #
228
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
229
+ # (defaults to current locale).
230
+ # * <tt>:precision</tt> - Sets the precision of the number
231
+ # (defaults to 3).
232
+ # * <tt>:significant</tt> - If +true+, precision will be the #
233
+ # of significant_digits. If +false+, the # of fractional
234
+ # digits (defaults to +false+).
235
+ # * <tt>:separator</tt> - Sets the separator between the
236
+ # fractional and integer digits (defaults to ".").
237
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
238
+ # to "").
239
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
240
+ # insignificant zeros after the decimal separator (defaults to
241
+ # +false+).
242
+ # * <tt>:format</tt> - Specifies the format of the percentage
243
+ # string The number field is <tt>%n</tt> (defaults to "%n%").
244
+ #
245
+ # ==== Examples
246
+ #
247
+ # number_to_percentage(100) # => 100.000%
248
+ # number_to_percentage('98') # => 98.000%
249
+ # number_to_percentage(100, precision: 0) # => 100%
250
+ # number_to_percentage(1000, delimiter: '.', separator: ,') # => 1.000,000%
251
+ # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
252
+ # number_to_percentage(1000, locale: :fr) # => 1 000,000%
253
+ # number_to_percentage('98a') # => 98a%
254
+ # number_to_percentage(100, format: '%n %') # => 100 %
255
+ def number_to_percentage(number, options = {})
256
+ return unless number
257
+ options = options.symbolize_keys
258
+
259
+ defaults = format_options(options[:locale], :percentage)
260
+ options = defaults.merge!(options)
261
+
262
+ format = options[:format] || "%n%"
263
+ format.gsub('%n', self.number_to_rounded(number, options))
264
+ end
265
+
266
+ # Formats a +number+ with grouped thousands using +delimiter+
267
+ # (e.g., 12,324). You can customize the format in the +options+
268
+ # hash.
269
+ #
270
+ # ==== Options
271
+ #
272
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
273
+ # (defaults to current locale).
274
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
275
+ # to ",").
276
+ # * <tt>:separator</tt> - Sets the separator between the
277
+ # fractional and integer digits (defaults to ".").
278
+ #
279
+ # ==== Examples
280
+ #
281
+ # number_to_delimited(12345678) # => 12,345,678
282
+ # number_to_delimited('123456') # => 123,456
283
+ # number_to_delimited(12345678.05) # => 12,345,678.05
284
+ # number_to_delimited(12345678, delimiter: '.') # => 12.345.678
285
+ # number_to_delimited(12345678, delimiter: ',') # => 12,345,678
286
+ # number_to_delimited(12345678.05, separator: ' ') # => 12,345,678 05
287
+ # number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
288
+ # number_to_delimited('112a') # => 112a
289
+ # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
290
+ # # => 98 765 432,98
291
+ def number_to_delimited(number, options = {})
292
+ options = options.symbolize_keys
293
+
294
+ return number unless valid_float?(number)
295
+
296
+ options = format_options(options[:locale]).merge!(options)
297
+
298
+ parts = number.to_s.to_str.split('.')
299
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
300
+ parts.join(options[:separator])
301
+ end
302
+
303
+ # Formats a +number+ with the specified level of
304
+ # <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
305
+ # +:significant+ is +false+, and 5 if +:significant+ is +true+).
306
+ # You can customize the format in the +options+ hash.
307
+ #
308
+ # ==== Options
309
+ #
310
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
311
+ # (defaults to current locale).
312
+ # * <tt>:precision</tt> - Sets the precision of the number
313
+ # (defaults to 3).
314
+ # * <tt>:significant</tt> - If +true+, precision will be the #
315
+ # of significant_digits. If +false+, the # of fractional
316
+ # digits (defaults to +false+).
317
+ # * <tt>:separator</tt> - Sets the separator between the
318
+ # fractional and integer digits (defaults to ".").
319
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
320
+ # to "").
321
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
322
+ # insignificant zeros after the decimal separator (defaults to
323
+ # +false+).
324
+ #
325
+ # ==== Examples
326
+ #
327
+ # number_to_rounded(111.2345) # => 111.235
328
+ # number_to_rounded(111.2345, precision: 2) # => 111.23
329
+ # number_to_rounded(13, precision: 5) # => 13.00000
330
+ # number_to_rounded(389.32314, precision: 0) # => 389
331
+ # number_to_rounded(111.2345, significant: true) # => 111
332
+ # number_to_rounded(111.2345, precision: 1, significant: true) # => 100
333
+ # number_to_rounded(13, precision: 5, significant: true) # => 13.000
334
+ # number_to_rounded(111.234, locale: :fr) # => 111,234
335
+ #
336
+ # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
337
+ # # => 13
338
+ #
339
+ # number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
340
+ # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
341
+ # # => 1.111,23
342
+ def number_to_rounded(number, options = {})
343
+ return number unless valid_float?(number)
344
+ number = Float(number)
345
+ options = options.symbolize_keys
346
+
347
+ defaults = format_options(options[:locale], :precision)
348
+ options = defaults.merge!(options)
349
+
350
+ precision = options.delete :precision
351
+ significant = options.delete :significant
352
+ strip_insignificant_zeros = options.delete :strip_insignificant_zeros
353
+
354
+ if significant && precision > 0
355
+ if number == 0
356
+ digits, rounded_number = 1, 0
357
+ else
358
+ digits = (Math.log10(number.abs) + 1).floor
359
+ rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
360
+ digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
361
+ end
362
+ precision -= digits
363
+ precision = 0 if precision < 0 # don't let it be negative
364
+ else
365
+ rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
366
+ rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
367
+ end
368
+ formatted_number = self.number_to_delimited("%01.#{precision}f" % rounded_number, options)
369
+ if strip_insignificant_zeros
370
+ escaped_separator = Regexp.escape(options[:separator])
371
+ formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
372
+ else
373
+ formatted_number
374
+ end
375
+ end
376
+
377
+ # Formats the bytes in +number+ into a more understandable
378
+ # representation (e.g., giving it 1500 yields 1.5 KB). This
379
+ # method is useful for reporting file sizes to users. You can
380
+ # customize the format in the +options+ hash.
381
+ #
382
+ # See <tt>number_to_human</tt> if you want to pretty-print a
383
+ # generic number.
384
+ #
385
+ # ==== Options
386
+ #
387
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
388
+ # (defaults to current locale).
389
+ # * <tt>:precision</tt> - Sets the precision of the number
390
+ # (defaults to 3).
391
+ # * <tt>:significant</tt> - If +true+, precision will be the #
392
+ # of significant_digits. If +false+, the # of fractional
393
+ # digits (defaults to +true+)
394
+ # * <tt>:separator</tt> - Sets the separator between the
395
+ # fractional and integer digits (defaults to ".").
396
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
397
+ # to "").
398
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
399
+ # insignificant zeros after the decimal separator (defaults to
400
+ # +true+)
401
+ # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
402
+ # prefix (defaults to :binary)
403
+ #
404
+ # ==== Examples
405
+ #
406
+ # number_to_human_size(123) # => 123 Bytes
407
+ # number_to_human_size(1234) # => 1.21 KB
408
+ # number_to_human_size(12345) # => 12.1 KB
409
+ # number_to_human_size(1234567) # => 1.18 MB
410
+ # number_to_human_size(1234567890) # => 1.15 GB
411
+ # number_to_human_size(1234567890123) # => 1.12 TB
412
+ # number_to_human_size(1234567, precision: 2) # => 1.2 MB
413
+ # number_to_human_size(483989, precision: 2) # => 470 KB
414
+ # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
415
+ #
416
+ # Non-significant zeros after the fractional separator are stripped out by
417
+ # default (set <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
418
+ #
419
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
420
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
421
+ def number_to_human_size(number, options = {})
422
+ options = options.symbolize_keys
423
+
424
+ return number unless valid_float?(number)
425
+ number = Float(number)
426
+
427
+ defaults = format_options(options[:locale], :human)
428
+ options = defaults.merge!(options)
429
+
430
+ #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
431
+ options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
432
+
433
+ storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
434
+
435
+ base = options[:prefix] == :si ? 1000 : 1024
436
+
437
+ if number.to_i < base
438
+ unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
439
+ storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
440
+ else
441
+ max_exp = STORAGE_UNITS.size - 1
442
+ exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
443
+ exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
444
+ number /= base ** exponent
445
+
446
+ unit_key = STORAGE_UNITS[exponent]
447
+ unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
448
+
449
+ formatted_number = self.number_to_rounded(number, options)
450
+ storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
451
+ end
452
+ end
453
+
454
+ # Pretty prints (formats and approximates) a number in a way it
455
+ # is more readable by humans (eg.: 1200000000 becomes "1.2
456
+ # Billion"). This is useful for numbers that can get very large
457
+ # (and too hard to read).
458
+ #
459
+ # See <tt>number_to_human_size</tt> if you want to print a file
460
+ # size.
461
+ #
462
+ # You can also define you own unit-quantifier names if you want
463
+ # to use other decimal units (eg.: 1500 becomes "1.5
464
+ # kilometers", 0.150 becomes "150 milliliters", etc). You may
465
+ # define a wide range of unit quantifiers, even fractional ones
466
+ # (centi, deci, mili, etc).
467
+ #
468
+ # ==== Options
469
+ #
470
+ # * <tt>:locale</tt> - Sets the locale to be used for formatting
471
+ # (defaults to current locale).
472
+ # * <tt>:precision</tt> - Sets the precision of the number
473
+ # (defaults to 3).
474
+ # * <tt>:significant</tt> - If +true+, precision will be the #
475
+ # of significant_digits. If +false+, the # of fractional
476
+ # digits (defaults to +true+)
477
+ # * <tt>:separator</tt> - Sets the separator between the
478
+ # fractional and integer digits (defaults to ".").
479
+ # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
480
+ # to "").
481
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
482
+ # insignificant zeros after the decimal separator (defaults to
483
+ # +true+)
484
+ # * <tt>:units</tt> - A Hash of unit quantifier names. Or a
485
+ # string containing an i18n scope where to find this hash. It
486
+ # might have the following keys:
487
+ # * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
488
+ # *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
489
+ # *<tt>:billion</tt>, <tt>:trillion</tt>,
490
+ # *<tt>:quadrillion</tt>
491
+ # * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
492
+ # *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
493
+ # *<tt>:pico</tt>, <tt>:femto</tt>
494
+ # * <tt>:format</tt> - Sets the format of the output string
495
+ # (defaults to "%n %u"). The field types are:
496
+ # * %u - The quantifier (ex.: 'thousand')
497
+ # * %n - The number
498
+ #
499
+ # ==== Examples
500
+ #
501
+ # number_to_human(123) # => "123"
502
+ # number_to_human(1234) # => "1.23 Thousand"
503
+ # number_to_human(12345) # => "12.3 Thousand"
504
+ # number_to_human(1234567) # => "1.23 Million"
505
+ # number_to_human(1234567890) # => "1.23 Billion"
506
+ # number_to_human(1234567890123) # => "1.23 Trillion"
507
+ # number_to_human(1234567890123456) # => "1.23 Quadrillion"
508
+ # number_to_human(1234567890123456789) # => "1230 Quadrillion"
509
+ # number_to_human(489939, precision: 2) # => "490 Thousand"
510
+ # number_to_human(489939, precision: 4) # => "489.9 Thousand"
511
+ # number_to_human(1234567, precision: 4,
512
+ # significant: false) # => "1.2346 Million"
513
+ # number_to_human(1234567, precision: 1,
514
+ # separator: ',',
515
+ # significant: false) # => "1,2 Million"
516
+ #
517
+ # Non-significant zeros after the decimal separator are stripped
518
+ # out by default (set <tt>:strip_insignificant_zeros</tt> to
519
+ # +false+ to change that):
520
+ #
521
+ # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
522
+ # number_to_human(500000000, precision: 5) # => "500 Million"
523
+ #
524
+ # ==== Custom Unit Quantifiers
525
+ #
526
+ # You can also use your own custom unit quantifiers:
527
+ # number_to_human(500000, units: { unit: 'ml', thousand: 'lt' }) # => "500 lt"
528
+ #
529
+ # If in your I18n locale you have:
530
+ #
531
+ # distance:
532
+ # centi:
533
+ # one: "centimeter"
534
+ # other: "centimeters"
535
+ # unit:
536
+ # one: "meter"
537
+ # other: "meters"
538
+ # thousand:
539
+ # one: "kilometer"
540
+ # other: "kilometers"
541
+ # billion: "gazillion-distance"
542
+ #
543
+ # Then you could do:
544
+ #
545
+ # number_to_human(543934, units: :distance) # => "544 kilometers"
546
+ # number_to_human(54393498, units: :distance) # => "54400 kilometers"
547
+ # number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
548
+ # number_to_human(343, units: :distance, precision: 1) # => "300 meters"
549
+ # number_to_human(1, units: :distance) # => "1 meter"
550
+ # number_to_human(0.34, units: :distance) # => "34 centimeters"
551
+ def number_to_human(number, options = {})
552
+ options = options.symbolize_keys
553
+
554
+ return number unless valid_float?(number)
555
+ number = Float(number)
556
+
557
+ defaults = format_options(options[:locale], :human)
558
+ options = defaults.merge!(options)
559
+
560
+ #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
561
+ options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
562
+
563
+ inverted_du = DECIMAL_UNITS.invert
564
+
565
+ units = options.delete :units
566
+ unit_exponents = case units
567
+ when Hash
568
+ units
569
+ when String, Symbol
570
+ I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
571
+ when nil
572
+ translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
573
+ else
574
+ raise ArgumentError, ":units must be a Hash or String translation scope."
575
+ end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
576
+
577
+ number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
578
+ display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
579
+ number /= 10 ** display_exponent
580
+
581
+ unit = case units
582
+ when Hash
583
+ units[DECIMAL_UNITS[display_exponent]] || ''
584
+ when String, Symbol
585
+ I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
586
+ else
587
+ translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
588
+ end
589
+
590
+ decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
591
+ formatted_number = self.number_to_rounded(number, options)
592
+ decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
593
+ end
594
+
595
+ def self.private_module_and_instance_method(method_name) #:nodoc:
596
+ private method_name
597
+ private_class_method method_name
598
+ end
599
+ private_class_method :private_module_and_instance_method
600
+
601
+ def format_options(locale, namespace = nil) #:nodoc:
602
+ default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
603
+ end
604
+ private_module_and_instance_method :format_options
605
+
606
+ def default_format_options(namespace = nil) #:nodoc:
607
+ options = DEFAULTS[:format].dup
608
+ options.merge!(DEFAULTS[namespace][:format]) if namespace
609
+ options
610
+ end
611
+ private_module_and_instance_method :default_format_options
612
+
613
+ def i18n_format_options(locale, namespace = nil) #:nodoc:
614
+ options = I18n.translate(:'number.format', locale: locale, default: {}).dup
615
+ if namespace
616
+ options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
617
+ end
618
+ options
619
+ end
620
+ private_module_and_instance_method :i18n_format_options
621
+
622
+ def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
623
+ default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
624
+
625
+ I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
626
+ end
627
+ private_module_and_instance_method :translate_number_value_with_default
628
+
629
+ def valid_float?(number) #:nodoc:
630
+ Float(number)
631
+ rescue ArgumentError, TypeError
632
+ false
633
+ end
634
+ private_module_and_instance_method :valid_float?
635
+ end
636
+ end