activesupport 5.2.4.3 → 7.0.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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +244 -459
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +31 -5
  8. data/lib/active_support/benchmarkable.rb +3 -3
  9. data/lib/active_support/cache/file_store.rb +47 -41
  10. data/lib/active_support/cache/mem_cache_store.rb +151 -40
  11. data/lib/active_support/cache/memory_store.rb +68 -34
  12. data/lib/active_support/cache/null_store.rb +16 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +103 -101
  14. data/lib/active_support/cache/strategy/local_cache.rb +56 -64
  15. data/lib/active_support/cache.rb +333 -116
  16. data/lib/active_support/callbacks.rb +244 -128
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +72 -5
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  20. data/lib/active_support/concurrency/share_lock.rb +2 -3
  21. data/lib/active_support/configurable.rb +15 -16
  22. data/lib/active_support/configuration_file.rb +51 -0
  23. data/lib/active_support/core_ext/array/access.rb +15 -7
  24. data/lib/active_support/core_ext/array/conversions.rb +18 -17
  25. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  26. data/lib/active_support/core_ext/array/extract.rb +21 -0
  27. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  28. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  29. data/lib/active_support/core_ext/array.rb +2 -1
  30. data/lib/active_support/core_ext/benchmark.rb +2 -2
  31. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  32. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  33. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  34. data/lib/active_support/core_ext/date/blank.rb +1 -1
  35. data/lib/active_support/core_ext/date/calculations.rb +15 -14
  36. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  37. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  38. data/lib/active_support/core_ext/date.rb +1 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  42. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  43. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  44. data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
  45. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  46. data/lib/active_support/core_ext/date_time.rb +1 -0
  47. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  48. data/lib/active_support/core_ext/enumerable.rb +241 -76
  49. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  50. data/lib/active_support/core_ext/hash/conversions.rb +3 -4
  51. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  52. data/lib/active_support/core_ext/hash/except.rb +2 -2
  53. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  54. data/lib/active_support/core_ext/hash/keys.rb +2 -31
  55. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  56. data/lib/active_support/core_ext/hash.rb +1 -2
  57. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  58. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  59. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  60. data/lib/active_support/core_ext/kernel.rb +0 -1
  61. data/lib/active_support/core_ext/load_error.rb +1 -1
  62. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  63. data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
  64. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
  65. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  66. data/lib/active_support/core_ext/module/delegation.rb +70 -33
  67. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  68. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  69. data/lib/active_support/core_ext/module.rb +0 -1
  70. data/lib/active_support/core_ext/name_error.rb +23 -2
  71. data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
  72. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  73. data/lib/active_support/core_ext/numeric.rb +1 -1
  74. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  75. data/lib/active_support/core_ext/object/blank.rb +3 -4
  76. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  77. data/lib/active_support/core_ext/object/duplicable.rb +14 -110
  78. data/lib/active_support/core_ext/object/json.rb +44 -27
  79. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  80. data/lib/active_support/core_ext/object/try.rb +24 -14
  81. data/lib/active_support/core_ext/object/with_options.rb +21 -2
  82. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  83. data/lib/active_support/core_ext/pathname.rb +3 -0
  84. data/lib/active_support/core_ext/range/compare_range.rb +23 -27
  85. data/lib/active_support/core_ext/range/conversions.rb +32 -30
  86. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  87. data/lib/active_support/core_ext/range/each.rb +1 -2
  88. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  89. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  90. data/lib/active_support/core_ext/range.rb +1 -1
  91. data/lib/active_support/core_ext/regexp.rb +8 -5
  92. data/lib/active_support/core_ext/securerandom.rb +23 -3
  93. data/lib/active_support/core_ext/string/access.rb +5 -16
  94. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  95. data/lib/active_support/core_ext/string/filters.rb +42 -1
  96. data/lib/active_support/core_ext/string/inflections.rb +46 -7
  97. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  98. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  99. data/lib/active_support/core_ext/string/output_safety.rb +129 -20
  100. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  101. data/lib/active_support/core_ext/string/strip.rb +3 -1
  102. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  103. data/lib/active_support/core_ext/symbol.rb +3 -0
  104. data/lib/active_support/core_ext/time/calculations.rb +59 -10
  105. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  106. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  107. data/lib/active_support/core_ext/time/zones.rb +7 -22
  108. data/lib/active_support/core_ext/time.rb +1 -0
  109. data/lib/active_support/core_ext/uri.rb +3 -22
  110. data/lib/active_support/core_ext.rb +2 -1
  111. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  112. data/lib/active_support/current_attributes.rb +47 -16
  113. data/lib/active_support/dependencies/interlock.rb +10 -18
  114. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  115. data/lib/active_support/dependencies.rb +60 -715
  116. data/lib/active_support/deprecation/behaviors.rb +21 -5
  117. data/lib/active_support/deprecation/disallowed.rb +56 -0
  118. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  119. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +31 -8
  121. data/lib/active_support/deprecation/reporting.rb +50 -7
  122. data/lib/active_support/deprecation.rb +7 -2
  123. data/lib/active_support/descendants_tracker.rb +190 -34
  124. data/lib/active_support/digest.rb +5 -3
  125. data/lib/active_support/duration/iso8601_parser.rb +5 -7
  126. data/lib/active_support/duration/iso8601_serializer.rb +27 -15
  127. data/lib/active_support/duration.rb +149 -67
  128. data/lib/active_support/encrypted_configuration.rb +12 -5
  129. data/lib/active_support/encrypted_file.rb +23 -5
  130. data/lib/active_support/environment_inquirer.rb +20 -0
  131. data/lib/active_support/error_reporter.rb +117 -0
  132. data/lib/active_support/evented_file_update_checker.rb +85 -122
  133. data/lib/active_support/execution_context/test_helper.rb +13 -0
  134. data/lib/active_support/execution_context.rb +53 -0
  135. data/lib/active_support/execution_wrapper.rb +44 -21
  136. data/lib/active_support/executor/test_helper.rb +7 -0
  137. data/lib/active_support/file_update_checker.rb +0 -1
  138. data/lib/active_support/fork_tracker.rb +71 -0
  139. data/lib/active_support/gem_version.rb +5 -5
  140. data/lib/active_support/hash_with_indifferent_access.rb +73 -43
  141. data/lib/active_support/html_safe_translation.rb +43 -0
  142. data/lib/active_support/i18n.rb +2 -0
  143. data/lib/active_support/i18n_railtie.rb +15 -8
  144. data/lib/active_support/inflector/inflections.rb +25 -14
  145. data/lib/active_support/inflector/methods.rb +38 -71
  146. data/lib/active_support/inflector/transliterate.rb +47 -18
  147. data/lib/active_support/isolated_execution_state.rb +72 -0
  148. data/lib/active_support/json/decoding.rb +25 -26
  149. data/lib/active_support/json/encoding.rb +14 -6
  150. data/lib/active_support/key_generator.rb +23 -38
  151. data/lib/active_support/lazy_load_hooks.rb +19 -5
  152. data/lib/active_support/locale/en.rb +33 -0
  153. data/lib/active_support/locale/en.yml +8 -4
  154. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  155. data/lib/active_support/log_subscriber.rb +51 -11
  156. data/lib/active_support/logger.rb +6 -22
  157. data/lib/active_support/logger_silence.rb +11 -19
  158. data/lib/active_support/logger_thread_safe_level.rb +45 -10
  159. data/lib/active_support/message_encryptor.rb +20 -19
  160. data/lib/active_support/message_verifier.rb +53 -21
  161. data/lib/active_support/messages/metadata.rb +13 -4
  162. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  163. data/lib/active_support/messages/rotator.rb +10 -9
  164. data/lib/active_support/multibyte/chars.rb +17 -76
  165. data/lib/active_support/multibyte/unicode.rb +7 -331
  166. data/lib/active_support/multibyte.rb +1 -1
  167. data/lib/active_support/notifications/fanout.rb +163 -37
  168. data/lib/active_support/notifications/instrumenter.rb +90 -11
  169. data/lib/active_support/notifications.rb +88 -30
  170. data/lib/active_support/number_helper/number_converter.rb +6 -9
  171. data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
  172. data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
  173. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  174. data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -4
  175. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  176. data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
  177. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
  178. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  179. data/lib/active_support/number_helper.rb +36 -12
  180. data/lib/active_support/option_merger.rb +15 -4
  181. data/lib/active_support/ordered_hash.rb +2 -2
  182. data/lib/active_support/ordered_options.rb +14 -4
  183. data/lib/active_support/parameter_filter.rb +138 -0
  184. data/lib/active_support/per_thread_registry.rb +6 -1
  185. data/lib/active_support/rails.rb +1 -10
  186. data/lib/active_support/railtie.rb +77 -5
  187. data/lib/active_support/reloader.rb +5 -6
  188. data/lib/active_support/rescuable.rb +8 -8
  189. data/lib/active_support/ruby_features.rb +7 -0
  190. data/lib/active_support/secure_compare_rotator.rb +51 -0
  191. data/lib/active_support/security_utils.rb +19 -12
  192. data/lib/active_support/string_inquirer.rb +2 -3
  193. data/lib/active_support/subscriber.rb +79 -46
  194. data/lib/active_support/tagged_logging.rb +58 -9
  195. data/lib/active_support/test_case.rb +79 -0
  196. data/lib/active_support/testing/assertions.rb +62 -11
  197. data/lib/active_support/testing/deprecation.rb +52 -2
  198. data/lib/active_support/testing/file_fixtures.rb +2 -0
  199. data/lib/active_support/testing/isolation.rb +4 -4
  200. data/lib/active_support/testing/method_call_assertions.rb +32 -5
  201. data/lib/active_support/testing/parallelization/server.rb +82 -0
  202. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  203. data/lib/active_support/testing/parallelization.rb +55 -0
  204. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  205. data/lib/active_support/testing/stream.rb +4 -7
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +60 -14
  208. data/lib/active_support/time_with_zone.rb +139 -64
  209. data/lib/active_support/values/time_zone.rb +66 -30
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +3 -4
  212. data/lib/active_support/xml_mini/libxml.rb +7 -7
  213. data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
  214. data/lib/active_support/xml_mini/nokogiri.rb +6 -6
  215. data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
  216. data/lib/active_support/xml_mini/rexml.rb +11 -4
  217. data/lib/active_support/xml_mini.rb +7 -14
  218. data/lib/active_support.rb +30 -1
  219. metadata +64 -35
  220. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  221. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  222. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  223. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  224. data/lib/active_support/core_ext/marshal.rb +0 -24
  225. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  226. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  227. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  228. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,34 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Hash
