activesupport 4.0.12 → 7.0.2.4

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 (295) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +249 -501
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +41 -13
  9. data/lib/active_support/benchmarkable.rb +7 -15
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +96 -74
  12. data/lib/active_support/cache/mem_cache_store.rb +211 -103
  13. data/lib/active_support/cache/memory_store.rb +90 -58
  14. data/lib/active_support/cache/null_store.rb +19 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +468 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +86 -83
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  18. data/lib/active_support/cache.rb +580 -241
  19. data/lib/active_support/callbacks.rb +812 -425
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +103 -14
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
  23. data/lib/active_support/concurrency/share_lock.rb +226 -0
  24. data/lib/active_support/configurable.rb +21 -19
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +47 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +35 -44
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +26 -16
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  34. data/lib/active_support/core_ext/array.rb +10 -7
  35. data/lib/active_support/core_ext/benchmark.rb +5 -3
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  37. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  38. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  40. data/lib/active_support/core_ext/class/subclasses.rb +25 -26
  41. data/lib/active_support/core_ext/class.rb +4 -4
  42. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  43. data/lib/active_support/core_ext/date/blank.rb +14 -0
  44. data/lib/active_support/core_ext/date/calculations.rb +31 -18
  45. data/lib/active_support/core_ext/date/conversions.rb +43 -32
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +5 -34
  48. data/lib/active_support/core_ext/date.rb +7 -4
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +8 -4
  59. data/lib/active_support/core_ext/digest/uuid.rb +79 -0
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +249 -17
  62. data/lib/active_support/core_ext/file/atomic.rb +41 -32
  63. data/lib/active_support/core_ext/file.rb +3 -1
  64. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +14 -5
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +39 -56
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -23
  72. data/lib/active_support/core_ext/hash.rb +10 -8
  73. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +11 -33
  76. data/lib/active_support/core_ext/integer.rb +5 -3
  77. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  80. data/lib/active_support/core_ext/kernel.rb +5 -4
  81. data/lib/active_support/core_ext/load_error.rb +5 -21
  82. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  83. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  84. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
  87. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  88. data/lib/active_support/core_ext/module/delegation.rb +172 -45
  89. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  90. data/lib/active_support/core_ext/module/introspection.rb +23 -38
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -10
  94. data/lib/active_support/core_ext/name_error.rb +45 -4
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  99. data/lib/active_support/core_ext/numeric.rb +6 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +70 -20
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  104. data/lib/active_support/core_ext/object/duplicable.rb +17 -47
  105. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +244 -0
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  110. data/lib/active_support/core_ext/object/try.rb +106 -26
  111. data/lib/active_support/core_ext/object/with_options.rb +64 -5
  112. data/lib/active_support/core_ext/object.rb +14 -12
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +18 -17
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  121. data/lib/active_support/core_ext/range.rb +7 -4
  122. data/lib/active_support/core_ext/regexp.rb +10 -1
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string/access.rb +42 -51
  125. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  126. data/lib/active_support/core_ext/string/conversions.rb +18 -13
  127. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  128. data/lib/active_support/core_ext/string/filters.rb +97 -7
  129. data/lib/active_support/core_ext/string/indent.rb +6 -4
  130. data/lib/active_support/core_ext/string/inflections.rb +106 -25
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  133. data/lib/active_support/core_ext/string/output_safety.rb +227 -54
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +6 -5
  136. data/lib/active_support/core_ext/string/zones.rb +4 -1
  137. data/lib/active_support/core_ext/string.rb +15 -13
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  141. data/lib/active_support/core_ext/time/calculations.rb +178 -116
  142. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  143. data/lib/active_support/core_ext/time/conversions.rb +37 -25
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +44 -42
  146. data/lib/active_support/core_ext/time.rb +8 -5
  147. data/lib/active_support/core_ext/uri.rb +4 -25
  148. data/lib/active_support/core_ext.rb +4 -2
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +3 -1
  152. data/lib/active_support/dependencies/interlock.rb +49 -0
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +71 -696
  155. data/lib/active_support/deprecation/behaviors.rb +65 -16
  156. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  159. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
  161. data/lib/active_support/deprecation/reporting.rb +81 -18
  162. data/lib/active_support/deprecation.rb +19 -11
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  166. data/lib/active_support/duration/iso8601_serializer.rb +67 -0
  167. data/lib/active_support/duration.rb +437 -39
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +117 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +170 -0
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +151 -0
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +8 -0
  178. data/lib/active_support/file_update_checker.rb +62 -37
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +17 -0
  181. data/lib/active_support/gzip.rb +7 -5
  182. data/lib/active_support/hash_with_indifferent_access.rb +207 -54
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +10 -6
  185. data/lib/active_support/i18n_railtie.rb +48 -19
  186. data/lib/active_support/inflections.rb +19 -12
  187. data/lib/active_support/inflector/inflections.rb +97 -37
  188. data/lib/active_support/inflector/methods.rb +192 -157
  189. data/lib/active_support/inflector/transliterate.rb +83 -33
  190. data/lib/active_support/inflector.rb +7 -5
  191. data/lib/active_support/isolated_execution_state.rb +64 -0
  192. data/lib/active_support/json/decoding.rb +37 -42
  193. data/lib/active_support/json/encoding.rb +93 -293
  194. data/lib/active_support/json.rb +4 -2
  195. data/lib/active_support/key_generator.rb +30 -47
  196. data/lib/active_support/lazy_load_hooks.rb +54 -21
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +10 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  200. data/lib/active_support/log_subscriber.rb +61 -18
  201. data/lib/active_support/logger.rb +40 -4
  202. data/lib/active_support/logger_silence.rb +17 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +69 -0
  204. data/lib/active_support/message_encryptor.rb +178 -55
  205. data/lib/active_support/message_verifier.rb +195 -26
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +45 -92
  210. data/lib/active_support/multibyte/unicode.rb +44 -377
  211. data/lib/active_support/multibyte.rb +5 -3
  212. data/lib/active_support/notifications/fanout.rb +177 -44
  213. data/lib/active_support/notifications/instrumenter.rb +117 -17
  214. data/lib/active_support/notifications.rb +106 -39
  215. data/lib/active_support/number_helper/number_converter.rb +181 -0
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  223. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  224. data/lib/active_support/number_helper.rb +152 -394
  225. data/lib/active_support/option_merger.rb +18 -5
  226. data/lib/active_support/ordered_hash.rb +8 -6
  227. data/lib/active_support/ordered_options.rb +43 -7
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +24 -11
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +10 -11
  232. data/lib/active_support/railtie.rb +118 -12
  233. data/lib/active_support/reloader.rb +130 -0
  234. data/lib/active_support/rescuable.rb +112 -57
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +38 -0
  238. data/lib/active_support/string_inquirer.rb +11 -4
  239. data/lib/active_support/subscriber.rb +109 -39
  240. data/lib/active_support/tagged_logging.rb +54 -17
  241. data/lib/active_support/test_case.rb +121 -37
  242. data/lib/active_support/testing/assertions.rb +177 -39
  243. data/lib/active_support/testing/autorun.rb +5 -3
  244. data/lib/active_support/testing/constant_lookup.rb +3 -6
  245. data/lib/active_support/testing/declarative.rb +10 -22
  246. data/lib/active_support/testing/deprecation.rb +65 -11
  247. data/lib/active_support/testing/file_fixtures.rb +38 -0
  248. data/lib/active_support/testing/isolation.rb +56 -87
  249. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  255. data/lib/active_support/testing/stream.rb +41 -0
  256. data/lib/active_support/testing/tagged_logging.rb +6 -4
  257. data/lib/active_support/testing/time_helpers.rb +246 -0
  258. data/lib/active_support/time.rb +13 -13
  259. data/lib/active_support/time_with_zone.rb +315 -90
  260. data/lib/active_support/values/time_zone.rb +306 -135
  261. data/lib/active_support/version.rb +6 -7
  262. data/lib/active_support/xml_mini/jdom.rb +117 -115
  263. data/lib/active_support/xml_mini/libxml.rb +22 -21
  264. data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
  265. data/lib/active_support/xml_mini/nokogiri.rb +19 -19
  266. data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
  267. data/lib/active_support/xml_mini/rexml.rb +25 -17
  268. data/lib/active_support/xml_mini.rb +67 -56
  269. data/lib/active_support.rb +58 -3
  270. metadata +125 -66
  271. data/lib/active_support/basic_object.rb +0 -11
  272. data/lib/active_support/buffered_logger.rb +0 -21
  273. data/lib/active_support/concurrency/latch.rb +0 -27
  274. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  275. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  276. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  277. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  278. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  279. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  280. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  281. data/lib/active_support/core_ext/logger.rb +0 -67
  282. data/lib/active_support/core_ext/marshal.rb +0 -21
  283. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  284. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  285. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  286. data/lib/active_support/core_ext/proc.rb +0 -17
  287. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  288. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  289. data/lib/active_support/core_ext/struct.rb +0 -6
  290. data/lib/active_support/core_ext/thread.rb +0 -79
  291. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  292. data/lib/active_support/file_watcher.rb +0 -36
  293. data/lib/active_support/json/variable.rb +0 -18
  294. data/lib/active_support/testing/pending.rb +0 -14
  295. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -0,0 +1,65 @@
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
+ end
13
+
14
+ def define_cached_method(name, as: name)
15
+ name = name.to_sym
16
+ as = as.to_sym
17
+ @methods.fetch(name) do
18
+ unless @cache.method_defined?(as)
19
+ yield @sources
20
+ end
21
+ @methods[name] = as
22
+ end
23
+ end
24
+
25
+ def apply(owner, path, line)
26
+ unless @sources.empty?
27
+ @cache.module_eval("# frozen_string_literal: true\n" + @sources.join(";"), path, line)
28
+ end
29
+ @methods.each do |name, as|
30
+ owner.define_method(name, @cache.instance_method(as))
31
+ end
32
+ end
33
+ end
34
+
35
+ class << self
36
+ def batch(owner, path, line)
37
+ if owner.is_a?(CodeGenerator)
38
+ yield owner
39
+ else
40
+ instance = new(owner, path, line)
41
+ result = yield instance
42
+ instance.execute
43
+ result
44
+ end
45
+ end
46
+ end
47
+
48
+ def initialize(owner, path, line)
49
+ @owner = owner
50
+ @path = path
51
+ @line = line
52
+ @namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
53
+ end
54
+
55
+ def define_cached_method(name, namespace:, as: name, &block)
56
+ @namespaces[namespace].define_cached_method(name, as: as, &block)
57
+ end
58
+
59
+ def execute
60
+ @namespaces.each_value do |method_set|
61
+ method_set.apply(@owner, @path, @line - 1)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  # A typical module looks like this:
3
5
  #
