activesupport 4.2.8 → 5.2.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 (256) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +424 -373
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +4 -5
  5. data/lib/active_support/all.rb +5 -3
  6. data/lib/active_support/array_inquirer.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +7 -5
  8. data/lib/active_support/benchmarkable.rb +6 -4
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +41 -35
  11. data/lib/active_support/cache/mem_cache_store.rb +91 -91
  12. data/lib/active_support/cache/memory_store.rb +27 -30
  13. data/lib/active_support/cache/null_store.rb +7 -8
  14. data/lib/active_support/cache/redis_cache_store.rb +466 -0
  15. data/lib/active_support/cache/strategy/local_cache.rb +67 -34
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  17. data/lib/active_support/cache.rb +287 -196
  18. data/lib/active_support/callbacks.rb +640 -590
  19. data/lib/active_support/concern.rb +11 -5
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  21. data/lib/active_support/concurrency/share_lock.rb +227 -0
  22. data/lib/active_support/configurable.rb +8 -5
  23. data/lib/active_support/core_ext/array/access.rb +29 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +22 -18
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -6
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -22
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -6
  37. data/lib/active_support/core_ext/class.rb +4 -3
  38. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  39. data/lib/active_support/core_ext/date/blank.rb +14 -0
  40. data/lib/active_support/core_ext/date/calculations.rb +11 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +25 -23
  42. data/lib/active_support/core_ext/date/zones.rb +4 -2
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +170 -58
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -17
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
  47. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  48. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  49. data/lib/active_support/core_ext/date_time/calculations.rb +29 -16
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +14 -1
  51. data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
  52. data/lib/active_support/core_ext/date_time.rb +7 -5
  53. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +101 -33
  56. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/hash/compact.rb +14 -9
  59. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +11 -8
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +33 -27
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +8 -8
  66. data/lib/active_support/core_ext/hash/transform_values.rb +14 -5
  67. data/lib/active_support/core_ext/hash.rb +11 -9
  68. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  69. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  70. data/lib/active_support/core_ext/integer/time.rb +11 -18
  71. data/lib/active_support/core_ext/integer.rb +5 -3
  72. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +6 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +8 -8
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  85. data/lib/active_support/core_ext/module/delegation.rb +99 -29
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -9
  88. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  89. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  90. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  91. data/lib/active_support/core_ext/module.rb +14 -11
  92. data/lib/active_support/core_ext/name_error.rb +22 -2
  93. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  94. data/lib/active_support/core_ext/numeric/conversions.rb +78 -81
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  97. data/lib/active_support/core_ext/numeric.rb +6 -3
  98. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  99. data/lib/active_support/core_ext/object/blank.rb +27 -2
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  102. data/lib/active_support/core_ext/object/duplicable.rb +41 -14
  103. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +49 -19
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  108. data/lib/active_support/core_ext/object/try.rb +69 -21
  109. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  110. data/lib/active_support/core_ext/object.rb +14 -13
  111. data/lib/active_support/core_ext/range/compare_range.rb +61 -0
  112. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  113. data/lib/active_support/core_ext/range/each.rb +19 -17
  114. data/lib/active_support/core_ext/range/include_range.rb +2 -22
  115. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  116. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  117. data/lib/active_support/core_ext/range.rb +7 -4
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string/access.rb +8 -6
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +7 -4
  123. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  124. data/lib/active_support/core_ext/string/filters.rb +6 -5
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +61 -24
  127. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  128. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  129. data/lib/active_support/core_ext/string/output_safety.rb +34 -38
  130. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  131. data/lib/active_support/core_ext/string/strip.rb +4 -5
  132. data/lib/active_support/core_ext/string/zones.rb +4 -2
  133. data/lib/active_support/core_ext/string.rb +15 -13
  134. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  135. data/lib/active_support/core_ext/time/calculations.rb +88 -52
  136. data/lib/active_support/core_ext/time/compatibility.rb +12 -1
  137. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  138. data/lib/active_support/core_ext/time/zones.rb +41 -7
  139. data/lib/active_support/core_ext/time.rb +7 -6
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -1
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies.rb +152 -161
  146. data/lib/active_support/deprecation/behaviors.rb +44 -11
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +66 -20
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/deprecation.rb +12 -9
  153. data/lib/active_support/descendants_tracker.rb +2 -0
  154. data/lib/active_support/digest.rb +20 -0
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/duration.rb +314 -38
  158. data/lib/active_support/encrypted_configuration.rb +49 -0
  159. data/lib/active_support/encrypted_file.rb +99 -0
  160. data/lib/active_support/evented_file_update_checker.rb +205 -0
  161. data/lib/active_support/execution_wrapper.rb +128 -0
  162. data/lib/active_support/executor.rb +8 -0
  163. data/lib/active_support/file_update_checker.rb +63 -37
  164. data/lib/active_support/gem_version.rb +5 -3
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +127 -28
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +37 -13
  169. data/lib/active_support/inflections.rb +13 -11
  170. data/lib/active_support/inflector/inflections.rb +61 -12
  171. data/lib/active_support/inflector/methods.rb +163 -136
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/inflector.rb +7 -5
  174. data/lib/active_support/json/decoding.rb +16 -13
  175. data/lib/active_support/json/encoding.rb +11 -58
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +25 -25
  178. data/lib/active_support/lazy_load_hooks.rb +50 -20
  179. data/lib/active_support/locale/en.yml +2 -0
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/log_subscriber.rb +13 -10
  182. data/lib/active_support/logger.rb +19 -7
  183. data/lib/active_support/logger_silence.rb +6 -4
  184. data/lib/active_support/logger_thread_safe_level.rb +7 -5
  185. data/lib/active_support/message_encryptor.rb +168 -53
  186. data/lib/active_support/message_verifier.rb +150 -17
  187. data/lib/active_support/messages/metadata.rb +71 -0
  188. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  189. data/lib/active_support/messages/rotator.rb +56 -0
  190. data/lib/active_support/multibyte/chars.rb +36 -23
  191. data/lib/active_support/multibyte/unicode.rb +100 -96
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +11 -9
  194. data/lib/active_support/notifications/instrumenter.rb +27 -7
  195. data/lib/active_support/notifications.rb +11 -7
  196. data/lib/active_support/number_helper/number_converter.rb +13 -11
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +94 -68
  206. data/lib/active_support/option_merger.rb +3 -1
  207. data/lib/active_support/ordered_hash.rb +6 -4
  208. data/lib/active_support/ordered_options.rb +23 -5
  209. data/lib/active_support/per_thread_registry.rb +9 -4
  210. data/lib/active_support/proxy_object.rb +2 -0
  211. data/lib/active_support/rails.rb +16 -8
  212. data/lib/active_support/railtie.rb +43 -9
  213. data/lib/active_support/reloader.rb +131 -0
  214. data/lib/active_support/rescuable.rb +108 -53
  215. data/lib/active_support/security_utils.rb +15 -11
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +21 -16
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +19 -47
  220. data/lib/active_support/testing/assertions.rb +137 -20
  221. data/lib/active_support/testing/autorun.rb +4 -2
  222. data/lib/active_support/testing/constant_lookup.rb +2 -1
  223. data/lib/active_support/testing/declarative.rb +3 -1
  224. data/lib/active_support/testing/deprecation.rb +14 -10
  225. data/lib/active_support/testing/file_fixtures.rb +36 -0
  226. data/lib/active_support/testing/isolation.rb +34 -25
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +3 -1
  231. data/lib/active_support/testing/time_helpers.rb +91 -23
  232. data/lib/active_support/time.rb +14 -12
  233. data/lib/active_support/time_with_zone.rb +182 -40
  234. data/lib/active_support/values/time_zone.rb +196 -61
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +3 -1
  237. data/lib/active_support/xml_mini/jdom.rb +116 -114
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +11 -9
  243. data/lib/active_support/xml_mini.rb +37 -37
  244. data/lib/active_support.rb +12 -11
  245. metadata +54 -25
  246. data/lib/active_support/concurrency/latch.rb +0 -27
  247. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  248. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  249. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  250. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  251. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  252. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  253. data/lib/active_support/core_ext/object/itself.rb +0 -15
  254. data/lib/active_support/core_ext/struct.rb +0 -6
  255. data/lib/active_support/core_ext/thread.rb +0 -86
  256. data/lib/active_support/core_ext/time/marshal.rb +0 -30
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "active_support/message_encryptor"
5
+
6
+ module ActiveSupport
7
+ class EncryptedFile
8
+ class MissingContentError < RuntimeError
9
+ def initialize(content_path)
10
+ super "Missing encrypted content file in #{content_path}."
11
+ end
12
+ end
13
+
14
+ class MissingKeyError < RuntimeError
15
+ def initialize(key_path:, env_key:)
16
+ super \
17
+ "Missing encryption key to decrypt file with. " +
18
+ "Ask your team for your master key and write it to #{key_path} or put it in the ENV['#{env_key}']."
19
+ end
20
+ end
21
+
22
+ CIPHER = "aes-128-gcm"
23
+
24
+ def self.generate_key
25
+ SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
26
+ end
27
+
28
+
29
+ attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
30
+
31
+ def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
32
+ @content_path, @key_path = Pathname.new(content_path), Pathname.new(key_path)
33
+ @env_key, @raise_if_missing_key = env_key, raise_if_missing_key
34
+ end
35
+
36
+ def key
37
+ read_env_key || read_key_file || handle_missing_key
38
+ end
39
+
40
+ def read
41
+ if !key.nil? && content_path.exist?
42
+ decrypt content_path.binread
43
+ else
44
+ raise MissingContentError, content_path
45
+ end
46
+ end
47
+
48
+ def write(contents)
49
+ IO.binwrite "#{content_path}.tmp", encrypt(contents)
50
+ FileUtils.mv "#{content_path}.tmp", content_path
51
+ end
52
+
53
+ def change(&block)
54
+ writing read, &block
55
+ end
56
+
57
+
58
+ private
59
+ def writing(contents)
60
+ tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
61
+ tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
62
+ tmp_path.binwrite contents
63
+
64
+ yield tmp_path
65
+
66
+ updated_contents = tmp_path.binread
67
+
68
+ write(updated_contents) if updated_contents != contents
69
+ ensure
70
+ FileUtils.rm(tmp_path) if tmp_path.exist?
71
+ end
72
+
73
+
74
+ def encrypt(contents)
75
+ encryptor.encrypt_and_sign contents
76
+ end
77
+
78
+ def decrypt(contents)
79
+ encryptor.decrypt_and_verify contents
80
+ end
81
+
82
+ def encryptor
83
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
84
+ end
85
+
86
+
87
+ def read_env_key
88
+ ENV[env_key]
89
+ end
90
+
91
+ def read_key_file
92
+ key_path.binread.strip if key_path.exist?
93
+ end
94
+
95
+ def handle_missing_key
96
+ raise MissingKeyError, key_path: key_path, env_key: env_key if raise_if_missing_key
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "pathname"
5
+ require "concurrent/atomic/atomic_boolean"
6
+
7
+ module ActiveSupport
8
+ # Allows you to "listen" to changes in a file system.
9
+ # The evented file updater does not hit disk when checking for updates
10
+ # instead it uses platform specific file system events to trigger a change
11
+ # in state.
12
+ #
13
+ # The file checker takes an array of files to watch or a hash specifying directories
14
+ # and file extensions to watch. It also takes a block that is called when
15
+ # EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated
16
+ # is run and there have been changes to the file system.
17
+ #
18
+ # Note: Forking will cause the first call to `updated?` to return `true`.
19
+ #
20
+ # Example:
21
+ #
22
+ # checker = ActiveSupport::EventedFileUpdateChecker.new(["/tmp/foo"]) { puts "changed" }
23
+ # checker.updated?
24
+ # # => false
25
+ # checker.execute_if_updated
26
+ # # => nil
27
+ #
28
+ # FileUtils.touch("/tmp/foo")
29
+ #
30
+ # checker.updated?
31
+ # # => true
32
+ # checker.execute_if_updated
33
+ # # => "changed"
34
+ #
35
+ class EventedFileUpdateChecker #:nodoc: all
36
+ def initialize(files, dirs = {}, &block)
37
+ unless block
38
+ raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
39
+ end
40
+
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!
68
+ end
69
+
70
+ def updated?
71
+ @boot_mutex.synchronize do
72
+ if @pid != Process.pid
73
+ boot!
74
+ @pid = Process.pid
75
+ @updated.make_true
76
+ end
77
+ end
78
+ @updated.true?
79
+ end
80
+
81
+ def execute
82
+ @updated.make_false
83
+ @block.call
84
+ end
85
+
86
+ def execute_if_updated
87
+ if updated?
88
+ yield if block_given?
89
+ execute
90
+ true
91
+ end
92
+ end
93
+
94
+ private
95
+ def boot!
96
+ Listen.to(*@dtw, &method(:changed)).start
97
+ end
98
+
99
+ def changed(modified, added, removed)
100
+ unless updated?
101
+ @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
102
+ end
103
+ end
104
+
105
+ def watching?(file)
106
+ file = @ph.xpath(file)
107
+
108
+ if @files.member?(file)
109
+ true
110
+ elsif file.directory?
111
+ false
112
+ else
113
+ ext = @ph.normalize_extension(file.extname)
114
+
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
123
+ end
124
+
125
+ def directories_to_watch
126
+ dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
127
+ dtw.compact!
128
+ dtw.uniq!
129
+
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) }
133
+ end
134
+
135
+ @ph.filter_out_descendants(dtw)
136
+ end
137
+
138
+ class PathHelper
139
+ def xpath(path)
140
+ Pathname.new(path).expand_path
141
+ end
142
+
143
+ def normalize_extension(ext)
144
+ ext.to_s.sub(/\A\./, "")
145
+ end
146
+
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
166
+
167
+ lcsp
168
+ end
169
+
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
175
+ end
176
+
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
180
+
181
+ dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length }
182
+ descendants = []
183
+
184
+ until dirs_sorted_by_nparts.empty?
185
+ dir = dirs_sorted_by_nparts.shift
186
+
187
+ dirs_sorted_by_nparts.reject! do |possible_descendant|
188
+ ascendant_of?(dir, possible_descendant) && descendants << possible_descendant
189
+ end
190
+ end
191
+
192
+ # Array#- preserves order.
193
+ dirs - descendants
194
+ end
195
+
196
+ private
197
+
198
+ def ascendant_of?(base, other)
199
+ base != other && other.ascend do |ascendant|
200
+ break true if base == ascendant
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/callbacks"
4
+
5
+ module ActiveSupport
6
+ class ExecutionWrapper
7
+ include ActiveSupport::Callbacks
8
+
9
+ Null = Object.new # :nodoc:
10
+ def Null.complete! # :nodoc:
11
+ end
12
+
13
+ define_callbacks :run
14
+ define_callbacks :complete
15
+
16
+ def self.to_run(*args, &block)
17
+ set_callback(:run, *args, &block)
18
+ end
19
+
20
+ def self.to_complete(*args, &block)
21
+ set_callback(:complete, *args, &block)
22
+ end
23
+
24
+ RunHook = Struct.new(:hook) do # :nodoc:
25
+ def before(target)
26
+ hook_state = target.send(:hook_state)
27
+ hook_state[hook] = hook.run
28
+ end
29
+ end
30
+
31
+ CompleteHook = Struct.new(:hook) do # :nodoc:
32
+ def before(target)
33
+ hook_state = target.send(:hook_state)
34
+ if hook_state.key?(hook)
35
+ hook.complete hook_state[hook]
36
+ end
37
+ end
38
+ alias after before
39
+ end
40
+
41
+ # Register an object to be invoked during both the +run+ and
42
+ # +complete+ steps.
43
+ #
44
+ # +hook.complete+ will be passed the value returned from +hook.run+,
45
+ # and will only be invoked if +run+ has previously been called.
46
+ # (Mostly, this means it won't be invoked if an exception occurs in
47
+ # a preceding +to_run+ block; all ordinary +to_complete+ blocks are
48
+ # invoked in that situation.)
49
+ def self.register_hook(hook, outer: false)
50
+ if outer
51
+ to_run RunHook.new(hook), prepend: true
52
+ to_complete :after, CompleteHook.new(hook)
53
+ else
54
+ to_run RunHook.new(hook)
55
+ to_complete CompleteHook.new(hook)
56
+ end
57
+ end
58
+
59
+ # Run this execution.
60
+ #
61
+ # Returns an instance, whose +complete!+ method *must* be invoked
62
+ # after the work has been performed.
63
+ #
64
+ # Where possible, prefer +wrap+.
65
+ def self.run!
66
+ if active?
67
+ Null
68
+ else
69
+ new.tap do |instance|
70
+ success = nil
71
+ begin
72
+ instance.run!
73
+ success = true
74
+ ensure
75
+ instance.complete! unless success
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # Perform the work in the supplied block as an execution.
82
+ def self.wrap
83
+ return yield if active?
84
+
85
+ instance = run!
86
+ begin
87
+ yield
88
+ ensure
89
+ instance.complete!
90
+ end
91
+ end
92
+
93
+ class << self # :nodoc:
94
+ attr_accessor :active
95
+ end
96
+
97
+ def self.inherited(other) # :nodoc:
98
+ super
99
+ other.active = Concurrent::Hash.new
100
+ end
101
+
102
+ self.active = Concurrent::Hash.new
103
+
104
+ def self.active? # :nodoc:
105
+ @active[Thread.current]
106
+ end
107
+
108
+ def run! # :nodoc:
109
+ self.class.active[Thread.current] = true
110
+ run_callbacks(:run)
111
+ end
112
+
113
+ # Complete this in-flight execution. This method *must* be called
114
+ # exactly once on the result of any call to +run!+.
115
+ #
116
+ # Where possible, prefer +wrap+.
117
+ def complete!
118
+ run_callbacks(:complete)
119
+ ensure
120
+ self.class.active.delete Thread.current
121
+ end
122
+
123
+ private
124
+ def hook_state
125
+ @_hook_state ||= {}
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/execution_wrapper"
4
+
5
+ module ActiveSupport
6
+ class Executor < ExecutionWrapper
7
+ end
8
+ end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/time/calculations"
4
+
1
5
  module ActiveSupport