4
- # Slices a hash to include only the given keys. Returns a hash containing
5
- # the given keys.
6
- #
7
- # { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
8
- # # => {:a=>1, :b=>2}
9
- #
10
- # This is useful for limiting an options hash to valid keys before
11
- # passing to a method:
12
- #
13
- # def search(criteria = {})
14
- # criteria.assert_valid_keys(:mass, :velocity, :time)
15
- # end
16
- #
17
- # search(options.slice(:mass, :velocity, :time))
18
- #
19
- # If you have an array of keys you want to limit to, you should splat them:
20
- #
21
- # valid_keys = [:mass, :velocity, :time]
22
- # search(options.slice(*valid_keys))
23
- def slice(*keys)
24
- keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
25
- end unless method_defined?(:slice)
26
-
27
4
  # Replaces the hash with only the given keys.
28
5
  # Returns a hash containing the removed key/value pairs.
29
6
  #
30
- # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
31
- # # => {:c=>3, :d=>4}
7
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
8
+ # hash.slice!(:a, :b) # => {:c=>3, :d=>4}
9
+ # hash # => {:a=>1, :b=>2}
32
10
  def slice!(*keys)
33
11
  omit = slice(*self.keys - keys)
34
12
  hash = slice(*keys)
@@ -40,8 +18,9 @@ class Hash
40
18
 
