activesupport 5.2.7.1 → 6.1.4.6

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -434
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +34 -34
  10. data/lib/active_support/cache/mem_cache_store.rb +39 -24
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +72 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +148 -78
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +70 -3
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  36. data/lib/active_support/core_ext/enumerable.rb +171 -75
  37. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  38. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  39. data/lib/active_support/core_ext/hash/except.rb +2 -2
  40. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  41. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  42. data/lib/active_support/core_ext/hash.rb +1 -2
  43. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  44. data/lib/active_support/core_ext/kernel.rb +0 -1
  45. data/lib/active_support/core_ext/load_error.rb +1 -1
  46. data/lib/active_support/core_ext/marshal.rb +2 -0
  47. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  48. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  49. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  50. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  51. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  52. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  53. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  54. data/lib/active_support/core_ext/module.rb +0 -1
  55. data/lib/active_support/core_ext/name_error.rb +29 -2
  56. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  57. data/lib/active_support/core_ext/numeric.rb +0 -1
  58. data/lib/active_support/core_ext/object/blank.rb +1 -2
  59. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  60. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  61. data/lib/active_support/core_ext/object/json.rb +14 -2
  62. data/lib/active_support/core_ext/object/try.rb +17 -7
  63. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  64. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  65. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  66. data/lib/active_support/core_ext/range/each.rb +0 -1
  67. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  68. data/lib/active_support/core_ext/regexp.rb +8 -5
  69. data/lib/active_support/core_ext/securerandom.rb +23 -3
  70. data/lib/active_support/core_ext/string/access.rb +5 -16
  71. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  72. data/lib/active_support/core_ext/string/filters.rb +42 -1
  73. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  74. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  75. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  76. data/lib/active_support/core_ext/string/output_safety.rb +70 -41
  77. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  78. data/lib/active_support/core_ext/string/strip.rb +3 -1
  79. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  80. data/lib/active_support/core_ext/symbol.rb +3 -0
  81. data/lib/active_support/core_ext/time/calculations.rb +51 -3
  82. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  83. data/lib/active_support/core_ext/uri.rb +6 -1
  84. data/lib/active_support/core_ext.rb +1 -1
  85. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  86. data/lib/active_support/current_attributes.rb +16 -2
  87. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/deprecation/behaviors.rb +16 -3
  90. data/lib/active_support/deprecation/disallowed.rb +56 -0
  91. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  92. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  93. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  94. data/lib/active_support/deprecation/reporting.rb +50 -7
  95. data/lib/active_support/deprecation.rb +6 -1
  96. data/lib/active_support/descendants_tracker.rb +59 -9
  97. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  98. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  99. data/lib/active_support/duration.rb +78 -30
  100. data/lib/active_support/encrypted_configuration.rb +0 -4
  101. data/lib/active_support/encrypted_file.rb +22 -4
  102. data/lib/active_support/environment_inquirer.rb +20 -0
  103. data/lib/active_support/evented_file_update_checker.rb +82 -117
  104. data/lib/active_support/execution_wrapper.rb +2 -1
  105. data/lib/active_support/file_update_checker.rb +0 -1
  106. data/lib/active_support/fork_tracker.rb +64 -0
  107. data/lib/active_support/gem_version.rb +4 -4
  108. data/lib/active_support/hash_with_indifferent_access.rb +70 -42
  109. data/lib/active_support/i18n.rb +1 -0
  110. data/lib/active_support/i18n_railtie.rb +15 -8
  111. data/lib/active_support/inflector/inflections.rb +2 -7
  112. data/lib/active_support/inflector/methods.rb +49 -58
  113. data/lib/active_support/inflector/transliterate.rb +47 -18
  114. data/lib/active_support/json/decoding.rb +25 -26
  115. data/lib/active_support/json/encoding.rb +11 -3
  116. data/lib/active_support/key_generator.rb +1 -33
  117. data/lib/active_support/lazy_load_hooks.rb +5 -2
  118. data/lib/active_support/locale/en.rb +33 -0
  119. data/lib/active_support/locale/en.yml +7 -3
  120. data/lib/active_support/log_subscriber.rb +39 -9
  121. data/lib/active_support/logger.rb +2 -17
  122. data/lib/active_support/logger_silence.rb +11 -19
  123. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  124. data/lib/active_support/message_encryptor.rb +8 -13
  125. data/lib/active_support/message_verifier.rb +10 -10
  126. data/lib/active_support/messages/metadata.rb +11 -2
  127. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  128. data/lib/active_support/messages/rotator.rb +10 -9
  129. data/lib/active_support/multibyte/chars.rb +10 -68
  130. data/lib/active_support/multibyte/unicode.rb +15 -327
  131. data/lib/active_support/notifications/fanout.rb +116 -16
  132. data/lib/active_support/notifications/instrumenter.rb +71 -9
  133. data/lib/active_support/notifications.rb +72 -8
  134. data/lib/active_support/number_helper/number_converter.rb +5 -6
  135. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  136. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  137. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  138. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  139. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  140. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  141. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  142. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  143. data/lib/active_support/number_helper.rb +38 -12
  144. data/lib/active_support/option_merger.rb +22 -3
  145. data/lib/active_support/ordered_hash.rb +1 -1
  146. data/lib/active_support/ordered_options.rb +13 -3
  147. data/lib/active_support/parameter_filter.rb +133 -0
  148. data/lib/active_support/per_thread_registry.rb +1 -1
  149. data/lib/active_support/rails.rb +1 -10
  150. data/lib/active_support/railtie.rb +23 -1
  151. data/lib/active_support/reloader.rb +4 -5
  152. data/lib/active_support/rescuable.rb +4 -4
  153. data/lib/active_support/secure_compare_rotator.rb +51 -0
  154. data/lib/active_support/security_utils.rb +19 -12
  155. data/lib/active_support/string_inquirer.rb +4 -3
  156. data/lib/active_support/subscriber.rb +72 -28
  157. data/lib/active_support/tagged_logging.rb +42 -8
  158. data/lib/active_support/test_case.rb +91 -0
  159. data/lib/active_support/testing/assertions.rb +30 -9
  160. data/lib/active_support/testing/deprecation.rb +0 -1
  161. data/lib/active_support/testing/file_fixtures.rb +2 -0
  162. data/lib/active_support/testing/isolation.rb +2 -2
  163. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  164. data/lib/active_support/testing/parallelization/server.rb +78 -0
  165. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  166. data/lib/active_support/testing/parallelization.rb +51 -0
  167. data/lib/active_support/testing/stream.rb +1 -2
  168. data/lib/active_support/testing/time_helpers.rb +47 -12
  169. data/lib/active_support/time_with_zone.rb +81 -47
  170. data/lib/active_support/values/time_zone.rb +32 -17
  171. data/lib/active_support/xml_mini/jdom.rb +2 -3
  172. data/lib/active_support/xml_mini/libxml.rb +2 -2
  173. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  174. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  176. data/lib/active_support/xml_mini/rexml.rb +10 -3
  177. data/lib/active_support/xml_mini.rb +2 -10
  178. data/lib/active_support.rb +14 -1
  179. metadata +55 -29
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -3,6 +3,8 @@
3
3
  require "set"
