activesupport 5.2.4.4 → 6.1.1

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +353 -435
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support.rb +14 -1
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/array_inquirer.rb +4 -2
  8. data/lib/active_support/backtrace_cleaner.rb +29 -3
  9. data/lib/active_support/benchmarkable.rb +1 -1
  10. data/lib/active_support/cache.rb +142 -78
  11. data/lib/active_support/cache/file_store.rb +33 -33
  12. data/lib/active_support/cache/mem_cache_store.rb +32 -20
  13. data/lib/active_support/cache/memory_store.rb +59 -33
  14. data/lib/active_support/cache/null_store.rb +8 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +70 -43
  16. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  17. data/lib/active_support/callbacks.rb +81 -64
  18. data/lib/active_support/concern.rb +70 -3
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  20. data/lib/active_support/concurrency/share_lock.rb +0 -1
  21. data/lib/active_support/configurable.rb +10 -14
  22. data/lib/active_support/configuration_file.rb +46 -0
  23. data/lib/active_support/core_ext.rb +1 -1
  24. data/lib/active_support/core_ext/array.rb +1 -1
  25. data/lib/active_support/core_ext/array/access.rb +18 -6
  26. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  30. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  31. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  32. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  35. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  36. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  37. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  38. data/lib/active_support/core_ext/enumerable.rb +171 -75
  39. data/lib/active_support/core_ext/hash.rb +1 -2
  40. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  41. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  42. data/lib/active_support/core_ext/hash/except.rb +2 -2
  43. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  44. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  45. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  46. data/lib/active_support/core_ext/kernel.rb +0 -1
  47. data/lib/active_support/core_ext/load_error.rb +1 -1
  48. data/lib/active_support/core_ext/marshal.rb +2 -0
  49. data/lib/active_support/core_ext/module.rb +0 -1
  50. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  51. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  52. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  53. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  54. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  55. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  56. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  57. data/lib/active_support/core_ext/name_error.rb +29 -2
  58. data/lib/active_support/core_ext/numeric.rb +0 -1
  59. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  60. data/lib/active_support/core_ext/object/blank.rb +1 -2
  61. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  62. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  63. data/lib/active_support/core_ext/object/json.rb +14 -2
  64. data/lib/active_support/core_ext/object/try.rb +17 -7
  65. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  66. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  67. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  68. data/lib/active_support/core_ext/range/each.rb +0 -1
  69. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  70. data/lib/active_support/core_ext/regexp.rb +8 -5
  71. data/lib/active_support/core_ext/securerandom.rb +23 -3
  72. data/lib/active_support/core_ext/string/access.rb +5 -16
  73. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  74. data/lib/active_support/core_ext/string/filters.rb +42 -1
  75. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  76. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  77. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  78. data/lib/active_support/core_ext/string/output_safety.rb +70 -13
  79. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  80. data/lib/active_support/core_ext/string/strip.rb +3 -1
  81. data/lib/active_support/core_ext/symbol.rb +3 -0
  82. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  83. data/lib/active_support/core_ext/time/calculations.rb +50 -3
  84. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  85. data/lib/active_support/core_ext/uri.rb +6 -1
  86. data/lib/active_support/current_attributes.rb +15 -2
  87. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  90. data/lib/active_support/deprecation.rb +6 -1
  91. data/lib/active_support/deprecation/behaviors.rb +16 -3
  92. data/lib/active_support/deprecation/disallowed.rb +56 -0
  93. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  94. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  95. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  96. data/lib/active_support/deprecation/reporting.rb +50 -7
  97. data/lib/active_support/descendants_tracker.rb +59 -9
  98. data/lib/active_support/duration.rb +90 -38
  99. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  100. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  101. data/lib/active_support/encrypted_configuration.rb +0 -4
  102. data/lib/active_support/encrypted_file.rb +22 -4
  103. data/lib/active_support/environment_inquirer.rb +20 -0
  104. data/lib/active_support/evented_file_update_checker.rb +82 -117
  105. data/lib/active_support/execution_wrapper.rb +1 -0
  106. data/lib/active_support/file_update_checker.rb +0 -1
  107. data/lib/active_support/fork_tracker.rb +62 -0
  108. data/lib/active_support/gem_version.rb +4 -4
  109. data/lib/active_support/hash_with_indifferent_access.rb +64 -41
  110. data/lib/active_support/i18n.rb +1 -0
  111. data/lib/active_support/i18n_railtie.rb +15 -8
  112. data/lib/active_support/inflector/inflections.rb +2 -7
  113. data/lib/active_support/inflector/methods.rb +49 -58
  114. data/lib/active_support/inflector/transliterate.rb +47 -18
  115. data/lib/active_support/json/decoding.rb +25 -26
  116. data/lib/active_support/json/encoding.rb +11 -3
  117. data/lib/active_support/key_generator.rb +1 -33
  118. data/lib/active_support/lazy_load_hooks.rb +5 -2
  119. data/lib/active_support/locale/en.rb +33 -0
  120. data/lib/active_support/locale/en.yml +7 -3
  121. data/lib/active_support/log_subscriber.rb +39 -9
  122. data/lib/active_support/logger.rb +2 -17
  123. data/lib/active_support/logger_silence.rb +11 -19
  124. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  125. data/lib/active_support/message_encryptor.rb +8 -13
  126. data/lib/active_support/message_verifier.rb +10 -10
  127. data/lib/active_support/messages/metadata.rb +11 -2
  128. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  129. data/lib/active_support/messages/rotator.rb +10 -9
  130. data/lib/active_support/multibyte/chars.rb +10 -68
  131. data/lib/active_support/multibyte/unicode.rb +15 -327
  132. data/lib/active_support/notifications.rb +72 -8
  133. data/lib/active_support/notifications/fanout.rb +116 -16
  134. data/lib/active_support/notifications/instrumenter.rb +71 -9
  135. data/lib/active_support/number_helper.rb +38 -12
  136. data/lib/active_support/number_helper/number_converter.rb +5 -6
  137. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  138. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  139. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  140. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  141. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  142. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  143. data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
  144. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  145. data/lib/active_support/option_merger.rb +22 -3
  146. data/lib/active_support/ordered_hash.rb +1 -1
  147. data/lib/active_support/ordered_options.rb +13 -3
  148. data/lib/active_support/parameter_filter.rb +133 -0
  149. data/lib/active_support/per_thread_registry.rb +1 -1
  150. data/lib/active_support/rails.rb +1 -10
  151. data/lib/active_support/railtie.rb +23 -1
  152. data/lib/active_support/reloader.rb +4 -5
  153. data/lib/active_support/rescuable.rb +4 -4
  154. data/lib/active_support/secure_compare_rotator.rb +51 -0
  155. data/lib/active_support/security_utils.rb +19 -12
  156. data/lib/active_support/string_inquirer.rb +4 -3
  157. data/lib/active_support/subscriber.rb +72 -28
  158. data/lib/active_support/tagged_logging.rb +42 -8
  159. data/lib/active_support/test_case.rb +91 -0
  160. data/lib/active_support/testing/assertions.rb +30 -9
  161. data/lib/active_support/testing/deprecation.rb +0 -1
  162. data/lib/active_support/testing/file_fixtures.rb +2 -0
  163. data/lib/active_support/testing/isolation.rb +2 -2
  164. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  165. data/lib/active_support/testing/parallelization.rb +51 -0
  166. data/lib/active_support/testing/parallelization/server.rb +78 -0
  167. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  168. data/lib/active_support/testing/stream.rb +1 -2
  169. data/lib/active_support/testing/time_helpers.rb +47 -12
  170. data/lib/active_support/time_with_zone.rb +81 -47
  171. data/lib/active_support/values/time_zone.rb +32 -17
  172. data/lib/active_support/xml_mini.rb +2 -10
  173. data/lib/active_support/xml_mini/jdom.rb +2 -3
  174. data/lib/active_support/xml_mini/libxml.rb +2 -2
  175. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  176. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  177. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  178. data/lib/active_support/xml_mini/rexml.rb +10 -3
  179. metadata +58 -32
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -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"
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/xml_mini"
4
- require "active_support/time"
5
4
  require "active_support/core_ext/object/blank"
