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
@@ -5,17 +5,18 @@ module ActiveSupport
5
5
 
6
6
  extend self
7
7
 
8
- # A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
8
+ # A list of all available normalization forms.
9
+ # See http://www.unicode.org/reports/tr15/tr15-29.html for more
9
10
  # information about normalization.
10
11
  NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
11
12
 
12
13
  # The Unicode version that is supported by the implementation
13
- UNICODE_VERSION = '5.2.0'
14
+ UNICODE_VERSION = '6.2.0'
14
15
 
15
- # The default normalization used for operations that require normalization. It can be set to any of the
16
- # normalizations in NORMALIZATION_FORMS.
16
+ # The default normalization used for operations that require
17
+ # normalization. It can be set to any of the normalizations
18
+ # in NORMALIZATION_FORMS.
17
19
  #
18
- # Example:
19
20
  # ActiveSupport::Multibyte::Unicode.default_normalization_form = :c
20
21
  attr_accessor :default_normalization_form
21
22
  @default_normalization_form = :kc
@@ -50,32 +51,22 @@ module ActiveSupport
50
51
  0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
51
52
  ].flatten.freeze
52
53
 
53
- # BOM (byte order mark) can also be seen as whitespace, it's a non-rendering character used to distinguish
54
- # between little and big endian. This is not an issue in utf-8, so it must be ignored.
54
+ # BOM (byte order mark) can also be seen as whitespace, it's a
55
+ # non-rendering character used to distinguish between little and big
56
+ # endian. This is not an issue in utf-8, so it must be ignored.
55
57
  LEADERS_AND_TRAILERS = WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
56
58
 
57
- # Returns a regular expression pattern that matches the passed Unicode codepoints
59
+ # Returns a regular expression pattern that matches the passed Unicode
60
+ # codepoints.
58
61
  def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
59
62
  array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
60
63
  end
61
64
  TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
62
65
  LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
63
66
 
64
- # Unpack the string at codepoints boundaries. Raises an EncodingError when the encoding of the string isn't
65
- # valid UTF-8.
66
- #
67
- # Example:
68
- # Unicode.u_unpack('Café') # => [67, 97, 102, 233]
69
- def u_unpack(string)
70
- begin
71
- string.unpack 'U*'
72
- rescue ArgumentError
73
- raise EncodingError, 'malformed UTF-8 character'
74
- end
75
- end
76
-
77
- # Detect whether the codepoint is in a certain character class. Returns +true+ when it's in the specified
78
- # character class and +false+ otherwise. Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
67
+ # Detect whether the codepoint is in a certain character class. Returns
68
+ # +true+ when it's in the specified character class and +false+ otherwise.
69
+ # Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
79
70
  # <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
80
71
  #
81
72
  # Primarily used by the grapheme cluster support.
@@ -83,13 +74,13 @@ module ActiveSupport
83
74
  classes.detect { |c| database.boundary[c] === codepoint } ? true : false
84
75
  end
85
76
 
86
- # Unpack the string at grapheme boundaries. Returns a list of character lists.
77
+ # Unpack the string at grapheme boundaries. Returns a list of character
78
+ # lists.
87
79
  #
88
- # Example:
89
- # Unicode.g_unpack('क्षि') # => [[2325, 2381], [2359], [2367]]
90
- # Unicode.g_unpack('Café') # => [[67], [97], [102], [233]]
91
- def g_unpack(string)
92
- codepoints = u_unpack(string)
80
+ # Unicode.unpack_graphemes('क्षि') # => [[2325, 2381], [2359], [2367]]
81
+ # Unicode.unpack_graphemes('Café') # => [[67], [97], [102], [233]]
82
+ def unpack_graphemes(string)
83
+ codepoints = string.codepoints.to_a
93
84
  unpacked = []
94
85
  pos = 0
95
86
  marker = 0
@@ -118,12 +109,11 @@ module ActiveSupport
118
109
  unpacked
119
110
  end
120
111
 
121
- # Reverse operation of g_unpack.
112
+ # Reverse operation of unpack_graphemes.
122
113
  #
123
- # Example:
124
- # Unicode.g_pack(Unicode.g_unpack('क्षि')) # => 'क्षि'
125
- def g_pack(unpacked)
126
- (unpacked.flatten).pack('U*')
114
+ # Unicode.pack_graphemes(Unicode.unpack_graphemes('क्षि')) # => 'क्षि'
115
+ def pack_graphemes(unpacked)
116
+ unpacked.flatten.pack('U*')
127
117
  end