@@ -17,7 +19,7 @@ module ActiveSupport
17
19
  # By using <tt>ActiveSupport::Concern</tt> the above module could instead be
18
20
  # written as:
19
21
  #
20
- # require 'active_support/concern'
22
+ # require "active_support/concern"
21
23
  #
22
24
  # module M
23
25
  # extend ActiveSupport::Concern
@@ -26,7 +28,7 @@ module ActiveSupport
26
28
  # scope :disabled, -> { where(disabled: true) }
27
29
  # end
28
30
  #
29
- # module ClassMethods
31
+ # class_methods do
30
32
  # ...
31
33
  # end
32
34
  # end
@@ -74,7 +76,7 @@ module ActiveSupport
74
76
  # is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
75
77
  # module dependencies are properly resolved:
76
78
  #
77
- # require 'active_support/concern'
79
+ # require "active_support/concern"
78
80
  #
79
81
  # module Foo
80
82
  # extend ActiveSupport::Concern
@@ -95,32 +97,119 @@ module ActiveSupport
95
97
  # end
96
98
  #
97
99
  # class Host
98
- # include Bar # works, Bar takes care now of its dependencies
100
+ # include Bar # It works, now Bar takes care of its dependencies
99
101
  # end
102
+ #
103
+ # === Prepending concerns
104
+ #
105
+ # Just like <tt>include</tt>, concerns also support <tt>prepend</tt> with a corresponding
106
+ # <tt>prepended do</tt> callback. <tt>module ClassMethods</tt> or <tt>class_methods do</tt> are
107
+ # prepended as well.
108
+ #
109
+ # <tt>prepend</tt> is also used for any dependencies.
100
110
  module Concern
