activesupport 5.0.0 → 6.1.0

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 (268) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +343 -590
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -4
  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 +11 -5
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +45 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +81 -79
  13. data/lib/active_support/cache/memory_store.rb +69 -41
  14. data/lib/active_support/cache/null_store.rb +11 -4
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +74 -37
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +332 -161
  19. data/lib/active_support/callbacks.rb +657 -586
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +59 -19
  23. data/lib/active_support/configurable.rb +15 -17
  24. data/lib/active_support/configuration_file.rb +46 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +20 -18
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +3 -1
  30. data/lib/active_support/core_ext/array/inquiry.rb +3 -1
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +9 -7
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +6 -6
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -26
  39. data/lib/active_support/core_ext/class.rb +4 -2
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +3 -1
  42. data/lib/active_support/core_ext/date/calculations.rb +16 -13
  43. data/lib/active_support/core_ext/date/conversions.rb +23 -21
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +7 -5
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +82 -53
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -5
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +9 -9
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  51. data/lib/active_support/core_ext/date_time/calculations.rb +23 -11
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +15 -2
  53. data/lib/active_support/core_ext/date_time/conversions.rb +14 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +165 -29
  58. data/lib/active_support/core_ext/file/atomic.rb +7 -5
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +40 -39
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -2
  65. data/lib/active_support/core_ext/hash/keys.rb +9 -36
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +3 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +3 -1
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -4
  77. data/lib/active_support/core_ext/load_error.rb +2 -23
  78. data/lib/active_support/core_ext/marshal.rb +6 -2
  79. data/lib/active_support/core_ext/module/aliasing.rb +5 -48
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +7 -5
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +53 -59
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +31 -24
  84. data/lib/active_support/core_ext/module/concerning.rb +16 -11
  85. data/lib/active_support/core_ext/module/delegation.rb +159 -44
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -26
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +13 -12
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +129 -134
  94. data/lib/active_support/core_ext/numeric/time.rb +18 -26
  95. data/lib/active_support/core_ext/numeric.rb +5 -4
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +14 -2
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +4 -2
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -62
  101. data/lib/active_support/core_ext/object/inclusion.rb +3 -1
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +42 -15
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +20 -8
  107. data/lib/active_support/core_ext/object/with_options.rb +15 -2
  108. data/lib/active_support/core_ext/object.rb +14 -12
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +28 -6
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +47 -4
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +78 -29
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +10 -5
  126. data/lib/active_support/core_ext/string/output_safety.rb +86 -31
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +117 -45
  135. data/lib/active_support/core_ext/time/compatibility.rb +13 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +18 -12
  137. data/lib/active_support/core_ext/time/zones.rb +9 -7
  138. data/lib/active_support/core_ext/time.rb +7 -5
  139. data/lib/active_support/core_ext/uri.rb +12 -7
  140. data/lib/active_support/core_ext.rb +3 -2
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +208 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +7 -1
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  146. data/lib/active_support/dependencies.rb +172 -98
  147. data/lib/active_support/deprecation/behaviors.rb +45 -13
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +32 -17
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +35 -7
  153. data/lib/active_support/deprecation/reporting.rb +61 -16
  154. data/lib/active_support/deprecation.rb +17 -9
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +20 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +67 -66
  158. data/lib/active_support/duration/iso8601_serializer.rb +25 -17
  159. data/lib/active_support/duration.rb +349 -46
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +88 -112
  164. data/lib/active_support/execution_wrapper.rb +25 -13
  165. data/lib/active_support/executor.rb +3 -1
  166. data/lib/active_support/file_update_checker.rb +56 -51
  167. data/lib/active_support/fork_tracker.rb +62 -0
  168. data/lib/active_support/gem_version.rb +4 -2
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +153 -49
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +30 -20
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +28 -15
  175. data/lib/active_support/inflector/methods.rb +120 -109
  176. data/lib/active_support/inflector/transliterate.rb +60 -25
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +30 -29
  179. data/lib/active_support/json/encoding.rb +22 -11
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +6 -36
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +11 -9
  186. data/lib/active_support/log_subscriber.rb +51 -18
  187. data/lib/active_support/logger.rb +9 -22
  188. data/lib/active_support/logger_silence.rb +14 -21
  189. data/lib/active_support/logger_thread_safe_level.rb +55 -8
  190. data/lib/active_support/message_encryptor.rb +170 -53
  191. data/lib/active_support/message_verifier.rb +91 -20
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +24 -78
  196. data/lib/active_support/multibyte/unicode.rb +21 -352
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +121 -19
  199. data/lib/active_support/notifications/instrumenter.rb +78 -14
  200. data/lib/active_support/notifications.rb +80 -12
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -3
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -12
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -13
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -4
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +18 -55
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +45 -16
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +6 -4
  213. data/lib/active_support/ordered_options.rb +23 -9
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +7 -5
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +8 -9
  218. data/lib/active_support/railtie.rb +62 -11
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +20 -11
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +12 -3
  224. data/lib/active_support/subscriber.rb +77 -23
  225. data/lib/active_support/tagged_logging.rb +52 -17
  226. data/lib/active_support/test_case.rb +106 -29
  227. data/lib/active_support/testing/assertions.rb +144 -8
  228. data/lib/active_support/testing/autorun.rb +5 -10
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +4 -2
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +19 -24
  234. data/lib/active_support/testing/method_call_assertions.rb +31 -2
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  239. data/lib/active_support/testing/stream.rb +30 -29
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +125 -24
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +142 -55
  244. data/lib/active_support/values/time_zone.rb +160 -53
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +115 -114
  247. data/lib/active_support/xml_mini/libxml.rb +15 -14
  248. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  249. data/lib/active_support/xml_mini/nokogiri.rb +13 -13
  250. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +44 -42
  253. data/lib/active_support.rb +19 -10
  254. metadata +79 -37
  255. data/lib/active_support/concurrency/latch.rb +0 -19
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/hash/compact.rb +0 -20
  258. data/lib/active_support/core_ext/hash/transform_values.rb +0 -29
  259. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  260. data/lib/active_support/core_ext/kernel/debugger.rb +0 -3
  261. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -3
  262. data/lib/active_support/core_ext/module/qualified_const.rb +0 -70
  263. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  264. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  265. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  266. data/lib/active_support/core_ext/struct.rb +0 -3
  267. data/lib/active_support/core_ext/time/marshal.rb +0 -3
  268. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,9 +1,9 @@
