activesupport 4.2.11.1 → 6.1.7.3

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 (272) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +464 -391
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -7
  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 +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +34 -6
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +61 -55
  12. data/lib/active_support/cache/mem_cache_store.rb +115 -100
  13. data/lib/active_support/cache/memory_store.rb +81 -58
  14. data/lib/active_support/cache/null_store.rb +11 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +90 -42
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +386 -225
  19. data/lib/active_support/callbacks.rb +661 -594
  20. data/lib/active_support/concern.rb +80 -7
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +226 -0
  23. data/lib/active_support/configurable.rb +16 -17
  24. data/lib/active_support/configuration_file.rb +51 -0
  25. data/lib/active_support/core_ext/array/access.rb +41 -1
  26. data/lib/active_support/core_ext/array/conversions.rb +24 -20
  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 +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  32. data/lib/active_support/core_ext/array.rb +9 -6
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -48
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -25
  39. data/lib/active_support/core_ext/class.rb +4 -3
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +14 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  43. data/lib/active_support/core_ext/date/conversions.rb +27 -24
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +6 -4
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +167 -65
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +19 -3
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -13
  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 +14 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +37 -19
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
  53. data/lib/active_support/core_ext/date_time/conversions.rb +16 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +8 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +186 -22
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +13 -10
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +20 -43
  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 +5 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +10 -8
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +63 -69
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +148 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +19 -14
  85. data/lib/active_support/core_ext/module/delegation.rb +164 -51
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -22
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  90. data/lib/active_support/core_ext/module.rb +13 -11
  91. data/lib/active_support/core_ext/name_error.rb +51 -4
  92. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +133 -136
  94. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  95. data/lib/active_support/core_ext/numeric.rb +5 -3
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +27 -3
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -93
  101. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  102. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  103. data/lib/active_support/core_ext/object/json.rb +63 -21
  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 +81 -23
  107. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  108. data/lib/active_support/core_ext/object.rb +14 -13
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  111. data/lib/active_support/core_ext/range/each.rb +18 -17
  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 +45 -0
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  119. data/lib/active_support/core_ext/string/conversions.rb +8 -4
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +48 -6
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +102 -26
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  126. data/lib/active_support/core_ext/string/output_safety.rb +125 -40
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +6 -5
  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 +137 -53
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +22 -13
  137. data/lib/active_support/core_ext/time/zones.rb +41 -7
  138. data/lib/active_support/core_ext/time.rb +7 -6
  139. data/lib/active_support/core_ext/uri.rb +11 -8
  140. data/lib/active_support/core_ext.rb +3 -1
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +210 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
  146. data/lib/active_support/dependencies.rb +241 -175
  147. data/lib/active_support/deprecation/behaviors.rb +58 -12
  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 +62 -21
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +81 -30
  153. data/lib/active_support/deprecation/reporting.rb +81 -18
  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 +22 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  158. data/lib/active_support/duration/iso8601_serializer.rb +59 -0
  159. data/lib/active_support/duration.rb +364 -39
  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 +170 -0
  164. data/lib/active_support/execution_wrapper.rb +132 -0
  165. data/lib/active_support/executor.rb +8 -0
  166. data/lib/active_support/file_update_checker.rb +62 -37
  167. data/lib/active_support/fork_tracker.rb +64 -0
  168. data/lib/active_support/gem_version.rb +7 -5
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +171 -48
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +47 -16
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +58 -14
  175. data/lib/active_support/inflector/methods.rb +186 -169
  176. data/lib/active_support/inflector/transliterate.rb +83 -33
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +32 -30
  179. data/lib/active_support/json/encoding.rb +22 -61
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +11 -43
  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 +9 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  186. data/lib/active_support/log_subscriber.rb +52 -19
  187. data/lib/active_support/logger.rb +10 -24
  188. data/lib/active_support/logger_silence.rb +14 -20
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -10
  190. data/lib/active_support/message_encryptor.rb +167 -57
  191. data/lib/active_support/message_verifier.rb +151 -18
  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 +35 -80
  196. data/lib/active_support/multibyte/unicode.rb +22 -330
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +125 -23
  199. data/lib/active_support/notifications/instrumenter.rb +98 -16
  200. data/lib/active_support/notifications.rb +82 -14
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -14
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +11 -4
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +14 -11
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +12 -10
  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 +15 -5
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +29 -57
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +123 -71
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +7 -5
  213. data/lib/active_support/ordered_options.rb +35 -7
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +10 -4
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +10 -11
  218. data/lib/active_support/railtie.rb +66 -10
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +112 -57
  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 +13 -4
  224. data/lib/active_support/subscriber.rb +80 -31
  225. data/lib/active_support/tagged_logging.rb +54 -17
  226. data/lib/active_support/test_case.rb +107 -44
  227. data/lib/active_support/testing/assertions.rb +158 -20
  228. data/lib/active_support/testing/autorun.rb +4 -2
  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 +13 -10
  232. data/lib/active_support/testing/file_fixtures.rb +38 -0
  233. data/lib/active_support/testing/isolation.rb +35 -26
  234. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  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 +43 -0
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +121 -20
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +215 -51
  244. data/lib/active_support/values/time_zone.rb +223 -71
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +116 -115
  247. data/lib/active_support/xml_mini/libxml.rb +16 -13
  248. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  249. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  250. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +38 -46
  253. data/lib/active_support.rb +25 -11
  254. metadata +100 -43
  255. data/lib/active_support/concurrency/latch.rb +0 -27
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  258. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  259. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  260. data/lib/active_support/core_ext/hash/compact.rb +0 -24
  261. data/lib/active_support/core_ext/hash/transform_values.rb +0 -23
  262. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  263. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  264. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  265. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  266. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  267. data/lib/active_support/core_ext/object/itself.rb +0 -15
  268. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  269. data/lib/active_support/core_ext/struct.rb +0 -6
  270. data/lib/active_support/core_ext/thread.rb +0 -86
  271. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  272. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extends the module object with class/module and instance accessors for
