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,20 +1,34 @@
1
1
  module ActiveSupport
2
- module Deprecation
3
- class << self
2
+ class Deprecation
3
+ module Reporting
4
+ # Whether to print a message (silent mode)
4
5
  attr_accessor :silenced
6
+ # Name of gem where method is deprecated
7
+ attr_accessor :gem_name
5
8
 
6
- # Outputs a deprecation warning to the output configured by <tt>ActiveSupport::Deprecation.behavior</tt>
9
+ # Outputs a deprecation warning to the output configured by
10
+ # <tt>ActiveSupport::Deprecation.behavior</tt>.
7
11
  #
8
- # ActiveSupport::Deprecation.warn("something broke!")
12
+ # ActiveSupport::Deprecation.warn('something broke!')
9
13
  # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
10
- def warn(message = nil, callstack = caller)
14
+ def warn(message = nil, callstack = nil)
11
15
  return if silenced
16
+
17
+ callstack ||= caller(2)
12
18
  deprecation_message(callstack, message).tap do |m|
13
19
  behavior.each { |b| b.call(m, callstack) }
14
20
  end
15
21
  end
16
22
 
17
23
  # Silence deprecation warnings within the block.
24
+ #
25
+ # ActiveSupport::Deprecation.warn('something broke!')
26
+ # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
27
+ #
28
+ # ActiveSupport::Deprecation.silence do
29
+ # ActiveSupport::Deprecation.warn('something broke!')
30
+ # end
31
+ # # => nil
18
32
  def silence
19
33
  old_silenced, @silenced = @silenced, true
20
34
  yield
@@ -22,16 +36,31 @@ module ActiveSupport
22
36
  @silenced = old_silenced
23
37
  end
24
38
 
25
- def deprecated_method_warning(method_name, message = nil)
26
- warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}"
27
- case message
28
- when Symbol then "#{warning} (use #{message} instead)"
29
- when String then "#{warning} (#{message})"
30
- else warning
39
+ def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
40
+ caller_backtrace ||= caller(2)
41
+ deprecated_method_warning(deprecated_method_name, message).tap do |msg|
42
+ warn(msg, caller_backtrace)
31
43
  end
32
44
  end
33
45
 
34
46
  private
47
+ # Outputs a deprecation warning message
48
+ #
49
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
50
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
51
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
52
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
53
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, "Optional message")
54
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
55
+ def deprecated_method_warning(method_name, message = nil)
56
+ warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
57
+ case message
58
+ when Symbol then "#{warning} (use #{message} instead)"
59
+ when String then "#{warning} (#{message})"
60
+ else warning
61
+ end
62
+ end
63
+
35
64
  def deprecation_message(callstack, message = nil)
36
65
  message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
37
66
  message += '.' unless message =~ /\.$/
@@ -2,35 +2,50 @@ module ActiveSupport
2
2
  # This module provides an internal implementation to track descendants
3
3
  # which is faster than iterating through ObjectSpace.
4
4
  module DescendantsTracker
5
- @@direct_descendants = Hash.new { |h, k| h[k] = [] }
5
+ @@direct_descendants = {}
6
6
 
7
- def self.direct_descendants(klass)
8
- @@direct_descendants[klass]
9
- end
7
+ class << self
8
+ def direct_descendants(klass)
9
+ @@direct_descendants[klass] || []
10
+ end
10
11
 
11
- def self.descendants(klass)
12
- @@direct_descendants[klass].inject([]) do |descendants, _klass|
13
- descendants << _klass
14
- descendants.concat _klass.descendants
12
+ def descendants(klass)
13
+ arr = []
14
+ accumulate_descendants(klass, arr)
15
+ arr
15
16
  end
16
- end
17
17
 