101
- def self.extended(base) #:nodoc:
102
- base.instance_variable_set("@_dependencies", [])
111
+ class MultipleIncludedBlocks < StandardError # :nodoc:
112
+ def initialize
113
+ super "Cannot define multiple 'included' blocks for a Concern"
114
+ end
115
+ end
116
+
117
+ class MultiplePrependBlocks < StandardError # :nodoc:
118
+ def initialize
119
+ super "Cannot define multiple 'prepended' blocks for a Concern"
120
+ end
121
+ end
122
+
123
+ def self.extended(base) # :nodoc:
124
+ base.instance_variable_set(:@_dependencies, [])
125
+ end
126
+
127
+ def append_features(base) # :nodoc:
128
+ if base.instance_variable_defined?(:@_dependencies)
129
+ base.instance_variable_get(:@_dependencies) << self
130
+ false
131
+ else
132
+ return false if base < self
133
+ @_dependencies.each { |dep| base.include(dep) }
134
+ super
135
+ base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
136
+ base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
137
+ end
103
138
  end
104
139
 
105
- def append_features(base)
106
- if base.instance_variable_defined?("@_dependencies")
107
- base.instance_variable_get("@_dependencies") << self
108
- return false
140
+ def prepend_features(base) # :nodoc:
141
+ if base.instance_variable_defined?(:@_dependencies)
142
+ base.instance_variable_get(:@_dependencies).unshift self
143
+ false
109
144
  else