4
+ # class/module attributes, just like the native attr* accessors for instance
5
+ # attributes, but does so on a per-thread basis.
6
+ #
7
+ # So the values are scoped within the Thread.current space under the class name
8
+ # of the module.
9
+ class Module
10
+ # Defines a per-thread class attribute and creates class and instance reader methods.
11
+ # The underlying per-thread class variable is set to +nil+, if it is not previously defined.
12
+ #
13
+ # module Current
14
+ # thread_mattr_reader :user
15
+ # end
16
+ #
17
+ # Current.user # => nil
18
+ # Thread.current[:attr_Current_user] = "DHH"
19
+ # Current.user # => "DHH"
20
+ #
21
+ # The attribute name must be a valid method name in Ruby.
22
+ #
23
+ # module Foo
24
+ # thread_mattr_reader :"1_Badname"
25
+ # end
26
+ # # => NameError: invalid attribute name: 1_Badname
27
+ #
28
+ # To omit the instance reader method, pass
29
+ # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
30
+ #
31
+ # class Current
32
+ # thread_mattr_reader :user, instance_reader: false
33
+ # end
34
+ #
35
+ # Current.new.user # => NoMethodError
36
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
37
+ syms.each do |sym|
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.
42
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
43
+ def self.#{sym}
44
+ Thread.current["attr_" + name + "_#{sym}"]
45
+ end
46
+ EOS
47
+
48
+ if instance_reader && instance_accessor
49
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
50
+ def #{sym}
51
+ self.class.#{sym}
52
+ end
53
+ EOS
54
+ end
55
+
56
+ Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
57
+ end
58
+ end
59
+ alias :thread_cattr_reader :thread_mattr_reader
60
+
61
+ # Defines a per-thread class attribute and creates a class and instance writer methods to
62
+ # allow assignment to the attribute.
63
+ #
64
+ # module Current
65
+ # thread_mattr_writer :user
66
+ # end
67
+ #
68
+ # Current.user = "DHH"
69
+ # Thread.current[:attr_Current_user] # => "DHH"
70
+ #
71
+ # To omit the instance writer method, pass
72
+ # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
73
+ #
74
+ # class Current
75
+ # thread_mattr_writer :user, instance_writer: false
76
+ # end
77
+ #
78
+ # Current.new.user = "DHH" # => NoMethodError
79
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
80
+ syms.each do |sym|
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.
85
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
86
+ def self.#{sym}=(obj)
87
+ Thread.current["attr_" + name + "_#{sym}"] = obj
88
+ end
89
+ EOS
90
+
91
+ if instance_writer && instance_accessor
92
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
93
+ def #{sym}=(obj)
94
+ self.class.#{sym} = obj
95
+ end
96
+ EOS
97
+ end
98
+
99
+ public_send("#{sym}=", default) unless default.nil?
100
+ end
101
+ end
102
+ alias :thread_cattr_writer :thread_mattr_writer
103
+
104
+ # Defines both class and instance accessors for class attributes.
105
+ #
106
+ # class Account
107
+ # thread_mattr_accessor :user
108
+ # end
109
+ #
110
+ # Account.user = "DHH"
111
+ # Account.user # => "DHH"
112
+ # Account.new.user # => "DHH"
113
+ #
114
+ # If a subclass changes the value, the parent class' value is not changed.
115
+ # Similarly, if the parent class changes the value, the value of subclasses
116
+ # is not changed.
117
+ #
118
+ # class Customer < Account
119
+ # end
120
+ #
121
+ # Customer.user = "Rafael"
122
+ # Customer.user # => "Rafael"
123
+ # Account.user # => "DHH"
124
+ #
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>.
127
+ #
128
+ # class Current
129
+ # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
130
+ # end
131
+ #
132
+ # Current.new.user = "DHH" # => NoMethodError
133
+ # Current.new.user # => NoMethodError
134
+ #
135
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
136
+ #
137
+ # class Current
138
+ # thread_mattr_accessor :user, instance_accessor: false
139
+ # end
140
+ #
141
+ # Current.new.user = "DHH" # => NoMethodError
142
+ # Current.new.user # => NoMethodError
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)
146
+ end
147
+ alias :thread_cattr_accessor :thread_mattr_accessor
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
@@ -63,12 +63,12 @@ class Module
63
63
  #