41
19
  # Removes and returns the key/value pairs matching the given keys.
42
20
  #
43
- # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
44
- # { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
21
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
22
+ # hash.extract!(:a, :b) # => {:a=>1, :b=>2}
23
+ # hash # => {:c=>3, :d=>4}
45
24
  def extract!(*keys)
46
25
  keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
47
26
  end
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/hash/compact"
4
3
  require "active_support/core_ext/hash/conversions"
5
4
  require "active_support/core_ext/hash/deep_merge"
5
+ require "active_support/core_ext/hash/deep_transform_values"
6
6
  require "active_support/core_ext/hash/except"
7
7
  require "active_support/core_ext/hash/indifferent_access"
8
8
  require "active_support/core_ext/hash/keys"
9
9
  require "active_support/core_ext/hash/reverse_merge"
10
10
  require "active_support/core_ext/hash/slice"
11
- require "active_support/core_ext/hash/transform_values"
@@ -7,6 +7,6 @@ class Integer
7
7
  # 6.multiple_of?(5) # => false
8
8
  # 10.multiple_of?(2) # => true
9
9
  def multiple_of?(number)
10
- number != 0 ? self % number == 0 : zero?
10
+ number == 0 ? self == 0 : self % number == 0
11
11
  end
12
12
  end
@@ -11,14 +11,14 @@ module Kernel
11
11
  # end