128
118
 
129
119
  # Re-order codepoints so the string becomes canonical.
@@ -143,7 +133,7 @@ module ActiveSupport
143
133
  end
144
134
 
145
135
  # Decompose composed characters to the decomposed form.
146
- def decompose_codepoints(type, codepoints)
136
+ def decompose(type, codepoints)
147
137
  codepoints.inject([]) do |decomposed, cp|
148
138
  # if it's a hangul syllable starter character
149
139
  if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
@@ -156,7 +146,7 @@ module ActiveSupport
156
146
  decomposed.concat ncp
157
147
  # if the codepoint is decomposable in with the current decomposition type
158
148
  elsif (ncp = database.codepoints[cp].decomp_mapping) and (!database.codepoints[cp].decomp_type || type == :compatability)
159
- decomposed.concat decompose_codepoints(type, ncp.dup)
149
+ decomposed.concat decompose(type, ncp.dup)
160
150
  else
161
151
  decomposed << cp
162
152
  end
@@ -164,7 +154,7 @@ module ActiveSupport
164
154
  end
165
155
 
166
156
  # Compose decomposed characters to the composed form.
167
- def compose_codepoints(codepoints)
157
+ def compose(codepoints)
168
158
  pos = 0
169
159
  eoa = codepoints.length - 1
170
160
  starter_pos = 0
@@ -222,9 +212,11 @@ module ActiveSupport
222
212
  codepoints
223
213
  end
224
214
 
225
- # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
215
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
216
+ # resulting in a valid UTF-8 string.
226
217
  #
227
- # Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP1252 or ISO-8859-1.
218
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's
219
+ # encoding is entirely CP1252 or ISO-8859-1.
228
220
  def tidy_bytes(string, force = false)
229
221
  if force
230
222
  return string.unpack("C*").map do |b|
@@ -273,48 +265,54 @@ module ActiveSupport
273
265
  bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
274
266
  end
275
267
 
276
- # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
277
- # passing strings to databases and validations.
268
+ # Returns the KC normalization of the string by default. NFKC is
269
+ # considered the best normalization form for passing strings to databases
270
+ # and validations.
278
271
  #
279
272
  # * <tt>string</tt> - The string to perform normalization on.
280
- # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
281
- # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
282
- # ActiveSupport::Multibyte.default_normalization_form
273
+ # * <tt>form</tt> - The form you want to normalize in. Should be one of
274
+ # the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
275
+ # Default is ActiveSupport::Multibyte.default_normalization_form.
283
276
  def normalize(string, form=nil)
284
277
  form ||= @default_normalization_form
285
278
  # See http://www.unicode.org/reports/tr15, Table 1
286
- codepoints = u_unpack(string)
279
+ codepoints = string.codepoints.to_a
287
280
  case form
288
281
  when :d
289
- reorder_characters(decompose_codepoints(:canonical, codepoints))
282
+ reorder_characters(decompose(:canonical, codepoints))
290
283
  when :c
291
- compose_codepoints(reorder_characters(decompose_codepoints(:canonical, codepoints)))
284
+ compose(reorder_characters(decompose(:canonical, codepoints)))
292
285
  when :kd
293
- reorder_characters(decompose_codepoints(:compatability, codepoints))
286
+ reorder_characters(decompose(:compatability, codepoints))
294
287
  when :kc
295
- compose_codepoints(reorder_characters(decompose_codepoints(:compatability, codepoints)))
288
+ compose(reorder_characters(decompose(:compatability, codepoints)))
296
289
  else
297
290
  raise ArgumentError, "#{form} is not a valid normalization variant", caller
298
291
  end.pack('U*')
299
292
  end
300
293
 
301
- def apply_mapping(string, mapping) #:nodoc:
302
- u_unpack(string).map do |codepoint|
303
- cp = database.codepoints[codepoint]
304
- if cp and (ncp = cp.send(mapping)) and ncp > 0
305
- ncp
306
- else
307
- codepoint
308
- end
309
- end.pack('U*')
294
+ def downcase(string)
295
+ apply_mapping string, :lowercase_mapping
296
+ end
297
+
298
+ def upcase(string)
299
+ apply_mapping string, :uppercase_mapping
310
300
  end
311
301
 
312
- # Holds data about a codepoint in the Unicode database
302
+ def swapcase(string)
303
+ apply_mapping string, :swapcase_mapping
304
+ end
305
+
306
+ # Holds data about a codepoint in the Unicode database.
313
307
  class Codepoint
314
308
  attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