18
- def self.clear
19
- if defined? ActiveSupport::Dependencies
20
- @@direct_descendants.each do |klass, descendants|
21
- if ActiveSupport::Dependencies.autoloaded?(klass)
22
- @@direct_descendants.delete(klass)
23
- else
24
- descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
18
+ def clear
19
+ if defined? ActiveSupport::Dependencies
20
+ @@direct_descendants.each do |klass, descendants|
21
+ if ActiveSupport::Dependencies.autoloaded?(klass)
22
+ @@direct_descendants.delete(klass)
23
+ else
24
+ descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
25
+ end
25
26
  end
27
+ else
28
+ @@direct_descendants.clear
29
+ end
30
+ end
31
+
32
+ # This is the only method that is not thread safe, but is only ever called
33
+ # during the eager loading phase.
34
+ def store_inherited(klass, descendant)
35
+ (@@direct_descendants[klass] ||= []) << descendant
36
+ end
37
+
38
+ private
39
+ def accumulate_descendants(klass, acc)
40
+ if direct_descendants = @@direct_descendants[klass]
41
+ acc.concat(direct_descendants)
42
+ direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
26
43
  end
27
- else
28
- @@direct_descendants.clear
29
44
  end
30
45
  end
31
46
 
32
47
  def inherited(base)
33
- self.direct_descendants << base
48
+ DescendantsTracker.store_inherited(self, base)
34
49
  super
35
50
  end
36
51
 
@@ -1,16 +1,14 @@
1
- require 'active_support/basic_object'
1
+ require 'active_support/proxy_object'
2
2
  require 'active_support/core_ext/array/conversions'
3
3
  require 'active_support/core_ext/object/acts_like'
4
4
 
5
5
  module ActiveSupport
6
6
  # Provides accurate date and time measurements using Date#advance and
7
7
  # Time#advance, respectively. It mainly supports the methods on Numeric.
8
- # Example:
9
8
  #
10
- # 1.month.ago # equivalent to Time.now.advance(:months => -1)
11
- class Duration < BasicObject
9
+ # 1.month.ago # equivalent to Time.now.advance(months: -1)
10
+ class Duration < ProxyObject
12
11
  attr_accessor :value, :parts
13
- delegate :duplicable?, :to => :value # required when using ActiveSupport's BasicObject on 1.8
14
12
 
15
13
  def initialize(value, parts) #:nodoc:
16
14
  @value, @parts = value, parts
@@ -41,8 +39,8 @@ module ActiveSupport
41
39
  end
42
40
  alias :kind_of? :is_a?
43
41
 
44
- # Returns true if <tt>other</tt> is also a Duration instance with the
45
- # same <tt>value</tt>, or if <tt>other == value</tt>.
42
+ # Returns +true+ if +other+ is also a Duration instance with the
43
+ # same +value+, or if <tt>other == value</tt>.
46
44
  def ==(other)
47
45
  if Duration === other
48
46
  other.value == value
@@ -72,7 +70,7 @@ module ActiveSupport
72
70
  alias :until :ago
73
71
 
74
72
  def inspect #:nodoc:
75
- consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
73
+ consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
76
74
  parts = [:years, :months, :days, :minutes, :seconds].map do |length|
77
75
  n = consolidated[length]
78
76
  "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
@@ -1,26 +1,21 @@
1
- require "active_support/core_ext/array/wrap"
2
- require "active_support/core_ext/array/extract_options"
3
-
4
1
  module ActiveSupport
5
- # \FileUpdateChecker specifies the API used by Rails to watch files
2
+ # FileUpdateChecker specifies the API used by Rails to watch files
6
3
  # and control reloading. The API depends on four methods:
7
4
  #
8
5
  # * +initialize+ which expects two parameters and one block as
9
- # described below;
6
+ # described below.
10
7
  #
11
8
  # * +updated?+ which returns a boolean if there were updates in
12
- # the filesystem or not;
9
+ # the filesystem or not.
13
10
  #
14
11
  # * +execute+ which executes the given block on initialization
15
- # and updates the counter to the latest timestamp;
12
+ # and updates the latest watched files and timestamp.
16
13
  #
17
- # * +execute_if_updated+ which just executes the block if it was updated;
14
+ # * +execute_if_updated+ which just executes the block if it was updated.
18
15
  #