110
145
  return false if base < self
111
- @_dependencies.each { |dep| base.send(:include, dep) }
146
+ @_dependencies.each { |dep| base.prepend(dep) }
112
147
  super
113
- base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
114
- base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
148
+ base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods)
149
+ base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block)
115
150
  end
116
151
  end
117
152
 
153
+ # Evaluate given block in context of base class,
154
+ # so that you can write class macros here.
155
+ # When you define more than one +included+ block, it raises an exception.
118
156
  def included(base = nil, &block)
119
157
  if base.nil?
120
- @_included_block = block
158
+ if instance_variable_defined?(:@_included_block)
159
+ if @_included_block.source_location != block.source_location
160
+ raise MultipleIncludedBlocks
161
+ end
162
+ else
163
+ @_included_block = block
164
+ end
121
165
  else
122
166
  super
123
167
  end
124
168
  end
169
+
170
+ # Evaluate given block in context of base class,
171
+ # so that you can write class macros here.
172
+ # When you define more than one +prepended+ block, it raises an exception.
173
+ def prepended(base = nil, &block)
174
+ if base.nil?
175
+ if instance_variable_defined?(:@_prepended_block)
176
+ if @_prepended_block.source_location != block.source_location
177
+ raise MultiplePrependBlocks
178
+ end
179
+ else
180
+ @_prepended_block = block
181
+ end
182
+ else
183
+ super
184
+ end
185
+ end
186
+
187
+ # Define class methods from given block.
188
+ # You can define private class methods as well.
189
+ #
190
+ # module Example
191
+ # extend ActiveSupport::Concern
192
+ #
193
+ # class_methods do
194
+ # def foo; puts 'foo'; end
195
+ #
196
+ # private
197
+ # def bar; puts 'bar'; end
198
+ # end
199
+ # end
200
+ #
201
+ # class Buzz
202
+ # include Example
203
+ # end
204
+ #
205
+ # Buzz.foo # => "foo"
206
+ # Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
207
+ def class_methods(&class_methods_module_definition)
208
+ mod = const_defined?(:ClassMethods, false) ?
209
+ const_get(:ClassMethods) :
210
+ const_set(:ClassMethods, Module.new)
211
+
212
+ mod.module_eval(&class_methods_module_definition)
213
+ end
125
214
  end
