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
@@ -1,40 +1,57 @@
1
- require 'active_support/secure_random'
2
- require 'active_support/core_ext/module/delegation'
1
+ require 'securerandom'
3
2
 
4
3
  module ActiveSupport
5
4
  module Notifications
5
+ # Instrumenters are stored in a thread local.
6
6
  class Instrumenter
7
7
  attr_reader :id
8
8
 
9
9
  def initialize(notifier)
10
- @id = unique_id
10
+ @id = unique_id
11
11
  @notifier = notifier
12
12
  end
13
13
 
14
14
  # Instrument the given block by measuring the time taken to execute it
15
15
  # and publish it. Notice that events get sent even if an error occurs
16
- # in the passed-in block
16
+ # in the passed-in block.
17
17
  def instrument(name, payload={})
18
- started = Time.now
19
-
18
+ # some of the listeners might have state
19
+ listeners_state = start name, payload
20
20
  begin
21
- yield
21
+ yield payload
22
22
  rescue Exception => e
23
23
  payload[:exception] = [e.class.name, e.message]
24
+ payload[:exception_object] = e
24
25
  raise e
25
26
  ensure
26
- @notifier.publish(name, started, Time.now, @id, payload)
27
+ finish_with_state listeners_state, name, payload
27
28
  end
28
29
  end
29
30
 
31
+ # Send a start notification with +name+ and +payload+.
32
+ def start(name, payload)
33
+ @notifier.start name, @id, payload
34
+ end
35
+
36
+ # Send a finish notification with +name+ and +payload+.
37
+ def finish(name, payload)
38
+ @notifier.finish name, @id, payload
39
+ end
40
+
41
+ def finish_with_state(listeners_state, name, payload)
42
+ @notifier.finish name, @id, payload, listeners_state
43
+ end
44
+
30
45
  private
31
- def unique_id
32
- ::SecureRandom.hex(10)
33
- end
46
+
47
+ def unique_id
48
+ SecureRandom.hex(10)
49
+ end
34
50
  end
35
51
 
36
52
  class Event
37
- attr_reader :name, :time, :end, :transaction_id, :payload, :duration
53
+ attr_reader :name, :time, :transaction_id, :payload, :children
54
+ attr_accessor :end
38
55
 
39
56
  def initialize(name, start, ending, transaction_id, payload)
40
57
  @name = name
@@ -42,12 +59,32 @@ module ActiveSupport
42
59
  @time = start
43
60
  @transaction_id = transaction_id
44
61
  @end = ending
45
- @duration = 1000.0 * (@end - @time)
62
+ @children = []
63
+ @duration = nil
64
+ end
65
+
66
+ # Returns the difference in milliseconds between when the execution of the
67
+ # event started and when it ended.
68
+ #
69
+ # ActiveSupport::Notifications.subscribe('wait') do |*args|
70
+ # @event = ActiveSupport::Notifications::Event.new(*args)
71
+ # end
72
+ #
73
+ # ActiveSupport::Notifications.instrument('wait') do
74
+ # sleep 1
75
+ # end
76
+ #
77
+ # @event.duration # => 1000.138
78
+ def duration
79
+ @duration ||= 1000.0 * (self.end - time)
80
+ end
81
+
82
+ def <<(event)
83
+ @children << event
46
84
  end
47
85
 
48
86
  def parent_of?(event)
49
- start = (time - event.time) * 1000
50
- start <= 0 && (start + duration >= event.duration)
87
+ @children.include? event
51
88
  end
52
89
  end
53
90
  end
@@ -1,46 +1,157 @@
1
+ require 'active_support/notifications/instrumenter'
2
+ require 'active_support/notifications/fanout'
3
+ require 'active_support/per_thread_registry'
4
+
1
5
  module ActiveSupport
2
- # Notifications provides an instrumentation API for Ruby. To instrument an
3
- # action in Ruby you just need to do:
6
+ # = Notifications
7
+ #
8
+ # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
9
+ # Ruby.
10
+ #
11
+ # == Instrumenters
12
+ #
13
+ # To instrument an event you just need to do:
4
14
  #
5
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
6
- # render :text => "Foo"
15
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
16
+ # render text: 'Foo'
7
17
  # end
8
18
  #