19
16
  # After initialization, a call to +execute_if_updated+ must execute
20
17
  # the block only if there was really a change in the filesystem.
21
18
  #
22
- # == Examples
23
- #
24
19
  # This class is used by Rails to reload the I18n framework whenever
25
20
  # they are changed upon a new request.
26
21
  #
@@ -31,52 +26,55 @@ module ActiveSupport
31
26
  # ActionDispatch::Reloader.to_prepare do
32
27
  # i18n_reloader.execute_if_updated
33
28
  # end
34
- #
35
29
  class FileUpdateChecker
36
30
  # It accepts two parameters on initialization. The first is an array
37
31
  # of files and the second is an optional hash of directories. The hash must
38
32
  # have directories as keys and the value is an array of extensions to be
39
33
  # watched under that directory.
40
34
  #
41
- # This method must also receive a block that will be called once a path changes.
42
- #
43
- # == Implementation details
44
- #
45
- # This particular implementation checks for added and updated files,
46
- # but not removed files. Directories lookup are compiled to a glob for
47
- # performance. Therefore, while someone can add new files to the +files+
48
- # array after initialization (and parts of Rails do depend on this feature),
49
- # adding new directories after initialization is not allowed.
50
- #
51
- # Notice that other objects that implements FileUpdateChecker API may
52
- # not even allow new files to be added after initialization. If this
53
- # is the case, we recommend freezing the +files+ after initialization to
54
- # avoid changes that won't make effect.
35
+ # This method must also receive a block that will be called once a path
36
+ # changes. The array of files and list of directories cannot be changed
37
+ # after FileUpdateChecker has been initialized.
55
38
  def initialize(files, dirs={}, &block)
56
- @files = files
39
+ @files = files.freeze
57
40
  @glob = compile_glob(dirs)
58
41
  @block = block
42
+
43
+ @watched = nil
59
44
  @updated_at = nil
60
- @last_update_at = updated_at
45
+
46
+ @last_watched = watched
47
+ @last_update_at = updated_at(@last_watched)
61
48
  end
62
49
 
63
- # Check if any of the entries were updated. If so, the updated_at
64
- # value is cached until the block is executed via +execute+ or +execute_if_updated+
50
+ # Check if any of the entries were updated. If so, the watched and/or
51
+ # updated_at values are cached until the block is executed via +execute+
52
+ # or +execute_if_updated+.
65
53
  def updated?
66
- current_updated_at = updated_at
67
- if @last_update_at < current_updated_at
68
- @updated_at = updated_at
54
+ current_watched = watched
55
+ if @last_watched.size != current_watched.size
56
+ @watched = current_watched
69
57
  true
70
58
  else
71
- false
59
+ current_updated_at = updated_at(current_watched)
60
+ if @last_update_at < current_updated_at
61
+ @watched = current_watched
62
+ @updated_at = current_updated_at
63
+ true
64
+ else
65
+ false
66
+ end
72
67
  end
73
68
  end
74
69
 
75
- # Executes the given block and updates the counter to latest timestamp.
70
+ # Executes the given block and updates the latest watched files and
71
+ # timestamp.
76
72
  def execute
77
- @last_update_at = updated_at
73
+ @last_watched = watched
74
+ @last_update_at = updated_at(@last_watched)
78
75
  @block.call
79
76
  ensure
77
+ @watched = nil
80
78
  @updated_at = nil
81
79
  end
82
80
 
@@ -92,28 +90,46 @@ module ActiveSupport
92
90
 
93
91
  private
94
92
 
95
- def updated_at #:nodoc:
96
- @updated_at || begin
97
- all = []
98
- all.concat @files.select { |f| File.exists?(f) }
99
- all.concat Dir[@glob] if @glob
100
- all.map { |path| File.mtime(path) }.max || Time.at(0)
93
+ def watched
94
+ @watched || begin
95
+ all = @files.select { |f| File.exists?(f) }
96
+ all.concat(Dir[@glob]) if @glob
97
+ all
101
98
  end