4
4
  require "pathname"
5
5
  require "concurrent/atomic/atomic_boolean"
6
+ require "listen"
7
+ require "active_support/fork_tracker"
6
8
 
7
9
  module ActiveSupport
8
10
  # Allows you to "listen" to changes in a file system.
@@ -38,48 +40,22 @@ module ActiveSupport
38
40
  raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
39
41
  end
40
42
 
41
- @ph = PathHelper.new
42
- @files = files.map { |f| @ph.xpath(f) }.to_set
43
-
44
- @dirs = {}
45
- dirs.each do |dir, exts|
46
- @dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
47
- end
48
-
49
- @block = block
50
- @updated = Concurrent::AtomicBoolean.new(false)
51
- @lcsp = @ph.longest_common_subpath(@dirs.keys)
52
- @pid = Process.pid
53
- @boot_mutex = Mutex.new
54
-
55
- if (@dtw = directories_to_watch).any?
56
- # Loading listen triggers warnings. These are originated by a legit
57
- # usage of attr_* macros for private attributes, but adds a lot of noise
58
- # to our test suite. Thus, we lazy load it and disable warnings locally.
59
- silence_warnings do
60
- begin
61
- require "listen"
62
- rescue LoadError => e
63
- raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
64
- end
65
- end
66
- end
67
- boot!
43
+ @block = block
44
+ @core = Core.new(files, dirs)
45
+ ObjectSpace.define_finalizer(self, @core.finalizer)
68
46
  end