1
- require 'active_support/core_ext/array/extract_options'
1
+ # frozen_string_literal: true
2
2
 
3
3
  # Extends the module object with class/module and instance accessors for
4
4
  # class/module attributes, just like the native attr* accessors for instance
5
5
  # attributes, but does so on a per-thread basis.
6
- #
6
+ #
7
7
  # So the values are scoped within the Thread.current space under the class name
8
8
  # of the module.
9
9
  class Module
@@ -25,7 +25,7 @@ class Module
25
25
  # end
26
26
  # # => NameError: invalid attribute name: 1_Badname
27
27
  #
28
- # If you want to opt out the creation on the instance reader method, pass
28
+ # To omit the instance reader method, pass
29
29
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
30
30
  #
31
31
  # class Current
@@ -33,24 +33,27 @@ class Module
33
33
  # end
34
34
  #
35
35
  # Current.new.user # => NoMethodError
36
- def thread_mattr_reader(*syms)
37
- options = syms.extract_options!
38
-
36
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
39
37
  syms.each do |sym|
40
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
38
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
39
+
40
+ # The following generated method concatenates `name` because we want it
41
+ # to work with inheritance via polymorphism.
41
42
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
42
43
  def self.#{sym}
43
- Thread.current[:"attr_#{name}_#{sym}"]
44
+ Thread.current["attr_" + name + "_#{sym}"]
44
45
  end
45
46
  EOS
46
47
 
47
- unless options[:instance_reader] == false || options[:instance_accessor] == false
48
+ if instance_reader && instance_accessor
48
49
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
49
50
  def #{sym}
50
- Thread.current[:"attr_#{name}_#{sym}"]
51
+ self.class.#{sym}
51
52
  end
52
53
  EOS
53
54
  end
55
+
56
+ Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
54
57
  end
55
58
  end
56
59
  alias :thread_cattr_reader :thread_mattr_reader
@@ -65,7 +68,7 @@ class Module
65
68
  # Current.user = "DHH"
66
69
  # Thread.current[:attr_Current_user] # => "DHH"
67
70
  #
68
- # If you want to opt out the instance writer method, pass
71
+ # To omit the instance writer method, pass
69
72
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
70
73
  #
71
74
  # class Current
@@ -73,23 +76,27 @@ class Module
73
76
  # end
74
77
  #
75
78
  # Current.new.user = "DHH" # => NoMethodError
76
- def thread_mattr_writer(*syms)
77
- options = syms.extract_options!
79
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
78
80
  syms.each do |sym|
79
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
81
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
82
+
83
+ # The following generated method concatenates `name` because we want it
84
+ # to work with inheritance via polymorphism.
80
85
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
81
86
  def self.#{sym}=(obj)