2
6
  # FileUpdateChecker specifies the API used by Rails to watch files
3
7
  # and control reloading. The API depends on four methods:
@@ -23,7 +27,7 @@ module ActiveSupport
23
27
  # I18n.reload!
24
28
  # end
25
29
  #
26
- # ActionDispatch::Reloader.to_prepare do
30
+ # ActiveSupport::Reloader.to_prepare do
27
31
  # i18n_reloader.execute_if_updated
28
32
  # end
29
33
  class FileUpdateChecker
@@ -35,7 +39,11 @@ module ActiveSupport
35
39
  # This method must also receive a block that will be called once a path
36
40
  # changes. The array of files and list of directories cannot be changed
37
41
  # after FileUpdateChecker has been initialized.
38
- def initialize(files, dirs={}, &block)
42
+ def initialize(files, dirs = {}, &block)
43
+ unless block
44
+ raise ArgumentError, "A block is required to initialize a FileUpdateChecker"
45
+ end
46
+
39
47
  @files = files.freeze
40
48
  @glob = compile_glob(dirs)
41
49
  @block = block
@@ -81,6 +89,7 @@ module ActiveSupport
81
89
  # Execute the block given if updated.
82
90
  def execute_if_updated
83
91
  if updated?
92
+ yield if block_given?
84
93
  execute
