activesupport 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 (236) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1018 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/lib/active_support.rb +99 -0
  6. data/lib/active_support/all.rb +3 -0
  7. data/lib/active_support/array_inquirer.rb +44 -0
  8. data/lib/active_support/backtrace_cleaner.rb +103 -0
  9. data/lib/active_support/benchmarkable.rb +49 -0
  10. data/lib/active_support/builder.rb +6 -0
  11. data/lib/active_support/cache.rb +701 -0
  12. data/lib/active_support/cache/file_store.rb +204 -0
  13. data/lib/active_support/cache/mem_cache_store.rb +207 -0
  14. data/lib/active_support/cache/memory_store.rb +167 -0
  15. data/lib/active_support/cache/null_store.rb +41 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +172 -0
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  18. data/lib/active_support/callbacks.rb +791 -0
  19. data/lib/active_support/concern.rb +142 -0
  20. data/lib/active_support/concurrency/latch.rb +26 -0
  21. data/lib/active_support/concurrency/share_lock.rb +226 -0
  22. data/lib/active_support/configurable.rb +148 -0
  23. data/lib/active_support/core_ext.rb +4 -0
  24. data/lib/active_support/core_ext/array.rb +7 -0
  25. data/lib/active_support/core_ext/array/access.rb +90 -0
  26. data/lib/active_support/core_ext/array/conversions.rb +211 -0
  27. data/lib/active_support/core_ext/array/extract_options.rb +29 -0
  28. data/lib/active_support/core_ext/array/grouping.rb +107 -0
  29. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  30. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +46 -0
  32. data/lib/active_support/core_ext/benchmark.rb +14 -0
  33. data/lib/active_support/core_ext/big_decimal.rb +1 -0
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  35. data/lib/active_support/core_ext/class.rb +2 -0
  36. data/lib/active_support/core_ext/class/attribute.rb +128 -0
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -0
  38. data/lib/active_support/core_ext/class/subclasses.rb +41 -0
  39. data/lib/active_support/core_ext/date.rb +5 -0
  40. data/lib/active_support/core_ext/date/acts_like.rb +8 -0
  41. data/lib/active_support/core_ext/date/blank.rb +12 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +143 -0
  43. data/lib/active_support/core_ext/date/conversions.rb +95 -0
  44. data/lib/active_support/core_ext/date/zones.rb +6 -0
  45. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  46. data/lib/active_support/core_ext/date_and_time/compatibility.rb +14 -0
  47. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  48. data/lib/active_support/core_ext/date_time.rb +5 -0
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +14 -0
  50. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +199 -0
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/conversions.rb +105 -0
  54. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  55. data/lib/active_support/core_ext/enumerable.rb +146 -0
  56. data/lib/active_support/core_ext/file.rb +1 -0
  57. data/lib/active_support/core_ext/file/atomic.rb +68 -0
  58. data/lib/active_support/core_ext/hash.rb +9 -0
  59. data/lib/active_support/core_ext/hash/compact.rb +24 -0
  60. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +38 -0
  62. data/lib/active_support/core_ext/hash/except.rb +22 -0
  63. data/lib/active_support/core_ext/hash/indifferent_access.rb +23 -0
  64. data/lib/active_support/core_ext/hash/keys.rb +170 -0
  65. data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -0
  66. data/lib/active_support/core_ext/hash/slice.rb +48 -0
  67. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  68. data/lib/active_support/core_ext/integer.rb +3 -0
  69. data/lib/active_support/core_ext/integer/inflections.rb +29 -0
  70. data/lib/active_support/core_ext/integer/multiple.rb +10 -0
  71. data/lib/active_support/core_ext/integer/time.rb +29 -0
  72. data/lib/active_support/core_ext/kernel.rb +4 -0
  73. data/lib/active_support/core_ext/kernel/agnostics.rb +11 -0
  74. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  75. data/lib/active_support/core_ext/kernel/debugger.rb +3 -0
  76. data/lib/active_support/core_ext/kernel/reporting.rb +43 -0
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +6 -0
  78. data/lib/active_support/core_ext/load_error.rb +31 -0
  79. data/lib/active_support/core_ext/marshal.rb +22 -0
  80. data/lib/active_support/core_ext/module.rb +12 -0
  81. data/lib/active_support/core_ext/module/aliasing.rb +74 -0
  82. data/lib/active_support/core_ext/module/anonymous.rb +28 -0
  83. data/lib/active_support/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  87. data/lib/active_support/core_ext/module/delegation.rb +216 -0
  88. data/lib/active_support/core_ext/module/deprecation.rb +23 -0
  89. data/lib/active_support/core_ext/module/introspection.rb +68 -0
  90. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  91. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  92. data/lib/active_support/core_ext/module/reachable.rb +8 -0
  93. data/lib/active_support/core_ext/module/remove_method.rb +35 -0
  94. data/lib/active_support/core_ext/name_error.rb +31 -0
  95. data/lib/active_support/core_ext/numeric.rb +4 -0
  96. data/lib/active_support/core_ext/numeric/bytes.rb +64 -0
  97. data/lib/active_support/core_ext/numeric/conversions.rb +144 -0
  98. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  99. data/lib/active_support/core_ext/numeric/time.rb +74 -0
  100. data/lib/active_support/core_ext/object.rb +14 -0
  101. data/lib/active_support/core_ext/object/acts_like.rb +10 -0
  102. data/lib/active_support/core_ext/object/blank.rb +143 -0
  103. data/lib/active_support/core_ext/object/conversions.rb +4 -0
  104. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  105. data/lib/active_support/core_ext/object/duplicable.rb +124 -0
  106. data/lib/active_support/core_ext/object/inclusion.rb +27 -0
  107. data/lib/active_support/core_ext/object/instance_variables.rb +28 -0
  108. data/lib/active_support/core_ext/object/json.rb +205 -0
  109. data/lib/active_support/core_ext/object/to_param.rb +1 -0
  110. data/lib/active_support/core_ext/object/to_query.rb +84 -0
  111. data/lib/active_support/core_ext/object/try.rb +146 -0
  112. data/lib/active_support/core_ext/object/with_options.rb +69 -0
  113. data/lib/active_support/core_ext/range.rb +4 -0
  114. data/lib/active_support/core_ext/range/conversions.rb +31 -0
  115. data/lib/active_support/core_ext/range/each.rb +21 -0
  116. data/lib/active_support/core_ext/range/include_range.rb +23 -0
  117. data/lib/active_support/core_ext/range/overlaps.rb +8 -0
  118. data/lib/active_support/core_ext/regexp.rb +5 -0
  119. data/lib/active_support/core_ext/securerandom.rb +23 -0
  120. data/lib/active_support/core_ext/string.rb +13 -0
  121. data/lib/active_support/core_ext/string/access.rb +104 -0
  122. data/lib/active_support/core_ext/string/behavior.rb +6 -0
  123. data/lib/active_support/core_ext/string/conversions.rb +57 -0
  124. data/lib/active_support/core_ext/string/exclude.rb +11 -0
  125. data/lib/active_support/core_ext/string/filters.rb +102 -0
  126. data/lib/active_support/core_ext/string/indent.rb +43 -0
  127. data/lib/active_support/core_ext/string/inflections.rb +244 -0
  128. data/lib/active_support/core_ext/string/inquiry.rb +13 -0
  129. data/lib/active_support/core_ext/string/multibyte.rb +53 -0
  130. data/lib/active_support/core_ext/string/output_safety.rb +260 -0
  131. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -0
  132. data/lib/active_support/core_ext/string/strip.rb +23 -0
  133. data/lib/active_support/core_ext/string/zones.rb +14 -0
  134. data/lib/active_support/core_ext/struct.rb +3 -0
  135. data/lib/active_support/core_ext/time.rb +5 -0
  136. data/lib/active_support/core_ext/time/acts_like.rb +8 -0
  137. data/lib/active_support/core_ext/time/calculations.rb +290 -0
  138. data/lib/active_support/core_ext/time/compatibility.rb +14 -0
  139. data/lib/active_support/core_ext/time/conversions.rb +67 -0
  140. data/lib/active_support/core_ext/time/marshal.rb +3 -0
  141. data/lib/active_support/core_ext/time/zones.rb +111 -0
  142. data/lib/active_support/core_ext/uri.rb +24 -0
  143. data/lib/active_support/dependencies.rb +755 -0
  144. data/lib/active_support/dependencies/autoload.rb +77 -0
  145. data/lib/active_support/dependencies/interlock.rb +55 -0
  146. data/lib/active_support/deprecation.rb +43 -0
  147. data/lib/active_support/deprecation/behaviors.rb +90 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +37 -0
  149. data/lib/active_support/deprecation/method_wrappers.rb +70 -0
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +149 -0
  151. data/lib/active_support/deprecation/reporting.rb +112 -0
  152. data/lib/active_support/descendants_tracker.rb +60 -0
  153. data/lib/active_support/duration.rb +235 -0
  154. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  155. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  156. data/lib/active_support/evented_file_update_checker.rb +199 -0
  157. data/lib/active_support/execution_wrapper.rb +126 -0
  158. data/lib/active_support/executor.rb +6 -0
  159. data/lib/active_support/file_update_checker.rb +157 -0
  160. data/lib/active_support/gem_version.rb +15 -0
  161. data/lib/active_support/gzip.rb +36 -0
  162. data/lib/active_support/hash_with_indifferent_access.rb +329 -0
  163. data/lib/active_support/i18n.rb +13 -0
  164. data/lib/active_support/i18n_railtie.rb +115 -0
  165. data/lib/active_support/inflections.rb +70 -0
  166. data/lib/active_support/inflector.rb +7 -0
  167. data/lib/active_support/inflector/inflections.rb +242 -0
  168. data/lib/active_support/inflector/methods.rb +390 -0
  169. data/lib/active_support/inflector/transliterate.rb +112 -0
  170. data/lib/active_support/json.rb +2 -0
  171. data/lib/active_support/json/decoding.rb +74 -0
  172. data/lib/active_support/json/encoding.rb +127 -0
  173. data/lib/active_support/key_generator.rb +71 -0
  174. data/lib/active_support/lazy_load_hooks.rb +76 -0
  175. data/lib/active_support/locale/en.yml +135 -0
  176. data/lib/active_support/log_subscriber.rb +109 -0
  177. data/lib/active_support/log_subscriber/test_helper.rb +104 -0
  178. data/lib/active_support/logger.rb +106 -0
  179. data/lib/active_support/logger_silence.rb +28 -0
  180. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  181. data/lib/active_support/message_encryptor.rb +114 -0
  182. data/lib/active_support/message_verifier.rb +134 -0
  183. data/lib/active_support/multibyte.rb +21 -0
  184. data/lib/active_support/multibyte/chars.rb +231 -0
  185. data/lib/active_support/multibyte/unicode.rb +413 -0
  186. data/lib/active_support/notifications.rb +212 -0
  187. data/lib/active_support/notifications/fanout.rb +157 -0
  188. data/lib/active_support/notifications/instrumenter.rb +91 -0
  189. data/lib/active_support/number_helper.rb +368 -0
  190. data/lib/active_support/number_helper/number_converter.rb +182 -0
  191. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  192. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  193. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  194. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  195. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  196. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  197. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  198. data/lib/active_support/option_merger.rb +25 -0
  199. data/lib/active_support/ordered_hash.rb +48 -0
  200. data/lib/active_support/ordered_options.rb +81 -0
  201. data/lib/active_support/per_thread_registry.rb +58 -0
  202. data/lib/active_support/proxy_object.rb +13 -0
  203. data/lib/active_support/rails.rb +27 -0
  204. data/lib/active_support/railtie.rb +51 -0
  205. data/lib/active_support/reloader.rb +129 -0
  206. data/lib/active_support/rescuable.rb +173 -0
  207. data/lib/active_support/security_utils.rb +27 -0
  208. data/lib/active_support/string_inquirer.rb +26 -0
  209. data/lib/active_support/subscriber.rb +120 -0
  210. data/lib/active_support/tagged_logging.rb +77 -0
  211. data/lib/active_support/test_case.rb +88 -0
  212. data/lib/active_support/testing/assertions.rb +99 -0
  213. data/lib/active_support/testing/autorun.rb +5 -0
  214. data/lib/active_support/testing/constant_lookup.rb +50 -0
  215. data/lib/active_support/testing/declarative.rb +26 -0
  216. data/lib/active_support/testing/deprecation.rb +36 -0
  217. data/lib/active_support/testing/file_fixtures.rb +34 -0
  218. data/lib/active_support/testing/isolation.rb +115 -0
  219. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  220. data/lib/active_support/testing/setup_and_teardown.rb +50 -0
  221. data/lib/active_support/testing/stream.rb +42 -0
  222. data/lib/active_support/testing/tagged_logging.rb +25 -0
  223. data/lib/active_support/testing/time_helpers.rb +136 -0
  224. data/lib/active_support/time.rb +18 -0
  225. data/lib/active_support/time_with_zone.rb +511 -0
  226. data/lib/active_support/values/time_zone.rb +484 -0
  227. data/lib/active_support/values/unicode_tables.dat +0 -0
  228. data/lib/active_support/version.rb +8 -0
  229. data/lib/active_support/xml_mini.rb +209 -0
  230. data/lib/active_support/xml_mini/jdom.rb +181 -0
  231. data/lib/active_support/xml_mini/libxml.rb +77 -0
  232. data/lib/active_support/xml_mini/libxmlsax.rb +82 -0
  233. data/lib/active_support/xml_mini/nokogiri.rb +81 -0
  234. data/lib/active_support/xml_mini/nokogirisax.rb +85 -0
  235. data/lib/active_support/xml_mini/rexml.rb +128 -0
  236. metadata +349 -0
