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,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ class Module
6
+ # = Bite-sized separation of concerns
7
+ #
8
+ # We often find ourselves with a medium-sized chunk of behavior that we'd
9
+ # like to extract, but only mix in to a single class.
10
+ #
11
+ # Extracting a plain old Ruby object to encapsulate it and collaborate or
12
+ # delegate to the original object is often a good choice, but when there's
13
+ # no additional state to encapsulate or we're making DSL-style declarations
14
+ # about the parent class, introducing new collaborators can obfuscate rather
15
+ # than simplify.
16
+ #
17
+ # The typical route is to just dump everything in a monolithic class, perhaps
18
+ # with a comment, as a least-bad alternative. Using modules in separate files
19
+ # means tedious sifting to get a big-picture view.
20
+ #
21
+ # = Dissatisfying ways to separate small concerns
22
+ #
23
+ # == Using comments:
24
+ #
25
+ # class Todo < ApplicationRecord
26
+ # # Other todo implementation
27
+ # # ...
28
+ #
29
+ # ## Event tracking
30
+ # has_many :events
31
+ #
32
+ # before_create :track_creation
33
+ #
34
+ # private
35
+ # def track_creation
36
+ # # ...
37
+ # end
38
+ # end
39
+ #
40
+ # == With an inline module:
41
+ #
42
+ # Noisy syntax.
43
+ #
44
+ # class Todo < ApplicationRecord
45
+ # # Other todo implementation
46
+ # # ...
47
+ #
48
+ # module EventTracking
49
+ # extend ActiveSupport::Concern
50
+ #
51
+ # included do
52
+ # has_many :events
53
+ # before_create :track_creation
54
+ # end
55
+ #
56
+ # private
57
+ # def track_creation
58
+ # # ...
59
+ # end
60
+ # end
61
+ # include EventTracking
62
+ # end
63
+ #
64
+ # == Mix-in noise exiled to its own file:
65
+ #
66
+ # Once our chunk of behavior starts pushing the scroll-to-understand-it
67
+ # boundary, we give in and move it to a separate file. At this size, the
68
+ # increased overhead can be a reasonable tradeoff even if it reduces our
69
+ # at-a-glance perception of how things work.
70
+ #
71
+ # class Todo < ApplicationRecord
72
+ # # Other todo implementation
73
+ # # ...
74
+ #
75
+ # include TodoEventTracking
76
+ # end
77
+ #
78
+ # = Introducing Module#concerning
79
+ #
80
+ # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
81
+ # separate bite-sized concerns.
82
+ #
83
+ # class Todo < ApplicationRecord
84
+ # # Other todo implementation
85
+ # # ...
86
+ #
87
+ # concerning :EventTracking do
88
+ # included do
89
+ # has_many :events
90
+ # before_create :track_creation
91
+ # end
92
+ #
93
+ # private
94
+ # def track_creation
95
+ # # ...
96
+ # end
97
+ # end
98
+ # end
99
+ #
100
+ # Todo.ancestors
101
+ # # => [Todo, Todo::EventTracking, ApplicationRecord, Object]
102
+ #
103
+ # This small step has some wonderful ripple effects. We can
104
+ # * grok the behavior of our class in one glance,
105
+ # * clean up monolithic junk-drawer classes by separating their concerns, and
106
+ # * stop leaning on protected/private for crude "this is internal stuff" modularity.
107
+ #
108
+ # === Prepending concerning
109
+ #
110
+ # <tt>concerning</tt> supports a <tt>prepend: true</tt> argument which will <tt>prepend</tt> the
111
+ # concern instead of using <tt>include</tt> for it.
112
+ module Concerning
113
+ # Define a new concern and mix it in.
114
+ def concerning(topic, prepend: false, &block)
115
+ method = prepend ? :prepend : :include
116
+ __send__(method, concern(topic, &block))
117
+ end
118
+
119
+ # A low-cruft shortcut to define a concern.
120
+ #
121
+ # concern :EventTracking do
122
+ # ...
123
+ # end
124
+ #
125
+ # is equivalent to
126
+ #
127
+ # module EventTracking
128
+ # extend ActiveSupport::Concern
129
+ #
130
+ # ...
131
+ # end
132
+ def concern(topic, &module_definition)
133
+ const_set topic, Module.new {
134
+ extend ::ActiveSupport::Concern
135
+ module_eval(&module_definition)
136
+ }
137
+ end
138
+ end
139
+ include Concerning
140
+ end
@@ -1,7 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
1
5
  class Module