102
99
  end
103
100
 
104
- def compile_glob(hash) #:nodoc:
101
+ def updated_at(paths)
102
+ @updated_at || max_mtime(paths) || Time.at(0)
103
+ end
104
+
105
+ # This method returns the maximum mtime of the files in +paths+, or +nil+
106
+ # if the array is empty.
107
+ #
108
+ # Files with a mtime in the future are ignored. Such abnormal situation
109
+ # can happen for example if the user changes the clock by hand. It is
110
+ # healthy to consider this edge case because with mtimes in the future
111
+ # reloading is not triggered.
112
+ def max_mtime(paths)
113
+ time_now = Time.now
114
+ paths.map {|path| File.mtime(path)}.reject {|mtime| time_now < mtime}.max
115
+ end
116
+
117
+ def compile_glob(hash)
105
118
  hash.freeze # Freeze so changes aren't accidently pushed
106
119
  return if hash.empty?
107
120
 
108
- globs = []
109
- hash.each do |key, value|
110
- globs << "#{key}/**/*#{compile_ext(value)}"
121
+ globs = hash.map do |key, value|
122
+ "#{escape(key)}/**/*#{compile_ext(value)}"
111
123
  end
112
124
  "{#{globs.join(",")}}"
113
125
  end
114
126
 
115
- def compile_ext(array) #:nodoc:
116
- array = Array.wrap(array)
127
+ def escape(key)
128
+ key.gsub(',','\,')
129
+ end
130
+
131
+ def compile_ext(array)
132
+ array = Array(array)
117
133
  return if array.empty?
118
134
  ".{#{array.join(",")}}"
119
135
  end
@@ -1,14 +1,20 @@
1
1
  require 'zlib'
2
2
  require 'stringio'
3
- require 'active_support/core_ext/string/encoding'
4
3
 
5
4
  module ActiveSupport
6
- # A convenient wrapper for the zlib standard library that allows compression/decompression of strings with gzip.
5
+ # A convenient wrapper for the zlib standard library that allows
6
+ # compression/decompression of strings with gzip.
7
+ #
8
+ # gzip = ActiveSupport::Gzip.compress('compress me!')
9
+ # # => "\x1F\x8B\b\x00o\x8D\xCDO\x00\x03K\xCE\xCF-(J-.V\xC8MU\x04\x00R>n\x83\f\x00\x00\x00"
10
+ #
11
+ # ActiveSupport::Gzip.decompress(gzip)
12
+ # # => "compress me!"
7
13
  module Gzip
8
14
  class Stream < StringIO
9
15
  def initialize(*)
10
16
  super
11
- set_encoding "BINARY" if "".encoding_aware?
17
+ set_encoding "BINARY"
12
18
  end
13
19
  def close; rewind; end
14
20
  end
@@ -19,9 +25,9 @@ module ActiveSupport
19
25
  end
20
26
 
21
27
  # Compresses a string using gzip.