82
- Thread.current[:"attr_#{name}_#{sym}"] = obj
87
+ Thread.current["attr_" + name + "_#{sym}"] = obj
83
88
  end
84
89
  EOS
85
90
 
86
- unless options[:instance_writer] == false || options[:instance_accessor] == false
91
+ if instance_writer && instance_accessor
87
92
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
88
93
  def #{sym}=(obj)
89
- Thread.current[:"attr_#{name}_#{sym}"] = obj
94
+ self.class.#{sym} = obj
90
95
  end
91
96
  EOS
92
97
  end
98
+
99
+ public_send("#{sym}=", default) unless default.nil?
93
100
  end
94
101
  end
95
102
  alias :thread_cattr_writer :thread_mattr_writer
@@ -115,8 +122,8 @@ class Module
115
122
  # Customer.user # => "Rafael"
116
123
  # Account.user # => "DHH"
117
124
  #
118
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
119
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
125
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
126
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
120
127
  #
121
128
  # class Current
122
129
  # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
@@ -125,17 +132,17 @@ class Module
125
132
  # Current.new.user = "DHH" # => NoMethodError
126
133
  # Current.new.user # => NoMethodError
127
134
  #
128
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
135
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
129
136
  #
130
137
  # class Current
131
- # mattr_accessor :user, instance_accessor: false
138
+ # thread_mattr_accessor :user, instance_accessor: false
132
139
  # end
133
140
  #
134
141
  # Current.new.user = "DHH" # => NoMethodError
135
142
  # Current.new.user # => NoMethodError
136
- def thread_mattr_accessor(*syms, &blk)
137
- thread_mattr_reader(*syms, &blk)
138
- thread_mattr_writer(*syms, &blk)
143
+ def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
144
+ thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
145
+ thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
139
146
  end
140
147
  alias :thread_cattr_accessor :thread_mattr_accessor
141
148
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
2
4
 
3
5
  class Module
4
6
  # = Bite-sized separation of concerns
@@ -20,7 +22,7 @@ class Module
20
22
  #
21
23
  # == Using comments:
22
24
  #
23
- # class Todo
25
+ # class Todo < ApplicationRecord
24
26
  # # Other todo implementation
25
27
  # # ...
26
28
  #
@@ -28,7 +30,6 @@ class Module
28
30
  # has_many :events
29
31
  #
30
32
  # before_create :track_creation
31
- # after_destroy :track_deletion
32
33
  #
33
34
  # private
34
35
  # def track_creation
@@ -40,7 +41,7 @@ class Module
40
41
  #
41
42
  # Noisy syntax.
42
43
  #
43
- # class Todo
44
+ # class Todo < ApplicationRecord
44
45
  # # Other todo implementation
45
46
  # # ...
46
47
  #
@@ -50,7 +51,6 @@ class Module
50
51
  # included do
51
52
  # has_many :events
52
53
  # before_create :track_creation
53
- # after_destroy :track_deletion
54
54
  # end
55
55
  #
56
56
  # private
@@ -68,7 +68,7 @@ class Module
68
68
  # increased overhead can be a reasonable tradeoff even if it reduces our
69
69
  # at-a-glance perception of how things work.
70
70
  #
71
- # class Todo
71
+ # class Todo < ApplicationRecord
72
72
  # # Other todo implementation
73
73
  # # ...
74
74
  #
@@ -80,7 +80,7 @@ class Module
80
80
  # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
81
81
  # separate bite-sized concerns.
82
82
  #
83
- # class Todo
83
+ # class Todo < ApplicationRecord
84
84
  # # Other todo implementation
85
85
  # # ...
86
86
  #
@@ -88,7 +88,6 @@ class Module
88
88
  # included do
89
89
  # has_many :events
90
90
  # before_create :track_creation
91
- # after_destroy :track_deletion
92
91
  # end
93
92
  #
94
93
  # private
@@ -99,16 +98,22 @@ class Module
99
98
  # end
100
99
  #
101
100
  # Todo.ancestors
102
- # # => [Todo, Todo::EventTracking, Object]
101
+ # # => [Todo, Todo::EventTracking, ApplicationRecord, Object]
103
102
  #
104
103
  # This small step has some wonderful ripple effects. We can
105
104
  # * grok the behavior of our class in one glance,
106
105
  # * clean up monolithic junk-drawer classes by separating their concerns, and
107
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.
108
112
  module Concerning
109
113
  # Define a new concern and mix it in.
110
- def concerning(topic, &block)
111
- include concern(topic, &block)
114
+ def concerning(topic, prepend: false, &block)
115
+ method = prepend ? :prepend : :include
116
+ __send__(method, concern(topic, &block))
112
117
  end
