activesupport 4.2.11.3 → 5.0.7.2

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +678 -348
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +2 -3
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +1 -1
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +36 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +63 -54
  10. data/lib/active_support/cache/memory_store.rb +16 -21
  11. data/lib/active_support/cache/null_store.rb +1 -4
  12. data/lib/active_support/cache/strategy/local_cache.rb +31 -20
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -4
  14. data/lib/active_support/cache.rb +71 -87
  15. data/lib/active_support/callbacks.rb +109 -113
  16. data/lib/active_support/concern.rb +1 -1
  17. data/lib/active_support/concurrency/latch.rb +11 -12
  18. data/lib/active_support/concurrency/share_lock.rb +226 -0
  19. data/lib/active_support/configurable.rb +1 -0
  20. data/lib/active_support/core_ext/array/access.rb +27 -1
  21. data/lib/active_support/core_ext/array/conversions.rb +6 -4
  22. data/lib/active_support/core_ext/array/grouping.rb +9 -18
  23. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  24. data/lib/active_support/core_ext/array/wrap.rb +5 -4
  25. data/lib/active_support/core_ext/array.rb +1 -0
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
  27. data/lib/active_support/core_ext/class/attribute.rb +10 -9
  28. data/lib/active_support/core_ext/class/subclasses.rb +3 -2
  29. data/lib/active_support/core_ext/class.rb +0 -1
  30. data/lib/active_support/core_ext/date/blank.rb +12 -0
  31. data/lib/active_support/core_ext/date/calculations.rb +1 -1
  32. data/lib/active_support/core_ext/date/conversions.rb +7 -6
  33. data/lib/active_support/core_ext/date.rb +1 -1
  34. data/lib/active_support/core_ext/date_and_time/calculations.rb +100 -27
  35. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -1
  36. data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
  37. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  38. data/lib/active_support/core_ext/date_time/calculations.rb +14 -8
  39. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  40. data/lib/active_support/core_ext/date_time.rb +1 -1
  41. data/lib/active_support/core_ext/enumerable.rb +75 -25
  42. data/lib/active_support/core_ext/file/atomic.rb +30 -25
  43. data/lib/active_support/core_ext/hash/conversions.rb +22 -2
  44. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
  45. data/lib/active_support/core_ext/hash/except.rb +9 -8
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +25 -21
  48. data/lib/active_support/core_ext/hash/slice.rb +1 -1
  49. data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
  50. data/lib/active_support/core_ext/integer/time.rb +2 -2
  51. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
  53. data/lib/active_support/core_ext/kernel/reporting.rb +2 -84
  54. data/lib/active_support/core_ext/kernel.rb +0 -1
  55. data/lib/active_support/core_ext/load_error.rb +5 -2
  56. data/lib/active_support/core_ext/marshal.rb +7 -9
  57. data/lib/active_support/core_ext/module/aliasing.rb +6 -1
  58. data/lib/active_support/core_ext/module/anonymous.rb +10 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  62. data/lib/active_support/core_ext/module/concerning.rb +4 -4
  63. data/lib/active_support/core_ext/module/delegation.rb +11 -20
  64. data/lib/active_support/core_ext/module/deprecation.rb +2 -2
  65. data/lib/active_support/core_ext/module/introspection.rb +8 -2
  66. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -13
  67. data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
  68. data/lib/active_support/core_ext/module/remove_method.rb +23 -0
  69. data/lib/active_support/core_ext/module.rb +1 -0
  70. data/lib/active_support/core_ext/name_error.rb +15 -2
  71. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  72. data/lib/active_support/core_ext/numeric/conversions.rb +78 -77
  73. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  74. data/lib/active_support/core_ext/numeric/time.rb +26 -6
  75. data/lib/active_support/core_ext/numeric.rb +1 -0
  76. data/lib/active_support/core_ext/object/blank.rb +15 -3
  77. data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
  78. data/lib/active_support/core_ext/object/duplicable.rb +7 -12
  79. data/lib/active_support/core_ext/object/inclusion.rb +2 -2
  80. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  81. data/lib/active_support/core_ext/object/json.rb +15 -7
  82. data/lib/active_support/core_ext/object/to_query.rb +1 -1
  83. data/lib/active_support/core_ext/object/try.rb +67 -21
  84. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  85. data/lib/active_support/core_ext/object.rb +0 -1
  86. data/lib/active_support/core_ext/range/conversions.rb +18 -6
  87. data/lib/active_support/core_ext/range/each.rb +16 -18
  88. data/lib/active_support/core_ext/range/include_range.rb +20 -20
  89. data/lib/active_support/core_ext/securerandom.rb +23 -0
  90. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  91. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  92. data/lib/active_support/core_ext/string/filters.rb +1 -2
  93. data/lib/active_support/core_ext/string/inflections.rb +32 -5
  94. data/lib/active_support/core_ext/string/multibyte.rb +11 -7
  95. data/lib/active_support/core_ext/string/output_safety.rb +12 -14
  96. data/lib/active_support/core_ext/string/strip.rb +3 -6
  97. data/lib/active_support/core_ext/struct.rb +3 -6
  98. data/lib/active_support/core_ext/time/calculations.rb +18 -9
  99. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  100. data/lib/active_support/core_ext/time/marshal.rb +2 -29
  101. data/lib/active_support/core_ext/time/zones.rb +36 -4
  102. data/lib/active_support/core_ext/time.rb +0 -1
  103. data/lib/active_support/core_ext/uri.rb +1 -3
  104. data/lib/active_support/core_ext.rb +2 -1
  105. data/lib/active_support/dependencies/interlock.rb +55 -0
  106. data/lib/active_support/dependencies.rb +88 -95
  107. data/lib/active_support/deprecation/behaviors.rb +15 -1
  108. data/lib/active_support/deprecation/instance_delegator.rb +13 -0
  109. data/lib/active_support/deprecation/method_wrappers.rb +42 -16
  110. data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
  111. data/lib/active_support/deprecation/reporting.rb +23 -5
  112. data/lib/active_support/deprecation.rb +1 -1
  113. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  114. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  115. data/lib/active_support/duration.rb +90 -15
  116. data/lib/active_support/evented_file_update_checker.rb +199 -0
  117. data/lib/active_support/execution_wrapper.rb +126 -0
  118. data/lib/active_support/executor.rb +6 -0
  119. data/lib/active_support/file_update_checker.rb +23 -3
  120. data/lib/active_support/gem_version.rb +5 -5
  121. data/lib/active_support/gzip.rb +1 -1
  122. data/lib/active_support/hash_with_indifferent_access.rb +40 -11
  123. data/lib/active_support/i18n_railtie.rb +25 -4
  124. data/lib/active_support/inflector/inflections.rb +36 -5
  125. data/lib/active_support/inflector/methods.rb +97 -90
  126. data/lib/active_support/inflector/transliterate.rb +36 -21
  127. data/lib/active_support/json/decoding.rb +11 -10
  128. data/lib/active_support/json/encoding.rb +1 -51
  129. data/lib/active_support/key_generator.rb +7 -9
  130. data/lib/active_support/lazy_load_hooks.rb +46 -18
  131. data/lib/active_support/locale/en.yml +2 -0
  132. data/lib/active_support/log_subscriber/test_helper.rb +3 -3
  133. data/lib/active_support/log_subscriber.rb +1 -1
  134. data/lib/active_support/logger.rb +3 -4
  135. data/lib/active_support/logger_silence.rb +2 -1
  136. data/lib/active_support/logger_thread_safe_level.rb +2 -3
  137. data/lib/active_support/message_encryptor.rb +7 -7
  138. data/lib/active_support/message_verifier.rb +70 -8
  139. data/lib/active_support/multibyte/chars.rb +12 -3
  140. data/lib/active_support/multibyte/unicode.rb +44 -21
  141. data/lib/active_support/notifications/fanout.rb +5 -5
  142. data/lib/active_support/notifications/instrumenter.rb +20 -2
  143. data/lib/active_support/notifications.rb +2 -2
  144. data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
  145. data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
  146. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
  147. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
  148. data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
  149. data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
  150. data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
  151. data/lib/active_support/number_helper.rb +90 -67
  152. data/lib/active_support/ordered_hash.rb +1 -1
  153. data/lib/active_support/ordered_options.rb +15 -1
  154. data/lib/active_support/per_thread_registry.rb +3 -0
  155. data/lib/active_support/rails.rb +2 -2
  156. data/lib/active_support/railtie.rb +6 -1
  157. data/lib/active_support/reloader.rb +129 -0
  158. data/lib/active_support/rescuable.rb +101 -47
  159. data/lib/active_support/string_inquirer.rb +1 -1
  160. data/lib/active_support/subscriber.rb +5 -10
  161. data/lib/active_support/tagged_logging.rb +8 -7
  162. data/lib/active_support/test_case.rb +17 -29
  163. data/lib/active_support/testing/assertions.rb +15 -13
  164. data/lib/active_support/testing/deprecation.rb +9 -8
  165. data/lib/active_support/testing/file_fixtures.rb +34 -0
  166. data/lib/active_support/testing/isolation.rb +22 -8
  167. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  168. data/lib/active_support/testing/stream.rb +42 -0
  169. data/lib/active_support/testing/time_helpers.rb +3 -1
  170. data/lib/active_support/time_with_zone.rb +123 -33
  171. data/lib/active_support/values/time_zone.rb +101 -47
  172. data/lib/active_support/values/unicode_tables.dat +0 -0
  173. data/lib/active_support/xml_mini/jdom.rb +1 -1
  174. data/lib/active_support/xml_mini/libxml.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  176. data/lib/active_support.rb +11 -6
  177. metadata +36 -17
  178. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  179. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  180. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  181. data/lib/active_support/core_ext/object/itself.rb +0 -15
  182. data/lib/active_support/core_ext/thread.rb +0 -86