6
+ # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
7
+ # option is not used.
8
+ class DelegationError < NoMethodError; end
9
+
10
+ RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
11
+ else elsif END end ensure false for if in module next nil not or redo rescue retry
12
+ return self super then true undef unless until when while yield)
13
+ DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
14
+ DELEGATION_RESERVED_METHOD_NAMES = Set.new(
15
+ RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
16
+ ).freeze
17
+
2
18
  # Provides a +delegate+ class method to easily expose contained objects'
3
19
  # public methods as your own.
4
20
  #
21
+ # ==== Options
22
+ # * <tt>:to</tt> - Specifies the target object name as a symbol or string
23
+ # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
24
+ # * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+
25
+ # from being raised
26
+ # * <tt>:private</tt> - If set to true, changes method visibility to private
27
+ #
5
28
  # The macro receives one or more method names (specified as symbols or
6
29
  # strings) and the name of the target object via the <tt>:to</tt> option
7
30
  # (also a symbol or string).
@@ -91,19 +114,34 @@ class Module
91
114
  # invoice.customer_name # => 'John Doe'
92
115
  # invoice.customer_address # => 'Vimmersvej 13'
93
116
  #
117
+ # The delegated methods are public by default.
118
+ # Pass <tt>private: true</tt> to change that.
119
+ #
120
+ # class User < ActiveRecord::Base
121
+ # has_one :profile
122
+ # delegate :first_name, to: :profile
123
+ # delegate :date_of_birth, to: :profile, private: true
124
+ #
125
+ # def age
126
+ # Date.today.year - date_of_birth.year
127
+ # end
128
+ # end
129
+ #
130
+ # User.new.first_name # => "Tomas"
131
+ # User.new.date_of_birth # => NoMethodError: private method `date_of_birth' called for #<User:0x00000008221340>
132
+ # User.new.age # => 2
133
+ #
94
134
  # If the target is +nil+ and does not respond to the delegated method a
95
- # +NoMethodError+ is raised, as with any other value. Sometimes, however, it
96
- # makes sense to be robust to that situation and that is the purpose of the
97
- # <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
98
- # responds to the method, everything works as usual. But if it is +nil+ and
99
- # does not respond to the delegated method, +nil+ is returned.
135
+ # +Module::DelegationError+ is raised. If you wish to instead return +nil+,
136
+ # use the <tt>:allow_nil</tt> option.
100
137
  #
101
138
  # class User < ActiveRecord::Base
102
139
  # has_one :profile
103
140
  # delegate :age, to: :profile
104
141
  # end
105
142
  #
106
- # User.new.age # raises NoMethodError: undefined method `age'
143
+ # User.new.age
144
+ # # => Module::DelegationError: User#age delegated to profile.age, but profile is nil
107
145
  #
108
146
  # But if not having a profile yet is fine and should not be an error
109
147
  # condition:
@@ -129,69 +167,158 @@ class Module
129
167
  #
130
168
  # Foo.new("Bar").name # raises NoMethodError: undefined method `name'
131
169
  #
132
- def delegate(*methods)
133
- options = methods.pop
134
- unless options.is_a?(Hash) && to = options[:to]
135
- raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
170
+ # The target method must be public, otherwise it will raise +NoMethodError+.
171
+ def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
172
+ unless to
173
+ raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
136
174
  end
137
175
 
138
- prefix, allow_nil = options.values_at(:prefix, :allow_nil)
139
-
140
- if prefix == true && to =~ /^[^a-z_]/
141
- raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
176
+ if prefix == true && /^[^a-z_]/.match?(to)
177
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
142
178
  end
143
179
 
144
180
  method_prefix = \
145
181
  if prefix
146
182
  "#{prefix == true ? to : prefix}_"
147
183
  else
148
- ''
184
+ ""
149
185
  end
150
186
 
151
- file, line = caller.first.split(':', 2)
152
- line = line.to_i
187
+ location = caller_locations(1, 1).first
188
+ file, line = location.path, location.lineno
153
189
 
154
190
  to = to.to_s
155
- to = 'self.class' if to == 'class'
191
+ to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
192
+
193
+ method_def = []
194
+ method_names = []
195
+
196
+ methods.map do |method|
197
+ method_name = prefix ? "#{method_prefix}#{method}" : method
198
+ method_names << method_name.to_sym
156
199
 
157
- methods.each do |method|
158
200
  # Attribute writer methods only accept one argument. Makes sure []=
159
201
  # methods still accept two arguments.
160
- definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
202
+ definition = /[^\]]=\z/.match?(method) ? "arg" : "..."
161
203
 