126
215
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+
5
+ module ActiveSupport
6
+ module Concurrency
7
+ # A monitor that will permit dependency loading while blocked waiting for
8
+ # the lock.
9
+ class LoadInterlockAwareMonitor < Monitor
10
+ EXCEPTION_NEVER = { Exception => :never }.freeze
11
+ EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze
12
+ private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
13
+
14
+ # Enters an exclusive section, but allows dependency loading while blocked
15
+ def mon_enter
16
+ mon_try_enter ||
17
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads { super }
18
+ end
19
+
20
+ def synchronize(&block)
21
+ Thread.handle_interrupt(EXCEPTION_NEVER) do
22
+ mon_enter
23
+
24
+ begin
25
+ Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
26
+ ensure
27
+ mon_exit
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+ require "monitor"
5
+
6
+ module ActiveSupport
7
+ module Concurrency
8
+ # A share/exclusive lock, otherwise known as a read/write lock.
9
+ #
10
+ # https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
11
+ class ShareLock
12
+ include MonitorMixin
13
+
14
+ # We track Thread objects, instead of just using counters, because
15
+ # we need exclusive locks to be reentrant, and we need to be able
16
+ # to upgrade share locks to exclusive.
17
+
18
+ def raw_state # :nodoc:
19
+ synchronize do
20
+ threads = @sleeping.keys | @sharing.keys | @waiting.keys
21
+ threads |= [@exclusive_thread] if @exclusive_thread
22
+
23
+ data = {}
24
+
25
+ threads.each do |thread|
26
+ purpose, compatible = @waiting[thread]
27
+
28
+ data[thread] = {
29
+ thread: thread,
30
+ sharing: @sharing[thread],
31
+ exclusive: @exclusive_thread == thread,
32
+ purpose: purpose,
33
+ compatible: compatible,
34
+ waiting: !!@waiting[thread],
35
+ sleeper: @sleeping[thread],
36
+ }
37
+ end
38
+
39
+ # NB: Yields while holding our *internal* synchronize lock,
40
+ # which is supposed to be used only for a few instructions at
41
+ # a time. This allows the caller to inspect additional state
42
+ # without things changing out from underneath, but would have
43
+ # disastrous effects upon normal operation. Fortunately, this
44
+ # method is only intended to be called when things have
45
+ # already gone wrong.
46
+ yield data
47
+ end
48
+ end
49
+
50
+ def initialize
51
+ super()
52
+
53
+ @cv = new_cond
54
+
55
+ @sharing = Hash.new(0)
56
+ @waiting = {}
57
+ @sleeping = {}
58
+ @exclusive_thread = nil
59
+ @exclusive_depth = 0
60
+ end
61
+
62
+ # Returns false if +no_wait+ is set and the lock is not
63
+ # immediately available. Otherwise, returns true after the lock
64
+ # has been acquired.
65
+ #
66
+ # +purpose+ and +compatible+ work together; while this thread is
67
+ # waiting for the exclusive lock, it will yield its share (if any)
68
+ # to any other attempt whose +purpose+ appears in this attempt's
69
+ # +compatible+ list. This allows a "loose" upgrade, which, being
70
+ # less strict, prevents some classes of deadlocks.
71
+ #
72
+ # For many resources, loose upgrades are sufficient: if a thread
73
+ # is awaiting a lock, it is not running any other code. With
74
+ # +purpose+ matching, it is possible to yield only to other
75
+ # threads whose activity will not interfere.
76
+ def start_exclusive(purpose: nil, compatible: [], no_wait: false)
77
+ synchronize do
78
+ unless @exclusive_thread == Thread.current
79
+ if busy_for_exclusive?(purpose)
80
+ return false if no_wait
81
+
82
+ yield_shares(purpose: purpose, compatible: compatible, block_share: true) do
83
+ wait_for(:start_exclusive) { busy_for_exclusive?(purpose) }
84
+ end
85
+ end
86
+ @exclusive_thread = Thread.current
87
+ end
88
+ @exclusive_depth += 1
89
+
90
+ true
91
+ end
92
+ end
93
+
94
+ # Relinquish the exclusive lock. Must only be called by the thread
95
+ # that called start_exclusive (and currently holds the lock).
96
+ def stop_exclusive(compatible: [])
97
+ synchronize do
98
+ raise "invalid unlock" if @exclusive_thread != Thread.current
99
+
100
+ @exclusive_depth -= 1
101
+ if @exclusive_depth == 0
102
+ @exclusive_thread = nil
103
+
104
+ if eligible_waiters?(compatible)
105
+ yield_shares(compatible: compatible, block_share: true) do
106
+ wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) }
107
+ end
108
+ end
109
+ @cv.broadcast
110
+ end
111
+ end
112
+ end
113
+
114
+ def start_sharing
115
+ synchronize do
116
+ if @sharing[Thread.current] > 0 || @exclusive_thread == Thread.current
117
+ # We already hold a lock; nothing to wait for
118
+ elsif @waiting[Thread.current]
119
+ # We're nested inside a +yield_shares+ call: we'll resume as
120
+ # soon as there isn't an exclusive lock in our way
121
+ wait_for(:start_sharing) { @exclusive_thread }
122
+ else
123
+ # This is an initial / outermost share call: any outstanding
124
+ # requests for an exclusive lock get to go first
125
+ wait_for(:start_sharing) { busy_for_sharing?(false) }
126
+ end
127
+ @sharing[Thread.current] += 1
128
+ end
129
+ end
130
+
131
+ def stop_sharing
132
+ synchronize do
133
+ if @sharing[Thread.current] > 1
134
+ @sharing[Thread.current] -= 1
135
+ else
136
+ @sharing.delete Thread.current
137
+ @cv.broadcast
138
+ end
139
+ end
140
+ end
141
+
142
+ # Execute the supplied block while holding the Exclusive lock. If
143
+ # +no_wait+ is set and the lock is not immediately available,
144
+ # returns +nil+ without yielding. Otherwise, returns the result of
145
+ # the block.
146
+ #
147
+ # See +start_exclusive+ for other options.
148
+ def exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false)
149
+ if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait)
150
+ begin
151
+ yield
152
+ ensure
153
+ stop_exclusive(compatible: after_compatible)
154
+ end
155
+ end
156
+ end
157
+
158
+ # Execute the supplied block while holding the Share lock.
159
+ def sharing
160
+ start_sharing
161
+ begin
162
+ yield
163
+ ensure
164
+ stop_sharing
165
+ end
166
+ end
167
+
168
+ # Temporarily give up all held Share locks while executing the
169
+ # supplied block, allowing any +compatible+ exclusive lock request
170
+ # to proceed.
171
+ def yield_shares(purpose: nil, compatible: [], block_share: false)
172
+ loose_shares = previous_wait = nil
173
+ synchronize do
174
+ if loose_shares = @sharing.delete(Thread.current)
175
+ if previous_wait = @waiting[Thread.current]
176
+ purpose = nil unless purpose == previous_wait[0]
177
+ compatible &= previous_wait[1]
178
+ end
179
+ compatible |= [false] unless block_share
180
+ @waiting[Thread.current] = [purpose, compatible]
181
+ end
182
+
183
+ @cv.broadcast
184
+ end
185
+
186
+ begin
187
+ yield
188
+ ensure
189
+ synchronize do
190
+ wait_for(:yield_shares) { @exclusive_thread && @exclusive_thread != Thread.current }
191
+
192
+ if previous_wait
193
+ @waiting[Thread.current] = previous_wait
194
+ else
195
+ @waiting.delete Thread.current
196
+ end
197
+ @sharing[Thread.current] = loose_shares if loose_shares
198
+ end
199
+ end
200
+ end
201
+
202
+ private
203
+ # Must be called within synchronize
204
+ def busy_for_exclusive?(purpose)
205
+ busy_for_sharing?(purpose) ||
206
+ @sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0)
207
+ end
208
+
209
+ def busy_for_sharing?(purpose)
210
+ (@exclusive_thread && @exclusive_thread != Thread.current) ||
211
+ @waiting.any? { |t, (_, c)| t != Thread.current && !c.include?(purpose) }
212
+ end
213
+
214
+ def eligible_waiters?(compatible)
215
+ @waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } }
216
+ end
217
+
218
+ def wait_for(method, &block)
219
+ @sleeping[Thread.current] = method
220
+ @cv.wait_while(&block)
221
+ ensure
222
+ @sleeping.delete Thread.current
223
+ end
224
+ end
225
+ end
226
+ end
@@ -1,10 +1,11 @@
1
- require 'active_support/concern'
2
- require 'active_support/ordered_options'
3
- require 'active_support/core_ext/array/extract_options'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+ require "active_support/ordered_options"
4
5
 