@@ -0,0 +1,199 @@
1
+ require 'set'
2
+ require 'pathname'
3
+ require 'concurrent/atomic/atomic_boolean'
4
+
5
+ module ActiveSupport
6
+ # Allows you to "listen" to changes in a file system.
7
+ # The evented file updater does not hit disk when checking for updates
8
+ # instead it uses platform specific file system events to trigger a change
9
+ # in state.
10
+ #
11
+ # The file checker takes an array of files to watch or a hash specifying directories
12
+ # and file extensions to watch. It also takes a block that is called when
13
+ # EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated
14
+ # is run and there have been changes to the file system.
15
+ #
16
+ # Note: Forking will cause the first call to `updated?` to return `true`.
17
+ #
18
+ # Example:
19
+ #
20
+ # checker = EventedFileUpdateChecker.new(["/tmp/foo"], -> { puts "changed" })
21
+ # checker.updated?
22
+ # # => false
23
+ # checker.execute_if_updated
24
+ # # => nil
25
+ #
26
+ # FileUtils.touch("/tmp/foo")
27
+ #
28
+ # checker.updated?
29
+ # # => true
30
+ # checker.execute_if_updated
31
+ # # => "changed"
32
+ #
33
+ class EventedFileUpdateChecker #:nodoc: all
34
+ def initialize(files, dirs = {}, &block)
35
+ @ph = PathHelper.new
36
+ @files = files.map { |f| @ph.xpath(f) }.to_set
37
+
38
+ @dirs = {}
39
+ dirs.each do |dir, exts|
40
+ @dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
41
+ end
42
+
43
+ @block = block
44
+ @updated = Concurrent::AtomicBoolean.new(false)
45
+ @lcsp = @ph.longest_common_subpath(@dirs.keys)
46
+ @pid = Process.pid
47
+ @boot_mutex = Mutex.new
48
+
49
+ if (@dtw = directories_to_watch).any?
50
+ # Loading listen triggers warnings. These are originated by a legit
51
+ # usage of attr_* macros for private attributes, but adds a lot of noise
52
+ # to our test suite. Thus, we lazy load it and disable warnings locally.
53
+ silence_warnings do
54
+ begin
55
+ require 'listen'
56
+ rescue LoadError => e
57
+ raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
58
+ end
59
+ end
60
+ end
61
+ boot!
62
+ end
63
+
64
+ def updated?
65
+ @boot_mutex.synchronize do
66
+ if @pid != Process.pid
67
+ boot!
68
+ @pid = Process.pid
69
+ @updated.make_true
70
+ end
71
+ end
72
+ @updated.true?
73
+ end
74
+
75
+ def execute
76
+ @updated.make_false
77
+ @block.call
78
+ end
79
+
80
+ def execute_if_updated
81
+ if updated?
82
+ yield if block_given?
83
+ execute
84
+ true
85
+ end
86
+ end
87
+
88
+ private
89
+ def boot!
90
+ Listen.to(*@dtw, &method(:changed)).start
91
+ end
92
+
93
+ def changed(modified, added, removed)
94
+ unless updated?
95
+ @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
96
+ end
97
+ end
98
+
99
+ def watching?(file)
100
+ file = @ph.xpath(file)
101
+
102
+ if @files.member?(file)
103
+ true
104
+ elsif file.directory?
105
+ false
106
+ else
107
+ ext = @ph.normalize_extension(file.extname)
108
+
109
+ file.dirname.ascend do |dir|
110
+ if @dirs.fetch(dir, []).include?(ext)
111
+ break true
112
+ elsif dir == @lcsp || dir.root?
113
+ break false
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def directories_to_watch
120
+ dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
121
+ dtw.compact!
122
+ dtw.uniq!
123
+
124
+ normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
125
+ dtw = dtw.reject do |path|
126
+ normalized_gem_paths.any? { |gem_path| path.to_s.start_with?(gem_path) }
127
+ end
128
+
129
+ @ph.filter_out_descendants(dtw)
130
+ end
131
+
132
+ class PathHelper
133
+ def xpath(path)
134
+ Pathname.new(path).expand_path
135
+ end
136
+
137
+ def normalize_extension(ext)
138
+ ext.to_s.sub(/\A\./, '')
139
+ end
140
+
141
+ # Given a collection of Pathname objects returns the longest subpath
142
+ # common to all of them, or +nil+ if there is none.
143
+ def longest_common_subpath(paths)
144
+ return if paths.empty?
145
+
146
+ lcsp = Pathname.new(paths[0])
147
+
148
+ paths[1..-1].each do |path|
149
+ until ascendant_of?(lcsp, path)
150
+ if lcsp.root?
151
+ # If we get here a root directory is not an ascendant of path.
152
+ # This may happen if there are paths in different drives on
153
+ # Windows.
154
+ return
155
+ else
156
+ lcsp = lcsp.parent
157
+ end
158
+ end
159
+ end
160
+
161
+ lcsp
162
+ end
163
+
164
+ # Returns the deepest existing ascendant, which could be the argument itself.
165
+ def existing_parent(dir)
166
+ dir.ascend do |ascendant|
167
+ break ascendant if ascendant.directory?
168
+ end
169
+ end
170
+
171
+ # Filters out directories which are descendants of others in the collection (stable).
172
+ def filter_out_descendants(dirs)
173
+ return dirs if dirs.length < 2
174
+
175
+ dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length }
176
+ descendants = []
177
+
178
+ until dirs_sorted_by_nparts.empty?
179
+ dir = dirs_sorted_by_nparts.shift
180
+
181
+ dirs_sorted_by_nparts.reject! do |possible_descendant|
182
+ ascendant_of?(dir, possible_descendant) && descendants << possible_descendant
183
+ end
184
+ end
185
+
186
+ # Array#- preserves order.
187
+ dirs - descendants
188
+ end
189
+
190
+ private
191
+
192
+ def ascendant_of?(base, other)
193
+ base != other && other.ascend do |ascendant|
194
+ break true if base == ascendant
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,126 @@
1
+ require 'active_support/callbacks'
2
+
3
+ module ActiveSupport
4
+ class ExecutionWrapper
5
+ include ActiveSupport::Callbacks
6
+
7
+ Null = Object.new # :nodoc:
8
+ def Null.complete! # :nodoc:
9
+ end
10
+
11
+ define_callbacks :run
12
+ define_callbacks :complete
13
+
14
+ def self.to_run(*args, &block)
15
+ set_callback(:run, *args, &block)
16
+ end
17
+
18
+ def self.to_complete(*args, &block)
19
+ set_callback(:complete, *args, &block)
20
+ end
21
+
22
+ class RunHook < Struct.new(:hook) # :nodoc:
23
+ def before(target)
24
+ hook_state = target.send(:hook_state)
25
+ hook_state[hook] = hook.run
26
+ end
27
+ end
28
+
29
+ class CompleteHook < Struct.new(:hook) # :nodoc:
30
+ def before(target)
31
+ hook_state = target.send(:hook_state)
32
+ if hook_state.key?(hook)
33
+ hook.complete hook_state[hook]
34
+ end
35
+ end
36
+ alias after before
37
+ end
38
+
39
+ # Register an object to be invoked during both the +run+ and
40
+ # +complete+ steps.
41
+ #
42
+ # +hook.complete+ will be passed the value returned from +hook.run+,
43
+ # and will only be invoked if +run+ has previously been called.
44
+ # (Mostly, this means it won't be invoked if an exception occurs in
45
+ # a preceding +to_run+ block; all ordinary +to_complete+ blocks are
46
+ # invoked in that situation.)
47
+ def self.register_hook(hook, outer: false)
48
+ if outer
49
+ to_run RunHook.new(hook), prepend: true
50
+ to_complete :after, CompleteHook.new(hook)
51
+ else
52
+ to_run RunHook.new(hook)
53
+ to_complete CompleteHook.new(hook)
54
+ end
55
+ end
56
+
57
+ # Run this execution.
58
+ #
59
+ # Returns an instance, whose +complete!+ method *must* be invoked
60
+ # after the work has been performed.
61
+ #
62
+ # Where possible, prefer +wrap+.
63
+ def self.run!
64
+ if active?
65
+ Null
66
+ else
67
+ new.tap do |instance|
68
+ success = nil
69
+ begin
70
+ instance.run!
71
+ success = true
72
+ ensure
73
+ instance.complete! unless success
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ # Perform the work in the supplied block as an execution.
80
+ def self.wrap
81
+ return yield if active?
82
+
83
+ instance = run!
84
+ begin
85
+ yield
86
+ ensure
87
+ instance.complete!
88
+ end
89
+ end
90
+
91
+ class << self # :nodoc:
92
+ attr_accessor :active
93
+ end
94
+
95
+ def self.inherited(other) # :nodoc:
96
+ super
97
+ other.active = Concurrent::Hash.new
98
+ end
99
+
100
+ self.active = Concurrent::Hash.new
101
+
102
+ def self.active? # :nodoc:
103
+ @active[Thread.current]
104
+ end
105
+
106
+ def run! # :nodoc:
107
+ self.class.active[Thread.current] = true
108
+ run_callbacks(:run)
109
+ end
110
+
111
+ # Complete this in-flight execution. This method *must* be called
112
+ # exactly once on the result of any call to +run!+.
113
+ #
114
+ # Where possible, prefer +wrap+.
115
+ def complete!
116
+ run_callbacks(:complete)
117
+ ensure
118
+ self.class.active.delete Thread.current
119
+ end
120
+
121
+ private
122
+ def hook_state
123
+ @_hook_state ||= {}
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,6 @@
1
+ require 'active_support/execution_wrapper'
2
+
3
+ module ActiveSupport
4
+ class Executor < ExecutionWrapper
5
+ end
6
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/time/calculations'
2
+
1
3
  module ActiveSupport