64
64
  # == Mix-in noise exiled to its own file:
65
65
  #
66
- # Once our chunk of behavior starts pushing the scroll-to-understand it's
66
+ # Once our chunk of behavior starts pushing the scroll-to-understand-it
67
67
  # boundary, we give in and move it to a separate file. At this size, the
68
- # overhead feels in good proportion to the size of our extraction, despite
69
- # diluting our at-a-glance sense of how things really work.
68
+ # increased overhead can be a reasonable tradeoff even if it reduces our
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,23 +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
 
8
- RUBY_RESERVED_WORDS = Set.new(
9
- %w(alias and BEGIN begin break case class def defined? do else elsif END
10
- end ensure false for if in module next nil not or redo rescue retry
11
- return self super then true undef unless until when while yield)
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
12
16
  ).freeze
13
17
 
14
18
  # Provides a +delegate+ class method to easily expose contained objects'
15
19
  # public methods as your own.
16
20
  #
17
21
  # ==== Options
18
- # * <tt>:to</tt> - Specifies the target object
22
+ # * <tt>:to</tt> - Specifies the target object name as a symbol or string
19
23
  # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
20
- # * <tt>:allow_nil</tt> - if set to true, prevents a +NoMethodError+ to be 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
21
27
  #
22
28
  # The macro receives one or more method names (specified as symbols or
23
29
  # strings) and the name of the target object via the <tt>:to</tt> option
@@ -108,19 +114,34 @@ class Module
108
114
  # invoice.customer_name # => 'John Doe'
109
115
  # invoice.customer_address # => 'Vimmersvej 13'
110
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
+ #
111
134
  # If the target is +nil+ and does not respond to the delegated method a
112
- # +NoMethodError+ is raised, as with any other value. Sometimes, however, it
113
- # makes sense to be robust to that situation and that is the purpose of the
114
- # <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
115
- # responds to the method, everything works as usual. But if it is +nil+ and
116
- # 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.
117
137
  #
118
138
  # class User < ActiveRecord::Base
119
139
  # has_one :profile
120
140
  # delegate :age, to: :profile
121
141
  # end
122
142
  #
123
- # 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
124
145
  #
125
146
  # But if not having a profile yet is fine and should not be an error
126
147
  # condition:
@@ -147,36 +168,44 @@ class Module
147
168
  # Foo.new("Bar").name # raises NoMethodError: undefined method `name'
148
169
  #
149
170
  # The target method must be public, otherwise it will raise +NoMethodError+.
150
- #
151
- def delegate(*methods)
152
- options = methods.pop
153
- unless options.is_a?(Hash) && to = options[: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).'
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)."
155
174
  end
156
175
 
157
- prefix, allow_nil = options.values_at(:prefix, :allow_nil)
158
-
159
- if prefix == true && to =~ /^[^a-z_]/
160
- 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."
161
178
  end
162
179
 
163
180
  method_prefix = \
164
181
  if prefix
165
182
  "#{prefix == true ? to : prefix}_"
166
183
  else
167
- ''
184
+ ""
168
185
  end
169
186
 
170
- file, line = caller.first.split(':', 2)
171
- line = line.to_i
187
+ location = caller_locations(1, 1).first
188
+ file, line = location.path, location.lineno
172
189
 
173
190
  to = to.to_s
174
- to = "self.#{to}" if RUBY_RESERVED_WORDS.include?(to)
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
175
199
 
176
- methods.each do |method|
177
200
  # Attribute writer methods only accept one argument. Makes sure []=
178
201
  # methods still accept two arguments.
179
- 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
180
209
 
181
210
  # The following generated method calls the target exactly once, storing
182
211
  # the returned value in a dummy variable.
@@ -186,32 +215,116 @@ class Module
186
215
  # whereas conceptually, from the user point of view, the delegator should
187
216
  # be doing one call.
188
217
  if allow_nil
189
- method_def = [
190
- "def #{method_prefix}#{method}(#{definition})",
191
- "_ = #{to}",
192
- "if !_.nil? || nil.respond_to?(:#{method})",
193
- " _.#{method}(#{definition})",
194
- "end",
195
- "end"
196
- ].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"
197
227
  else
198
- exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
199
-
200
- method_def = [
201
- "def #{method_prefix}#{method}(#{definition})",
202
- " _ = #{to}",
203
- " _.#{method}(#{definition})",
204
- "rescue NoMethodError => e",
205
- " if _.nil? && e.name == :#{method}",
206
- " #{exception}",
207
- " else",
208
- " raise",
209
- " 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" <<
210
241
  "end"
211
- ].join ';'
212
242
  end
213
-
214
- module_eval(method_def, file, line)
215
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
216
329
  end
217
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'
@@ -13,8 +15,8 @@ class Module
13
15
  #
14
16
  # class MyLib::Deprecator
15
17
  # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
16
- # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
17
- # Kernel.warn message
18
+ # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
19
+ # Kernel.warn message
18
20
  # end
19
21
  # end
20
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,23 +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
  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