69
47
 
70
48
  def updated?
71
- @boot_mutex.synchronize do
72
- if @pid != Process.pid
73
- boot!
74
- @pid = Process.pid
75
- @updated.make_true
76
- end
49
+ if @core.restart?
50
+ @core.thread_safely(&:restart)
51
+ @core.updated.make_true
77
52
  end
78
- @updated.true?
53
+
54
+ @core.updated.true?
79
55
  end
80
56
 
81
57
  def execute
82
- @updated.make_false
58
+ @core.updated.make_false
83
59
  @block.call
84
60
  end
85
61
 
@@ -91,115 +67,104 @@ module ActiveSupport
91
67
  end
92
68
  end
93
69
 
94
- private
95
- def boot!
96
- Listen.to(*@dtw, &method(:changed)).start
97
- end
70
+ class Core
71
+ attr_reader :updated
98
72
 
99
- def changed(modified, added, removed)
100
- unless updated?
101
- @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
73
+ def initialize(files, dirs)
74
+ @files = files.map { |file| Pathname(file).expand_path }.to_set
75
+
76
+ @dirs = dirs.each_with_object({}) do |(dir, exts), hash|
77
+ hash[Pathname(dir).expand_path] = Array(exts).map { |ext| ext.to_s.sub(/\A\.?/, ".") }.to_set
102
78
  end
103
- end
104
79
 
105
- def watching?(file)
106
- file = @ph.xpath(file)
80
+ @common_path = common_path(@dirs.keys)
107
81
 
108
- if @files.member?(file)
109
- true
110
- elsif file.directory?
111
- false
112
- else
113
- ext = @ph.normalize_extension(file.extname)
82
+ @dtw = directories_to_watch
83
+ @missing = []
114
84
 
115
- file.dirname.ascend do |dir|
116
- if @dirs.fetch(dir, []).include?(ext)
117
- break true
118
- elsif dir == @lcsp || dir.root?
119
- break false
120
- end
121
- end
122
- end
85
+ @updated = Concurrent::AtomicBoolean.new(false)
86
+ @mutex = Mutex.new
87
+
88
+ start
89
+ @after_fork = ActiveSupport::ForkTracker.after_fork { start }
123
90
  end
124
91
 
125
- def directories_to_watch
126
- dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
127
- dtw.compact!
128
- dtw.uniq!
92
+ def finalizer
93
+ proc do
94
+ stop
95
+ ActiveSupport::ForkTracker.unregister(@after_fork)
96
+ end
97
+ end
129
98
 
130
- normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
131
- dtw = dtw.reject do |path|
132
- normalized_gem_paths.any? { |gem_path| path.to_s.start_with?(gem_path) }
99
+ def thread_safely
100
+ @mutex.synchronize do
101
+ yield self
133
102
  end
103
+ end
134
104
 
135
- @ph.filter_out_descendants(dtw)
105
+ def start
106
+ normalize_dirs!
107
+ @dtw, @missing = [*@dtw, *@missing].partition(&:exist?)
108
+ @listener = @dtw.any? ? Listen.to(*@dtw, &method(:changed)) : nil
109
+ @listener&.start
136
110
  end
137
111
 
138
- class PathHelper
139
- def xpath(path)
140
- Pathname.new(path).expand_path
141
- end
112
+ def stop
113
+ @listener&.stop
114
+ end
142
115
 
143
- def normalize_extension(ext)
144
- ext.to_s.sub(/\A\./, "")
145
- end
116
+ def restart
117
+ stop
118
+ start
119
+ end
146
120
 