12
12
  #
13
13
  # noisy_call # warning voiced
14
- def silence_warnings
15
- with_warnings(nil) { yield }
14
+ def silence_warnings(&block)
15
+ with_warnings(nil, &block)
16
16
  end
17
17
 
18
18
  # Sets $VERBOSE to +true+ for the duration of the block and back to its
19
19
  # original value afterwards.
20
- def enable_warnings
21
- with_warnings(true) { yield }
20
+ def enable_warnings(&block)
21
+ with_warnings(true, &block)
22
22
  end
23
23
 
24
24
  # Sets $VERBOSE for the duration of the block and back to its original
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kernel
4
- # class_eval on an object acts like singleton_class.class_eval.
4
+ # class_eval on an object acts like +singleton_class.class_eval+.
5
5
  def class_eval(*args, &block)
6
6
  singleton_class.class_eval(*args, &block)
7
7
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/kernel/agnostics"
4
3
  require "active_support/core_ext/kernel/concern"
5
4
  require "active_support/core_ext/kernel/reporting"
6
5
  require "active_support/core_ext/kernel/singleton_class"
@@ -4,6 +4,6 @@ class LoadError
4
4
  # Returns true if the given path name (except perhaps for the ".rb"
5
5
  # extension) is the missing file which caused the exception to be raised.
6
6
  def is_missing?(location)
7
- location.sub(/\.rb$/, "".freeze) == path.sub(/\.rb$/, "".freeze)
7
+ location.delete_suffix(".rb") == path.to_s.delete_suffix(".rb")
8
8
  end
9
9
  end
@@ -28,9 +28,9 @@ class Module
28
28
  end
29
29
 
30
30
  def attr_internal_define(attr_name, type)
31
- internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, "")
31
+ internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
32
32
  # use native attr_* methods as they are faster on some Ruby implementations
33
- send("attr_#{type}", internal_name)
33
+ public_send("attr_#{type}", internal_name)
34
34
  attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
35
35
  alias_method attr_name, internal_name
36
36
  remove_method internal_name
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/array/extract_options"
4
- require "active_support/core_ext/regexp"
5
-
3
+ # == Attribute Accessors
4
+ #
6
5
  # Extends the module object with class/module and instance accessors for