85
94
  true
86
95
  else
@@ -90,48 +99,65 @@ module ActiveSupport
90
99
 
91
100
  private
92
101
 
93
- def watched
94
- @watched || begin
95
- all = @files.select { |f| File.exist?(f) }
96
- all.concat(Dir[@glob]) if @glob
97
- all
102
+ def watched
103
+ @watched || begin
104
+ all = @files.select { |f| File.exist?(f) }
105
+ all.concat(Dir[@glob]) if @glob
106
+ all
107
+ end
98
108
  end
99
- end
100
109
 
101
- def updated_at(paths)
102
- @updated_at || max_mtime(paths) || Time.at(0)
103
- end
110
+ def updated_at(paths)
111
+ @updated_at || max_mtime(paths) || Time.at(0)
112
+ end
104
113
 
105
- # This method returns the maximum mtime of the files in +paths+, or +nil+
106
- # if the array is empty.
107
- #
108
- # Files with a mtime in the future are ignored. Such abnormal situation
109
- # can happen for example if the user changes the clock by hand. It is
110
- # healthy to consider this edge case because with mtimes in the future
111
- # reloading is not triggered.
112
- def max_mtime(paths)
113
- time_now = Time.now
114
- paths.map {|path| File.mtime(path)}.reject {|mtime| time_now < mtime}.max
115
- end
114
+ # This method returns the maximum mtime of the files in +paths+, or +nil+
115
+ # if the array is empty.
116
+ #
117
+ # Files with a mtime in the future are ignored. Such abnormal situation
118
+ # can happen for example if the user changes the clock by hand. It is
119
+ # healthy to consider this edge case because with mtimes in the future
120
+ # reloading is not triggered.
121
+ def max_mtime(paths)
122
+ time_now = Time.now
123
+ max_mtime = nil
124
+
125
+ # Time comparisons are performed with #compare_without_coercion because
126
+ # AS redefines these operators in a way that is much slower and does not
127
+ # bring any benefit in this particular code.
128
+ #
129
+ # Read t1.compare_without_coercion(t2) < 0 as t1 < t2.
130
+ paths.each do |path|
131
+ mtime = File.mtime(path)
116
132
 