309
+
310
+ def swapcase_mapping
311
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
312
+ end
315
313
  end
316
314
 
317
- # Holds static data from the Unicode database
315
+ # Holds static data from the Unicode database.
318
316
  class UnicodeDatabase
319
317
  ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
320
318
 
@@ -338,11 +336,12 @@ module ActiveSupport
338
336
  EOS
339
337
  end
340
338
 
341
- # Loads the Unicode database and returns all the internal objects of UnicodeDatabase.
339
+ # Loads the Unicode database and returns all the internal objects of
340
+ # UnicodeDatabase.
342
341
  def load
343
342
  begin
344
343
  @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
345
- rescue Exception => e
344
+ rescue => e
346
345
  raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
347
346
  end
348
347
 
@@ -361,12 +360,12 @@ module ActiveSupport
361
360
  end
362
361
  end
363
362
 
364
- # Returns the directory in which the data files are stored
363
+ # Returns the directory in which the data files are stored.
365
364
  def self.dirname
366
365
  File.dirname(__FILE__) + '/../values/'
367
366
  end
368
367
 
369
- # Returns the filename for the data file for this version
368
+ # Returns the filename for the data file for this version.
370
369
  def self.filename
371
370
  File.expand_path File.join(dirname, "unicode_tables.dat")
372
371
  end
@@ -374,6 +373,17 @@ module ActiveSupport
374
373
 
375
374
  private
376
375
 
376
+ def apply_mapping(string, mapping) #:nodoc:
377
+ string.each_codepoint.map do |codepoint|
378
+ cp = database.codepoints[codepoint]
379
+ if cp and (ncp = cp.send(mapping)) and ncp > 0
380
+ ncp
381
+ else
382
+ codepoint
383
+ end
384
+ end.pack('U*')
385
+ end
386
+
377
387
  def tidy_byte(byte)
378
388
  if byte < 160
379
389
  [database.cp1252[byte] || byte].pack("U").unpack("C*")
@@ -1,19 +1,23 @@
1
+ require 'active_support/notifications/instrumenter'
2
+ require 'active_support/notifications/fanout'
3
+
1
4
  module ActiveSupport
2
5
  # = Notifications
3
6
  #
4
- # +ActiveSupport::Notifications+ provides an instrumentation API for Ruby.
7
+ # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
8
+ # Ruby.
5
9
  #
6
10
  # == Instrumenters
7
11
  #
8
12
  # To instrument an event you just need to do:
9
13
  #
10
- # ActiveSupport::Notifications.instrument("render", :extra => :information) do
11
- # render :text => "Foo"
14
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
15
+ # render text: 'Foo'
12
16
  # end
13
17
  #
14
18
  # That executes the block first and notifies all subscribers once done.
15
19
  #
16
- # In the example above "render" is the name of the event, and the rest is called
20
+ # In the example above +render+ is the name of the event, and the rest is called
17
21
  # the _payload_. The payload is a mechanism that allows instrumenters to pass
18
22
  # extra information to subscribers. Payloads consist of a hash whose contents
19
23
  # are arbitrary and generally depend on the event.
@@ -21,46 +25,83 @@ module ActiveSupport
21
25
  # == Subscribers
22
26
  #
23
27
  # You can consume those events and the information they provide by registering
24
- # a subscriber. For instance, let's store all "render" events in an array:
28
+ # a subscriber.
29
+ #
30
+ # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
31
+ # name # => String, name of the event (such as 'render' from above)
32
+ # start # => Time, when the instrumented block started execution
33
+ # finish # => Time, when the instrumented block ended execution
34
+ # id # => String, unique ID for this notification
35
+ # payload # => Hash, the payload
36
+ # end
37
+ #
38
+ # For instance, let's store all "render" events in an array:
25
39
  #
26
40
  # events = []
27
41
  #
28
- # ActiveSupport::Notifications.subscribe("render") do |*args|
42
+ # ActiveSupport::Notifications.subscribe('render') do |*args|
29
43
  # events << ActiveSupport::Notifications::Event.new(*args)
30
44
  # end
31
45
  #
32
46
  # That code returns right away, you are just subscribing to "render" events.
33
- # The block will be called asynchronously whenever someone instruments "render":
47
+ # The block is saved and will be called whenever someone instruments "render":
34
48
  #
35
- # ActiveSupport::Notifications.instrument("render", :extra => :information) do
36
- # render :text => "Foo"
49
+ # ActiveSupport::Notifications.instrument('render', extra: :information) do
50
+ # render text: 'Foo'
37
51
  # end