7
6
  # class/module attributes, just like the native attr* accessors for instance
8
7
  # attributes.
@@ -27,7 +26,7 @@ class Module
27
26
  # end
28
27
  # # => NameError: invalid attribute name: 1_Badname
29
28
  #
30
- # If you want to opt out the creation on the instance reader method, pass
29
+ # To omit the instance reader method, pass
31
30
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
32
31
  #
33
32
  # module HairColors
@@ -51,28 +50,25 @@ class Module
51
50
  # end
52
51
  #
53
52
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
54
- def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
53
+ def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
54
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
55
+ location ||= caller_locations(1, 1).first
56
+
57
+ definition = []
55
58
  syms.each do |sym|
56
59
  raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
57
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
58
- @@#{sym} = nil unless defined? @@#{sym}
59
60
 
60
- def self.#{sym}
61
- @@#{sym}
62
- end
63
- EOS
61
+ definition << "def self.#{sym}; @@#{sym}; end"
64
62
 
65
63
  if instance_reader && instance_accessor
66
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
67
- def #{sym}
68
- @@#{sym}
69
- end
70
- EOS
64
+ definition << "def #{sym}; @@#{sym}; end"
71
65
  end
72
66
 
73
67
  sym_default_value = (block_given? && default.nil?) ? yield : default
74
- class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
68
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
75
69
  end
70
+
71
+ module_eval(definition.join(";"), location.path, location.lineno)
76
72
  end
77
73
  alias :cattr_reader :mattr_reader
78
74
 
@@ -94,7 +90,7 @@ class Module
94
90
  # Person.new.hair_colors = [:blonde, :red]
95
91
  # HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
96
92
  #
97
- # If you want to opt out the instance writer method, pass
93
+ # To omit the instance writer method, pass
98
94
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
99
95
  #
100
96
  # module HairColors
@@ -118,28 +114,24 @@ class Module
118
114
  # end
119
115
  #
120
116
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
121
- def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
117
+ def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
118
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
119
+ location ||= caller_locations(1, 1).first
120
+
121
+ definition = []
122
122
  syms.each do |sym|
123
123
  raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
124
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
125
- @@#{sym} = nil unless defined? @@#{sym}
126
-
127
- def self.#{sym}=(obj)
128
- @@#{sym} = obj
129
- end
130
- EOS
124
+ definition << "def self.#{sym}=(val); @@#{sym} = val; end"
131
125
 
132
126
  if instance_writer && instance_accessor
133
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
134
- def #{sym}=(obj)
135
- @@#{sym} = obj
136
- end
137
- EOS
127
+ definition << "def #{sym}=(val); @@#{sym} = val; end"
138
128
  end
139
129
 
140
130
  sym_default_value = (block_given? && default.nil?) ? yield : default
141
- send("#{sym}=", sym_default_value) unless sym_default_value.nil?
131
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
142
132
  end
133
+
134
+ module_eval(definition.join(";"), location.path, location.lineno)
143
135
  end
144
136
  alias :cattr_writer :mattr_writer
145
137
 
@@ -163,14 +155,14 @@ class Module
163
155
  # parent class. Similarly if parent class changes the value then that would
164
156
  # change the value of subclasses too.
165
157
  #
166
- # class Male < Person
158
+ # class Citizen < Person
167
159
  # end
168
160
  #
169
- # Male.new.hair_colors << :blue
161
+ # Citizen.new.hair_colors << :blue
170
162
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
171
163
  #
172
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
173
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
164
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
165
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
174
166
  #
175
167
  # module HairColors
176
168
  # mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
@@ -183,7 +175,7 @@ class Module
183
175
  # Person.new.hair_colors = [:brown] # => NoMethodError
184
176
  # Person.new.hair_colors # => NoMethodError
185
177
  #
186
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
178
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
187
179
  #