19
+ # That first executes the block and then notifies all subscribers once done.
20
+ #
21
+ # In the example above +render+ is the name of the event, and the rest is called
22
+ # the _payload_. The payload is a mechanism that allows instrumenters to pass
23
+ # extra information to subscribers. Payloads consist of a hash whose contents
24
+ # are arbitrary and generally depend on the event.
25
+ #
26
+ # == Subscribers
27
+ #
9
28
  # You can consume those events and the information they provide by registering
10
- # a log subscriber. For instance, let's store all instrumented events in an array:
29
+ # a subscriber.
11
30
  #
12
- # @events = []
31
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
32
+ # name # => String, name of the event (such as 'render' from above)
33
+ # start # => Time, when the instrumented block started execution
34
+ # finish # => Time, when the instrumented block ended execution
35
+ # id # => String, unique ID for this notification
36
+ # payload # => Hash, the payload
37
+ # end
38
+ #
39
+ # For instance, let's store all "render" events in an array:
40
+ #
41
+ # events = []
13
42
  #
14
- # ActiveSupport::Notifications.subscribe do |*args|
15
- # @events << ActiveSupport::Notifications::Event.new(*args)
43
+ # ActiveSupport::Notifications.subscribe('render') do |*args|
44
+ # events << ActiveSupport::Notifications::Event.new(*args)
16
45
  # end
17
46
  #
18
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
19
- # render :text => "Foo"
47
+ # That code returns right away, you are just subscribing to "render" events.
48
+ # The block is saved and will be called whenever someone instruments "render":
49
+ #
50
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
51
+ # render text: 'Foo'
20
52
  # end
21
53
  #
22
- # event = @events.first
23
- # event.name # => :render
54
+ # event = events.first
55
+ # event.name # => "render"
24
56
  # event.duration # => 10 (in milliseconds)
25
- # event.payload # => { :extra => :information }
57
+ # event.payload # => { extra: :information }
58
+ #
59
+ # The block in the <tt>subscribe</tt> call gets the name of the event, start
60
+ # timestamp, end timestamp, a string with a unique identifier for that event
61
+ # (something like "535801666f04d0298cd6"), and a hash with the payload, in
62
+ # that order.
63
+ #
64
+ # If an exception happens during that particular instrumentation the payload will
65
+ # have a key <tt>:exception</tt> with an array of two elements as value: a string with
66
+ # the name of the exception class, and the exception message.
26
67
  #
27
- # When subscribing to Notifications, you can pass a pattern, to only consume
28
- # events that match the pattern:
68
+ # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
69
+ # is able to take the arguments as they come and provide an object-oriented
70
+ # interface to that data.
29
71
  #
30
- # ActiveSupport::Notifications.subscribe(/render/) do |event|
31
- # @render_events << event
72
+ # It is also possible to pass an object which responds to <tt>call</tt> method
73
+ # as the second parameter to the <tt>subscribe</tt> method instead of a block:
74
+ #
75
+ # module ActionController
76
+ # class PageRequest
77
+ # def call(name, started, finished, unique_id, payload)
78
+ # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
79
+ # end
80
+ # end
81
+ # end
82
+ #
83
+ # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
84
+ #
85
+ # resulting in the following output within the logs including a hash with the payload:
86
+ #
87
+ # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
88
+ # controller: "Devise::SessionsController",
89
+ # action: "new",
90
+ # params: {"action"=>"new", "controller"=>"devise/sessions"},
91
+ # format: :html,
92
+ # method: "GET",
93
+ # path: "/login/sign_in",
94
+ # status: 200,
95
+ # view_runtime: 279.3080806732178,
96
+ # db_runtime: 40.053
97
+ # }
98
+ #
99
+ # You can also subscribe to all events whose name matches a certain regexp:
100
+ #
101
+ # ActiveSupport::Notifications.subscribe(/render/) do |*args|
102
+ # ...
32
103
  # end
33
104
  #