2
4
  # FileUpdateChecker specifies the API used by Rails to watch files
3
5
  # and control reloading. The API depends on four methods:
@@ -23,7 +25,7 @@ module ActiveSupport
23
25
  # I18n.reload!
24
26
  # end
25
27
  #
26
- # ActionDispatch::Reloader.to_prepare do
28
+ # ActiveSupport::Reloader.to_prepare do
27
29
  # i18n_reloader.execute_if_updated
28
30
  # end
29
31
  class FileUpdateChecker
@@ -35,7 +37,7 @@ module ActiveSupport
35
37
  # This method must also receive a block that will be called once a path
36
38
  # changes. The array of files and list of directories cannot be changed
37
39
  # after FileUpdateChecker has been initialized.
38
- def initialize(files, dirs={}, &block)
40
+ def initialize(files, dirs = {}, &block)
39
41
  @files = files.freeze
40
42
  @glob = compile_glob(dirs)
41
43
  @block = block
@@ -81,6 +83,7 @@ module ActiveSupport
81
83
  # Execute the block given if updated.
82
84
  def execute_if_updated
83
85
  if updated?
86
+ yield if block_given?
84
87
  execute
85
88
  true
86
89
  else
@@ -111,7 +114,24 @@ module ActiveSupport
111
114
  # reloading is not triggered.