188
180
  # module HairColors
189
181
  # mattr_accessor :hair_colors, instance_accessor: false
@@ -208,8 +200,9 @@ class Module
208
200
  #
209
201
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
210
202
  def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
211
- mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, &blk)
212
- mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default)
203
+ location = caller_locations(1, 1).first
204
+ mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
205
+ mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
213
206
  end
214
207
  alias :cattr_accessor :mattr_accessor
215
208
  end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/array/extract_options"
4
- require "active_support/core_ext/regexp"
5
-
3
+ # == Attribute Accessors per Thread
4
+ #
6
5
  # Extends the module object with class/module and instance accessors for
7
6
  # class/module attributes, just like the native attr* accessors for instance
8
7
  # attributes, but does so on a per-thread basis.
9
8
  #
10
9
  # So the values are scoped within the Thread.current space under the class name
11
10
  # of the module.
11
+ #
12
+ # Note that it can also be scoped per-fiber if +Rails.application.config.active_support.isolation_level+
13
+ # is set to +:fiber+.
12
14
  class Module
13
15
  # Defines a per-thread class attribute and creates class and instance reader methods.
14
16
  # The underlying per-thread class variable is set to +nil+, if it is not previously defined.
@@ -17,9 +19,9 @@ class Module
17
19
  # thread_mattr_reader :user
18
20
  # end
19
21
  #
20
- # Current.user # => nil
21
- # Thread.current[:attr_Current_user] = "DHH"
22
+ # Current.user = "DHH"
22
23
  # Current.user # => "DHH"
24
+ # Thread.new { Current.user }.value # => nil
23
25
  #
24
26
  # The attribute name must be a valid method name in Ruby.
25
27
  #
@@ -28,7 +30,7 @@ class Module
28
30
  # end
29
31
  # # => NameError: invalid attribute name: 1_Badname
30
32
  #
31
- # If you want to opt out of the creation of the instance reader method, pass
33
+ # To omit the instance reader method, pass
32
34
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
33
35
  #
34
36
  # class Current
@@ -36,9 +38,7 @@ class Module
36
38
  # end
37
39
  #
38
40
  # Current.new.user # => NoMethodError
39
- def thread_mattr_reader(*syms) # :nodoc:
40
- options = syms.extract_options!
41
-
41
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
42
42
  syms.each do |sym|
43
43
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
44
44
 
@@ -46,17 +46,20 @@ class Module
46
46
  # to work with inheritance via polymorphism.
47
47
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
48
48
  def self.#{sym}
49
- Thread.current["attr_" + name + "_#{sym}"]
49
+ @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
50
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
50
51
  end
51
52
  EOS
52
53
 
53
- unless options[:instance_reader] == false || options[:instance_accessor] == false
54
+ if instance_reader && instance_accessor
54
55
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
55
56
  def #{sym}
56
57
  self.class.#{sym}
57
58
  end
58
59
  EOS
59
60
  end
61
+
62
+ ::ActiveSupport::IsolatedExecutionState["attr_#{name}_#{sym}"] = default unless default.nil?
60
63
  end
61
64
  end
62
65
  alias :thread_cattr_reader :thread_mattr_reader
@@ -71,7 +74,7 @@ class Module
71
74
  # Current.user = "DHH"
72
75
  # Thread.current[:attr_Current_user] # => "DHH"
73
76
  #
74
- # If you want to opt out of the creation of the instance writer method, pass
77
+ # To omit the instance writer method, pass
75
78
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
76
79
  #
77
80
  # class Current
@@ -79,8 +82,7 @@ class Module
79
82
  # end
80
83
  #
81
84
  # Current.new.user = "DHH" # => NoMethodError
82
- def thread_mattr_writer(*syms) # :nodoc:
83
- options = syms.extract_options!
85
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
84
86
  syms.each do |sym|
85
87
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
86
88
 
@@ -88,17 +90,20 @@ class Module
88
90
  # to work with inheritance via polymorphism.
