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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +678 -348
- data/MIT-LICENSE +2 -2
- data/README.rdoc +2 -3
- data/lib/active_support/array_inquirer.rb +44 -0
- data/lib/active_support/backtrace_cleaner.rb +1 -1
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +36 -22
- data/lib/active_support/cache/mem_cache_store.rb +63 -54
- data/lib/active_support/cache/memory_store.rb +16 -21
- data/lib/active_support/cache/null_store.rb +1 -4
- data/lib/active_support/cache/strategy/local_cache.rb +31 -20
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -4
- data/lib/active_support/cache.rb +71 -87
- data/lib/active_support/callbacks.rb +109 -113
- data/lib/active_support/concern.rb +1 -1
- data/lib/active_support/concurrency/latch.rb +11 -12
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +1 -0
- data/lib/active_support/core_ext/array/access.rb +27 -1
- data/lib/active_support/core_ext/array/conversions.rb +6 -4
- data/lib/active_support/core_ext/array/grouping.rb +9 -18
- data/lib/active_support/core_ext/array/inquiry.rb +17 -0
- data/lib/active_support/core_ext/array/wrap.rb +5 -4
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
- data/lib/active_support/core_ext/class/attribute.rb +10 -9
- data/lib/active_support/core_ext/class/subclasses.rb +3 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/date/blank.rb +12 -0
- data/lib/active_support/core_ext/date/calculations.rb +1 -1
- data/lib/active_support/core_ext/date/conversions.rb +7 -6
- data/lib/active_support/core_ext/date.rb +1 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +100 -27
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
- data/lib/active_support/core_ext/date_time/blank.rb +12 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +14 -8
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
- data/lib/active_support/core_ext/date_time.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +75 -25
- data/lib/active_support/core_ext/file/atomic.rb +30 -25
- data/lib/active_support/core_ext/hash/conversions.rb +22 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +9 -8
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +25 -21
- data/lib/active_support/core_ext/hash/slice.rb +1 -1
- data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
- data/lib/active_support/core_ext/integer/time.rb +2 -2
- data/lib/active_support/core_ext/kernel/concern.rb +2 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
- data/lib/active_support/core_ext/kernel/reporting.rb +2 -84
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +5 -2
- data/lib/active_support/core_ext/marshal.rb +7 -9
- data/lib/active_support/core_ext/module/aliasing.rb +6 -1
- data/lib/active_support/core_ext/module/anonymous.rb +10 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
- data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
- data/lib/active_support/core_ext/module/concerning.rb +4 -4
- data/lib/active_support/core_ext/module/delegation.rb +11 -20
- data/lib/active_support/core_ext/module/deprecation.rb +2 -2
- data/lib/active_support/core_ext/module/introspection.rb +8 -2
- data/lib/active_support/core_ext/module/method_transplanting.rb +3 -13
- data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
- data/lib/active_support/core_ext/module/remove_method.rb +23 -0
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/name_error.rb +15 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +78 -77
- data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
- data/lib/active_support/core_ext/numeric/time.rb +26 -6
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/blank.rb +15 -3
- data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
- data/lib/active_support/core_ext/object/duplicable.rb +7 -12
- data/lib/active_support/core_ext/object/inclusion.rb +2 -2
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +15 -7
- data/lib/active_support/core_ext/object/to_query.rb +1 -1
- data/lib/active_support/core_ext/object/try.rb +67 -21
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/object.rb +0 -1
- data/lib/active_support/core_ext/range/conversions.rb +18 -6
- data/lib/active_support/core_ext/range/each.rb +16 -18
- data/lib/active_support/core_ext/range/include_range.rb +20 -20
- data/lib/active_support/core_ext/securerandom.rb +23 -0
- data/lib/active_support/core_ext/string/behavior.rb +1 -1
- data/lib/active_support/core_ext/string/conversions.rb +3 -2
- data/lib/active_support/core_ext/string/filters.rb +1 -2
- data/lib/active_support/core_ext/string/inflections.rb +32 -5
- data/lib/active_support/core_ext/string/multibyte.rb +11 -7
- data/lib/active_support/core_ext/string/output_safety.rb +12 -14
- data/lib/active_support/core_ext/string/strip.rb +3 -6
- data/lib/active_support/core_ext/struct.rb +3 -6
- data/lib/active_support/core_ext/time/calculations.rb +18 -9
- data/lib/active_support/core_ext/time/conversions.rb +4 -2
- data/lib/active_support/core_ext/time/marshal.rb +2 -29
- data/lib/active_support/core_ext/time/zones.rb +36 -4
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext/uri.rb +1 -3
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/dependencies/interlock.rb +55 -0
- data/lib/active_support/dependencies.rb +88 -95
- data/lib/active_support/deprecation/behaviors.rb +15 -1
- data/lib/active_support/deprecation/instance_delegator.rb +13 -0
- data/lib/active_support/deprecation/method_wrappers.rb +42 -16
- data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
- data/lib/active_support/deprecation/reporting.rb +23 -5
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/duration/iso8601_parser.rb +122 -0
- data/lib/active_support/duration/iso8601_serializer.rb +51 -0
- data/lib/active_support/duration.rb +90 -15
- data/lib/active_support/evented_file_update_checker.rb +199 -0
- data/lib/active_support/execution_wrapper.rb +126 -0
- data/lib/active_support/executor.rb +6 -0
- data/lib/active_support/file_update_checker.rb +23 -3
- data/lib/active_support/gem_version.rb +5 -5
- data/lib/active_support/gzip.rb +1 -1
- data/lib/active_support/hash_with_indifferent_access.rb +40 -11
- data/lib/active_support/i18n_railtie.rb +25 -4
- data/lib/active_support/inflector/inflections.rb +36 -5
- data/lib/active_support/inflector/methods.rb +97 -90
- data/lib/active_support/inflector/transliterate.rb +36 -21
- data/lib/active_support/json/decoding.rb +11 -10
- data/lib/active_support/json/encoding.rb +1 -51
- data/lib/active_support/key_generator.rb +7 -9
- data/lib/active_support/lazy_load_hooks.rb +46 -18
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber/test_helper.rb +3 -3
- data/lib/active_support/log_subscriber.rb +1 -1
- data/lib/active_support/logger.rb +3 -4
- data/lib/active_support/logger_silence.rb +2 -1
- data/lib/active_support/logger_thread_safe_level.rb +2 -3
- data/lib/active_support/message_encryptor.rb +7 -7
- data/lib/active_support/message_verifier.rb +70 -8
- data/lib/active_support/multibyte/chars.rb +12 -3
- data/lib/active_support/multibyte/unicode.rb +44 -21
- data/lib/active_support/notifications/fanout.rb +5 -5
- data/lib/active_support/notifications/instrumenter.rb +20 -2
- data/lib/active_support/notifications.rb +2 -2
- data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
- data/lib/active_support/number_helper.rb +90 -67
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +15 -1
- data/lib/active_support/per_thread_registry.rb +3 -0
- data/lib/active_support/rails.rb +2 -2
- data/lib/active_support/railtie.rb +6 -1
- data/lib/active_support/reloader.rb +129 -0
- data/lib/active_support/rescuable.rb +101 -47
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/subscriber.rb +5 -10
- data/lib/active_support/tagged_logging.rb +8 -7
- data/lib/active_support/test_case.rb +17 -29
- data/lib/active_support/testing/assertions.rb +15 -13
- data/lib/active_support/testing/deprecation.rb +9 -8
- data/lib/active_support/testing/file_fixtures.rb +34 -0
- data/lib/active_support/testing/isolation.rb +22 -8
- data/lib/active_support/testing/method_call_assertions.rb +41 -0
- data/lib/active_support/testing/stream.rb +42 -0
- data/lib/active_support/testing/time_helpers.rb +3 -1
- data/lib/active_support/time_with_zone.rb +123 -33
- data/lib/active_support/values/time_zone.rb +101 -47
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support.rb +11 -6
- metadata +36 -17
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
- data/lib/active_support/core_ext/date_time/zones.rb +0 -6
- data/lib/active_support/core_ext/object/itself.rb +0 -15
- 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
|
@@ -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
|
-
#
|
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
|
-
|
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 =
|
9
|
-
MINOR =
|
10
|
-
TINY =
|
11
|
-
PRE = "
|
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
|
data/lib/active_support/gzip.rb
CHANGED
@@ -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(
|
68
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
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] # =>
|
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.
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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 '
|
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__ =
|
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 = [], [],
|
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
|
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
|