112
115
  def max_mtime(paths)
113
116
  time_now = Time.now
114
- paths.map {|path| File.mtime(path)}.reject {|mtime| time_now < mtime}.max
117
+ max_mtime = nil
118
+
119
+ # Time comparisons are performed with #compare_without_coercion because
120
+ # AS redefines these operators in a way that is much slower and does not
121
+ # bring any benefit in this particular code.
122
+ #
123
+ # Read t1.compare_without_coercion(t2) < 0 as t1 < t2.
124
+ paths.each do |path|
125
+ mtime = File.mtime(path)
126
+
127
+ next if time_now.compare_without_coercion(mtime) < 0
128
+
129
+ if max_mtime.nil? || max_mtime.compare_without_coercion(mtime) < 0
130
+ max_mtime = mtime
131
+ end
132
+ end
133
+
134
+ max_mtime
115
135
  end
116
136
 
117
137
  def compile_glob(hash)
@@ -1,14 +1,14 @@
1
1
  module ActiveSupport
2
- # Returns the version of the currently loaded Active Support as a <tt>Gem::Version</tt>
2
+ # Returns the version of the currently loaded Active Support as a <tt>Gem::Version</tt>.
3
3
  def self.gem_version
4
4
  Gem::Version.new VERSION::STRING
5
5
  end