147
- # Given a collection of Pathname objects returns the longest subpath
148
- # common to all of them, or +nil+ if there is none.
149
- def longest_common_subpath(paths)
150
- return if paths.empty?
151
-
152
- lcsp = Pathname.new(paths[0])
153
-
154
- paths[1..-1].each do |path|
155
- until ascendant_of?(lcsp, path)
156
- if lcsp.root?
157
- # If we get here a root directory is not an ascendant of path.
158
- # This may happen if there are paths in different drives on
159
- # Windows.
160
- return
161
- else
162
- lcsp = lcsp.parent
163
- end
164
- end
165
- end
121
+ def restart?
122
+ @missing.any?(&:exist?)
123
+ end
166
124
 
167
- lcsp
125
+ def normalize_dirs!
126
+ @dirs.transform_keys! do |dir|
127
+ dir.exist? ? dir.realpath : dir
168
128
  end
129
+ end
169
130
 
170
- # Returns the deepest existing ascendant, which could be the argument itself.
171
- def existing_parent(dir)
172
- dir.ascend do |ascendant|
173
- break ascendant if ascendant.directory?
174
- end
131
+ def changed(modified, added, removed)
132
+ unless @updated.true?
133
+ @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
175
134
  end
135
+ end
176
136
 
177
- # Filters out directories which are descendants of others in the collection (stable).
178
- def filter_out_descendants(dirs)
179
- return dirs if dirs.length < 2
137
+ def watching?(file)
138
+ file = Pathname(file)
180
139
 
181
- dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length }
182
- descendants = []
140
+ if @files.member?(file)
141
+ true
142
+ elsif file.directory?
143
+ false
144
+ else
145
+ ext = file.extname
183
146
 
184
- until dirs_sorted_by_nparts.empty?
185
- dir = dirs_sorted_by_nparts.shift
147
+ file.dirname.ascend do |dir|
148
+ matching = @dirs[dir]
186
149
 
187
- dirs_sorted_by_nparts.reject! do |possible_descendant|
188
- ascendant_of?(dir, possible_descendant) && descendants << possible_descendant
150
+ if matching && (matching.empty? || matching.include?(ext))
151
+ break true
152
+ elsif dir == @common_path || dir.root?
153
+ break false
189
154
  end
190
155
  end
191
-
192
- # Array#- preserves order.
193
- dirs - descendants
194
156
  end
157
+ end
195
158
 
196
- private
159
+ def directories_to_watch
160
+ dtw = @dirs.keys | @files.map(&:dirname)
161
+ accounted_for = dtw.to_set + Gem.path.map { |path| Pathname(path) }
162
+ dtw.reject { |dir| dir.ascend.drop(1).any? { |parent| accounted_for.include?(parent) } }
163
+ end
197
164
 
198
- def ascendant_of?(base, other)
199
- base != other && other.ascend do |ascendant|
200
- break true if base == ascendant
201
- end
202
- end
165
+ def common_path(paths)
166
+ paths.map { |path| path.ascend.to_a }.reduce(&:&)&.first
203
167
  end
168
+ end
204
169
  end
205
170
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/callbacks"
4
+ require "concurrent/hash"
4
5
 
5
6
  module ActiveSupport
6
7
  class ExecutionWrapper
@@ -65,7 +66,7 @@ module ActiveSupport
65
66
  def self.run!(reset: false)
66
67
  if reset
67
68
  lost_instance = active.delete(Thread.current)
68
- lost_instance.complete! unless lost_instance.nil?
69
+ lost_instance&.complete!
69
70
  else
70
71
  return Null if active?
71
72
  end
@@ -98,7 +98,6 @@ module ActiveSupport
98
98
  end
99
99
 
100
100
  private
101
-
102
101
  def watched
103
102
  @watched || begin