89
91
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
90
92
  def self.#{sym}=(obj)
91
- Thread.current["attr_" + name + "_#{sym}"] = obj
93
+ @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
94
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
92
95
  end
93
96
  EOS
94
97
 
95
- unless options[:instance_writer] == false || options[:instance_accessor] == false
98
+ if instance_writer && instance_accessor
96
99
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
97
100
  def #{sym}=(obj)
98
101
  self.class.#{sym} = obj
99
102
  end
100
103
  EOS
101
104
  end
105
+
106
+ public_send("#{sym}=", default) unless default.nil?
102
107
  end
103
108
  end
104
109
  alias :thread_cattr_writer :thread_mattr_writer
@@ -113,19 +118,21 @@ class Module
113
118
  # Account.user # => "DHH"
114
119
  # Account.new.user # => "DHH"
115
120
  #
121
+ # Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
116
122
  # If a subclass changes the value, the parent class' value is not changed.
117
- # Similarly, if the parent class changes the value, the value of subclasses
118
- # is not changed.
123
+ # If the parent class changes the value, the value of subclasses is not changed.
119
124
  #
120
125
  # class Customer < Account
121
126
  # end
122
127
  #
123
- # Customer.user = "Rafael"
124
- # Customer.user # => "Rafael"
125
- # Account.user # => "DHH"
128
+ # Account.user # => "DHH"
129
+ # Customer.user # => nil
130
+ # Customer.user = "Rafael"
131
+ # Customer.user # => "Rafael"
132
+ # Account.user # => "DHH"
126
133
  #
127
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
128
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
134
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
135
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
129
136
  #
130
137
  # class Current
131
138
  # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
@@ -134,17 +141,17 @@ class Module
134
141
  # Current.new.user = "DHH" # => NoMethodError
135
142
  # Current.new.user # => NoMethodError
136
143
  #
137
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
144
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
138
145
  #
139
146
  # class Current
140
- # mattr_accessor :user, instance_accessor: false
147
+ # thread_mattr_accessor :user, instance_accessor: false
141
148
  # end
142
149
  #
143
150
  # Current.new.user = "DHH" # => NoMethodError
144
151
  # Current.new.user # => NoMethodError
145
- def thread_mattr_accessor(*syms)
146
- thread_mattr_reader(*syms)
147
- thread_mattr_writer(*syms)
152
+ def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
153
+ thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
154
+ thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
148
155
  end
149
156
  alias :thread_cattr_accessor :thread_mattr_accessor
150
157
  end
@@ -104,10 +104,16 @@ class Module
104
104
  # * grok the behavior of our class in one glance,
105
105
  # * clean up monolithic junk-drawer classes by separating their concerns, and
106
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.
107
112
  module Concerning
108
113
  # Define a new concern and mix it in.
109
- def concerning(topic, &block)
110
- include concern(topic, &block)
114
+ def concerning(topic, prepend: false, &block)
115
+ method = prepend ? :prepend : :include
116
+ __send__(method, concern(topic, &block))
111
117
  end
112
118
 
113
119
  # A low-cruft shortcut to define a concern.
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
- require "active_support/core_ext/regexp"
5
4
 
6
5
  class Module
7
6
  # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
@@ -20,10 +19,11 @@ class Module
20
19
  # public methods as your own.
21
20
  #
22
21
  # ==== Options
23
- # * <tt>:to</tt> - Specifies the target object
22
+ # * <tt>:to</tt> - Specifies the target object name as a symbol or string
24
23
  # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
25
- # * <tt>:allow_nil</tt> - if set to true, prevents a +Module::DelegationError+
24
+ # * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+
26
25
  # from being raised
26
+ # * <tt>:private</tt> - If set to true, changes method visibility to private
27
27
  #
28
28
  # The macro receives one or more method names (specified as symbols or
29
29
  # strings) and the name of the target object via the <tt>:to</tt> option