22
- def self.compress(source)
28
+ def self.compress(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
23
29
  output = Stream.new
24
- gz = Zlib::GzipWriter.new(output)
30
+ gz = Zlib::GzipWriter.new(output, level, strategy)
25
31
  gz.write(source)
26
32
  gz.close
27
33
  output.string
@@ -1,13 +1,47 @@
1
1
  require 'active_support/core_ext/hash/keys'
2
2
 
3
- # This class has dubious semantics and we only have it so that
4
- # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
5
- # and they get the same value for both keys.
6
-
7
3
  module ActiveSupport
4
+ # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
5
+ # to be the same.
6
+ #
7
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
8
+ #
9
+ # rgb[:black] = '#000000'
10
+ # rgb[:black] # => '#000000'
11
+ # rgb['black'] # => '#000000'
12
+ #
13
+ # rgb['white'] = '#FFFFFF'
14
+ # rgb[:white] # => '#FFFFFF'
15
+ # rgb['white'] # => '#FFFFFF'
16
+ #
17
+ # Internally symbols are mapped to strings when used as keys in the entire
18
+ # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
19
+ # mapping belongs to the public interface. For example, given:
20
+ #
21
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
22
+ #
23
+ # You are guaranteed that the key is returned as a string:
24
+ #
25
+ # hash.keys # => ["a"]
26
+ #
27
+ # Technically other types of keys are accepted:
28
+ #
29
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
30
+ # hash[0] = 0
31
+ # hash # => {"a"=>1, 0=>0}
32
+ #
33
+ # but this class is intended for use cases where strings or symbols are the
34
+ # expected keys and it is convenient to understand both as the same. For
35
+ # example the +params+ hash in Ruby on Rails.
36
+ #
37
+ # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
38
+ #
39
+ # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
40
+ #
41
+ # which may be handy.
8
42
  class HashWithIndifferentAccess < Hash
9
-
10
- # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
43
+ # Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
44
+ # this class.
11
45
  def extractable_options?
12
46
  true
13
47
  end
@@ -43,35 +77,60 @@ module ActiveSupport
43
77
  end
44
78
  end
45
79
 
80
+ def self.[](*args)
81
+ new.merge(Hash[*args])
82
+ end
83
+
46
84
  alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
47
85
  alias_method :regular_update, :update unless method_defined?(:regular_update)
48
86
 
49
87
  # Assigns a new value to the hash:
50
88
  #
51
- # hash = HashWithIndifferentAccess.new
52
- # hash[:key] = "value"
89
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
90
+ # hash[:key] = 'value'
53
91
  #
92
+ # This value can be later fetched using either +:key+ or +'key'+.
54
93
  def []=(key, value)
55
94
  regular_writer(convert_key(key), convert_value(value))
56
95
  end
57
96
 
58
97
  alias_method :store, :[]=
59
98
 
60
- # Updates the instantized hash with values from the second:
99
+ # Updates the receiver in-place, merging in the hash passed as argument:
61
100
  #
62
- # hash_1 = HashWithIndifferentAccess.new
63
- # hash_1[:key] = "value"
101
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
102
+ # hash_1[:key] = 'value'
64
103
  #
65
- # hash_2 = HashWithIndifferentAccess.new
66
- # hash_2[:key] = "New Value!"
104
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
105
+ # hash_2[:key] = 'New Value!'
67
106
  #
68
107
  # hash_1.update(hash_2) # => {"key"=>"New Value!"}
69
108
  #
109
+ # The argument can be either an
110
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
111
+ # In either case the merge respects the semantics of indifferent access.
112
+ #
113
+ # If the argument is a regular hash with keys +:key+ and +"key"+ only one
114
+ # of the values end up in the receiver, but which one is unspecified.
115
+ #
116
+ # When given a block, the value for duplicated keys will be determined
117
+ # by the result of invoking the block with the duplicated key, the value
118
+ # in the receiver, and the value in +other_hash+. The rules for duplicated
119
+ # keys follow the semantics of indifferent access:
120
+ #
121
+ # hash_1[:key] = 10
122
+ # hash_2['key'] = 12
123
+ # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
70
124
  def update(other_hash)
71
125
  if other_hash.is_a? HashWithIndifferentAccess
72
126
  super(other_hash)
73
127
  else
74
- other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
128
+ other_hash.each_pair do |key, value|
129
+ if block_given? && key?(key)
130
+ value = yield(convert_key(key), self[key], value)
131
+ end
132
+ regular_writer(convert_key(key), convert_value(value))
133
+ end
75
134
  self
76
135
  end
77
136
  end
@@ -80,11 +139,10 @@ module ActiveSupport
80
139
 
81
140
  # Checks the hash for a key matching the argument passed in:
82
141
  #
83
- # hash = HashWithIndifferentAccess.new
84
- # hash["key"] = "value"
85
- # hash.key? :key # => true
86
- # hash.key? "key" # => true
87
- #
142
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
143
+ # hash['key'] = 'value'
144
+ # hash.key?(:key) # => true
145
+ # hash.key?('key') # => true
88
146
  def key?(key)
89
147
  super(convert_key(key))
90
148
  end
@@ -96,25 +154,23 @@ module ActiveSupport
96
154
  # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
97
155
  # either a string or a symbol:
98
156
  #
99
- # counters = HashWithIndifferentAccess.new
157
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
100
158
  # counters[:foo] = 1
101
159
  #
102
- # counters.fetch("foo") # => 1
160
+ # counters.fetch('foo') # => 1
103
161
  # counters.fetch(:bar, 0) # => 0
104
162
  # counters.fetch(:bar) {|key| 0} # => 0
105
163
  # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
106
- #
107
164
  def fetch(key, *extras)
108
165
  super(convert_key(key), *extras)
109
166
  end
110
167
 
111
168
  # Returns an array of the values at the specified indices:
112
169
  #
113
- # hash = HashWithIndifferentAccess.new
114
- # hash[:a] = "x"
115
- # hash[:b] = "y"
116
- # hash.values_at("a", "b") # => ["x", "y"]
117
- #
170
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
171
+ # hash[:a] = 'x'
172
+ # hash[:b] = 'y'
173
+ # hash.values_at('a', 'b') # => ["x", "y"]
118
174
  def values_at(*indices)
119
175
  indices.collect {|key| self[convert_key(key)]}
120
176
  end
@@ -126,34 +182,52 @@ module ActiveSupport
126
182
  end
127
183
  end
128
184
 
129
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
130
- # Does not overwrite the existing hash.
131
- def merge(hash)
132
- self.dup.update(hash)
185
+ # This method has the same semantics of +update+, except it does not
186
+ # modify the receiver but rather returns a new hash with indifferent
187
+ # access with the result of the merge.
188
+ def merge(hash, &block)
189
+ self.dup.update(hash, &block)
133
190
  end
134
191
 
135
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
136
- # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
192
+ # Like +merge+ but the other way around: Merges the receiver into the
193
+ # argument and returns a new hash with indifferent access as result:
194
+ #
195
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
196
+ # hash['a'] = nil
197
+ # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
137
198
  def reverse_merge(other_hash)
138
- super self.class.new_from_hash_copying_default(other_hash)
199
+ super(self.class.new_from_hash_copying_default(other_hash))
139
200
  end
140
201
 
202
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
141
203
  def reverse_merge!(other_hash)
142
204
  replace(reverse_merge( other_hash ))
143
205
  end
144
206
 
145
- # Removes a specified key from the hash.
207
+ # Replaces the contents of this hash with other_hash.
208
+ #
209
+ # h = { "a" => 100, "b" => 200 }
210
+ # h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
211
+ def replace(other_hash)
212
+ super(self.class.new_from_hash_copying_default(other_hash))
213
+ end
214
+
215
+ # Removes the specified key from the hash.
146
216
  def delete(key)
147
217
  super(convert_key(key))
148
218
  end
149
219
 
150
220
  def stringify_keys!; self end
221
+ def deep_stringify_keys!; self end
151
222
  def stringify_keys; dup end
223
+ def deep_stringify_keys; dup end
152
224
  undef :symbolize_keys!
225
+ undef :deep_symbolize_keys!
153
226
  def symbolize_keys; to_hash.symbolize_keys end
227
+ def deep_symbolize_keys; to_hash.deep_symbolize_keys end
154
228
  def to_options!; self end
155
229
 
156
- # Convert to a Hash with String keys.
230
+ # Convert to a regular hash with string keys.
157
231
  def to_hash
158
232
  Hash.new(default).merge!(self)
159
233
  end
@@ -167,7 +241,8 @@ module ActiveSupport
167
241
  if value.is_a? Hash
168
242
  value.nested_under_indifferent_access
169
243
  elsif value.is_a?(Array)
170
- value.dup.replace(value.map { |e| convert_value(e) })
244
+ value = value.dup if value.frozen?
245
+ value.map! { |e| convert_value(e) }
171
246
  else
172
247
  value
173
248
  end