38
52
  #
39
53
  # event = events.first
40
54
  # event.name # => "render"
41
55
  # event.duration # => 10 (in milliseconds)
42
- # event.payload # => { :extra => :information }
56
+ # event.payload # => { extra: :information }
43
57
  #
44
- # The block in the +subscribe+ call gets the name of the event, start
58
+ # The block in the <tt>subscribe</tt> call gets the name of the event, start
45
59
  # timestamp, end timestamp, a string with a unique identifier for that event
46
60
  # (something like "535801666f04d0298cd6"), and a hash with the payload, in
47
61
  # that order.
48
62
  #
49
63
  # If an exception happens during that particular instrumentation the payload will
50
- # have a key +:exception+ with an array of two elements as value: a string with
64
+ # have a key <tt>:exception</tt> with an array of two elements as value: a string with
51
65
  # the name of the exception class, and the exception message.
52
66
  #
53
- # As the previous example depicts, the class +ActiveSupport::Notifications::Event+
67
+ # As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
54
68
  # is able to take the arguments as they come and provide an object-oriented
55
69
  # interface to that data.
56
70
  #
71
+ # It is also possible to pass an object as the second parameter passed to the
72
+ # <tt>subscribe</tt> method instead of a block:
73
+ #
74
+ # module ActionController
75
+ # class PageRequest
76
+ # def call(name, started, finished, unique_id, payload)
77
+ # Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
78
+ # end
79
+ # end
80
+ # end
81
+ #
82
+ # ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
83
+ #
84
+ # resulting in the following output within the logs including a hash with the payload:
85
+ #
86
+ # notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
87
+ # controller: "Devise::SessionsController",
88
+ # action: "new",
89
+ # params: {"action"=>"new", "controller"=>"devise/sessions"},
90
+ # format: :html,
91
+ # method: "GET",
92
+ # path: "/login/sign_in",
93
+ # status: 200,
94
+ # view_runtime: 279.3080806732178,
95
+ # db_runtime: 40.053
96
+ # }
97
+ #
57
98
  # You can also subscribe to all events whose name matches a certain regexp:
58
99
  #
59
100
  # ActiveSupport::Notifications.subscribe(/render/) do |*args|
60
101
  # ...
61
102
  # end
62
103
  #
63
- # and even pass no argument to +subscribe+, in which case you are subscribing
104
+ # and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
64
105
  # to all events.
65
106
  #
66
107
  # == Temporary Subscriptions
@@ -105,12 +146,6 @@ module ActiveSupport
105
146
  # to log subscribers in a thread. You can use any queue implementation you want.
106
147
  #
107
148
  module Notifications
108
- autoload :Instrumenter, 'active_support/notifications/instrumenter'
109
- autoload :Event, 'active_support/notifications/instrumenter'
110
- autoload :Fanout, 'active_support/notifications/fanout'
111
-
112
- @instrumenters = Hash.new { |h,k| h[k] = notifier.listening?(k) }
113
-
114
149
  class << self
115
150
  attr_accessor :notifier
116
151
 
@@ -119,7 +154,7 @@ module ActiveSupport
119
154
  end
120
155
 
121
156
  def instrument(name, payload = {})
122
- if @instrumenters[name]
157
+ if notifier.listening?(name)
123
158
  instrumenter.instrument(name, payload) { yield payload if block_given? }
124
159
  else
125
160
  yield payload if block_given?
@@ -127,9 +162,7 @@ module ActiveSupport
127
162
  end
128
163
 
129
164
  def subscribe(*args, &block)
130
- notifier.subscribe(*args, &block).tap do
131
- @instrumenters.clear
132
- end
165
+ notifier.subscribe(*args, &block)
133
166
  end
134
167
 
135
168
  def subscribed(callback, *args, &block)
@@ -141,7 +174,6 @@ module ActiveSupport
141
174
 
142
175
  def unsubscribe(args)
143
176
  notifier.unsubscribe(args)
144
- @instrumenters.clear
145
177
  end
146
178
 
147
179
  def instrumenter
@@ -1,24 +1,43 @@
1
+ require 'mutex_m'
2
+ require 'thread_safe'
3
+
1
4
  module ActiveSupport
2
5
  module Notifications
3
6
  # This is a default queue implementation that ships with Notifications.
4
7
  # It just pushes events to all registered log subscribers.
8
+ #
9
+ # This class is thread safe. All methods are reentrant.
5
10
  class Fanout
11
+ include Mutex_m
12
+
6
13
  def initialize