113
118
 
114
119
  # A low-cruft shortcut to define a concern.
@@ -1,24 +1,29 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
2
4
 
3
5
  class Module
4
6
  # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
5
7
  # option is not used.
6
8
  class DelegationError < NoMethodError; end
7
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)
8
14
  DELEGATION_RESERVED_METHOD_NAMES = Set.new(
9
- %w(_ arg args alias and BEGIN begin block break case class def defined? do
10
- else elsif END end ensure false for if in module next nil not or redo
11
- rescue retry return self super then true undef unless until when while
12
- yield)
15
+ RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
13
16
  ).freeze
14
17
 
15
18
  # Provides a +delegate+ class method to easily expose contained objects'
16
19
  # public methods as your own.
17
20
  #
18
21
  # ==== Options
19
- # * <tt>:to</tt> - Specifies the target object
22
+ # * <tt>:to</tt> - Specifies the target object name as a symbol or string
20
23
  # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
21
- # * <tt>:allow_nil</tt> - if set to true, prevents a +NoMethodError+ from being raised
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
22
27
  #
23
28
  # The macro receives one or more method names (specified as symbols or
24
29
  # strings) and the name of the target object via the <tt>:to</tt> option
@@ -109,19 +114,34 @@ class Module
109
114
  # invoice.customer_name # => 'John Doe'
110
115
  # invoice.customer_address # => 'Vimmersvej 13'
111
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
+ #
112
134
  # If the target is +nil+ and does not respond to the delegated method a
113
- # +NoMethodError+ is raised, as with any other value. Sometimes, however, it
114
- # makes sense to be robust to that situation and that is the purpose of the
115
- # <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
116
- # responds to the method, everything works as usual. But if it is +nil+ and
117
- # 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.
118
137
  #
119
138
  # class User < ActiveRecord::Base
120
139
  # has_one :profile
121
140
  # delegate :age, to: :profile
122
141
  # end
123
142
  #
124
- # 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
125
145
  #
126
146
  # But if not having a profile yet is fine and should not be an error
127
147
  # condition:
@@ -148,21 +168,20 @@ class Module
148
168
  # Foo.new("Bar").name # raises NoMethodError: undefined method `name'
149
169
  #
150
170
  # The target method must be public, otherwise it will raise +NoMethodError+.
151
- #
152
- def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
171
+ def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
153
172
  unless to
154
- raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
173
+ raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
155
174
  end
156
175
 
157
- if prefix == true && to =~ /^[^a-z_]/
158
- 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."
159
178
  end
160
179
 
161
180
  method_prefix = \
162
181
  if prefix
163
182
  "#{prefix == true ? to : prefix}_"
164
183
  else
165
- ''
184
+ ""
166
185
  end
167
186
 
168
187
  location = caller_locations(1, 1).first
@@ -171,10 +190,22 @@ class Module
171
190
  to = to.to_s
172
191
  to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
173
192
 
174
- methods.each do |method|
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
199
+
175
200
  # Attribute writer methods only accept one argument. Makes sure []=
176
201
  # methods still accept two arguments.
177
- definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
202
+ definition = if /[^\]]=$/.match?(method)
203
+ "arg"
204
+ elsif RUBY_VERSION >= "2.7"
205
+ "..."
206
+ else
207
+ "*args, &block"
208
+ end
178
209
 
179
210
  # The following generated method calls the target exactly once, storing
180
211
  # the returned value in a dummy variable.
@@ -184,32 +215,116 @@ class Module
184
215
  # whereas conceptually, from the user point of view, the delegator should
185
216
  # be doing one call.
186
217
  if allow_nil
187
- method_def = [
188
- "def #{method_prefix}#{method}(#{definition})",
189
- "_ = #{to}",
190
- "if !_.nil? || nil.respond_to?(:#{method})",
191
- " _.#{method}(#{definition})",
192
- "end",
193
- "end"
194
- ].join ';'
218
+ method = method.to_s
219
+
220
+ method_def <<
221
+ "def #{method_name}(#{definition})" <<
222
+ " _ = #{to}" <<
223
+ " if !_.nil? || nil.respond_to?(:#{method})" <<
224
+ " _.#{method}(#{definition})" <<
225
+ " end" <<
226
+ "end"
195
227
  else