@@ -114,6 +114,23 @@ class Module
114
114
  # invoice.customer_name # => 'John Doe'
115
115
  # invoice.customer_address # => 'Vimmersvej 13'
116
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
+ #
117
134
  # If the target is +nil+ and does not respond to the delegated method a
118
135
  # +Module::DelegationError+ is raised. If you wish to instead return +nil+,
119
136
  # use the <tt>:allow_nil</tt> option.
@@ -151,9 +168,9 @@ class Module
151
168
  # Foo.new("Bar").name # raises NoMethodError: undefined method `name'
152
169
  #
153
170
  # The target method must be public, otherwise it will raise +NoMethodError+.
154
- def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
171
+ def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
155
172
  unless to
156
- 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)."
157
174
  end
158
175
 
159
176
  if prefix == true && /^[^a-z_]/.match?(to)
@@ -173,10 +190,16 @@ class Module
173
190
  to = to.to_s
174
191
  to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
175
192
 
193
+ method_def = []
194
+ method_names = []
195
+
176
196
  methods.map do |method|
197
+ method_name = prefix ? "#{method_prefix}#{method}" : method
198
+ method_names << method_name.to_sym
199
+
177
200
  # Attribute writer methods only accept one argument. Makes sure []=
178
201
  # methods still accept two arguments.
179
- definition = /[^\]]=$/.match?(method) ? "arg" : "*args, &block"
202
+ definition = /[^\]]=\z/.match?(method) ? "arg" : "..."
180
203
 
181
204
  # The following generated method calls the target exactly once, storing
182
205
  # the returned value in a dummy variable.
@@ -186,33 +209,35 @@ class Module
186
209
  # whereas conceptually, from the user point of view, the delegator should
187
210
  # be doing one call.
188
211
  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 ";"
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"
197
221
  else
198
- exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
222
+ method = method.to_s
223
+ method_name = method_name.to_s
199
224
 
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",
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" <<
210
235
  "end"
211
- ].join ";"
212
236
  end
213
-
214
- module_eval(method_def, file, line)
215
237
  end
238
+ module_eval(method_def.join(";"), file, line)
239
+ private(*method_names) if private
240
+ method_names
216
241
  end
217
242
 
218
243
  # When building decorators, a common pattern may emerge:
@@ -223,7 +248,7 @@ class Module
223
248
  # end
224
249
  #
225
250
  # def person
226
- # @event.detail.person || @event.creator
251
+ # detail.person || creator
227
252
  # end
228
253
  #
229
254
  # private
@@ -246,7 +271,7 @@ class Module
246
271
  # end
247
272
  #
248
273
  # def person
249
- # @event.detail.person || @event.creator
274
+ # detail.person || creator
250
275
  # end
251
276
  # end
252
277
  #
@@ -254,8 +279,14 @@ class Module
254
279
  # variables, methods, constants, etc.
255
280
  #
256
281
  # The delegated method must be public on the target, otherwise it will
257
- # raise +NoMethodError+.
258
- def delegate_missing_to(target)
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)
259
290
  target = target.to_s
260
291
  target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
261
292
 
@@ -264,6 +295,7 @@ class Module
264
295
  # It may look like an oversight, but we deliberately do not pass
265
296
  # +include_private+, because they do not get delegated.
266
297
 
298
+ return false if name == :marshal_dump || name == :_dump
267
299
  #{target}.respond_to?(name) || super
268
300
  end
269
301
 
@@ -275,13 +307,18 @@ class Module
275
307
  super
276
308
  rescue NoMethodError
277
309
  if #{target}.nil?
278
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
310
+ if #{allow_nil == true}
311
+ nil
312
+ else
313
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
314
+ end
279
315
  else
280
316
  raise
281
317
  end
282
318
  end
283
319
  end
284
320
  end
321
+ ruby2_keywords(:method_missing)
285
322
  RUBY
286
323
  end
287
324
  end