117
- def compile_glob(hash)
118
- hash.freeze # Freeze so changes aren't accidentally pushed
119
- return if hash.empty?
133
+ next if time_now.compare_without_coercion(mtime) < 0
120
134
 
121
- globs = hash.map do |key, value|
122
- "#{escape(key)}/**/*#{compile_ext(value)}"
135
+ if max_mtime.nil? || max_mtime.compare_without_coercion(mtime) < 0
136
+ max_mtime = mtime
137
+ end
138
+ end
139
+
140
+ max_mtime
123
141
  end
124
- "{#{globs.join(",")}}"
125
- end
126
142
 
127
- def escape(key)
128
- key.gsub(',','\,')
129
- end
143
+ def compile_glob(hash)
144
+ hash.freeze # Freeze so changes aren't accidentally pushed
145
+ return if hash.empty?
130
146
 
131
- def compile_ext(array)
132
- array = Array(array)
133
- return if array.empty?
134
- ".{#{array.join(",")}}"
135
- end
147
+ globs = hash.map do |key, value|
148
+ "#{escape(key)}/**/*#{compile_ext(value)}"
149
+ end
150
+ "{#{globs.join(",")}}"
151
+ end
152
+
153
+ def escape(key)
154
+ key.gsub(",", '\,')
155
+ end
156
+
157
+ def compile_ext(array)
158
+ array = Array(array)
159
+ return if array.empty?
160
+ ".{#{array.join(",")}}"
161
+ end
136
162
  end