7
14
  @subscribers = []
8
- @listeners_for = {}
15
+ @listeners_for = ThreadSafe::Cache.new
16
+ super
9
17
  end
10
18
 
11
19
  def subscribe(pattern = nil, block = Proc.new)
12
- subscriber = Subscriber.new(pattern, block).tap do |s|
13
- @subscribers << s
20
+ subscriber = Subscribers.new pattern, block
21
+ synchronize do
22
+ @subscribers << subscriber
23
+ @listeners_for.clear
14
24
  end
15
- @listeners_for.clear
16
25
  subscriber
17
26
  end
18
27
 
19
28
  def unsubscribe(subscriber)
20
- @subscribers.reject! {|s| s.matches?(subscriber)}
21
- @listeners_for.clear
29
+ synchronize do
30
+ @subscribers.reject! { |s| s.matches?(subscriber) }
31
+ @listeners_for.clear
32
+ end
33
+ end
34
+
35
+ def start(name, id, payload)
36
+ listeners_for(name).each { |s| s.start(name, id, payload) }
37
+ end
38
+
39
+ def finish(name, id, payload)
40
+ listeners_for(name).each { |s| s.finish(name, id, payload) }
22
41
  end
23
42
 
24
43
  def publish(name, *args)
@@ -26,7 +45,11 @@ module ActiveSupport
26
45
  end
27
46
 
28
47
  def listeners_for(name)
29
- @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
48
+ # this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics)
49
+ @listeners_for[name] || synchronize do
50
+ # use synchronisation when accessing @subscribers
51
+ @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
52
+ end
30
53
  end
31
54
 
32
55
  def listening?(name)
@@ -37,23 +60,87 @@ module ActiveSupport
37
60
  def wait
38
61
  end
39
62
 
40
- class Subscriber #:nodoc:
41
- def initialize(pattern, delegate)
42
- @pattern = pattern
43
- @delegate = delegate
63
+ module Subscribers # :nodoc:
64
+ def self.new(pattern, listener)
65
+ if listener.respond_to?(:start) and listener.respond_to?(:finish)
66
+ subscriber = Evented.new pattern, listener
67
+ else
68
+ subscriber = Timed.new pattern, listener
69
+ end
70
+
71
+ unless pattern
72
+ AllMessages.new(subscriber)
73
+ else
74
+ subscriber
75
+ end
44
76
  end
45
77
 
46
- def publish(message, *args)
47
- @delegate.call(message, *args)
78
+ class Evented #:nodoc:
79
+ def initialize(pattern, delegate)
80
+ @pattern = pattern
81
+ @delegate = delegate
82
+ end
83
+
84
+ def start(name, id, payload)
85
+ @delegate.start name, id, payload
86
+ end
87
+
88
+ def finish(name, id, payload)
89
+ @delegate.finish name, id, payload
90
+ end
91
+
92
+ def subscribed_to?(name)
93
+ @pattern === name.to_s
94
+ end
95
+
96
+ def matches?(subscriber_or_name)
97
+ self === subscriber_or_name ||
98
+ @pattern && @pattern === subscriber_or_name
99
+ end
48
100
  end
49
101
 
50
- def subscribed_to?(name)
51
- !@pattern || @pattern === name.to_s
102
+ class Timed < Evented
103
+ def initialize(pattern, delegate)
104
+ @timestack = []
105
+ super
106
+ end
107
+
108
+ def publish(name, *args)
109
+ @delegate.call name, *args
110
+ end
111
+
112
+ def start(name, id, payload)
113
+ @timestack.push Time.now
114
+ end
115
+
116
+ def finish(name, id, payload)
117
+ started = @timestack.pop
118
+ @delegate.call(name, started, Time.now, id, payload)
119
+ end
52
120
  end
53
121
 
54
- def matches?(subscriber_or_name)
55
- self === subscriber_or_name ||
56
- @pattern && @pattern === subscriber_or_name
122
+ class AllMessages # :nodoc:
123
+ def initialize(delegate)
124
+ @delegate = delegate
125
+ end
126
+
127
+ def start(name, id, payload)
128
+ @delegate.start name, id, payload
129
+ end
130
+
131
+ def finish(name, id, payload)
132
+ @delegate.finish name, id, payload
133
+ end
134
+
135
+ def publish(name, *args)
136
+ @delegate.publish name, *args
137
+ end
138
+
139
+ def subscribed_to?(name)
140
+ true
141
+ end
142
+
143
+ alias :matches? :===
57
144
  end
58
145
  end
59
146
  end