196
- exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
197
-
198
- method_def = [
199
- "def #{method_prefix}#{method}(#{definition})",
200
- " _ = #{to}",
201
- " _.#{method}(#{definition})",
202
- "rescue NoMethodError => e",
203
- " if _.nil? && e.name == :#{method}",
204
- " #{exception}",
205
- " else",
206
- " raise",
207
- " end",
228
+ method = method.to_s
229
+ method_name = method_name.to_s
230
+
231
+ method_def <<
232
+ "def #{method_name}(#{definition})" <<
233
+ " _ = #{to}" <<
234
+ " _.#{method}(#{definition})" <<
235
+ "rescue NoMethodError => e" <<
236
+ " if _.nil? && e.name == :#{method}" <<
237
+ %( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") <<
238
+ " else" <<
239
+ " raise" <<
240
+ " end" <<
208
241
  "end"
209
- ].join ';'
210
242
  end
211
-
212
- module_eval(method_def, file, line)
213
243
  end
244
+ module_eval(method_def.join(";"), file, line)
245
+ private(*method_names) if private
246
+ method_names
247
+ end
248
+
249
+ # When building decorators, a common pattern may emerge:
250
+ #
251
+ # class Partition
252
+ # def initialize(event)
253
+ # @event = event
254
+ # end
255
+ #
256
+ # def person
257
+ # detail.person || creator
258
+ # end
259
+ #
260
+ # private
261
+ # def respond_to_missing?(name, include_private = false)
262
+ # @event.respond_to?(name, include_private)
263
+ # end
264
+ #
265
+ # def method_missing(method, *args, &block)
266
+ # @event.send(method, *args, &block)
267
+ # end
268
+ # end
269
+ #
270
+ # With <tt>Module#delegate_missing_to</tt>, the above is condensed to:
271
+ #
272
+ # class Partition
273
+ # delegate_missing_to :@event
274
+ #
275
+ # def initialize(event)
276
+ # @event = event
277
+ # end
278
+ #
279
+ # def person
280
+ # detail.person || creator
281
+ # end
282
+ # end
283
+ #
284
+ # The target can be anything callable within the object, e.g. instance
285
+ # variables, methods, constants, etc.
286
+ #
287
+ # The delegated method must be public on the target, otherwise it will
288
+ # raise +DelegationError+. If you wish to instead return +nil+,
289
+ # use the <tt>:allow_nil</tt> option.
290
+ #
291
+ # The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
292
+ # delegation due to possible interference when calling
293
+ # <tt>Marshal.dump(object)</tt>, should the delegation target method
294
+ # of <tt>object</tt> add or remove instance variables.
295
+ def delegate_missing_to(target, allow_nil: nil)
296
+ target = target.to_s
297
+ target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
298
+
299
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
300
+ def respond_to_missing?(name, include_private = false)
301
+ # It may look like an oversight, but we deliberately do not pass
302
+ # +include_private+, because they do not get delegated.
303
+
304
+ return false if name == :marshal_dump || name == :_dump
305
+ #{target}.respond_to?(name) || super
306
+ end
307
+
308
+ def method_missing(method, *args, &block)
309
+ if #{target}.respond_to?(method)
310
+ #{target}.public_send(method, *args, &block)
311
+ else
312
+ begin
313
+ super
314
+ rescue NoMethodError
315
+ if #{target}.nil?
316
+ if #{allow_nil == true}
317
+ nil
318
+ else
319
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
320
+ end
321
+ else
322
+ raise
323
+ end
324
+ end
325
+ end
326
+ end
327
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
328
+ RUBY
214
329
  end
215
330
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Module
2
4
  # deprecate :foo
3
5
  # deprecate bar: 'message'
@@ -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,27 +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
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
61
- Module#local_constants is deprecated and will be removed in Rails 5.1.
62
- Use Module#constants(false) instead.
63
- MSG
64
- constants(false)
65
- end
66
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,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/redefine_method"
4
+
1
5
  class Module
2
6
  # Removes the named method, if it exists.
3
7
  def remove_possible_method(method)
@@ -8,28 +12,6 @@ class Module
8
12
 
9
13
  # Removes the named singleton method, if it exists.
10
14
  def remove_possible_singleton_method(method)
11
- singleton_class.instance_eval do
12
- remove_possible_method(method)
13
- end
14
- end
15
-
16
- # Replaces the existing method definition, if there is one, with the passed
17
- # block as its body.
18
- def redefine_method(method, &block)
19
- visibility = method_visibility(method)
20
- remove_possible_method(method)
21
- define_method(method, &block)
22
- send(visibility, method)
23
- end
24
-
25
- def method_visibility(method) # :nodoc:
26
- case
27
- when private_method_defined?(method)
28
- :private
29
- when protected_method_defined?(method)
30
- :protected
31
- else
32
- :public
33
- end
15
+ singleton_class.remove_possible_method(method)
34
16
  end
35
17
  end