6
6
 
7
7
  module VERSION
8
- MAJOR = 4
9
- MINOR = 2
10
- TINY = 11
11
- PRE = "3"
8
+ MAJOR = 5
9
+ MINOR = 0
10
+ TINY = 7
11
+ PRE = "2"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -21,7 +21,7 @@ module ActiveSupport
21
21
 
22
22
  # Decompresses a gzipped string.
23
23
  def self.decompress(source)
24
- Zlib::GzipReader.new(StringIO.new(source)).read
24
+ Zlib::GzipReader.wrap(StringIO.new(source), &:read)
25
25
  end
26
26
 
27
27
  # Compresses a string using gzip.
@@ -40,6 +40,12 @@ module ActiveSupport
40
40
  # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
41
41
  #
42
42
  # which may be handy.
43
+ #
44
+ # To access this class outside of Rails, require the core extension with:
45
+ #
46
+ # require "active_support/core_ext/hash/indifferent_access"
47
+ #
48
+ # which will, in turn, require this file.
43
49
  class HashWithIndifferentAccess < Hash
44
50
  # Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
45
51
  # this class.
@@ -59,13 +65,19 @@ module ActiveSupport
59
65
  if constructor.respond_to?(:to_hash)
60
66
  super()
61
67
  update(constructor)
68
+
69
+ hash = constructor.to_hash
70
+ self.default = hash.default if hash.default
71
+ self.default_proc = hash.default_proc if hash.default_proc
62
72
  else