104
103
  all = @files.select { |f| File.exist?(f) }
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module ForkTracker # :nodoc:
5
+ module CoreExt
6
+ def fork(*)
7
+ if block_given?
8
+ super do
9
+ ForkTracker.check!
10
+ yield
11
+ end
12
+ else
13
+ unless pid = super
14
+ ForkTracker.check!
15
+ end
16
+ pid
17
+ end
18
+ end
19
+ ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
20
+ end
21
+
22
+ module CoreExtPrivate
23
+ include CoreExt
24
+
25
+ private
26
+ def fork(*)
27
+ super
28
+ end
29
+ ruby2_keywords(:fork) if respond_to?(:ruby2_keywords, true)
30
+ end
31
+
32
+ @pid = Process.pid
33
+ @callbacks = []
34
+
35
+ class << self
36
+ def check!
37
+ if @pid != Process.pid
38
+ @callbacks.each(&:call)
39
+ @pid = Process.pid
40
+ end
41
+ end
42
+
43
+ def hook!
44
+ if Process.respond_to?(:fork)
45
+ ::Object.prepend(CoreExtPrivate)
46
+ ::Kernel.prepend(CoreExtPrivate)
47
+ ::Kernel.singleton_class.prepend(CoreExt)
48
+ ::Process.singleton_class.prepend(CoreExt)
49
+ end
50
+ end
51
+
52
+ def after_fork(&block)
53
+ @callbacks << block
54
+ block
55
+ end
56
+
57
+ def unregister(callback)
58
+ @callbacks.delete(callback)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ ActiveSupport::ForkTracker.hook!
@@ -7,10 +7,10 @@ module ActiveSupport
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 5
11
- MINOR = 2
12
- TINY = 7
13
- PRE = "1"
10
+ MAJOR = 6
11
+ MINOR = 1
12
+ TINY = 4
13
+ PRE = "6"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "active_support/core_ext/hash/keys"
4
4
  require "active_support/core_ext/hash/reverse_merge"
5
+ require "active_support/core_ext/hash/except"
6
+ require "active_support/core_ext/hash/slice"
5
7
 
6
8
  module ActiveSupport
7
9
  # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -68,7 +70,7 @@ module ActiveSupport
68
70
  super()
69
71
  update(constructor)
70
72
 
71
- hash = constructor.to_hash
73
+ hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
72
74
  self.default = hash.default if hash.default
73
75
  self.default_proc = hash.default_proc if hash.default_proc
74
76
  else
@@ -90,12 +92,12 @@ module ActiveSupport
90
92
  #
91
93
  # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
92
94
  def []=(key, value)
93
- regular_writer(convert_key(key), convert_value(value, for: :assignment))
95
+ regular_writer(convert_key(key), convert_value(value, conversion: :assignment))
94
96
  end
95
97
 
96
98
  alias_method :store, :[]=
97
99
 
98
- # Updates the receiver in-place, merging in the hash passed as argument:
100
+ # Updates the receiver in-place, merging in the hashes passed as arguments:
99
101
  #
100
102
  # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
101
103
  # hash_1[:key] = 'value'
@@ -105,11 +107,14 @@ module ActiveSupport
105
107
  #
106
108
  # hash_1.update(hash_2) # => {"key"=>"New Value!"}
107
109
  #
108
- # The argument can be either an
110
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
111
+ # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 }
112
+ #
113
+ # The arguments can be either an
109
114
  # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
110
115
  # In either case the merge respects the semantics of indifferent access.
111
116
  #
112
- # If the argument is a regular hash with keys +:key+ and +"key"+ only one
117
+ # If the argument is a regular hash with keys +:key+ and <tt>"key"</tt> only one
113
118
  # of the values end up in the receiver, but which one is unspecified.
114
119
  #
115
120
  # When given a block, the value for duplicated keys will be determined
@@ -120,18 +125,15 @@ module ActiveSupport
120
125
  # hash_1[:key] = 10
121
126
  # hash_2['key'] = 12
122
127
  # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
123
- def update(other_hash)
124
- if other_hash.is_a? HashWithIndifferentAccess
125
- super(other_hash)
128
+ def update(*other_hashes, &block)
129
+ if other_hashes.size == 1
130
+ update_with_single_argument(other_hashes.first, block)
126
131
  else
127
- other_hash.to_hash.each_pair do |key, value|
128
- if block_given? && key?(key)
129
- value = yield(convert_key(key), self[key], value)
130
- end
131
- regular_writer(convert_key(key), convert_value(value))
132
+ other_hashes.each do |other_hash|
133
+ update_with_single_argument(other_hash, block)
132
134
  end
133
- self
134
135
  end
136
+ self
135
137
  end
136
138
 
137
139
  alias_method :merge!, :update
@@ -190,20 +192,18 @@ module ActiveSupport
190
192
  super(convert_key(key), *extras)