6
5
  require "active_support/core_ext/object/to_param"
7
6
  require "active_support/core_ext/object/to_query"
7
+ require "active_support/core_ext/object/try"
8
8
  require "active_support/core_ext/array/wrap"
9
9
  require "active_support/core_ext/hash/reverse_merge"
10
10
  require "active_support/core_ext/string/inflections"
@@ -73,7 +73,7 @@ class Hash
73
73
  # configure your own builder with the <tt>:builder</tt> option. The method also accepts
74
74
  # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
75
75
  def to_xml(options = {})
76
- require "active_support/builder" unless defined?(Builder)
76
+ require "active_support/builder" unless defined?(Builder::XmlMarkup)
77
77
 
78
78
  options = options.dup
79
79
  options[:indent] ||= 2
@@ -208,7 +208,7 @@ module ActiveSupport
208
208
  elsif become_empty_string?(value)
209
209
  ""
210
210
  elsif become_hash?(value)
211
- xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
211
+ xml_value = value.transform_values { |v| deep_to_h(v) }
212
212
 
213
213
  # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
214
214
  # how multipart uploaded files from HTML appear
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ # Returns a new hash with all values converted by the block operation.
5
+ # This includes the values from the root hash and from all
6
+ # nested hashes and arrays.
7
+ #
8
+ # hash = { person: { name: 'Rob', age: '28' } }
9
+ #
10
+ # hash.deep_transform_values{ |value| value.to_s.upcase }
11
+ # # => {person: {name: "ROB", age: "28"}}
12
+ def deep_transform_values(&block)
13
+ _deep_transform_values_in_object(self, &block)
14
+ end
15
+
16
+ # Destructively converts all values by using the block operation.
17
+ # This includes the values from the root hash and from all
18
+ # nested hashes and arrays.
19
+ def deep_transform_values!(&block)
20
+ _deep_transform_values_in_object!(self, &block)
21
+ end
22
+
23
+ private
24
+ # Support methods for deep transforming nested hashes and arrays.
25
+ def _deep_transform_values_in_object(object, &block)
26
+ case object
27
+ when Hash
28
+ object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
29
+ when Array
30
+ object.map { |e| _deep_transform_values_in_object(e, &block) }
31
+ else
32
+ yield(object)
33
+ end
34
+ end
35
+
36
+ def _deep_transform_values_in_object!(object, &block)
37
+ case object
38
+ when Hash
39
+ object.transform_values! { |value| _deep_transform_values_in_object!(value, &block) }
40
+ when Array
41
+ object.map! { |e| _deep_transform_values_in_object!(e, &block) }
42
+ else
43
+ yield(object)
44
+ end
45
+ end
46
+ end
@@ -10,8 +10,8 @@ class Hash
10
10
  # This is useful for limiting a set of parameters to everything but a few known toggles:
11
11
  # @person.update(params[:person].except(:admin))
12
12
  def except(*keys)
13
- dup.except!(*keys)
14
- end
13
+ slice(*self.keys - keys)
14
+ end unless method_defined?(:except)
15
15
 
16
16
  # Removes the given keys from hash and returns it.
17
17
  # hash = { a: true, b: false, c: nil }
@@ -1,35 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Hash
4
- # Returns a new hash with all keys converted using the +block+ operation.
5
- #
6
- # hash = { name: 'Rob', age: '28' }
7
- #
8
- # hash.transform_keys { |key| key.to_s.upcase } # => {"NAME"=>"Rob", "AGE"=>"28"}
9
- #
10
- # If you do not provide a +block+, it will return an Enumerator
11
- # for chaining with other methods:
12
- #
13
- # hash.transform_keys.with_index { |k, i| [k, i].join } # => {"name0"=>"Rob", "age1"=>"28"}
14
- def transform_keys
15
- return enum_for(:transform_keys) { size } unless block_given?
16
- result = {}
17
- each_key do |key|
18
- result[yield(key)] = self[key]
19
- end
20
- result
21
- end unless method_defined? :transform_keys
22
-
23
- # Destructively converts all keys using the +block+ operations.
24
- # Same as +transform_keys+ but modifies +self+.
25
- def transform_keys!
26
- return enum_for(:transform_keys!) { size } unless block_given?
27
- keys.each do |key|
28
- self[yield(key)] = delete(key)
29
- end
30
- self
31
- end unless method_defined? :transform_keys!
32
-
33
4
  # Returns a new hash with all keys converted to strings.
34
5
  #
35
6
  # hash = { name: 'Rob', age: '28' }
@@ -141,7 +112,7 @@ class Hash
141
112
  end
142
113
 
143
114
  private