5
6
  module ActiveSupport
6
7
  # Configurable provides a <tt>config</tt> method to store and retrieve
7
- # configuration options as an <tt>OrderedHash</tt>.
8
+ # configuration options as an <tt>OrderedOptions</tt>.
8
9
  module Configurable
9
10
  extend ActiveSupport::Concern
10
11
 
@@ -66,8 +67,8 @@ module ActiveSupport
66
67
  # end
67
68
  # # => NameError: invalid config attribute name
68
69
  #
69
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
70
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
70
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
71
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
71
72
  #
72
73
  # class User
73
74
  # include ActiveSupport::Configurable
@@ -80,7 +81,7 @@ module ActiveSupport
80
81
  # User.new.allowed_access = true # => NoMethodError
81
82
  # User.new.allowed_access # => NoMethodError
82
83
  #
83
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
84
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
84
85
  #
85
86
  # class User
86
87
  # include ActiveSupport::Configurable
@@ -93,21 +94,21 @@ module ActiveSupport
93
94
  # User.new.allowed_access = true # => NoMethodError
94
95
  # User.new.allowed_access # => NoMethodError
95
96
  #
96
- # Also you can pass a block to set up the attribute with a default value.
97
+ # Also you can pass <tt>default</tt> or a block to set up the attribute with a default value.
97
98
  #