63
73
  super(constructor)
64
74
  end
65
75
  end
66
76
 
67
- def default(key = nil)
68
- if key.is_a?(Symbol) && include?(key = key.to_s)
77
+ def default(*args)
78
+ arg_key = args.first
79
+
80
+ if include?(key = convert_key(arg_key))
69
81
  self[key]
70
82
  else
71
83
  super
@@ -73,11 +85,12 @@ module ActiveSupport
73
85
  end
74
86
 
75
87
  def self.new_from_hash_copying_default(hash)
76
- hash = hash.to_hash
77
- new(hash).tap do |new_hash|
78
- new_hash.default = hash.default
79
- new_hash.default_proc = hash.default_proc if hash.default_proc
80
- end
88
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
89
+ `ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default`
90
+ has been deprecated, and will be removed in Rails 5.1. The behavior of
91
+ this method is now identical to the behavior of `.new`.
92
+ MSG
93
+ new(hash)
81
94
  end
82
95
 
83
96
  def self.[](*args)
@@ -92,7 +105,7 @@ module ActiveSupport
92
105
  # hash = ActiveSupport::HashWithIndifferentAccess.new
93
106
  # hash[:key] = 'value'
94
107
  #
95
- # This value can be later fetched using either +:key+ or +'key'+.
108
+ # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
96
109
  def []=(key, value)