34
- # Notifications ships with a queue implementation that consumes and publish events
35
- # to log subscribers in a thread. You can use any queue implementation you want.
105
+ # and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
106
+ # to all events.
107
+ #
108
+ # == Temporary Subscriptions
109
+ #
110
+ # Sometimes you do not want to subscribe to an event for the entire life of
111
+ # the application. There are two ways to unsubscribe.
112
+ #
113
+ # WARNING: The instrumentation framework is designed for long-running subscribers,
114
+ # use this feature sparingly because it wipes some internal caches and that has
115
+ # a negative impact on performance.
116
+ #
117
+ # === Subscribe While a Block Runs
118
+ #
119
+ # You can subscribe to some event temporarily while some block runs. For
120
+ # example, in
121
+ #
122
+ # callback = lambda {|*args| ... }
123
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
124
+ # ...
125
+ # end
126
+ #
127
+ # the callback will be called for all "sql.active_record" events instrumented
128
+ # during the execution of the block. The callback is unsubscribed automatically
129
+ # after that.
130
+ #
131
+ # === Manual Unsubscription
132
+ #
133
+ # The +subscribe+ method returns a subscriber object:
134
+ #
135
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
136
+ # ...
137
+ # end
138
+ #
139
+ # To prevent that block from being called anymore, just unsubscribe passing
140
+ # that reference:
141
+ #
142
+ # ActiveSupport::Notifications.unsubscribe(subscriber)
143
+ #
144
+ # You can also unsubscribe by passing the name of the subscriber object. Note
145
+ # that this will unsubscribe all subscriptions with the given name:
146
+ #
147
+ # ActiveSupport::Notifications.unsubscribe("render")
148
+ #
149
+ # == Default Queue
150
+ #
151
+ # Notifications ships with a queue implementation that consumes and publishes events
152
+ # to all log subscribers. You can use any queue implementation you want.
36
153
  #
37
154
  module Notifications
38
- autoload :Instrumenter, 'active_support/notifications/instrumenter'
39
- autoload :Event, 'active_support/notifications/instrumenter'
40
- autoload :Fanout, 'active_support/notifications/fanout'
41
-
42
- @instrumenters = Hash.new { |h,k| h[k] = notifier.listening?(k) }
43
-
44
155
  class << self
45
156
  attr_accessor :notifier
46
157
 
@@ -49,7 +160,7 @@ module ActiveSupport
49
160
  end
50
161
 
51
162
  def instrument(name, payload = {})
52
- if @instrumenters[name]
163
+ if notifier.listening?(name)
53
164
  instrumenter.instrument(name, payload) { yield payload if block_given? }
54
165
  else
55
166
  yield payload if block_given?
@@ -57,18 +168,42 @@ module ActiveSupport
57
168
  end
58
169
 
59
170
  def subscribe(*args, &block)
60
- notifier.subscribe(*args, &block).tap do
61
- @instrumenters.clear
62
- end
171
+ notifier.subscribe(*args, &block)
172
+ end
173
+
174
+ def subscribed(callback, *args, &block)
175
+ subscriber = subscribe(*args, &callback)
176
+ yield
177
+ ensure
178
+ unsubscribe(subscriber)
63
179
  end
64
180
 
65
- def unsubscribe(args)
66
- notifier.unsubscribe(args)
67
- @instrumenters.clear
181
+ def unsubscribe(subscriber_or_name)
182
+ notifier.unsubscribe(subscriber_or_name)
68
183
  end
69
184
 
70
185
  def instrumenter
71
- Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
186
+ InstrumentationRegistry.instance.instrumenter_for(notifier)
187
+ end
188
+ end
189
+
190
+ # This class is a registry which holds all of the +Instrumenter+ objects
191
+ # in a particular thread local. To access the +Instrumenter+ object for a
192
+ # particular +notifier+, you can call the following method:
193
+ #
194
+ # InstrumentationRegistry.instrumenter_for(notifier)
195
+ #
196
+ # The instrumenters for multiple notifiers are held in a single instance of
197
+ # this class.
198
+ class InstrumentationRegistry # :nodoc:
199
+ extend ActiveSupport::PerThreadRegistry
200
+
201
+ def initialize
202
+ @registry = {}
203
+ end
204
+
205
+ def instrumenter_for(notifier)
206
+ @registry[notifier] ||= Instrumenter.new(notifier)
72
207
  end
73
208
  end
74
209
 