191
193
  end
192
194
 
193
- if Hash.new.respond_to?(:dig)
194
- # Same as <tt>Hash#dig</tt> where the key passed as argument can be
195
- # either a string or a symbol:
196
- #
197
- # counters = ActiveSupport::HashWithIndifferentAccess.new
198
- # counters[:foo] = { bar: 1 }
199
- #
200
- # counters.dig('foo', 'bar') # => 1
201
- # counters.dig(:foo, :bar) # => 1
202
- # counters.dig(:zoo) # => nil
203
- def dig(*args)
204
- args[0] = convert_key(args[0]) if args.size > 0
205
- super(*args)
206
- end
195
+ # Same as <tt>Hash#dig</tt> where the key passed as argument can be
196
+ # either a string or a symbol:
197
+ #
198
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
199
+ # counters[:foo] = { bar: 1 }
200
+ #
201
+ # counters.dig('foo', 'bar') # => 1
202
+ # counters.dig(:foo, :bar) # => 1
203
+ # counters.dig(:zoo) # => nil
204
+ def dig(*args)
205
+ args[0] = convert_key(args[0]) if args.size > 0
206
+ super(*args)
207
207
  end
208
208
 
209
209
  # Same as <tt>Hash#default</tt> where the key passed as argument can be
@@ -226,8 +226,8 @@ module ActiveSupport
226
226
  # hash[:a] = 'x'
227
227
  # hash[:b] = 'y'
228
228
  # hash.values_at('a', 'b') # => ["x", "y"]
229
- def values_at(*indices)
230
- indices.collect { |key| self[convert_key(key)] }
229
+ def values_at(*keys)
230
+ super(*keys.map { |key| convert_key(key) })
231
231
  end
232
232
 
233
233
  # Returns an array of the values at the specified indices, but also
@@ -240,8 +240,8 @@ module ActiveSupport
240
240
  # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
241
241
  # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
242
242
  def fetch_values(*indices, &block)
243
- indices.collect { |key| fetch(key, &block) }
244
- end if Hash.method_defined?(:fetch_values)
243
+ super(*indices.map { |key| convert_key(key) }, &block)
244
+ end
245
245
 
246
246
  # Returns a shallow copy of the hash.
247
247
  #
@@ -260,8 +260,8 @@ module ActiveSupport
260
260
  # This method has the same semantics of +update+, except it does not
261
261
  # modify the receiver but rather returns a new hash with indifferent
262
262
  # access with the result of the merge.
263
- def merge(hash, &block)
264
- dup.update(hash, &block)
263
+ def merge(*hashes, &block)
264
+ dup.update(*hashes, &block)
265
265
  end
266
266
 
267
267
  # Like +merge+ but the other way around: Merges the receiver into the
@@ -294,6 +294,15 @@ module ActiveSupport
294
294
  super(convert_key(key))
295
295
  end
296
296
 
297
+ # Returns a hash with indifferent access that includes everything except given keys.
298
+ # hash = { a: "x", b: "y", c: 10 }.with_indifferent_access
299
+ # hash.except(:a, "b") # => {c: 10}.with_indifferent_access
300
+ # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access
301
+ def except(*keys)
302
+ slice(*self.keys - keys.map { |key| convert_key(key) })
303
+ end
304
+ alias_method :without, :except
305
+
297
306
  def stringify_keys!; self end
298
307
  def deep_stringify_keys!; self end
299
308
  def stringify_keys; dup end
@@ -353,40 +362,59 @@ module ActiveSupport
353
362
  set_defaults(_new_hash)
354
363
 
355
364
  each do |key, value|
356
- _new_hash[key] = convert_value(value, for: :to_hash)
365
+ _new_hash[key] = convert_value(value, conversion: :to_hash)
357
366
  end
358
367
  _new_hash
359
368
  end
360
369
 
361
370
  private
362
- def convert_key(key) # :doc:
363
- key.kind_of?(Symbol) ? key.to_s : key
371
+ if Symbol.method_defined?(:name)
372
+ def convert_key(key)
373
+ key.kind_of?(Symbol) ? key.name : key
374
+ end
375
+ else
376
+ def convert_key(key)
377
+ key.kind_of?(Symbol) ? key.to_s : key
378
+ end
364
379
  end