98
99
  # class User
99
100
  # include ActiveSupport::Configurable
101
+ # config_accessor :allowed_access, default: false
100
102
  # config_accessor :hair_colors do
101
103
  # [:brown, :black, :blonde, :red]
102
104
  # end
103
105
  # end
104
106
  #
107
+ # User.allowed_access # => false
105
108
  # User.hair_colors # => [:brown, :black, :blonde, :red]
106
- def config_accessor(*names)
107
- options = names.extract_options!
108
-
109
+ def config_accessor(*names, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil) # :doc:
109
110
  names.each do |name|
110
- raise NameError.new('invalid config attribute name') unless name =~ /^[_A-Za-z]\w*$/
111
+ raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
111
112
 
112
113
  reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
113
114
  writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
@@ -115,18 +116,20 @@ module ActiveSupport
115
116
  singleton_class.class_eval reader, __FILE__, reader_line
116
117
  singleton_class.class_eval writer, __FILE__, writer_line
117
118
 
118
- unless options[:instance_accessor] == false
119
- class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
120
- class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
119
+ if instance_accessor
120
+ class_eval reader, __FILE__, reader_line if instance_reader
121
+ class_eval writer, __FILE__, writer_line if instance_writer
121
122
  end
122
- send("#{name}=", yield) if block_given?
123
+
124
+ send("#{name}=", block_given? ? yield : default)
123
125
  end
124
126
  end
127
+ private :config_accessor
125
128
  end
126
129
 
127
- # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
130
+ # Reads and writes attributes from a configuration <tt>OrderedOptions</tt>.
128
131
  #
129
- # require 'active_support/configurable'
132
+ # require "active_support/configurable"
130
133
  #
131
134
  # class User
132
135
  # include ActiveSupport::Configurable
@@ -144,4 +147,3 @@ module ActiveSupport
144
147
  end
145
148
  end
146
149
  end
147
-
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # Reads a YAML configuration file, evaluating any ERB, then
5
+ # parsing the resulting YAML.
6
+ #
7
+ # Warns in case of YAML confusing characters, like invisible
8
+ # non-breaking spaces.
9
+ class ConfigurationFile # :nodoc:
10
+ class FormatError < StandardError; end
11
+
12
+ def initialize(content_path)
13
+ @content_path = content_path.to_s
14
+ @content = read content_path
15
+ end
16
+
17
+ def self.parse(content_path, **options)
18
+ new(content_path).parse(**options)
19
+ end
20
+
21
+ def parse(context: nil, **options)
22
+ source = render(context)
23
+ if YAML.respond_to?(:unsafe_load)
24
+ YAML.unsafe_load(source, **options) || {}
25
+ else
26
+ YAML.load(source, **options) || {}
27
+ end
28
+ rescue Psych::SyntaxError => error
29
+ raise "YAML syntax error occurred while parsing #{@content_path}. " \
30
+ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
31
+ "Error: #{error.message}"
32
+ end
33
+
34
+ private
35
+ def read(content_path)
36
+ require "yaml"
37
+ require "erb"
38
+
39
+ File.read(content_path).tap do |content|
40
+ if content.include?("\u00A0")
41
+ warn "#{content_path} contains invisible non-breaking spaces, you may want to remove those"
42
+ end
43
+ end
44
+ end
45
+
46
+ def render(context)
47
+ erb = ERB.new(@content).tap { |e| e.filename = @content_path }
48
+ context ? erb.result(context) : erb.result
49
+ end
50
+ end
51
+ end