144
- # support methods for deep transforming nested hashes and arrays
115
+ # Support methods for deep transforming nested hashes and arrays.
145
116
  def _deep_transform_keys_in_object(object, &block)
146
117
  case object
147
118
  when Hash
@@ -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
@@ -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
@@ -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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/inflections"
4
+
3
5
  module ActiveSupport
4
6
  module MarshalWithAutoloading # :nodoc:
5
7
  def load(source, proc = nil)
@@ -3,7 +3,6 @@
3
3
  require "active_support/core_ext/module/aliasing"
4
4
  require "active_support/core_ext/module/introspection"
5
5
  require "active_support/core_ext/module/anonymous"
6
- require "active_support/core_ext/module/reachable"
7
6
  require "active_support/core_ext/module/attribute_accessors"
8
7
  require "active_support/core_ext/module/attribute_accessors_per_thread"
9
8
  require "active_support/core_ext/module/attr_internal"
@@ -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,5 @@
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
-
6
3
  # Extends the module object with class/module and instance accessors for
7
4
  # class/module attributes, just like the native attr* accessors for instance
8
5
  # attributes.
@@ -27,7 +24,7 @@ class Module
27
24
  # end
28
25
  # # => NameError: invalid attribute name: 1_Badname
29
26
  #
30
- # If you want to opt out the creation on the instance reader method, pass
27
+ # To omit the instance reader method, pass
31
28
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
32
29
  #
33
30
  # module HairColors
@@ -51,28 +48,25 @@ class Module
51
48
  # end
52
49
  #
53
50
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
54
- def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
51
+ def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
52
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
53
+ location ||= caller_locations(1, 1).first
54
+
55
+ definition = []
55
56
  syms.each do |sym|
56
57
  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
58
 
60
- def self.#{sym}
61
- @@#{sym}
62
- end
63
- EOS
59
+ definition << "def self.#{sym}; @@#{sym}; end"
64
60
 
65
61
  if instance_reader && instance_accessor
66
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
67
- def #{sym}
68
- @@#{sym}
69
- end
70
- EOS
62
+ definition << "def #{sym}; @@#{sym}; end"
71
63
  end
72
64
 
73
65
  sym_default_value = (block_given? && default.nil?) ? yield : default
74
- class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
66
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
75
67
  end
68
+
69
+ module_eval(definition.join(";"), location.path, location.lineno)
76
70
  end
77
71
  alias :cattr_reader :mattr_reader
78
72
 
@@ -94,7 +88,7 @@ class Module
94
88
  # Person.new.hair_colors = [:blonde, :red]
95
89
  # HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
96
90
  #
97
- # If you want to opt out the instance writer method, pass
91
+ # To omit the instance writer method, pass
98
92
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
99
93
  #
100
94
  # module HairColors
@@ -118,28 +112,24 @@ class Module
118
112
  # end
119
113
  #
120
114
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
121
- def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
115
+ def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
116
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
117
+ location ||= caller_locations(1, 1).first
118
+
119
+ definition = []
122
120
  syms.each do |sym|
123
121
  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
122
+ definition << "def self.#{sym}=(val); @@#{sym} = val; end"
131
123
 
132
124
  if instance_writer && instance_accessor
133
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
134
- def #{sym}=(obj)
135
- @@#{sym} = obj
136
- end
137
- EOS
125
+ definition << "def #{sym}=(val); @@#{sym} = val; end"
138
126
  end
139
127
 
140
128
  sym_default_value = (block_given? && default.nil?) ? yield : default
141
- send("#{sym}=", sym_default_value) unless sym_default_value.nil?
129
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
142
130
  end
131
+
132
+ module_eval(definition.join(";"), location.path, location.lineno)
143
133
  end
144
134
  alias :cattr_writer :mattr_writer
145
135
 
@@ -163,14 +153,14 @@ class Module
163
153
  # parent class. Similarly if parent class changes the value then that would
164
154
  # change the value of subclasses too.
165
155
  #
166
- # class Male < Person
156
+ # class Citizen < Person
167
157
  # end
168
158
  #
169
- # Male.new.hair_colors << :blue
159
+ # Citizen.new.hair_colors << :blue
170
160
  # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
171
161
  #
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>.
162
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
163
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
174
164
  #
175
165
  # module HairColors
176
166
  # mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