365
380
 
366
- def convert_value(value, options = {}) # :doc:
381
+ def convert_value(value, conversion: nil)
367
382
  if value.is_a? Hash
368
- if options[:for] == :to_hash
383
+ if conversion == :to_hash
369
384
  value.to_hash
370
385
  else
371
386
  value.nested_under_indifferent_access
372
387
  end
373
388
  elsif value.is_a?(Array)
374
- if options[:for] != :assignment || value.frozen?
389
+ if conversion != :assignment || value.frozen?
375
390
  value = value.dup
376
391
  end
377
- value.map! { |e| convert_value(e, options) }
392
+ value.map! { |e| convert_value(e, conversion: conversion) }
378
393
  else
379
394
  value
380
395
  end
381
396
  end
382
397
 
383
- def set_defaults(target) # :doc:
398
+ def set_defaults(target)
384
399
  if default_proc
385
400
  target.default_proc = default_proc.dup
386
401
  else
387
402
  target.default = default
388
403
  end
389
404
  end
405
+
406
+ def update_with_single_argument(other_hash, block)
407
+ if other_hash.is_a? HashWithIndifferentAccess
408
+ regular_update(other_hash, &block)
409
+ else
410
+ other_hash.to_hash.each_pair do |key, value|
411
+ if block && key?(key)
412
+ value = block.call(convert_key(key), self[key], value)
413
+ end
414
+ regular_writer(convert_key(key), convert_value(value))
415
+ end
416
+ end
417
+ end
390
418
  end
391
419
  end
392
420
 
@@ -13,3 +13,4 @@ require "active_support/lazy_load_hooks"
13
13
 
14
14
  ActiveSupport.run_load_hooks(:i18n)
15
15
  I18n.load_path << File.expand_path("locale/en.yml", __dir__)
16
+ I18n.load_path << File.expand_path("locale/en.rb", __dir__)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
- require "active_support/file_update_checker"
5
4
  require "active_support/core_ext/array/wrap"
6
5
 
7
6
  # :enddoc:
@@ -13,6 +12,8 @@ module I18n
13
12
  config.i18n.load_path = []
14
13
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
15
14
 
15
+ config.eager_load_namespaces << I18n
16
+
16
17
  # Set the i18n configuration after initialization since a lot of
17
18
  # configuration is still usually done in application initializers.
18
19
  config.after_initialize do |app|
@@ -47,8 +48,10 @@ module I18n
47
48
  app.config.i18n.load_path.unshift(*value.flat_map(&:existent))
48
49
  when :load_path
49
50
  I18n.load_path += value
51
+ when :raise_on_missing_translations
52
+ forward_raise_on_missing_translations_config(app)
50
53
  else
51
- I18n.send("#{setting}=", value)
54
+ I18n.public_send("#{setting}=", value)
52
55
  end
53
56
  end
54
57
 
@@ -61,8 +64,6 @@ module I18n
61
64
  reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
62
65
  I18n.load_path.keep_if { |p| File.exist?(p) }
63
66
  I18n.load_path |= reloadable_paths.flat_map(&:existent)
64
-
65
- I18n.reload!
66
67
  end
67
68
 
68
69
  app.reloaders << reloader
@@ -74,6 +75,16 @@ module I18n
74
75
  @i18n_inited = true
75
76
  end
76
77
 
78
+ def self.forward_raise_on_missing_translations_config(app)
79
+ ActiveSupport.on_load(:action_view) do
80
+ self.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
81
+ end
82
+
83
+ ActiveSupport.on_load(:action_controller) do
84
+ AbstractController::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
85
+ end
86
+ end
87
+
77
88
  def self.include_fallbacks_module
78
89
  I18n.backend.class.include(I18n::Backend::Fallbacks)
79
90
  end
@@ -91,10 +102,6 @@ module I18n
91
102
  [I18n.default_locale]
92
103
  end
93
104
 
94
- if args.empty? || args.first.is_a?(Hash)
95
- args.unshift I18n.default_locale
96
- end
97
-
98
105
  I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
99
106
  end
100
107