@@ -0,0 +1,51 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'active_support/core_ext/hash/transform_values'
3
+
4
+ module ActiveSupport
5
+ class Duration
6
+ # Serializes duration to string according to ISO 8601 Duration format.
7
+ class ISO8601Serializer
8
+ def initialize(duration, precision: nil)
9
+ @duration = duration
10
+ @precision = precision
11
+ end
12
+
13
+ # Builds and returns output string.
14
+ def serialize
15
+ output = 'P'
16
+ parts, sign = normalize
17
+ output << "#{parts[:years]}Y" if parts.key?(:years)
18
+ output << "#{parts[:months]}M" if parts.key?(:months)
19
+ output << "#{parts[:weeks]}W" if parts.key?(:weeks)
20
+ output << "#{parts[:days]}D" if parts.key?(:days)
21
+ time = ''
22
+ time << "#{parts[:hours]}H" if parts.key?(:hours)
23
+ time << "#{parts[:minutes]}M" if parts.key?(:minutes)
24
+ if parts.key?(:seconds)
25
+ time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
26
+ end
27
+ output << "T#{time}" if time.present?
28
+ "#{sign}#{output}"
29
+ end
30
+
31
+ private
32
+
33
+ # Return pair of duration's parts and whole duration sign.
34
+ # Parts are summarized (as they can become repetitive due to addition, etc).
35
+ # Zero parts are removed as not significant.
36
+ # If all parts are negative it will negate all of them and return minus as a sign.
37
+ def normalize
38
+ parts = @duration.parts.each_with_object(Hash.new(0)) do |(k,v),p|
39
+ p[k] += v unless v.zero?
40
+ end
41
+ # If all parts are negative - let's make a negative duration
42
+ sign = ''
43
+ if parts.values.all? { |v| v < 0 }
44
+ sign = '-'
45
+ parts.transform_values!(&:-@)
46
+ end
47
+ [parts, sign]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -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
@@ -0,0 +1,157 @@
1
+ require 'active_support/core_ext/time/calculations'
2
+
3
+ module ActiveSupport
4
+ # FileUpdateChecker specifies the API used by Rails to watch files
5
+ # and control reloading. The API depends on four methods:
6
+ #
7
+ # * +initialize+ which expects two parameters and one block as
8
+ # described below.
9
+ #
10
+ # * +updated?+ which returns a boolean if there were updates in
11
+ # the filesystem or not.
12
+ #
13
+ # * +execute+ which executes the given block on initialization
14
+ # and updates the latest watched files and timestamp.
15
+ #
16
+ # * +execute_if_updated+ which just executes the block if it was updated.
17
+ #
18
+ # After initialization, a call to +execute_if_updated+ must execute
19
+ # the block only if there was really a change in the filesystem.
20
+ #
21
+ # This class is used by Rails to reload the I18n framework whenever
22
+ # they are changed upon a new request.
23
+ #
24
+ # i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
25
+ # I18n.reload!
26
+ # end
27
+ #
28
+ # ActiveSupport::Reloader.to_prepare do
29
+ # i18n_reloader.execute_if_updated
30
+ # end
31
+ class FileUpdateChecker
32
+ # It accepts two parameters on initialization. The first is an array
33
+ # of files and the second is an optional hash of directories. The hash must
34
+ # have directories as keys and the value is an array of extensions to be
35
+ # watched under that directory.
36
+ #
37
+ # This method must also receive a block that will be called once a path
38
+ # changes. The array of files and list of directories cannot be changed
39
+ # after FileUpdateChecker has been initialized.
40
+ def initialize(files, dirs = {}, &block)
41
+ @files = files.freeze
42
+ @glob = compile_glob(dirs)
43
+ @block = block
44
+
45
+ @watched = nil
46
+ @updated_at = nil
47
+
48
+ @last_watched = watched
49
+ @last_update_at = updated_at(@last_watched)
50
+ end
51
+
52
+ # Check if any of the entries were updated. If so, the watched and/or
53
+ # updated_at values are cached until the block is executed via +execute+
54
+ # or +execute_if_updated+.
55
+ def updated?
56
+ current_watched = watched
57
+ if @last_watched.size != current_watched.size
58
+ @watched = current_watched
59
+ true
60
+ else
61
+ current_updated_at = updated_at(current_watched)
62
+ if @last_update_at < current_updated_at
63
+ @watched = current_watched
64
+ @updated_at = current_updated_at
65
+ true
66
+ else
67
+ false
68
+ end
69
+ end
70
+ end
71
+
72
+ # Executes the given block and updates the latest watched files and
73
+ # timestamp.
74
+ def execute
75
+ @last_watched = watched
76
+ @last_update_at = updated_at(@last_watched)
77
+ @block.call
78
+ ensure
79
+ @watched = nil
80
+ @updated_at = nil
81
+ end
82
+
83
+ # Execute the block given if updated.
84
+ def execute_if_updated
85
+ if updated?
86
+ yield if block_given?
87
+ execute
88
+ true
89
+ else
90
+ false
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def watched
97
+ @watched || begin
98
+ all = @files.select { |f| File.exist?(f) }
99
+ all.concat(Dir[@glob]) if @glob
100
+ all
101
+ end
102
+ end
103
+
104
+ def updated_at(paths)
105
+ @updated_at || max_mtime(paths) || Time.at(0)
106
+ end
107
+
108
+ # This method returns the maximum mtime of the files in +paths+, or +nil+
109
+ # if the array is empty.
110
+ #
111
+ # Files with a mtime in the future are ignored. Such abnormal situation
112
+ # can happen for example if the user changes the clock by hand. It is
113
+ # healthy to consider this edge case because with mtimes in the future
114
+ # reloading is not triggered.
115
+ def max_mtime(paths)
116
+ time_now = Time.now
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
135
+ end
136
+
137
+ def compile_glob(hash)
138
+ hash.freeze # Freeze so changes aren't accidentally pushed
139
+ return if hash.empty?
140
+
141
+ globs = hash.map do |key, value|
142
+ "#{escape(key)}/**/*#{compile_ext(value)}"
143
+ end
144
+ "{#{globs.join(",")}}"
145
+ end
146
+
147
+ def escape(key)
148
+ key.gsub(',','\,')
149
+ end
150
+
151
+ def compile_ext(array)
152
+ array = Array(array)
153
+ return if array.empty?
154
+ ".{#{array.join(",")}}"
155
+ end
156
+ end
157
+ end