97
110
  regular_writer(convert_key(key), convert_value(value, for: :assignment))
98
111
  end
@@ -154,6 +167,20 @@ module ActiveSupport
154
167
  alias_method :has_key?, :key?
155
168
  alias_method :member?, :key?
156
169
 
170
+
171
+ # Same as <tt>Hash#[]</tt> where the key passed as argument can be
172
+ # either a string or a symbol:
173
+ #
174
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
175
+ # counters[:foo] = 1
176
+ #
177
+ # counters['foo'] # => 1
178
+ # counters[:foo] # => 1
179
+ # counters[:zoo] # => nil
180
+ def [](key)
181
+ super(convert_key(key))
182
+ end
183
+
157
184
  # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
158
185
  # either a string or a symbol:
159
186
  #
@@ -184,7 +211,7 @@ module ActiveSupport
184
211
  # dup = hash.dup
185
212
  # dup[:a][:c] = 'c'
186
213
  #
187
- # hash[:a][:c] # => nil
214
+ # hash[:a][:c] # => "c"
188
215
  # dup[:a][:c] # => "c"
189
216
  def dup
190
217
  self.class.new(self).tap do |new_hash|
@@ -206,7 +233,7 @@ module ActiveSupport
206
233
  # hash['a'] = nil
207
234
  # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
208
235
  def reverse_merge(other_hash)
209
- super(self.class.new_from_hash_copying_default(other_hash))
236
+ super(self.class.new(other_hash))
210
237
  end
211
238
 
212
239
  # Same semantics as +reverse_merge+ but modifies the receiver in-place.
@@ -219,7 +246,7 @@ module ActiveSupport
219
246
  # h = { "a" => 100, "b" => 200 }
220
247
  # h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400}
221
248
  def replace(other_hash)
222
- super(self.class.new_from_hash_copying_default(other_hash))
249
+ super(self.class.new(other_hash))
223
250
  end
224
251
 
225
252
  # Removes the specified key from the hash.
@@ -238,10 +265,12 @@ module ActiveSupport
238
265
  def to_options!; self end
239
266
 
240
267
  def select(*args, &block)
268
+ return to_enum(:select) unless block_given?
241
269
  dup.tap { |hash| hash.select!(*args, &block) }
242
270
  end
243
271
 
244
272
  def reject(*args, &block)
273
+ return to_enum(:reject) unless block_given?
245
274
  dup.tap { |hash| hash.reject!(*args, &block) }
246
275
  end
247
276
 
@@ -37,10 +37,12 @@ module I18n
37
37
  enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
38
38
  I18n.enforce_available_locales = false
39
39
 
40
+ reloadable_paths = []
40
41
  app.config.i18n.each do |setting, value|
41
42
  case setting
42
43
  when :railties_load_path
43
- app.config.i18n.load_path.unshift(*value)
44
+ reloadable_paths = value
45
+ app.config.i18n.load_path.unshift(*value.map(&:existent).flatten)
44
46
  when :load_path
45
47
  I18n.load_path += value
46
48
  else
@@ -53,16 +55,29 @@ module I18n
53
55
  # Restore available locales check so it will take place from now on.
54
56
  I18n.enforce_available_locales = enforce_available_locales