@@ -0,0 +1,182 @@
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
+ require 'active_support/core_ext/class/attribute'
6
+
7
+ module ActiveSupport
8
+ module NumberHelper
9
+ class NumberConverter # :nodoc:
10
+ # Default and i18n option namespace per class
11
+ class_attribute :namespace
12
+
13
+ # Does the object need a number that is a valid float?
14
+ class_attribute :validate_float
15
+
16
+ attr_reader :number, :opts
17
+
18
+ DEFAULTS = {
19
+ # Used in number_to_delimited
20
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
21
+ format: {
22
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
23
+ separator: ".",
24
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
25
+ delimiter: ",",
26
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
27
+ precision: 3,
28
+ # If set to true, precision will mean the number of significant digits instead
29
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
30
+ significant: false,
31
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
32
+ strip_insignificant_zeros: false
33
+ },
34
+
35
+ # Used in number_to_currency
36
+ currency: {
37
+ format: {
38
+ format: "%u%n",
39
+ negative_format: "-%u%n",
40
+ unit: "$",
41
+ # These five are to override number.format and are optional
42
+ separator: ".",
43
+ delimiter: ",",
44
+ precision: 2,
45
+ significant: false,
46
+ strip_insignificant_zeros: false
47
+ }
48
+ },
49
+
50
+ # Used in number_to_percentage
51
+ percentage: {
52
+ format: {
53
+ delimiter: "",
54
+ format: "%n%"
55
+ }
56
+ },
57
+
58
+ # Used in number_to_rounded
59
+ precision: {
60
+ format: {
61
+ delimiter: ""
62
+ }
63
+ },
64
+
65
+ # Used in number_to_human_size and number_to_human
66
+ human: {
67
+ format: {
68
+ # These five are to override number.format and are optional
69
+ delimiter: "",
70
+ precision: 3,
71
+ significant: true,
72
+ strip_insignificant_zeros: true
73
+ },
74
+ # Used in number_to_human_size
75
+ storage_units: {
76
+ # Storage units output formatting.
77
+ # %u is the storage unit, %n is the number (default: 2 MB)
78
+ format: "%n %u",
79
+ units: {
80
+ byte: "Bytes",
81
+ kb: "KB",
82
+ mb: "MB",
83
+ gb: "GB",
84
+ tb: "TB"
85
+ }
86
+ },
87
+ # Used in number_to_human
88
+ decimal_units: {
89
+ format: "%n %u",
90
+ # Decimal units output formatting
91
+ # By default we will only quantify some of the exponents
92
+ # but the commented ones might be defined or overridden
93
+ # by the user.
94
+ units: {
95
+ # femto: Quadrillionth
96
+ # pico: Trillionth
97
+ # nano: Billionth
98
+ # micro: Millionth
99
+ # mili: Thousandth
100
+ # centi: Hundredth
101
+ # deci: Tenth
102
+ unit: "",
103
+ # ten:
104
+ # one: Ten
105
+ # other: Tens
106
+ # hundred: Hundred
107
+ thousand: "Thousand",
108
+ million: "Million",
109
+ billion: "Billion",
110
+ trillion: "Trillion",
111
+ quadrillion: "Quadrillion"
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ def self.convert(number, options)
118
+ new(number, options).execute
119
+ end
120
+
121
+ def initialize(number, options)
122
+ @number = number
123
+ @opts = options.symbolize_keys
124
+ end
125
+
126
+ def execute
127
+ if !number
128
+ nil
129
+ elsif validate_float? && !valid_float?
130
+ number
131
+ else
132
+ convert
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def options
139
+ @options ||= format_options.merge(opts)
140
+ end
141
+
142
+ def format_options #:nodoc:
143
+ default_format_options.merge!(i18n_format_options)
144
+ end
145
+
146
+ def default_format_options #:nodoc:
147
+ options = DEFAULTS[:format].dup
148
+ options.merge!(DEFAULTS[namespace][:format]) if namespace
149
+ options
150
+ end
151
+
152
+ def i18n_format_options #:nodoc:
153
+ locale = opts[:locale]
154
+ options = I18n.translate(:'number.format', locale: locale, default: {}).dup
155
+
156
+ if namespace
157
+ options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
158
+ end
159
+
160
+ options
161
+ end
162
+
163
+ def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
164
+ I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
165
+ end
166
+
167
+ def translate_in_locale(key, i18n_options = {})
168
+ translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
169
+ end
170
+
171
+ def default_value(key)
172
+ key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
173
+ end
174
+
175
+ def valid_float? #:nodoc:
176
+ Float(number)
177
+ rescue ArgumentError, TypeError
178
+ false
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,44 @@
1
+ require 'active_support/core_ext/numeric/inquiry'
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class NumberToCurrencyConverter < NumberConverter # :nodoc:
6
+ self.namespace = :currency
7
+
8
+ def convert
9
+ number = self.number.to_s.strip
10
+ format = options[:format]
11
+
12
+ if number.to_f.negative?
13
+ format = options[:negative_format]
14
+ number = absolute_value(number)
15
+ end
16
+
17
+ rounded_number = NumberToRoundedConverter.convert(number, options)
18
+ format.gsub('%n'.freeze, rounded_number).gsub('%u'.freeze, options[:unit])
19
+ end
20
+
21
+ private
22
+
23
+ def absolute_value(number)
24
+ number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, '')
25
+ end
26
+
27
+ def options
28
+ @options ||= begin
29
+ defaults = default_format_options.merge(i18n_opts)
30
+ # Override negative format if format options are given
31
+ defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
32
+ defaults.merge!(opts)
33
+ end
34
+ end
35
+
36
+ def i18n_opts
37
+ # Set International negative format if it does not exist
38
+ i18n = i18n_format_options
39
+ i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
40
+ i18n
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToDelimitedConverter < NumberConverter #:nodoc:
4
+ self.validate_float = true
5
+
6
+ DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
7
+
8
+ def convert
9
+ parts.join(options[:separator])
10
+ end
11
+
12
+ private
13
+
14
+ def parts
15
+ left, right = number.to_s.split('.'.freeze)
16
+ left.gsub!(delimiter_pattern) do |digit_to_delimit|
17
+ "#{digit_to_delimit}#{options[:delimiter]}"
18
+ end
19
+ [left, right].compact
20
+ end
21
+
22
+ def delimiter_pattern
23
+ options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToHumanConverter < NumberConverter # :nodoc:
4
+ DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
5
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
6
+ INVERTED_DECIMAL_UNITS = DECIMAL_UNITS.invert
7
+
8
+ self.namespace = :human
9
+ self.validate_float = true
10
+
11
+ def convert # :nodoc:
12
+ @number = Float(number)
13
+
14
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
15
+ unless options.key?(:strip_insignificant_zeros)
16
+ options[:strip_insignificant_zeros] = true
17
+ end
18
+
19
+ units = opts[:units]
20
+ exponent = calculate_exponent(units)
21
+ @number = number / (10 ** exponent)
22
+
23
+ until (rounded_number = NumberToRoundedConverter.convert(number, options)) != NumberToRoundedConverter.convert(1000, options)
24
+ @number = number / 1000.0
25
+ exponent += 3
26
+ end
27
+ unit = determine_unit(units, exponent)
28
+ format.gsub('%n'.freeze, rounded_number).gsub('%u'.freeze, unit).strip
29
+ end
30
+
31
+ private
32
+
33
+ def format
34
+ options[:format] || translate_in_locale('human.decimal_units.format')
35
+ end
36
+
37
+ def determine_unit(units, exponent)
38
+ exp = DECIMAL_UNITS[exponent]
39
+ case units
40
+ when Hash
41
+ units[exp] || ''
42
+ when String, Symbol
43
+ I18n.translate("#{units}.#{exp}", :locale => options[:locale], :count => number.to_i)
44
+ else
45
+ translate_in_locale("human.decimal_units.units.#{exp}", count: number.to_i)
46
+ end
47
+ end
48
+
49
+ def calculate_exponent(units)
50
+ exponent = number != 0 ? Math.log10(number.abs).floor : 0
51
+ unit_exponents(units).find { |e| exponent >= e } || 0
52
+ end
53
+
54
+ def unit_exponents(units)
55
+ case units
56
+ when Hash
57
+ units
58
+ when String, Symbol
59
+ I18n.translate(units.to_s, :locale => options[:locale], :raise => true)
60
+ when nil
61
+ translate_in_locale("human.decimal_units.units", raise: true)
62
+ else
63
+ raise ArgumentError, ":units must be a Hash or String translation scope."
64
+ end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by(&:-@)
65
+ end
66
+ end
67
+ end
68
+ end