omg-activesupport 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (289) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +86 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support/actionable_error.rb +50 -0
  6. data/lib/active_support/all.rb +5 -0
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +163 -0
  9. data/lib/active_support/benchmark.rb +21 -0
  10. data/lib/active_support/benchmarkable.rb +53 -0
  11. data/lib/active_support/broadcast_logger.rb +251 -0
  12. data/lib/active_support/builder.rb +8 -0
  13. data/lib/active_support/cache/coder.rb +153 -0
  14. data/lib/active_support/cache/entry.rb +134 -0
  15. data/lib/active_support/cache/file_store.rb +244 -0
  16. data/lib/active_support/cache/mem_cache_store.rb +290 -0
  17. data/lib/active_support/cache/memory_store.rb +262 -0
  18. data/lib/active_support/cache/null_store.rb +62 -0
  19. data/lib/active_support/cache/redis_cache_store.rb +492 -0
  20. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  21. data/lib/active_support/cache/strategy/local_cache.rb +201 -0
  22. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  23. data/lib/active_support/cache.rb +1104 -0
  24. data/lib/active_support/callbacks.rb +944 -0
  25. data/lib/active_support/class_attribute.rb +26 -0
  26. data/lib/active_support/code_generator.rb +79 -0
  27. data/lib/active_support/concern.rb +217 -0
  28. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +72 -0
  29. data/lib/active_support/concurrency/null_lock.rb +13 -0
  30. data/lib/active_support/concurrency/share_lock.rb +225 -0
  31. data/lib/active_support/configurable.rb +159 -0
  32. data/lib/active_support/configuration_file.rb +60 -0
  33. data/lib/active_support/core_ext/array/access.rb +100 -0
  34. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  35. data/lib/active_support/core_ext/array/extract.rb +21 -0
  36. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  37. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  38. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  39. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  40. data/lib/active_support/core_ext/array.rb +9 -0
  41. data/lib/active_support/core_ext/benchmark.rb +13 -0
  42. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  43. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  44. data/lib/active_support/core_ext/class/attribute.rb +122 -0
  45. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  46. data/lib/active_support/core_ext/class/subclasses.rb +24 -0
  47. data/lib/active_support/core_ext/class.rb +4 -0
  48. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  49. data/lib/active_support/core_ext/date/blank.rb +18 -0
  50. data/lib/active_support/core_ext/date/calculations.rb +161 -0
  51. data/lib/active_support/core_ext/date/conversions.rb +98 -0
  52. data/lib/active_support/core_ext/date/zones.rb +8 -0
  53. data/lib/active_support/core_ext/date.rb +7 -0
  54. data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
  55. data/lib/active_support/core_ext/date_and_time/compatibility.rb +58 -0
  56. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  57. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  58. data/lib/active_support/core_ext/date_time/blank.rb +18 -0
  59. data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
  60. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  61. data/lib/active_support/core_ext/date_time/conversions.rb +106 -0
  62. data/lib/active_support/core_ext/date_time.rb +7 -0
  63. data/lib/active_support/core_ext/digest/uuid.rb +76 -0
  64. data/lib/active_support/core_ext/digest.rb +3 -0
  65. data/lib/active_support/core_ext/enumerable.rb +267 -0
  66. data/lib/active_support/core_ext/erb/util.rb +201 -0
  67. data/lib/active_support/core_ext/file/atomic.rb +72 -0
  68. data/lib/active_support/core_ext/file.rb +3 -0
  69. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  70. data/lib/active_support/core_ext/hash/deep_merge.rb +42 -0
  71. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  72. data/lib/active_support/core_ext/hash/except.rb +12 -0
  73. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  74. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  75. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  76. data/lib/active_support/core_ext/hash/slice.rb +27 -0
  77. data/lib/active_support/core_ext/hash.rb +10 -0
  78. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  79. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  80. data/lib/active_support/core_ext/integer/time.rb +22 -0
  81. data/lib/active_support/core_ext/integer.rb +5 -0
  82. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  83. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  84. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  85. data/lib/active_support/core_ext/kernel.rb +5 -0
  86. data/lib/active_support/core_ext/load_error.rb +9 -0
  87. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  88. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  89. data/lib/active_support/core_ext/module/attr_internal.rb +49 -0
  90. data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
  91. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
  92. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  93. data/lib/active_support/core_ext/module/delegation.rb +225 -0
  94. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  95. data/lib/active_support/core_ext/module/introspection.rb +62 -0
  96. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  97. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  98. data/lib/active_support/core_ext/module.rb +13 -0
  99. data/lib/active_support/core_ext/name_error.rb +59 -0
  100. data/lib/active_support/core_ext/numeric/bytes.rb +75 -0
  101. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  102. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  103. data/lib/active_support/core_ext/numeric.rb +5 -0
  104. data/lib/active_support/core_ext/object/acts_like.rb +45 -0
  105. data/lib/active_support/core_ext/object/blank.rb +199 -0
  106. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  107. data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
  108. data/lib/active_support/core_ext/object/duplicable.rb +69 -0
  109. data/lib/active_support/core_ext/object/inclusion.rb +37 -0
  110. data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
  111. data/lib/active_support/core_ext/object/json.rb +260 -0
  112. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  113. data/lib/active_support/core_ext/object/to_query.rb +87 -0
  114. data/lib/active_support/core_ext/object/try.rb +158 -0
  115. data/lib/active_support/core_ext/object/with.rb +46 -0
  116. data/lib/active_support/core_ext/object/with_options.rb +101 -0
  117. data/lib/active_support/core_ext/object.rb +17 -0
  118. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  119. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  120. data/lib/active_support/core_ext/pathname.rb +4 -0
  121. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  122. data/lib/active_support/core_ext/range/conversions.rb +62 -0
  123. data/lib/active_support/core_ext/range/each.rb +24 -0
  124. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  125. data/lib/active_support/core_ext/range.rb +6 -0
  126. data/lib/active_support/core_ext/regexp.rb +14 -0
  127. data/lib/active_support/core_ext/securerandom.rb +41 -0
  128. data/lib/active_support/core_ext/string/access.rb +95 -0
  129. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  130. data/lib/active_support/core_ext/string/conversions.rb +60 -0
  131. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  132. data/lib/active_support/core_ext/string/filters.rb +151 -0
  133. data/lib/active_support/core_ext/string/indent.rb +45 -0
  134. data/lib/active_support/core_ext/string/inflections.rb +300 -0
  135. data/lib/active_support/core_ext/string/inquiry.rb +16 -0
  136. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  137. data/lib/active_support/core_ext/string/output_safety.rb +228 -0
  138. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/string/strip.rb +27 -0
  140. data/lib/active_support/core_ext/string/zones.rb +16 -0
  141. data/lib/active_support/core_ext/string.rb +15 -0
  142. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  143. data/lib/active_support/core_ext/symbol.rb +3 -0
  144. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  145. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  146. data/lib/active_support/core_ext/time/calculations.rb +386 -0
  147. data/lib/active_support/core_ext/time/compatibility.rb +32 -0
  148. data/lib/active_support/core_ext/time/conversions.rb +75 -0
  149. data/lib/active_support/core_ext/time/zones.rb +97 -0
  150. data/lib/active_support/core_ext/time.rb +7 -0
  151. data/lib/active_support/core_ext.rb +5 -0
  152. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  153. data/lib/active_support/current_attributes.rb +233 -0
  154. data/lib/active_support/deep_mergeable.rb +53 -0
  155. data/lib/active_support/delegation.rb +202 -0
  156. data/lib/active_support/dependencies/autoload.rb +72 -0
  157. data/lib/active_support/dependencies/interlock.rb +49 -0
  158. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  159. data/lib/active_support/dependencies.rb +98 -0
  160. data/lib/active_support/deprecation/behaviors.rb +148 -0
  161. data/lib/active_support/deprecation/constant_accessor.rb +74 -0
  162. data/lib/active_support/deprecation/deprecators.rb +104 -0
  163. data/lib/active_support/deprecation/disallowed.rb +54 -0
  164. data/lib/active_support/deprecation/method_wrappers.rb +68 -0
  165. data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
  166. data/lib/active_support/deprecation/reporting.rb +179 -0
  167. data/lib/active_support/deprecation.rb +81 -0
  168. data/lib/active_support/deprecator.rb +7 -0
  169. data/lib/active_support/descendants_tracker.rb +112 -0
  170. data/lib/active_support/digest.rb +22 -0
  171. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  172. data/lib/active_support/duration/iso8601_serializer.rb +64 -0
  173. data/lib/active_support/duration.rb +520 -0
  174. data/lib/active_support/encrypted_configuration.rb +126 -0
  175. data/lib/active_support/encrypted_file.rb +133 -0
  176. data/lib/active_support/environment_inquirer.rb +40 -0
  177. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  178. data/lib/active_support/error_reporter.rb +265 -0
  179. data/lib/active_support/evented_file_update_checker.rb +182 -0
  180. data/lib/active_support/execution_context/test_helper.rb +13 -0
  181. data/lib/active_support/execution_context.rb +53 -0
  182. data/lib/active_support/execution_wrapper.rb +150 -0
  183. data/lib/active_support/executor/test_helper.rb +7 -0
  184. data/lib/active_support/executor.rb +8 -0
  185. data/lib/active_support/file_update_checker.rb +164 -0
  186. data/lib/active_support/fork_tracker.rb +43 -0
  187. data/lib/active_support/gem_version.rb +17 -0
  188. data/lib/active_support/gzip.rb +40 -0
  189. data/lib/active_support/hash_with_indifferent_access.rb +445 -0
  190. data/lib/active_support/html_safe_translation.rb +56 -0
  191. data/lib/active_support/i18n.rb +17 -0
  192. data/lib/active_support/i18n_railtie.rb +138 -0
  193. data/lib/active_support/inflections.rb +72 -0
  194. data/lib/active_support/inflector/inflections.rb +273 -0
  195. data/lib/active_support/inflector/methods.rb +387 -0
  196. data/lib/active_support/inflector/transliterate.rb +149 -0
  197. data/lib/active_support/inflector.rb +9 -0
  198. data/lib/active_support/isolated_execution_state.rb +75 -0
  199. data/lib/active_support/json/decoding.rb +76 -0
  200. data/lib/active_support/json/encoding.rb +120 -0
  201. data/lib/active_support/json.rb +4 -0
  202. data/lib/active_support/key_generator.rb +66 -0
  203. data/lib/active_support/lazy_load_hooks.rb +107 -0
  204. data/lib/active_support/locale/en.rb +33 -0
  205. data/lib/active_support/locale/en.yml +141 -0
  206. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  207. data/lib/active_support/log_subscriber.rb +192 -0
  208. data/lib/active_support/logger.rb +55 -0
  209. data/lib/active_support/logger_silence.rb +21 -0
  210. data/lib/active_support/logger_thread_safe_level.rb +47 -0
  211. data/lib/active_support/message_encryptor.rb +374 -0
  212. data/lib/active_support/message_encryptors.rb +141 -0
  213. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  214. data/lib/active_support/message_pack/extensions.rb +305 -0
  215. data/lib/active_support/message_pack/serializer.rb +63 -0
  216. data/lib/active_support/message_pack.rb +50 -0
  217. data/lib/active_support/message_verifier.rb +368 -0
  218. data/lib/active_support/message_verifiers.rb +135 -0
  219. data/lib/active_support/messages/codec.rb +65 -0
  220. data/lib/active_support/messages/metadata.rb +146 -0
  221. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  222. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  223. data/lib/active_support/messages/rotator.rb +59 -0
  224. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  225. data/lib/active_support/multibyte/chars.rb +178 -0
  226. data/lib/active_support/multibyte/unicode.rb +42 -0
  227. data/lib/active_support/multibyte.rb +23 -0
  228. data/lib/active_support/notifications/fanout.rb +446 -0
  229. data/lib/active_support/notifications/instrumenter.rb +240 -0
  230. data/lib/active_support/notifications.rb +281 -0
  231. data/lib/active_support/number_helper/number_converter.rb +190 -0
  232. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  233. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  234. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  235. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  236. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  237. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  238. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  239. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  240. data/lib/active_support/number_helper.rb +479 -0
  241. data/lib/active_support/option_merger.rb +38 -0
  242. data/lib/active_support/ordered_hash.rb +50 -0
  243. data/lib/active_support/ordered_options.rb +147 -0
  244. data/lib/active_support/parameter_filter.rb +157 -0
  245. data/lib/active_support/proxy_object.rb +20 -0
  246. data/lib/active_support/rails.rb +26 -0
  247. data/lib/active_support/railtie.rb +161 -0
  248. data/lib/active_support/reloader.rb +138 -0
  249. data/lib/active_support/rescuable.rb +176 -0
  250. data/lib/active_support/secure_compare_rotator.rb +58 -0
  251. data/lib/active_support/security_utils.rb +38 -0
  252. data/lib/active_support/string_inquirer.rb +35 -0
  253. data/lib/active_support/subscriber.rb +146 -0
  254. data/lib/active_support/syntax_error_proxy.rb +60 -0
  255. data/lib/active_support/tagged_logging.rb +152 -0
  256. data/lib/active_support/test_case.rb +304 -0
  257. data/lib/active_support/testing/assertions.rb +332 -0
  258. data/lib/active_support/testing/autorun.rb +5 -0
  259. data/lib/active_support/testing/constant_lookup.rb +51 -0
  260. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  261. data/lib/active_support/testing/declarative.rb +28 -0
  262. data/lib/active_support/testing/deprecation.rb +82 -0
  263. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  264. data/lib/active_support/testing/file_fixtures.rb +38 -0
  265. data/lib/active_support/testing/isolation.rb +121 -0
  266. data/lib/active_support/testing/method_call_assertions.rb +69 -0
  267. data/lib/active_support/testing/parallelization/server.rb +85 -0
  268. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  269. data/lib/active_support/testing/parallelization.rb +55 -0
  270. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  271. data/lib/active_support/testing/setup_and_teardown.rb +57 -0
  272. data/lib/active_support/testing/stream.rb +41 -0
  273. data/lib/active_support/testing/strict_warnings.rb +43 -0
  274. data/lib/active_support/testing/tagged_logging.rb +27 -0
  275. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  276. data/lib/active_support/testing/time_helpers.rb +269 -0
  277. data/lib/active_support/time.rb +20 -0
  278. data/lib/active_support/time_with_zone.rb +609 -0
  279. data/lib/active_support/values/time_zone.rb +614 -0
  280. data/lib/active_support/version.rb +10 -0
  281. data/lib/active_support/xml_mini/jdom.rb +175 -0
  282. data/lib/active_support/xml_mini/libxml.rb +80 -0
  283. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  284. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  285. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  286. data/lib/active_support/xml_mini/rexml.rb +137 -0
  287. data/lib/active_support/xml_mini.rb +211 -0
  288. data/lib/active_support.rb +144 -0
  289. metadata +526 -0
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module ClassAttribute # :nodoc:
5
+ class << self
6
+ def redefine(owner, name, value)
7
+ if owner.singleton_class?
8
+ owner.redefine_method(name) { value }
9
+ owner.send(:public, name)
10
+ end
11
+
12
+ owner.redefine_singleton_method(name) { value }
13
+ owner.singleton_class.send(:public, name)
14
+
15
+ owner.redefine_singleton_method("#{name}=") do |new_value|
16
+ if owner.equal?(self)
17
+ value = new_value
18
+ else
19
+ ::ActiveSupport::ClassAttribute.redefine(self, name, new_value)
20
+ end
21
+ end
22
+ owner.singleton_class.send(:public, "#{name}=")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ class CodeGenerator # :nodoc:
5
+ class MethodSet
6
+ METHOD_CACHES = Hash.new { |h, k| h[k] = Module.new }
7
+
8
+ def initialize(namespace)
9
+ @cache = METHOD_CACHES[namespace]
10
+ @sources = []
11
+ @methods = {}
12
+ @canonical_methods = {}
13
+ end
14
+
15
+ def define_cached_method(canonical_name, as: nil)
16
+ canonical_name = canonical_name.to_sym
17
+ as = (as || canonical_name).to_sym
18
+
19
+ @methods.fetch(as) do
20
+ unless @cache.method_defined?(canonical_name) || @canonical_methods[canonical_name]
21
+ yield @sources
22
+ end
23
+ @canonical_methods[canonical_name] = true
24
+ @methods[as] = canonical_name
25
+ end
26
+ end
27
+
28
+ def apply(owner, path, line)
29
+ unless @sources.empty?
30
+ @cache.module_eval("# frozen_string_literal: true\n" + @sources.join(";"), path, line)
31
+ end
32
+ @canonical_methods.clear
33
+
34
+ @methods.each do |as, canonical_name|
35
+ owner.define_method(as, @cache.instance_method(canonical_name))
36
+ end
37
+ end
38
+ end
39
+
40
+ class << self
41
+ def batch(owner, path, line)
42
+ if owner.is_a?(CodeGenerator)
43
+ yield owner
44
+ else
45
+ instance = new(owner, path, line)
46
+ result = yield instance
47
+ instance.execute
48
+ result
49
+ end
50
+ end
51
+ end
52
+
53
+ def initialize(owner, path, line)
54
+ @owner = owner
55
+ @path = path
56
+ @line = line
57
+ @namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
58
+ @sources = []
59
+ end
60
+
61
+ def class_eval
62
+ yield @sources
63
+ end
64
+
65
+ def define_cached_method(canonical_name, namespace:, as: nil, &block)
66
+ @namespaces[namespace].define_cached_method(canonical_name, as: as, &block)
67
+ end
68
+
69
+ def execute
70
+ @namespaces.each_value do |method_set|
71
+ method_set.apply(@owner, @path, @line - 1)
72
+ end
73
+
74
+ unless @sources.empty?
75
+ @owner.class_eval("# frozen_string_literal: true\n" + @sources.join(";"), @path, @line - 1)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # = Active Support \Concern
5
+ #
6
+ # A typical module looks like this:
7
+ #
8
+ # module M
9
+ # def self.included(base)
10
+ # base.extend ClassMethods
11
+ # base.class_eval do
12
+ # scope :disabled, -> { where(disabled: true) }
13
+ # end
14
+ # end
15
+ #
16
+ # module ClassMethods
17
+ # ...
18
+ # end
19
+ # end
20
+ #
21
+ # By using +ActiveSupport::Concern+ the above module could instead be
22
+ # written as:
23
+ #
24
+ # require "active_support/concern"
25
+ #
26
+ # module M
27
+ # extend ActiveSupport::Concern
28
+ #
29
+ # included do
30
+ # scope :disabled, -> { where(disabled: true) }
31
+ # end
32
+ #
33
+ # class_methods do
34
+ # ...
35
+ # end
36
+ # end
37
+ #
38
+ # Moreover, it gracefully handles module dependencies. Given a +Foo+ module
39
+ # and a +Bar+ module which depends on the former, we would typically write the
40
+ # following:
41
+ #
42
+ # module Foo
43
+ # def self.included(base)
44
+ # base.class_eval do
45
+ # def self.method_injected_by_foo
46
+ # ...
47
+ # end
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # module Bar
53
+ # def self.included(base)
54
+ # base.method_injected_by_foo
55
+ # end
56
+ # end
57
+ #
58
+ # class Host
59
+ # include Foo # We need to include this dependency for Bar
60
+ # include Bar # Bar is the module that Host really needs
61
+ # end
62
+ #
63
+ # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
64
+ # could try to hide these from +Host+ directly including +Foo+ in +Bar+:
65
+ #
66
+ # module Bar
67
+ # include Foo
68
+ # def self.included(base)
69
+ # base.method_injected_by_foo
70
+ # end
71
+ # end
72
+ #
73
+ # class Host
74
+ # include Bar
75
+ # end
76
+ #
77
+ # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
78
+ # is the +Bar+ module, not the +Host+ class. With +ActiveSupport::Concern+,
79
+ # module dependencies are properly resolved:
80
+ #
81
+ # require "active_support/concern"
82
+ #
83
+ # module Foo
84
+ # extend ActiveSupport::Concern
85
+ # included do
86
+ # def self.method_injected_by_foo
87
+ # ...
88
+ # end
89
+ # end
90
+ # end
91
+ #
92
+ # module Bar
93
+ # extend ActiveSupport::Concern
94
+ # include Foo
95
+ #
96
+ # included do
97
+ # self.method_injected_by_foo
98
+ # end
99
+ # end
100
+ #
101
+ # class Host
102
+ # include Bar # It works, now Bar takes care of its dependencies
103
+ # end
104
+ #
105
+ # === Prepending concerns
106
+ #
107
+ # Just like <tt>include</tt>, concerns also support <tt>prepend</tt> with a corresponding
108
+ # <tt>prepended do</tt> callback. <tt>module ClassMethods</tt> or <tt>class_methods do</tt> are
109
+ # prepended as well.
110
+ #
111
+ # <tt>prepend</tt> is also used for any dependencies.
112
+ module Concern
113
+ class MultipleIncludedBlocks < StandardError # :nodoc:
114
+ def initialize
115
+ super "Cannot define multiple 'included' blocks for a Concern"
116
+ end
117
+ end
118
+
119
+ class MultiplePrependBlocks < StandardError # :nodoc:
120
+ def initialize
121
+ super "Cannot define multiple 'prepended' blocks for a Concern"
122
+ end
123
+ end
124
+
125
+ def self.extended(base) # :nodoc:
126
+ base.instance_variable_set(:@_dependencies, [])
127
+ end
128
+
129
+ def append_features(base) # :nodoc:
130
+ if base.instance_variable_defined?(:@_dependencies)
131
+ base.instance_variable_get(:@_dependencies) << self
132
+ false
133
+ else
134
+ return false if base < self
135
+ @_dependencies.each { |dep| base.include(dep) }
136
+ super
137
+ base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
138
+ base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
139
+ end
140
+ end
141
+
142
+ def prepend_features(base) # :nodoc:
143
+ if base.instance_variable_defined?(:@_dependencies)
144
+ base.instance_variable_get(:@_dependencies).unshift self
145
+ false
146
+ else
147
+ return false if base < self
148
+ @_dependencies.each { |dep| base.prepend(dep) }
149
+ super
150
+ base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods)
151
+ base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block)
152
+ end
153
+ end
154
+
155
+ # Evaluate given block in context of base class,
156
+ # so that you can write class macros here.
157
+ # When you define more than one +included+ block, it raises an exception.
158
+ def included(base = nil, &block)
159
+ if base.nil?
160
+ if instance_variable_defined?(:@_included_block)
161
+ if @_included_block.source_location != block.source_location
162
+ raise MultipleIncludedBlocks
163
+ end
164
+ else
165
+ @_included_block = block
166
+ end
167
+ else
168
+ super
169
+ end
170
+ end
171
+
172
+ # Evaluate given block in context of base class,
173
+ # so that you can write class macros here.
174
+ # When you define more than one +prepended+ block, it raises an exception.
175
+ def prepended(base = nil, &block)
176
+ if base.nil?
177
+ if instance_variable_defined?(:@_prepended_block)
178
+ if @_prepended_block.source_location != block.source_location
179
+ raise MultiplePrependBlocks
180
+ end
181
+ else
182
+ @_prepended_block = block
183
+ end
184
+ else
185
+ super
186
+ end
187
+ end
188
+
189
+ # Define class methods from given block.
190
+ # You can define private class methods as well.
191
+ #
192
+ # module Example
193
+ # extend ActiveSupport::Concern
194
+ #
195
+ # class_methods do
196
+ # def foo; puts 'foo'; end
197
+ #
198
+ # private
199
+ # def bar; puts 'bar'; end
200
+ # end
201
+ # end
202
+ #
203
+ # class Buzz
204
+ # include Example
205
+ # end
206
+ #
207
+ # Buzz.foo # => "foo"
208
+ # Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
209
+ def class_methods(&class_methods_module_definition)
210
+ mod = const_defined?(:ClassMethods, false) ?
211
+ const_get(:ClassMethods) :
212
+ const_set(:ClassMethods, Module.new)
213
+
214
+ mod.module_eval(&class_methods_module_definition)
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+
5
+ module ActiveSupport
6
+ module Concurrency
7
+ module LoadInterlockAwareMonitorMixin # :nodoc:
8
+ EXCEPTION_NEVER = { Exception => :never }.freeze
9
+ EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
10
+ private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
11
+
12
+ # Enters an exclusive section, but allows dependency loading while blocked
13
+ def mon_enter
14
+ mon_try_enter ||
15
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
16
+ end
17
+
18
+ def synchronize(&block)
19
+ Thread.handle_interrupt(EXCEPTION_NEVER) do
20
+ mon_enter
21
+
22
+ begin
23
+ Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
24
+ ensure
25
+ mon_exit
26
+ end
27
+ end
28
+ end
29
+ end
30
+ # A monitor that will permit dependency loading while blocked waiting for
31
+ # the lock.
32
+ class LoadInterlockAwareMonitor < Monitor
33
+ include LoadInterlockAwareMonitorMixin
34
+ end
35
+
36
+ class ThreadLoadInterlockAwareMonitor # :nodoc:
37
+ prepend LoadInterlockAwareMonitorMixin
38
+
39
+ def initialize
40
+ @owner = nil
41
+ @count = 0
42
+ @mutex = Mutex.new
43
+ end
44
+
45
+ private
46
+ def mon_try_enter
47
+ if @owner != Thread.current
48
+ return false unless @mutex.try_lock
49
+ @owner = Thread.current
50
+ end
51
+ @count += 1
52
+ end
53
+
54
+ def mon_enter
55
+ @mutex.lock if @owner != Thread.current
56
+ @owner = Thread.current
57
+ @count += 1
58
+ end
59
+
60
+ def mon_exit
61
+ unless @owner == Thread.current
62
+ raise ThreadError, "current thread not owner"
63
+ end
64
+
65
+ @count -= 1
66
+ return unless @count == 0
67
+ @owner = nil
68
+ @mutex.unlock
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Concurrency
5
+ module NullLock # :nodoc:
6
+ extend self
7
+
8
+ def synchronize
9
+ yield
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+
5
+ module ActiveSupport
6
+ module Concurrency
7
+ # A share/exclusive lock, otherwise known as a read/write lock.
8
+ #
9
+ # https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
10
+ class ShareLock
11
+ include MonitorMixin
12
+
13
+ # We track Thread objects, instead of just using counters, because
14
+ # we need exclusive locks to be reentrant, and we need to be able
15
+ # to upgrade share locks to exclusive.
16
+
17
+ def raw_state # :nodoc:
18
+ synchronize do
19
+ threads = @sleeping.keys | @sharing.keys | @waiting.keys
20
+ threads |= [@exclusive_thread] if @exclusive_thread
21
+
22
+ data = {}
23
+
24
+ threads.each do |thread|
25
+ purpose, compatible = @waiting[thread]
26
+
27
+ data[thread] = {
28
+ thread: thread,
29
+ sharing: @sharing[thread],
30
+ exclusive: @exclusive_thread == thread,
31
+ purpose: purpose,
32
+ compatible: compatible,
33
+ waiting: !!@waiting[thread],
34
+ sleeper: @sleeping[thread],
35
+ }
36
+ end
37
+
38
+ # NB: Yields while holding our *internal* synchronize lock,
39
+ # which is supposed to be used only for a few instructions at
40
+ # a time. This allows the caller to inspect additional state
41
+ # without things changing out from underneath, but would have
42
+ # disastrous effects upon normal operation. Fortunately, this
43
+ # method is only intended to be called when things have
44
+ # already gone wrong.
45
+ yield data
46
+ end
47
+ end
48
+
49
+ def initialize
50
+ super()
51
+
52
+ @cv = new_cond
53
+
54
+ @sharing = Hash.new(0)
55
+ @waiting = {}
56
+ @sleeping = {}
57
+ @exclusive_thread = nil
58
+ @exclusive_depth = 0
59
+ end
60
+
61
+ # Returns false if +no_wait+ is set and the lock is not
62
+ # immediately available. Otherwise, returns true after the lock
63
+ # has been acquired.
64
+ #
65
+ # +purpose+ and +compatible+ work together; while this thread is
66
+ # waiting for the exclusive lock, it will yield its share (if any)
67
+ # to any other attempt whose +purpose+ appears in this attempt's
68
+ # +compatible+ list. This allows a "loose" upgrade, which, being
69
+ # less strict, prevents some classes of deadlocks.
70
+ #
71
+ # For many resources, loose upgrades are sufficient: if a thread
72
+ # is awaiting a lock, it is not running any other code. With
73
+ # +purpose+ matching, it is possible to yield only to other
74
+ # threads whose activity will not interfere.
75
+ def start_exclusive(purpose: nil, compatible: [], no_wait: false)
76
+ synchronize do
77
+ unless @exclusive_thread == Thread.current
78
+ if busy_for_exclusive?(purpose)
79
+ return false if no_wait
80
+
81
+ yield_shares(purpose: purpose, compatible: compatible, block_share: true) do
82
+ wait_for(:start_exclusive) { busy_for_exclusive?(purpose) }
83
+ end
84
+ end
85
+ @exclusive_thread = Thread.current
86
+ end
87
+ @exclusive_depth += 1
88
+
89
+ true
90
+ end
91
+ end
92
+
93
+ # Relinquish the exclusive lock. Must only be called by the thread
94
+ # that called start_exclusive (and currently holds the lock).
95
+ def stop_exclusive(compatible: [])
96
+ synchronize do
97
+ raise "invalid unlock" if @exclusive_thread != Thread.current
98
+
99
+ @exclusive_depth -= 1
100
+ if @exclusive_depth == 0
101
+ @exclusive_thread = nil
102
+
103
+ if eligible_waiters?(compatible)
104
+ yield_shares(compatible: compatible, block_share: true) do
105
+ wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) }
106
+ end
107
+ end
108
+ @cv.broadcast
109
+ end
110
+ end
111
+ end
112
+
113
+ def start_sharing
114
+ synchronize do
115
+ if @sharing[Thread.current] > 0 || @exclusive_thread == Thread.current
116
+ # We already hold a lock; nothing to wait for
117
+ elsif @waiting[Thread.current]
118
+ # We're nested inside a +yield_shares+ call: we'll resume as
119
+ # soon as there isn't an exclusive lock in our way
120
+ wait_for(:start_sharing) { @exclusive_thread }
121
+ else
122
+ # This is an initial / outermost share call: any outstanding
123
+ # requests for an exclusive lock get to go first
124
+ wait_for(:start_sharing) { busy_for_sharing?(false) }
125
+ end
126
+ @sharing[Thread.current] += 1
127
+ end
128
+ end
129
+
130
+ def stop_sharing
131
+ synchronize do
132
+ if @sharing[Thread.current] > 1
133
+ @sharing[Thread.current] -= 1
134
+ else
135
+ @sharing.delete Thread.current
136
+ @cv.broadcast
137
+ end
138
+ end
139
+ end
140
+
141
+ # Execute the supplied block while holding the Exclusive lock. If
142
+ # +no_wait+ is set and the lock is not immediately available,
143
+ # returns +nil+ without yielding. Otherwise, returns the result of
144
+ # the block.
145
+ #
146
+ # See +start_exclusive+ for other options.
147
+ def exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false)
148
+ if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait)
149
+ begin
150
+ yield
151
+ ensure
152
+ stop_exclusive(compatible: after_compatible)
153
+ end
154
+ end
155
+ end
156
+
157
+ # Execute the supplied block while holding the Share lock.
158
+ def sharing
159
+ start_sharing
160
+ begin
161
+ yield
162
+ ensure
163
+ stop_sharing
164
+ end
165
+ end
166
+
167
+ # Temporarily give up all held Share locks while executing the
168
+ # supplied block, allowing any +compatible+ exclusive lock request
169
+ # to proceed.
170
+ def yield_shares(purpose: nil, compatible: [], block_share: false)
171
+ loose_shares = previous_wait = nil
172
+ synchronize do
173
+ if loose_shares = @sharing.delete(Thread.current)
174
+ if previous_wait = @waiting[Thread.current]
175
+ purpose = nil unless purpose == previous_wait[0]
176
+ compatible &= previous_wait[1]
177
+ end
178
+ compatible |= [false] unless block_share
179
+ @waiting[Thread.current] = [purpose, compatible]
180
+ end
181
+
182
+ @cv.broadcast
183
+ end
184
+
185
+ begin
186
+ yield
187
+ ensure
188
+ synchronize do
189
+ wait_for(:yield_shares) { @exclusive_thread && @exclusive_thread != Thread.current }
190
+
191
+ if previous_wait
192
+ @waiting[Thread.current] = previous_wait
193
+ else
194
+ @waiting.delete Thread.current
195
+ end
196
+ @sharing[Thread.current] = loose_shares if loose_shares
197
+ end
198
+ end
199
+ end
200
+
201
+ private
202
+ # Must be called within synchronize
203
+ def busy_for_exclusive?(purpose)
204
+ busy_for_sharing?(purpose) ||
205
+ @sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0)
206
+ end
207
+
208
+ def busy_for_sharing?(purpose)
209
+ (@exclusive_thread && @exclusive_thread != Thread.current) ||
210
+ @waiting.any? { |t, (_, c)| t != Thread.current && !c.include?(purpose) }
211
+ end
212
+
213
+ def eligible_waiters?(compatible)
214
+ @waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } }
215
+ end
216
+
217
+ def wait_for(method, &block)
218
+ @sleeping[Thread.current] = method
219
+ @cv.wait_while(&block)
220
+ ensure
221
+ @sleeping.delete Thread.current
222
+ end
223
+ end
224
+ end
225
+ end