137
163
  end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
- # Returns the version of the currently loaded Active Support as a <tt>Gem::Version</tt>
4
+ # Returns the version of the currently loaded Active Support as a <tt>Gem::Version</tt>.
3
5
  def self.gem_version
4
6
  Gem::Version.new VERSION::STRING
5
7
  end
6
8
 
7
9
  module VERSION
8
- MAJOR = 4
10
+ MAJOR = 5
9
11
  MINOR = 2
10
- TINY = 8
12
+ TINY = 6
11
13
  PRE = nil
12
14
 
13
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -1,5 +1,7 @@
1
- require 'zlib'
2
- require 'stringio'
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+ require "stringio"
3
5
 
4
6
  module ActiveSupport
5
7
  # A convenient wrapper for the zlib standard library that allows
@@ -9,7 +11,7 @@ module ActiveSupport
9
11
  # # => "\x1F\x8B\b\x00o\x8D\xCDO\x00\x03K\xCE\xCF-(J-.V\xC8MU\x04\x00R>n\x83\f\x00\x00\x00"
10
12
  #
11
13
  # ActiveSupport::Gzip.decompress(gzip)
12
- # # => "compress me!"
14
+ # # => "compress me!"
13
15
  module Gzip
14
16
  class Stream < StringIO
15
17
  def initialize(*)
@@ -21,11 +23,11 @@ module ActiveSupport
21
23
 
22
24
  # Decompresses a gzipped string.
23
25
  def self.decompress(source)
24
- Zlib::GzipReader.new(StringIO.new(source)).read
26
+ Zlib::GzipReader.wrap(StringIO.new(source), &:read)
25
27
  end
26
28
 
27
29
  # Compresses a string using gzip.
28
- def self.compress(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
30
+ def self.compress(source, level = Zlib::DEFAULT_COMPRESSION, strategy = Zlib::DEFAULT_STRATEGY)
29
31
  output = Stream.new
30
32
  gz = Zlib::GzipWriter.new(output, level, strategy)
31
33
  gz.write(source)