55
57
 
56
- reloader = ActiveSupport::FileUpdateChecker.new(I18n.load_path.dup){ I18n.reload! }
58
+ directories = watched_dirs_with_extensions(reloadable_paths)
59
+ reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
60
+ I18n.load_path.keep_if { |p| File.exist?(p) }
61
+ I18n.load_path |= reloadable_paths.map(&:existent).flatten
62
+
63
+ I18n.reload!
64
+ end
65
+
57
66
  app.reloaders << reloader
58
- ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
67
+ app.reloader.to_run do
68
+ reloader.execute_if_updated { require_unload_lock! }
69
+ # TODO: remove the following line as soon as the return value of
70
+ # callbacks is ignored, that is, returning `false` does not
71
+ # display a deprecation warning or halts the callback chain.
72
+ true
73
+ end
59
74
  reloader.execute
60
75
 
61
76
  @i18n_inited = true
62
77
  end
63
78
 
64
79
  def self.include_fallbacks_module
65
- I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
80
+ I18n.backend.class.include(I18n::Backend::Fallbacks)
66
81
  end
67
82
 
68
83
  def self.init_fallbacks(fallbacks)
@@ -90,5 +105,11 @@ module I18n
90
105
  raise "Unexpected fallback type #{fallbacks.inspect}"
91
106
  end
92
107
  end
108
+
109
+ def self.watched_dirs_with_extensions(paths)
110
+ paths.each_with_object({}) do |path, result|
111
+ result[path.absolute_current] = path.extensions
112
+ end
113
+ end
93
114
  end
94
115
  end
@@ -1,4 +1,4 @@
1
- require 'thread_safe'
1
+ require 'concurrent/map'
2
2
  require 'active_support/core_ext/array/prepend_and_append'
3
3
  require 'active_support/i18n'
4
4
 
@@ -25,7 +25,38 @@ module ActiveSupport
25
25
  # singularization rules that is runs. This guarantees that your rules run
26
26
  # before any of the rules that may already have been loaded.
27
27
  class Inflections
28
- @__instance__ = ThreadSafe::Cache.new
28
+ @__instance__ = Concurrent::Map.new
29
+
30
+ class Uncountables < Array
31
+ def initialize
32
+ @regex_array = []
33
+ super
34
+ end
35
+
36
+ def delete(entry)
37
+ super entry
38
+ @regex_array.delete(to_regex(entry))
39
+ end
40
+
41
+ def <<(*word)
42
+ add(word)
43
+ end
44
+
45
+ def add(words)
46
+ self.concat(words.flatten.map(&:downcase))
47
+ @regex_array += self.map {|word| to_regex(word) }
48
+ self
49
+ end
50
+
51
+ def uncountable?(str)
52
+ @regex_array.any? { |regex| regex === str }
53
+ end
54
+
55
+ private
56
+ def to_regex(string)
57
+ /\b#{::Regexp.escape(string)}\Z/i
58
+ end
59
+ end
29
60
 
30
61
  def self.instance(locale = :en)
31
62
  @__instance__[locale] ||= new
@@ -34,7 +65,7 @@ module ActiveSupport
34
65
  attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
35
66
 
36
67
  def initialize
37
- @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
68
+ @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], Uncountables.new, [], {}, /(?=a)b/
38
69
  end
39
70
 
40
71
  # Private, for the test suite.
@@ -160,7 +191,7 @@ module ActiveSupport
160
191
  # uncountable 'money', 'information'
161
192
  # uncountable %w( money information rice )
162
193
  def uncountable(*words)
163
- @uncountables += words.flatten.map(&:downcase)
194
+ @uncountables.add(words)
164
195
  end
165
196
 
166
197
  # Specifies a humanized form of a string by a regular expression rule or
@@ -185,7 +216,7 @@ module ActiveSupport
185
216
  def clear(scope = :all)
186
217
  case scope
187
218
  when :all
188
- @plurals, @singulars, @uncountables, @humans = [], [], [], []
219
+ @plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
189
220
  else
190
221
  instance_variable_set "@#{scope}", []
191
222
  end