162
- # The following generated methods call the target exactly once, storing
204
+ # The following generated method calls the target exactly once, storing
163
205
  # the returned value in a dummy variable.
164
206
  #
165
207
  # Reason is twofold: On one hand doing less calls is in general better.
166
208
  # On the other hand it could be that the target has side-effects,
167
- # whereas conceptualy, from the user point of view, the delegator should
209
+ # whereas conceptually, from the user point of view, the delegator should
168
210
  # be doing one call.
169
211
  if allow_nil
170
- module_eval(<<-EOS, file, line - 3)
171
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
172
- _ = #{to} # _ = client
173
- if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name)
174
- _.#{method}(#{definition}) # _.name(*args, &block)
175
- end # end
176
- end # end
177
- EOS
212
+ method = method.to_s
213
+
214
+ method_def <<
215
+ "def #{method_name}(#{definition})" <<
216
+ " _ = #{to}" <<
217
+ " if !_.nil? || nil.respond_to?(:#{method})" <<
218
+ " _.#{method}(#{definition})" <<
219
+ " end" <<
220
+ "end"
178
221
  else
179
- exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
180
-
181
- module_eval(<<-EOS, file, line - 2)
182
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
183
- _ = #{to} # _ = client
184
- _.#{method}(#{definition}) # _.name(*args, &block)
185
- rescue NoMethodError => e # rescue NoMethodError => e
186
- location = "%s:%d:in `%s'" % [__FILE__, __LINE__ - 2, '#{method_prefix}#{method}'] # location = "%s:%d:in `%s'" % [__FILE__, __LINE__ - 2, 'customer_name']
187
- if _.nil? && e.backtrace.first == location # if _.nil? && e.backtrace.first == location
188
- #{exception} # # add helpful message to the exception
189
- else # else
190
- raise # raise
191
- end # end
192
- end # end
193
- EOS
222
+ method = method.to_s
223
+ method_name = method_name.to_s
224
+
225
+ method_def <<
226
+ "def #{method_name}(#{definition})" <<
227
+ " _ = #{to}" <<
228
+ " _.#{method}(#{definition})" <<
229
+ "rescue NoMethodError => e" <<
230
+ " if _.nil? && e.name == :#{method}" <<
231
+ %( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") <<
232
+ " else" <<
233
+ " raise" <<
234
+ " end" <<
235
+ "end"
194
236
  end
195
237
  end
238
+ module_eval(method_def.join(";"), file, line)
239
+ private(*method_names) if private
240
+ method_names
241
+ end
242
+
243
+ # When building decorators, a common pattern may emerge:
244
+ #
245
+ # class Partition
246
+ # def initialize(event)
247
+ # @event = event
248
+ # end
249
+ #
250
+ # def person
251
+ # detail.person || creator
252
+ # end
253
+ #
254
+ # private
255
+ # def respond_to_missing?(name, include_private = false)
256
+ # @event.respond_to?(name, include_private)
257
+ # end
258
+ #
259
+ # def method_missing(method, *args, &block)
260
+ # @event.send(method, *args, &block)
261
+ # end
262
+ # end
263
+ #
264
+ # With <tt>Module#delegate_missing_to</tt>, the above is condensed to:
265
+ #
266
+ # class Partition
267
+ # delegate_missing_to :@event
268
+ #
269
+ # def initialize(event)
270
+ # @event = event
271
+ # end
272
+ #
273
+ # def person
274
+ # detail.person || creator
275
+ # end
276
+ # end
277
+ #
278
+ # The target can be anything callable within the object, e.g. instance
279
+ # variables, methods, constants, etc.
280
+ #
281
+ # The delegated method must be public on the target, otherwise it will
282
+ # raise +DelegationError+. If you wish to instead return +nil+,
283
+ # use the <tt>:allow_nil</tt> option.
284
+ #
285
+ # The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
286
+ # delegation due to possible interference when calling
287
+ # <tt>Marshal.dump(object)</tt>, should the delegation target method
288
+ # of <tt>object</tt> add or remove instance variables.
289
+ def delegate_missing_to(target, allow_nil: nil)
290
+ target = target.to_s
291
+ target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
292
+
293
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
294
+ def respond_to_missing?(name, include_private = false)
295
+ # It may look like an oversight, but we deliberately do not pass
296
+ # +include_private+, because they do not get delegated.
297
+
298
+ return false if name == :marshal_dump || name == :_dump
299
+ #{target}.respond_to?(name) || super
300
+ end
301
+
302
+ def method_missing(method, *args, &block)
303
+ if #{target}.respond_to?(method)
304
+ #{target}.public_send(method, *args, &block)
305
+ else
306
+ begin
307
+ super
308
+ rescue NoMethodError
309
+ if #{target}.nil?
310
+ if #{allow_nil == true}
311
+ nil
312
+ else
313
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
314
+ end
315
+ else
316
+ raise
317
+ end
318
+ end
319
+ end
320
+ end
321
+ ruby2_keywords(:method_missing)
322
+ RUBY
196
323
  end
197
324
  end
@@ -1,4 +1,4 @@
1
- require 'active_support/deprecation/method_wrappers'
1
+ # frozen_string_literal: true
2
2
 
3
3
  class Module
4
4
  # deprecate :foo
@@ -15,8 +15,8 @@ class Module
15
15
  #
16
16
  # class MyLib::Deprecator
17
17
  # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
18
- # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
19
- # Kernel.warn message
18
+ # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
19
+ # Kernel.warn message
20
20
  # end
21
21
  # end
22
22
  def deprecate(*method_names)
@@ -1,14 +1,19 @@
1
- require 'active_support/inflector'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
4
+ require "active_support/inflector"
2
5
 
3
6
  class Module
4
7
  # Returns the name of the module containing this one.
5
8
  #
6
- # M::N.parent_name # => "M"
7
- def parent_name
8
- if defined? @parent_name
9
+ # M::N.module_parent_name # => "M"
10
+ def module_parent_name
11
+ if defined?(@parent_name)
9
12
  @parent_name
10
13
  else
11
- @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
14
+ parent_name = name =~ /::[^:]+\z/ ? -$` : nil
15
+ @parent_name = parent_name unless frozen?
16
+ parent_name
12
17
  end
13
18
  end
14
19
 
@@ -20,15 +25,15 @@ class Module
20
25
  # end
21
26
  # X = M::N
22
27
  #
23
- # M::N.parent # => M
24
- # X.parent # => M
28
+ # M::N.module_parent # => M
29
+ # X.module_parent # => M
25
30
  #
26
31
  # The parent of top-level and anonymous modules is Object.
27
32
  #
28
- # M.parent # => Object
29
- # Module.new.parent # => Object
30
- def parent
31
- parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
33
+ # M.module_parent # => Object
34
+ # Module.new.module_parent # => Object
35
+ def module_parent
36
+ module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object
32
37
  end
33
38
 
34
39
  # Returns all the parents of this module according to its name, ordered from
@@ -40,39 +45,19 @@ class Module
40
45
  # end
41
46
  # X = M::N
42
47
  #
43
- # M.parents # => [Object]
44
- # M::N.parents # => [M, Object]
45
- # X.parents # => [M, Object]
46
- def parents
48
+ # M.module_parents # => [Object]
49
+ # M::N.module_parents # => [M, Object]
50
+ # X.module_parents # => [M, Object]
51
+ def module_parents
47
52
  parents = []
48
- if parent_name
49
- parts = parent_name.split('::')
53
+ if module_parent_name
54
+ parts = module_parent_name.split("::")
50
55
  until parts.empty?
51
- parents << ActiveSupport::Inflector.constantize(parts * '::')
56
+ parents << ActiveSupport::Inflector.constantize(parts * "::")
52
57
  parts.pop
53
58
  end
54
59
  end
55
60
  parents << Object unless parents.include? Object
56
61
  parents
57
62
  end
58
-
59
- def local_constants #:nodoc:
60
- constants(false)
61
- end
62
-
63
- # *DEPRECATED*: Use +local_constants+ instead.
64
- #
65
- # Returns the names of the constants defined locally as strings.
66
- #
67
- # module M
68
- # X = 1
69
- # end
70
- # M.local_constant_names # => ["X"]
71
- #
72
- # This method is useful for forward compatibility, since Ruby 1.8 returns
73
- # constant names as strings, whereas 1.9 returns them as symbols.
74
- def local_constant_names
75
- ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead'
76
- local_constants.map { |c| c.to_s }
77
- end
78
63
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Module
4
+ # Marks the named method as intended to be redefined, if it exists.
5
+ # Suppresses the Ruby method redefinition warning. Prefer
6
+ # #redefine_method where possible.
7
+ def silence_redefinition_of_method(method)
8
+ if method_defined?(method) || private_method_defined?(method)
9
+ # This suppresses the "method redefined" warning; the self-alias
10
+ # looks odd, but means we don't need to generate a unique name
11
+ alias_method method, method
12
+ end
13
+ end
14
+
15
+ # Replaces the existing method definition, if there is one, with the passed
16
+ # block as its body.
17
+ def redefine_method(method, &block)
18
+ visibility = method_visibility(method)
19
+ silence_redefinition_of_method(method)
20
+ define_method(method, &block)
21
+ send(visibility, method)
22
+ end
23
+
24
+ # Replaces the existing singleton method definition, if there is one, with
25
+ # the passed block as its body.
26
+ def redefine_singleton_method(method, &block)
27
+ singleton_class.redefine_method(method, &block)
28
+ end
29
+
30
+ def method_visibility(method) # :nodoc:
31
+ case
32
+ when private_method_defined?(method)
33
+ :private
34
+ when protected_method_defined?(method)
35
+ :protected
36
+ else
37
+ :public
38
+ end
39
+ end
40
+ end
@@ -1,12 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/redefine_method"
4
+
1
5
  class Module
6
+ # Removes the named method, if it exists.
2
7
  def remove_possible_method(method)
3
8
  if method_defined?(method) || private_method_defined?(method)
4
9
  undef_method(method)
5
10
  end
6
11
  end
7
12
 
8
- def redefine_method(method, &block)
9
- remove_possible_method(method)
10
- define_method(method, &block)
13
+ # Removes the named singleton method, if it exists.
14
+ def remove_possible_singleton_method(method)
15
+ singleton_class.remove_possible_method(method)
11
16
  end
12
17
  end
@@ -1,10 +1,13 @@
1
- require 'active_support/core_ext/module/aliasing'
2
- require 'active_support/core_ext/module/introspection'
3
- require 'active_support/core_ext/module/anonymous'
4
- require 'active_support/core_ext/module/reachable'
5
- require 'active_support/core_ext/module/attribute_accessors'
6
- require 'active_support/core_ext/module/attr_internal'
7
- require 'active_support/core_ext/module/delegation'
8
- require 'active_support/core_ext/module/deprecation'
9
- require 'active_support/core_ext/module/remove_method'
10
- require 'active_support/core_ext/module/qualified_const'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/aliasing"
4
+ require "active_support/core_ext/module/introspection"
5
+ require "active_support/core_ext/module/anonymous"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/core_ext/module/attribute_accessors_per_thread"
8
+ require "active_support/core_ext/module/attr_internal"
9
+ require "active_support/core_ext/module/concerning"
10
+ require "active_support/core_ext/module/delegation"
11
+ require "active_support/core_ext/module/deprecation"
12
+ require "active_support/core_ext/module/redefine_method"
13
+ require "active_support/core_ext/module/remove_method"
@@ -1,18 +1,59 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class NameError
2
4
  # Extract the name of the missing constant from the exception message.
5
+ #
6
+ # begin
7
+ # HelloWorld
8
+ # rescue NameError => e
9
+ # e.missing_name
10
+ # end
11
+ # # => "HelloWorld"
3
12
  def missing_name
4
- if /undefined local variable or method/ !~ message
5
- $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
13
+ # Since ruby v2.3.0 `did_you_mean` gem is loaded by default.
14
+ # It extends NameError#message with spell corrections which are SLOW.
15
+ # We should use original_message message instead.
16
+ message = respond_to?(:original_message) ? original_message : self.message
17
+ return unless message.start_with?("uninitialized constant ")
18
+
19
+ receiver = begin
20
+ self.receiver
21
+ rescue ArgumentError
22
+ nil
23
+ end
24
+
25
+ if receiver == Object
26
+ name.to_s
27
+ elsif receiver
28
+ "#{real_mod_name(receiver)}::#{self.name}"
29
+ else
30
+ if match = message.match(/((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/)
31
+ match[1]
32
+ end
6
33
  end
7
34
  end
8
35
 
9
36
  # Was this exception raised because the given name was missing?
37
+ #
38
+ # begin
39
+ # HelloWorld
40
+ # rescue NameError => e
41
+ # e.missing_name?("HelloWorld")
42
+ # end
43
+ # # => true
10
44
  def missing_name?(name)
11
45
  if name.is_a? Symbol
12
- last_name = (missing_name || '').split('::').last
13
- last_name == name.to_s
46
+ self.name == name
14
47
  else
15
48
  missing_name == name.to_s
16
49
  end
17
50
  end
51
+
52
+ private
53
+ UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
54
+ private_constant :UNBOUND_METHOD_MODULE_NAME
55
+
56
+ def real_mod_name(mod)
57
+ UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
58
+ end
18
59
  end