activesupport 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +572 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support.rb +96 -0
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/all.rb +5 -0
  8. data/lib/active_support/array_inquirer.rb +48 -0
  9. data/lib/active_support/backtrace_cleaner.rb +132 -0
  10. data/lib/active_support/benchmarkable.rb +51 -0
  11. data/lib/active_support/builder.rb +8 -0
  12. data/lib/active_support/cache.rb +830 -0
  13. data/lib/active_support/cache/file_store.rb +196 -0
  14. data/lib/active_support/cache/mem_cache_store.rb +212 -0
  15. data/lib/active_support/cache/memory_store.rb +174 -0
  16. data/lib/active_support/cache/null_store.rb +48 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +488 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +194 -0
  19. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  20. data/lib/active_support/callbacks.rb +856 -0
  21. data/lib/active_support/concern.rb +171 -0
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  23. data/lib/active_support/concurrency/share_lock.rb +227 -0
  24. data/lib/active_support/configurable.rb +146 -0
  25. data/lib/active_support/core_ext.rb +5 -0
  26. data/lib/active_support/core_ext/array.rb +9 -0
  27. data/lib/active_support/core_ext/array/access.rb +104 -0
  28. data/lib/active_support/core_ext/array/conversions.rb +213 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -0
  34. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  35. data/lib/active_support/core_ext/benchmark.rb +16 -0
  36. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  37. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  38. data/lib/active_support/core_ext/class.rb +4 -0
  39. data/lib/active_support/core_ext/class/attribute.rb +141 -0
  40. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/class/subclasses.rb +54 -0
  42. data/lib/active_support/core_ext/date.rb +7 -0
  43. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  44. data/lib/active_support/core_ext/date/blank.rb +14 -0
  45. data/lib/active_support/core_ext/date/calculations.rb +146 -0
  46. data/lib/active_support/core_ext/date/conversions.rb +96 -0
  47. data/lib/active_support/core_ext/date/zones.rb +8 -0
  48. data/lib/active_support/core_ext/date_and_time/calculations.rb +351 -0
  49. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  50. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  51. data/lib/active_support/core_ext/date_time.rb +7 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +211 -0
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +107 -0
  57. data/lib/active_support/core_ext/digest.rb +3 -0
  58. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  59. data/lib/active_support/core_ext/enumerable.rb +188 -0
  60. data/lib/active_support/core_ext/file.rb +3 -0
  61. data/lib/active_support/core_ext/file/atomic.rb +70 -0
  62. data/lib/active_support/core_ext/hash.rb +10 -0
  63. data/lib/active_support/core_ext/hash/compact.rb +5 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +263 -0
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +34 -0
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +24 -0
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
  69. data/lib/active_support/core_ext/hash/keys.rb +143 -0
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
  71. data/lib/active_support/core_ext/hash/slice.rb +26 -0
  72. data/lib/active_support/core_ext/hash/transform_values.rb +5 -0
  73. data/lib/active_support/core_ext/integer.rb +5 -0
  74. data/lib/active_support/core_ext/integer/inflections.rb +31 -0
  75. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  76. data/lib/active_support/core_ext/integer/time.rb +22 -0
  77. data/lib/active_support/core_ext/kernel.rb +5 -0
  78. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  79. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  80. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  81. data/lib/active_support/core_ext/load_error.rb +9 -0
  82. data/lib/active_support/core_ext/marshal.rb +24 -0
  83. data/lib/active_support/core_ext/module.rb +13 -0
  84. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  85. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  86. data/lib/active_support/core_ext/module/attr_internal.rb +38 -0
  87. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  88. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
  89. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  90. data/lib/active_support/core_ext/module/delegation.rb +313 -0
  91. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  92. data/lib/active_support/core_ext/module/introspection.rb +86 -0
  93. data/lib/active_support/core_ext/module/reachable.rb +6 -0
  94. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  95. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  96. data/lib/active_support/core_ext/name_error.rb +38 -0
  97. data/lib/active_support/core_ext/numeric.rb +5 -0
  98. data/lib/active_support/core_ext/numeric/bytes.rb +66 -0
  99. data/lib/active_support/core_ext/numeric/conversions.rb +136 -0
  100. data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
  101. data/lib/active_support/core_ext/numeric/time.rb +66 -0
  102. data/lib/active_support/core_ext/object.rb +16 -0
  103. data/lib/active_support/core_ext/object/acts_like.rb +21 -0
  104. data/lib/active_support/core_ext/object/blank.rb +155 -0
  105. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  106. data/lib/active_support/core_ext/object/deep_dup.rb +55 -0
  107. data/lib/active_support/core_ext/object/duplicable.rb +49 -0
  108. data/lib/active_support/core_ext/object/inclusion.rb +29 -0
  109. data/lib/active_support/core_ext/object/instance_variables.rb +30 -0
  110. data/lib/active_support/core_ext/object/json.rb +228 -0
  111. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  112. data/lib/active_support/core_ext/object/to_query.rb +89 -0
  113. data/lib/active_support/core_ext/object/try.rb +156 -0
  114. data/lib/active_support/core_ext/object/with_options.rb +82 -0
  115. data/lib/active_support/core_ext/range.rb +7 -0
  116. data/lib/active_support/core_ext/range/compare_range.rb +70 -0
  117. data/lib/active_support/core_ext/range/conversions.rb +41 -0
  118. data/lib/active_support/core_ext/range/each.rb +25 -0
  119. data/lib/active_support/core_ext/range/include_range.rb +9 -0
  120. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  121. data/lib/active_support/core_ext/range/overlaps.rb +10 -0
  122. data/lib/active_support/core_ext/regexp.rb +7 -0
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string.rb +15 -0
  125. data/lib/active_support/core_ext/string/access.rb +114 -0
  126. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  127. data/lib/active_support/core_ext/string/conversions.rb +59 -0
  128. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  129. data/lib/active_support/core_ext/string/filters.rb +145 -0
  130. data/lib/active_support/core_ext/string/indent.rb +45 -0
  131. data/lib/active_support/core_ext/string/inflections.rb +259 -0
  132. data/lib/active_support/core_ext/string/inquiry.rb +15 -0
  133. data/lib/active_support/core_ext/string/multibyte.rb +58 -0
  134. data/lib/active_support/core_ext/string/output_safety.rb +314 -0
  135. data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
  136. data/lib/active_support/core_ext/string/strip.rb +27 -0
  137. data/lib/active_support/core_ext/string/zones.rb +16 -0
  138. data/lib/active_support/core_ext/time.rb +7 -0
  139. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  140. data/lib/active_support/core_ext/time/calculations.rb +344 -0
  141. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  142. data/lib/active_support/core_ext/time/conversions.rb +72 -0
  143. data/lib/active_support/core_ext/time/zones.rb +113 -0
  144. data/lib/active_support/core_ext/uri.rb +25 -0
  145. data/lib/active_support/current_attributes.rb +203 -0
  146. data/lib/active_support/dependencies.rb +806 -0
  147. data/lib/active_support/dependencies/autoload.rb +79 -0
  148. data/lib/active_support/dependencies/interlock.rb +57 -0
  149. data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
  150. data/lib/active_support/deprecation.rb +46 -0
  151. data/lib/active_support/deprecation/behaviors.rb +109 -0
  152. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  153. data/lib/active_support/deprecation/instance_delegator.rb +39 -0
  154. data/lib/active_support/deprecation/method_wrappers.rb +78 -0
  155. data/lib/active_support/deprecation/proxy_wrappers.rb +173 -0
  156. data/lib/active_support/deprecation/reporting.rb +114 -0
  157. data/lib/active_support/descendants_tracker.rb +109 -0
  158. data/lib/active_support/digest.rb +20 -0
  159. data/lib/active_support/duration.rb +433 -0
  160. data/lib/active_support/duration/iso8601_parser.rb +124 -0
  161. data/lib/active_support/duration/iso8601_serializer.rb +54 -0
  162. data/lib/active_support/encrypted_configuration.rb +45 -0
  163. data/lib/active_support/encrypted_file.rb +100 -0
  164. data/lib/active_support/evented_file_update_checker.rb +235 -0
  165. data/lib/active_support/execution_wrapper.rb +129 -0
  166. data/lib/active_support/executor.rb +8 -0
  167. data/lib/active_support/file_update_checker.rb +163 -0
  168. data/lib/active_support/gem_version.rb +17 -0
  169. data/lib/active_support/gzip.rb +38 -0
  170. data/lib/active_support/hash_with_indifferent_access.rb +399 -0
  171. data/lib/active_support/i18n.rb +16 -0
  172. data/lib/active_support/i18n_railtie.rb +126 -0
  173. data/lib/active_support/inflections.rb +72 -0
  174. data/lib/active_support/inflector.rb +9 -0
  175. data/lib/active_support/inflector/inflections.rb +257 -0
  176. data/lib/active_support/inflector/methods.rb +398 -0
  177. data/lib/active_support/inflector/transliterate.rb +147 -0
  178. data/lib/active_support/json.rb +4 -0
  179. data/lib/active_support/json/decoding.rb +76 -0
  180. data/lib/active_support/json/encoding.rb +134 -0
  181. data/lib/active_support/key_generator.rb +41 -0
  182. data/lib/active_support/lazy_load_hooks.rb +82 -0
  183. data/lib/active_support/locale/en.rb +31 -0
  184. data/lib/active_support/locale/en.yml +135 -0
  185. data/lib/active_support/log_subscriber.rb +135 -0
  186. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  187. data/lib/active_support/logger.rb +93 -0
  188. data/lib/active_support/logger_silence.rb +45 -0
  189. data/lib/active_support/logger_thread_safe_level.rb +56 -0
  190. data/lib/active_support/message_encryptor.rb +227 -0
  191. data/lib/active_support/message_verifier.rb +205 -0
  192. data/lib/active_support/messages/metadata.rb +71 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  194. data/lib/active_support/messages/rotator.rb +56 -0
  195. data/lib/active_support/multibyte.rb +23 -0
  196. data/lib/active_support/multibyte/chars.rb +216 -0
  197. data/lib/active_support/multibyte/unicode.rb +157 -0
  198. data/lib/active_support/notifications.rb +253 -0
  199. data/lib/active_support/notifications/fanout.rb +244 -0
  200. data/lib/active_support/notifications/instrumenter.rb +164 -0
  201. data/lib/active_support/number_helper.rb +378 -0
  202. data/lib/active_support/number_helper/number_converter.rb +184 -0
  203. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  204. data/lib/active_support/number_helper/number_to_delimited_converter.rb +31 -0
  205. data/lib/active_support/number_helper/number_to_human_converter.rb +70 -0
  206. data/lib/active_support/number_helper/number_to_human_size_converter.rb +61 -0
  207. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  208. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  209. data/lib/active_support/number_helper/number_to_rounded_converter.rb +56 -0
  210. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  211. data/lib/active_support/option_merger.rb +27 -0
  212. data/lib/active_support/ordered_hash.rb +50 -0
  213. data/lib/active_support/ordered_options.rb +85 -0
  214. data/lib/active_support/parameter_filter.rb +129 -0
  215. data/lib/active_support/per_thread_registry.rb +60 -0
  216. data/lib/active_support/proxy_object.rb +15 -0
  217. data/lib/active_support/rails.rb +29 -0
  218. data/lib/active_support/railtie.rb +80 -0
  219. data/lib/active_support/reloader.rb +130 -0
  220. data/lib/active_support/rescuable.rb +174 -0
  221. data/lib/active_support/security_utils.rb +31 -0
  222. data/lib/active_support/string_inquirer.rb +34 -0
  223. data/lib/active_support/subscriber.rb +169 -0
  224. data/lib/active_support/tagged_logging.rb +88 -0
  225. data/lib/active_support/test_case.rb +163 -0
  226. data/lib/active_support/testing/assertions.rb +228 -0
  227. data/lib/active_support/testing/autorun.rb +7 -0
  228. data/lib/active_support/testing/constant_lookup.rb +51 -0
  229. data/lib/active_support/testing/declarative.rb +28 -0
  230. data/lib/active_support/testing/deprecation.rb +38 -0
  231. data/lib/active_support/testing/file_fixtures.rb +38 -0
  232. data/lib/active_support/testing/isolation.rb +110 -0
  233. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  234. data/lib/active_support/testing/parallelization.rb +128 -0
  235. data/lib/active_support/testing/setup_and_teardown.rb +55 -0
  236. data/lib/active_support/testing/stream.rb +44 -0
  237. data/lib/active_support/testing/tagged_logging.rb +27 -0
  238. data/lib/active_support/testing/time_helpers.rb +200 -0
  239. data/lib/active_support/time.rb +20 -0
  240. data/lib/active_support/time_with_zone.rb +561 -0
  241. data/lib/active_support/values/time_zone.rb +570 -0
  242. data/lib/active_support/version.rb +10 -0
  243. data/lib/active_support/xml_mini.rb +202 -0
  244. data/lib/active_support/xml_mini/jdom.rb +183 -0
  245. data/lib/active_support/xml_mini/libxml.rb +80 -0
  246. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  247. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  248. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  249. data/lib/active_support/xml_mini/rexml.rb +130 -0
  250. metadata +385 -0
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/number_helper/number_converter"
4
+
5
+ module ActiveSupport
6
+ module NumberHelper
7
+ class NumberToRoundedConverter < NumberConverter # :nodoc:
8
+ self.namespace = :precision
9
+ self.validate_float = true
10
+
11
+ def convert
12
+ helper = RoundingHelper.new(options)
13
+ rounded_number = helper.round(number)
14
+
15
+ if precision = options[:precision]
16
+ if options[:significant] && precision > 0
17
+ digits = helper.digit_count(rounded_number)
18
+ precision -= digits
19
+ precision = 0 if precision < 0 # don't let it be negative
20
+ end
21
+
22
+ formatted_string =
23
+ if BigDecimal === rounded_number && rounded_number.finite?
24
+ s = rounded_number.to_s("F")
25
+ s << "0" * precision
26
+ a, b = s.split(".", 2)
27
+ a << "."
28
+ a << b[0, precision]
29
+ else
30
+ "%00.#{precision}f" % rounded_number
31
+ end
32
+ else
33
+ formatted_string = rounded_number
34
+ end
35
+
36
+ delimited_number = NumberToDelimitedConverter.convert(formatted_string, options)
37
+ format_number(delimited_number)
38
+ end
39
+
40
+ private
41
+
42
+ def strip_insignificant_zeros
43
+ options[:strip_insignificant_zeros]
44
+ end
45
+
46
+ def format_number(number)
47
+ if strip_insignificant_zeros
48
+ escaped_separator = Regexp.escape(options[:separator])
49
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, "")
50
+ else
51
+ number
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module NumberHelper
5
+ class RoundingHelper # :nodoc:
6
+ attr_reader :options
7
+
8
+ def initialize(options)
9
+ @options = options
10
+ end
11
+
12
+ def round(number)
13
+ return number unless precision
14
+ number = convert_to_decimal(number)
15
+ if significant && precision > 0
16
+ round_significant(number)
17
+ else
18
+ round_without_significant(number)
19
+ end
20
+ end
21
+
22
+ def digit_count(number)
23
+ return 1 if number.zero?
24
+ (Math.log10(absolute_number(number)) + 1).floor
25
+ end
26
+
27
+ private
28
+ def round_without_significant(number)
29
+ number = number.round(precision)
30
+ number = number.to_i if precision == 0 && number.finite?
31
+ number = number.abs if number.zero? # prevent showing negative zeros
32
+ number
33
+ end
34
+
35
+ def round_significant(number)
36
+ return 0 if number.zero?
37
+ digits = digit_count(number)
38
+ multiplier = 10**(digits - precision)
39
+ (number / BigDecimal(multiplier.to_f.to_s)).round * multiplier
40
+ end
41
+
42
+ def convert_to_decimal(number)
43
+ case number
44
+ when Float, String
45
+ BigDecimal(number.to_s)
46
+ when Rational
47
+ BigDecimal(number, digit_count(number.to_i) + precision)
48
+ else
49
+ number.to_d
50
+ end
51
+ end
52
+
53
+ def precision
54
+ options[:precision]
55
+ end
56
+
57
+ def significant
58
+ options[:significant]
59
+ end
60
+
61
+ def absolute_number(number)
62
+ number.respond_to?(:abs) ? number.abs : number.to_d.abs
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/deep_merge"
4
+
5
+ module ActiveSupport
6
+ class OptionMerger #:nodoc:
7
+ instance_methods.each do |method|
8
+ undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
9
+ end
10
+
11
+ def initialize(context, options)
12
+ @context, @options = context, options
13
+ end
14
+
15
+ private
16
+ def method_missing(method, *arguments, &block)
17
+ if arguments.first.is_a?(Proc)
18
+ proc = arguments.pop
19
+ arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
20
+ else
21
+ arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
22
+ end
23
+
24
+ @context.__send__(method, *arguments, &block)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ YAML.add_builtin_type("omap") do |type, val|
6
+ ActiveSupport::OrderedHash[val.map { |v| v.to_a.first }]
7
+ end
8
+
9
+ module ActiveSupport
10
+ # DEPRECATED: <tt>ActiveSupport::OrderedHash</tt> implements a hash that preserves
11
+ # insertion order.
12
+ #
13
+ # oh = ActiveSupport::OrderedHash.new
14
+ # oh[:a] = 1
15
+ # oh[:b] = 2
16
+ # oh.keys # => [:a, :b], this order is guaranteed
17
+ #
18
+ # Also, maps the +omap+ feature for YAML files
19
+ # (See http://yaml.org/type/omap.html) to support ordered items
20
+ # when loading from yaml.
21
+ #
22
+ # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
23
+ # with other implementations.
24
+ class OrderedHash < ::Hash
25
+ def to_yaml_type
26
+ "!tag:yaml.org,2002:omap"
27
+ end
28
+
29
+ def encode_with(coder)
30
+ coder.represent_seq "!omap", map { |k, v| { k => v } }
31
+ end
32
+
33
+ def select(*args, &block)
34
+ dup.tap { |hash| hash.select!(*args, &block) }
35
+ end
36
+
37
+ def reject(*args, &block)
38
+ dup.tap { |hash| hash.reject!(*args, &block) }
39
+ end
40
+
41
+ def nested_under_indifferent_access
42
+ self
43
+ end
44
+
45
+ # Returns true to make sure that this hash is extractable via <tt>Array#extract_options!</tt>
46
+ def extractable_options?
47
+ true
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/blank"
4
+
5
+ module ActiveSupport
6
+ # Usually key value pairs are handled something like this:
7
+ #
8
+ # h = {}
9
+ # h[:boy] = 'John'
10
+ # h[:girl] = 'Mary'
11
+ # h[:boy] # => 'John'
12
+ # h[:girl] # => 'Mary'
13
+ # h[:dog] # => nil
14
+ #
15
+ # Using +OrderedOptions+, the above code could be reduced to:
16
+ #
17
+ # h = ActiveSupport::OrderedOptions.new
18
+ # h.boy = 'John'
19
+ # h.girl = 'Mary'
20
+ # h.boy # => 'John'
21
+ # h.girl # => 'Mary'
22
+ # h.dog # => nil
23
+ #
24
+ # To raise an exception when the value is blank, append a
25
+ # bang to the key name, like:
26
+ #
27
+ # h.dog! # => raises KeyError: :dog is blank
28
+ #
29
+ class OrderedOptions < Hash
30
+ alias_method :_get, :[] # preserve the original #[] method
31
+ protected :_get # make it protected
32
+
33
+ def []=(key, value)
34
+ super(key.to_sym, value)
35
+ end
36
+
37
+ def [](key)
38
+ super(key.to_sym)
39
+ end
40
+
41
+ def method_missing(name, *args)
42
+ name_string = name.to_s
43
+ if name_string.chomp!("=")
44
+ self[name_string] = args.first
45
+ else
46
+ bangs = name_string.chomp!("!")
47
+
48
+ if bangs
49
+ self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
50
+ else
51
+ self[name_string]
52
+ end
53
+ end
54
+ end
55
+
56
+ def respond_to_missing?(name, include_private)
57
+ true
58
+ end
59
+ end
60
+
61
+ # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
62
+ # hash inherited from another hash.
63
+ #
64
+ # Use this if you already have some hash and you want to create a new one based on it.
65
+ #
66
+ # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
67
+ # h.girl # => 'Mary'
68
+ # h.boy # => 'John'
69
+ class InheritableOptions < OrderedOptions
70
+ def initialize(parent = nil)
71
+ if parent.kind_of?(OrderedOptions)
72
+ # use the faster _get when dealing with OrderedOptions
73
+ super() { |h, k| parent._get(k) }
74
+ elsif parent
75
+ super() { |h, k| parent[k] }
76
+ else
77
+ super()
78
+ end
79
+ end
80
+
81
+ def inheritable_copy
82
+ self.class.new(self)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/duplicable"
4
+ require "active_support/core_ext/array/extract"
5
+
6
+ module ActiveSupport
7
+ # +ParameterFilter+ allows you to specify keys for sensitive data from
8
+ # hash-like object and replace corresponding value. Filtering only certain
9
+ # sub-keys from a hash is possible by using the dot notation:
10
+ # 'credit_card.number'. If a proc is given, each key and value of a hash and
11
+ # all sub-hashes are passed to it, where the value or the key can be replaced
12
+ # using String#replace or similar methods.
13
+ #
14
+ # ActiveSupport::ParameterFilter.new([:password])
15
+ # => replaces the value to all keys matching /password/i with "[FILTERED]"
16
+ #
17
+ # ActiveSupport::ParameterFilter.new([:foo, "bar"])
18
+ # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
19
+ #
20
+ # ActiveSupport::ParameterFilter.new(["credit_card.code"])
21
+ # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
22
+ # change { file: { code: "xxxx"} }
23
+ #
24
+ # ActiveSupport::ParameterFilter.new([-> (k, v) do
25
+ # v.reverse! if k =~ /secret/i
26
+ # end])
27
+ # => reverses the value to all keys matching /secret/i
28
+ class ParameterFilter
29
+ FILTERED = "[FILTERED]" # :nodoc:
30
+
31
+ # Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
32
+ # Other types of filters are treated as +String+ using +to_s+.
33
+ # For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
34
+ #
35
+ # ==== Options
36
+ #
37
+ # * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
38
+ def initialize(filters = [], mask: FILTERED)
39
+ @filters = filters
40
+ @mask = mask
41
+ end
42
+
43
+ # Mask value of +params+ if key matches one of filters.
44
+ def filter(params)
45
+ compiled_filter.call(params)
46
+ end
47
+
48
+ # Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
49
+ def filter_param(key, value)
50
+ @filters.empty? ? value : compiled_filter.value_for_key(key, value)
51
+ end
52
+
53
+ private
54
+
55
+ def compiled_filter
56
+ @compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
57
+ end
58
+
59
+ class CompiledFilter # :nodoc:
60
+ def self.compile(filters, mask:)
61
+ return lambda { |params| params.dup } if filters.empty?
62
+
63
+ strings, regexps, blocks = [], [], []
64
+
65
+ filters.each do |item|
66
+ case item
67
+ when Proc
68
+ blocks << item
69
+ when Regexp
70
+ regexps << item
71
+ else
72
+ strings << Regexp.escape(item.to_s)
73
+ end
74
+ end
75
+
76
+ deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
77
+ deep_strings = strings.extract! { |s| s.include?("\\.") }
78
+
79
+ regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
80
+ deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
81
+
82
+ new regexps, deep_regexps, blocks, mask: mask
83
+ end
84
+
85
+ attr_reader :regexps, :deep_regexps, :blocks
86
+
87
+ def initialize(regexps, deep_regexps, blocks, mask:)
88
+ @regexps = regexps
89
+ @deep_regexps = deep_regexps.any? ? deep_regexps : nil
90
+ @blocks = blocks
91
+ @mask = mask
92
+ end
93
+
94
+ def call(params, parents = [], original_params = params)
95
+ filtered_params = params.class.new
96
+
97
+ params.each do |key, value|
98
+ filtered_params[key] = value_for_key(key, value, parents, original_params)
99
+ end
100
+
101
+ filtered_params
102
+ end
103
+
104
+ def value_for_key(key, value, parents = [], original_params = nil)
105
+ parents.push(key) if deep_regexps
106
+ if regexps.any? { |r| r.match?(key) }
107
+ value = @mask
108
+ elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
109
+ value = @mask
110
+ elsif value.is_a?(Hash)
111
+ value = call(value, parents, original_params)
112
+ elsif value.is_a?(Array)
113
+ # If we don't pop the current parent it will be duplicated as we
114
+ # process each array value.
115
+ parents.pop if deep_regexps
116
+ value = value.map { |v| value_for_key(key, v, parents, original_params) }
117
+ # Restore the parent stack after processing the array.
118
+ parents.push(key) if deep_regexps
119
+ elsif blocks.any?
120
+ key = key.dup if key.duplicable?
121
+ value = value.dup if value.duplicable?
122
+ blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
123
+ end
124
+ parents.pop if deep_regexps
125
+ value
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/delegation"
4
+
5
+ module ActiveSupport
6
+ # NOTE: This approach has been deprecated for end-user code in favor of {thread_mattr_accessor}[rdoc-ref:Module#thread_mattr_accessor] and friends.
7
+ # Please use that approach instead.
8
+ #
9
+ # This module is used to encapsulate access to thread local variables.
10
+ #
11
+ # Instead of polluting the thread locals namespace:
12
+ #
13
+ # Thread.current[:connection_handler]
14
+ #
15
+ # you define a class that extends this module:
16
+ #
17
+ # module ActiveRecord
18
+ # class RuntimeRegistry
19
+ # extend ActiveSupport::PerThreadRegistry
20
+ #
21
+ # attr_accessor :connection_handler
22
+ # end
23
+ # end
24
+ #
25
+ # and invoke the declared instance accessors as class methods. So
26
+ #
27
+ # ActiveRecord::RuntimeRegistry.connection_handler = connection_handler
28
+ #
29
+ # sets a connection handler local to the current thread, and
30
+ #
31
+ # ActiveRecord::RuntimeRegistry.connection_handler
32
+ #
33
+ # returns a connection handler local to the current thread.
34
+ #
35
+ # This feature is accomplished by instantiating the class and storing the
36
+ # instance as a thread local keyed by the class name. In the example above
37
+ # a key "ActiveRecord::RuntimeRegistry" is stored in <tt>Thread.current</tt>.
38
+ # The class methods proxy to said thread local instance.
39
+ #
40
+ # If the class has an initializer, it must accept no arguments.
41
+ module PerThreadRegistry
42
+ def self.extended(object)
43
+ object.instance_variable_set "@per_thread_registry_key", object.name.freeze
44
+ end
45
+
46
+ def instance
47
+ Thread.current[@per_thread_registry_key] ||= new
48
+ end
49
+
50
+ private
51
+ def method_missing(name, *args, &block)
52
+ # Caches the method definition as a singleton method of the receiver.
53
+ #
54
+ # By letting #delegate handle it, we avoid an enclosure that'll capture args.
55
+ singleton_class.delegate name, to: :instance
56
+
57
+ send(name, *args, &block)
58
+ end
59
+ end
60
+ end