@@ -183,7 +173,7 @@ class Module
183
173
  # Person.new.hair_colors = [:brown] # => NoMethodError
184
174
  # Person.new.hair_colors # => NoMethodError
185
175
  #
186
- # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
176
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
187
177
  #
188
178
  # module HairColors
189
179
  # mattr_accessor :hair_colors, instance_accessor: false
@@ -208,8 +198,9 @@ class Module
208
198
  #
209
199
  # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
210
200
  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)
201
+ location = caller_locations(1, 1).first
202
+ mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
203
+ mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
213
204
  end
214
205
  alias :cattr_accessor :mattr_accessor
215
206
  end
@@ -1,8 +1,5 @@
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
-
6
3
  # Extends the module object with class/module and instance accessors for
7
4
  # class/module attributes, just like the native attr* accessors for instance
8
5
  # attributes, but does so on a per-thread basis.
@@ -28,7 +25,7 @@ class Module
28
25
  # end
29
26
  # # => NameError: invalid attribute name: 1_Badname
30
27
  #
31
- # If you want to opt out of the creation of the instance reader method, pass
28
+ # To omit the instance reader method, pass
32
29
  # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
33
30
  #
34
31
  # class Current
@@ -36,9 +33,7 @@ class Module
36
33
  # end
37
34
  #
38
35
  # Current.new.user # => NoMethodError
39
- def thread_mattr_reader(*syms) # :nodoc:
40
- options = syms.extract_options!
41
-
36
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
42
37
  syms.each do |sym|
43
38
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
44
39
 
@@ -50,13 +45,15 @@ class Module
50
45
  end
51
46
  EOS
52
47
 
53
- unless options[:instance_reader] == false || options[:instance_accessor] == false
48
+ if instance_reader && instance_accessor
54
49
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
55
50
  def #{sym}
56
51
  self.class.#{sym}
57
52
  end
58
53
  EOS
59
54
  end
55
+
56
+ Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
60
57
  end
61
58
  end
62
59
  alias :thread_cattr_reader :thread_mattr_reader
@@ -71,7 +68,7 @@ class Module
71
68
  # Current.user = "DHH"
72
69
  # Thread.current[:attr_Current_user] # => "DHH"
73
70
  #
74
- # If you want to opt out of the creation of the instance writer method, pass
71
+ # To omit the instance writer method, pass
75
72
  # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
76
73
  #
77
74
  # class Current
@@ -79,8 +76,7 @@ class Module
79
76
  # end
80
77
  #
81
78
  # Current.new.user = "DHH" # => NoMethodError
82
- def thread_mattr_writer(*syms) # :nodoc:
83
- options = syms.extract_options!
79
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
84
80
  syms.each do |sym|
85
81
  raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
86
82
 
@@ -92,13 +88,15 @@ class Module
92
88
  end
93
89
  EOS
94
90
 
95
- unless options[:instance_writer] == false || options[:instance_accessor] == false
91
+ if instance_writer && instance_accessor
96
92
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
97
93
  def #{sym}=(obj)
98
94
  self.class.#{sym} = obj
99
95
  end
100
96
  EOS
101
97
  end
98
+
99
+ public_send("#{sym}=", default) unless default.nil?
102
100
  end
103
101
  end
104
102
  alias :thread_cattr_writer :thread_mattr_writer
@@ -124,8 +122,8 @@ class Module
124
122
  # Customer.user # => "Rafael"
125
123
  # Account.user # => "DHH"
126
124
  #
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>.
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>.
129
127
  #
130
128
  # class Current
131
129
  # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
@@ -134,17 +132,17 @@ class Module
134
132
  # Current.new.user = "DHH" # => NoMethodError
135
133
  # Current.new.user # => NoMethodError
136
134
  #
137
- # 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.
138
136
  #
139
137
  # class Current
140
- # mattr_accessor :user, instance_accessor: false
138
+ # thread_mattr_accessor :user, instance_accessor: false
141
139
  # end
142
140
  #
143
141
  # Current.new.user = "DHH" # => NoMethodError
144
142
  # Current.new.user # => NoMethodError
145
- def thread_mattr_accessor(*syms)
146
- thread_mattr_reader(*syms)
147
- thread_mattr_writer(*syms)
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)
148
146
  end
149
147
  alias :thread_cattr_accessor :thread_mattr_accessor
150
148
  end