hierarchical_config 0.11 → 0.13

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 (63) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +52 -0
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +68 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +15 -2
  7. data/Gemfile.2.4 +19 -0
  8. data/Gemfile.2.4.lock +117 -0
  9. data/Gemfile.lock +124 -25
  10. data/README.md +0 -2
  11. data/bin/console +3 -3
  12. data/bin/tapioca +29 -0
  13. data/hierarchical_config.gemspec +14 -13
  14. data/lib/hierarchical_config/version.rb +3 -1
  15. data/lib/hierarchical_config.rb +164 -102
  16. data/sorbet/config +2 -0
  17. data/sorbet/rbi/annotations/activesupport.rbi +128 -0
  18. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  19. data/sorbet/rbi/gems/activesupport@7.0.4.2.rbi +16155 -0
  20. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  21. data/sorbet/rbi/gems/binding_of_caller@1.0.0.rbi +55 -0
  22. data/sorbet/rbi/gems/coderay@1.1.3.rbi +3426 -0
  23. data/sorbet/rbi/gems/concurrent-ruby@1.2.2.rbi +11545 -0
  24. data/sorbet/rbi/gems/debug_inspector@1.1.0.rbi +23 -0
  25. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1083 -0
  26. data/sorbet/rbi/gems/i18n@1.12.0.rbi +2296 -0
  27. data/sorbet/rbi/gems/interception@0.5.rbi +138 -0
  28. data/sorbet/rbi/gems/json@2.6.3.rbi +1541 -0
  29. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  30. data/sorbet/rbi/gems/minitest@5.17.0.rbi +1457 -0
  31. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  32. data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
  33. data/sorbet/rbi/gems/parser@3.2.1.0.rbi +7252 -0
  34. data/sorbet/rbi/gems/pry-rescue@1.5.2.rbi +186 -0
  35. data/sorbet/rbi/gems/pry-stack_explorer@0.6.1.rbi +295 -0
  36. data/sorbet/rbi/gems/pry@0.14.2.rbi +10081 -0
  37. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  38. data/sorbet/rbi/gems/rake@13.0.6.rbi +3018 -0
  39. data/sorbet/rbi/gems/rbi@0.0.16.rbi +3008 -0
  40. data/sorbet/rbi/gems/regexp_parser@2.7.0.rbi +3580 -0
  41. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
  42. data/sorbet/rbi/gems/rspec-core@3.12.1.rbi +10845 -0
  43. data/sorbet/rbi/gems/rspec-expectations@3.12.2.rbi +8100 -0
  44. data/sorbet/rbi/gems/rspec-mocks@3.12.3.rbi +5299 -0
  45. data/sorbet/rbi/gems/rspec-support@3.12.0.rbi +1611 -0
  46. data/sorbet/rbi/gems/rspec@3.12.0.rbi +82 -0
  47. data/sorbet/rbi/gems/rubocop-ast@1.27.0.rbi +6998 -0
  48. data/sorbet/rbi/gems/rubocop-performance@1.16.0.rbi +3004 -0
  49. data/sorbet/rbi/gems/rubocop@1.46.0.rbi +54549 -0
  50. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1239 -0
  51. data/sorbet/rbi/gems/spoom@1.1.15.rbi +2383 -0
  52. data/sorbet/rbi/gems/tapioca@0.11.1.rbi +3255 -0
  53. data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
  54. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5917 -0
  55. data/sorbet/rbi/gems/unicode-display_width@2.4.2.rbi +65 -0
  56. data/sorbet/rbi/gems/unparser@0.6.7.rbi +4524 -0
  57. data/sorbet/rbi/gems/webrick@1.7.0.rbi +2555 -0
  58. data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +441 -0
  59. data/sorbet/rbi/gems/yard@0.9.28.rbi +17841 -0
  60. data/sorbet/tapioca/config.yml +13 -0
  61. data/sorbet/tapioca/require.rb +4 -0
  62. metadata +74 -50
  63. data/.travis.yml +0 -6
@@ -1,84 +1,174 @@
1
- require 'ostruct'
1
+ # typed: strict
2
+
2
3
  require 'yaml'
3
4
  require 'erb'
4
5
  require 'set'
6
+ require 'sorbet-runtime'
7
+ require 'active_support'
8
+ require 'active_support/core_ext/hash/keys'
5
9
 
6
- require "hierarchical_config/version"
10
+ require 'hierarchical_config/version'
7
11
 
8
12
  module HierarchicalConfig
9
13
  REQUIRED = :REQUIRED
10
- #this is the incantation that works for ruby 1.8.7 (syck)
11
- YAML.add_builtin_type( 'REQUIRED' ){ REQUIRED }
12
- #and this works for 1.9.3 (Psych)
13
- YAML.add_domain_type( nil, 'REQUIRED' ){ REQUIRED }
14
-
15
- class OpenStruct < ::OpenStruct
16
- def method_missing( mid, *args ) # :nodoc:
17
- mname = mid.id2name
18
- len = args.length
19
- if mname.chomp!('=')
20
- if len != 1
21
- raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
22
- end
23
- modifiable[new_ostruct_member(mname)] = args[0]
24
- elsif mname =~ /\?$/
25
- !!send(mname.gsub("?",""))
26
- elsif len == 0 && @table.key?( mid )
27
- @table[mid]
14
+ T.unsafe(YAML).add_domain_type(nil, 'REQUIRED'){REQUIRED}
15
+
16
+ ClassOrModule = T.type_alias{T.any(Class, Module)}
17
+
18
+ module ConfigStruct
19
+ extend T::Sig
20
+ include Kernel
21
+
22
+ sig{returns(T::Hash[Symbol, T.untyped])}
23
+ def to_hash
24
+ Hash[self.class.props.keys.map{|key| [key, item_to_hash(send(key))]}] # rubocop:disable Style/HashConversion
25
+ end
26
+
27
+ sig{params(key: T.any(String, Symbol)).returns(T.untyped)}
28
+ def [](key)
29
+ send(key)
30
+ end
31
+
32
+ private
33
+
34
+ sig{params(item: BasicObject).returns(T.any(BasicObject, T::Hash[T.untyped, T.untyped]))}
35
+ def item_to_hash(item)
36
+ case item
37
+ when ConfigStruct
38
+ item.to_hash
39
+ when Array
40
+ item.map{|i| item_to_hash(i)}
28
41
  else
29
- raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
42
+ item
30
43
  end
31
44
  end
45
+ end
46
+
47
+ @@root_index = T.let(0, Integer) # rubocop:disable Style/ClassVars
32
48
 
33
- def [](attribute)
34
- send(attribute)
49
+ class << self
50
+ extend T::Sig
51
+
52
+ sig{params(value: T.untyped, path: String).returns(T::Array[String])}
53
+ def detect_errors(value, path)
54
+ errors = T.let([], T::Array[String])
55
+ case value
56
+ when Hash
57
+ value.each do |key, item|
58
+ errors += detect_errors(item, "#{path}.#{key}")
59
+ end
60
+ when Array
61
+ value.each_with_index do |item, index|
62
+ errors += detect_errors(item, "#{path}[#{index}]")
63
+ end
64
+ when REQUIRED
65
+ errors << "#{path} is REQUIRED"
66
+ end
67
+ errors
35
68
  end
36
69
 
37
- alias :each :each_pair
70
+ sig{params(current_item: Object, name: String, parent_class: ClassOrModule).returns(T.any(Class, T::Types::Base))}
71
+ def build_types(current_item, name, parent_class)
72
+ case current_item
73
+ when Hash
74
+ new_type_name = ActiveSupport::Inflector.camelize(ActiveSupport::Inflector.underscore(name))
75
+
76
+ return Hash if current_item.keys.to_a.any?{|k| k =~ /^[0-9]/ || k =~ /[- ]/}
38
77
 
39
- def to_hash
40
- @table.inject({}) do |hash, key_value|
41
- key, value = *key_value
42
- hash[key] = item_to_hash(value)
43
- hash
78
+ new_type =
79
+ if parent_class.const_defined?(new_type_name, false)
80
+ parent_class.const_get(new_type_name, false)
81
+ else
82
+ parent_class.const_set(new_type_name, Class.new(T::Struct).tap{|c| c.include ConfigStruct})
83
+ end
84
+
85
+ current_item.each do |key, value|
86
+ next if new_type.props.key?(key.to_sym)
87
+
88
+ new_type.const key.to_sym, build_types(value, key, new_type)
89
+ new_type.send(:define_method, "#{key}?") do
90
+ !!send(key)
91
+ end
92
+ end
93
+
94
+ new_type
95
+ when Array
96
+ types = current_item.each_with_index.map do |item, index|
97
+ build_types(item, "#{name}_#{index}", parent_class)
98
+ end
99
+ case types.size
100
+ when 0
101
+ T.untyped
102
+ when 1
103
+ T::Array[types.first]
104
+ else
105
+ T::Array[T.unsafe(T).any(*types)]
106
+ end
107
+ else
108
+ current_item.class
44
109
  end
45
110
  end
46
111
 
47
- private
112
+ sig{params(current_item: Object, name: String, parent_class: ClassOrModule).returns(T.untyped)}
113
+ def build_config(current_item, name, parent_class)
114
+ case current_item
115
+ when Hash
116
+ return current_item.symbolize_keys if current_item.keys.to_a.any?{|k| k =~ /^[0-9]/ || k =~ /[- ]/}
48
117
 
49
- def item_to_hash(value)
50
- case value
118
+ current_type = parent_class.const_get(ActiveSupport::Inflector.camelize(name))
119
+ current_type.new(Hash[current_item.map{|key, value| [key.to_sym, build_config(value, key, current_type)]}]) # rubocop:disable Style/HashConversion
51
120
  when Array
52
- value.map{|item| item_to_hash(item)}
53
- when OpenStruct
54
- value.to_hash
121
+ current_item.each_with_index.map do |item, index|
122
+ build_config(item, "#{name}_#{index}", parent_class)
123
+ end.freeze
55
124
  else
56
- value
125
+ current_item.freeze
57
126
  end
58
127
  end
59
- end
60
128
 
61
- class << self
62
- def load_config( name, dir, environment, preprocess_with=:erb )
129
+ sig{returns(Class)}
130
+ def build_new_root
131
+ @@root_index += 1 # rubocop:disable Style/ClassVars
132
+ const_set("ConfigRoot#{@@root_index}", Class.new)
133
+ end
134
+
135
+ sig do
136
+ params(
137
+ name: String,
138
+ dir: String,
139
+ environment: String,
140
+ preprocess_with: T.nilable(Symbol),
141
+ root_class: ClassOrModule,
142
+ ).returns(T::Struct)
143
+ end
144
+ def load_config(name, dir, environment, preprocess_with = :erb, root_class = build_new_root)
63
145
  primary_config_file = "#{dir}/#{name}.yml"
64
146
  overrides_config_file = "#{dir}/#{name}-overrides.yml"
65
147
 
66
- config_hash = load_hash_for_env( primary_config_file, environment, preprocess_with )
148
+ config_hash = load_hash_for_env(primary_config_file, environment, preprocess_with)
67
149
 
68
- if File.exists?( overrides_config_file )
69
- overrides_config_hash = load_hash_for_env( overrides_config_file, environment, preprocess_with )
70
- config_hash = deep_merge( config_hash, overrides_config_hash )
150
+ if File.exist?(overrides_config_file)
151
+ overrides_config_hash = load_hash_for_env(overrides_config_file, environment, preprocess_with)
152
+ config_hash = deep_merge(config_hash, overrides_config_hash)
71
153
  end
72
154
 
73
- config_hash, errors = lock_down_and_ostructify!( config_hash, name, environment )
155
+ errors = detect_errors(config_hash, name)
156
+ raise errors.map{|error| "#{error} for #{environment}"}.inspect unless errors.empty?
74
157
 
75
- raise errors.inspect unless errors.empty?
158
+ build_types(config_hash, name, root_class)
76
159
 
77
- config_hash
160
+ build_config(config_hash, name, root_class)
78
161
  end
79
162
 
80
- def load_hash_for_env( file, environment, preprocess_with )
81
- file_contents = IO.read(file)
163
+ sig do
164
+ params(
165
+ file: String,
166
+ environment: String,
167
+ preprocess_with: T.nilable(Symbol),
168
+ ).returns(T::Hash[String, BasicObject])
169
+ end
170
+ def load_hash_for_env(file, environment, preprocess_with)
171
+ file_contents = File.read(file)
82
172
  yaml_contents = case preprocess_with
83
173
  when :erb
84
174
  ERB.new(file_contents).result
@@ -87,103 +177,75 @@ module HierarchicalConfig
87
177
  else
88
178
  raise "Unknown preprocessor <#{preprocess_with}>"
89
179
  end
90
- yaml_config = YAML::load(yaml_contents)
180
+ yaml_config = YAML.safe_load(yaml_contents)
91
181
 
92
182
  ordered_stanza_labels = []
93
183
  ordered_stanza_labels << 'defaults' if yaml_config.key? 'defaults'
94
- ordered_stanza_labels += yaml_config.keys.grep(/^defaults\[.*#{environment}/).sort_by{ |a| a.count(',') }
184
+ ordered_stanza_labels += yaml_config.keys.grep(/^defaults\[.*#{environment}/).sort_by{|a| a.count(',')}
95
185
  ordered_stanza_labels << environment if yaml_config.key? environment
96
186
 
97
187
  config = deep_merge_hashes_in_keys(ordered_stanza_labels, yaml_config)
98
188
 
99
189
  env_config_labels = []
100
190
  env_config_labels << 'env_vars' if yaml_config.key? 'env_vars'
101
- env_config_labels += yaml_config.keys.grep(/^env_vars\[.*#{environment}/).sort_by{ |a| a.count(',') }
191
+ env_config_labels += yaml_config.keys.grep(/^env_vars\[.*#{environment}/).sort_by{|a| a.count(',')}
102
192
 
103
193
  env_config = deep_merge_hashes_in_keys(env_config_labels, yaml_config)
104
194
  env_config = fill_in_env_vars(env_config)
105
195
 
106
196
  deep_merge(config, env_config)
107
-
108
197
  rescue StandardError => e
109
198
  raise <<-ERROR
110
199
  Error loading config from file #{file}.
111
- #{$!.inspect}
112
- #{$@}
200
+ #{$ERROR_INFO.inspect}
201
+ #{$ERROR_POSITION}
202
+ #{e}
113
203
  ERROR
114
204
  end
115
205
 
116
206
  private
117
207
 
208
+ sig do
209
+ params(keys: T::Array[String],
210
+ root_hash: T::Hash[String,
211
+ T::Hash[String, T.untyped]]).returns(T::Hash[T.untyped, T.untyped])
212
+ end
118
213
  def deep_merge_hashes_in_keys(keys, root_hash)
119
214
  keys.inject({}) do |acc, label|
120
- deep_merge( acc, root_hash[label] )
215
+ deep_merge(acc, T.must(root_hash[label]))
121
216
  end
122
217
  end
123
218
 
219
+ sig{params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])}
124
220
  def fill_in_env_vars(hash)
125
221
  r = {}
126
- hash.each do |key,value|
222
+ hash.each do |key, value|
127
223
  if value.is_a? Hash
128
224
  leaf_hash = fill_in_env_vars(value)
129
- r[key]=leaf_hash unless leaf_hash.keys.empty?
225
+ r[key] = leaf_hash unless leaf_hash.keys.empty?
130
226
  elsif !value.nil? && ENV.key?(value)
131
- r[key]=ENV[value]
227
+ r[key] = ENV.fetch(value, nil)
132
228
  end
133
229
  end
134
230
  r
135
231
  end
136
232
 
137
233
  # merges two hashes with nested hashes if present
138
- def deep_merge( hash1, hash2 )
234
+ sig do
235
+ params(hash1: T::Hash[T.untyped, T.untyped],
236
+ hash2: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])
237
+ end
238
+ def deep_merge(hash1, hash2)
139
239
  hash1 = hash1.dup
140
- ( hash1.keys + hash2.keys ).each do | key |
141
- if hash1.key?( key ) && hash2.key?( key ) &&
142
- hash1[key].is_a?( Hash ) && hash2[key].is_a?( Hash )
143
- hash1[key] = deep_merge( hash1[key], hash2[key] )
144
- elsif hash2.key?( key )
240
+ (hash1.keys + hash2.keys).each do |key|
241
+ if hash1.key?(key) && hash2.key?(key) &&
242
+ hash1[key].is_a?(Hash) && hash2[key].is_a?(Hash)
243
+ hash1[key] = deep_merge(hash1[key], hash2[key])
244
+ elsif hash2.key?(key)
145
245
  hash1[key] = hash2[key]
146
246
  end
147
247
  end
148
248
  hash1
149
249
  end
150
-
151
- # Mutator method that does three things:
152
- # * checks if any of the keys were required and not set. Upon finding
153
- # it adds key to the error set
154
- # * recursively sets open structs for deep hashes
155
- # * recursively freezes config objects
156
- def lock_down_and_ostructify!( _hash, path, environment)
157
- hash = Hash[_hash.map{|k,v|[k.to_s, v]}] #stringify keys
158
- errors = []
159
- hash.each do | key, value |
160
- hash[key], child_errors = lock_down_and_ostructify_item!(value, path + '.' + key, environment)
161
- errors += child_errors
162
- end
163
- return OpenStruct.new(hash).freeze, errors
164
- end
165
-
166
- def lock_down_and_ostructify_item!(value, path, environment)
167
- errors = []
168
- return_value = case value
169
- when Hash
170
- child_hash, child_errors = lock_down_and_ostructify!( value, path, environment )
171
- errors += child_errors
172
- child_hash
173
- when Array
174
- value.each_with_index.map do |item, index|
175
- child_item, child_errors = lock_down_and_ostructify_item!( item, "#{path}[#{index}]", environment )
176
- errors += child_errors
177
- child_item
178
- end.freeze
179
- when REQUIRED
180
- errors << "#{path} is REQUIRED for #{environment}"
181
- nil
182
- else
183
- value.freeze
184
- end
185
-
186
- return return_value, errors
187
- end
188
250
  end
189
251
  end
data/sorbet/config ADDED
@@ -0,0 +1,2 @@
1
+ --dir
2
+ .
@@ -0,0 +1,128 @@
1
+ # typed: strict
2
+
3
+ # DO NOT EDIT MANUALLY
4
+ # This file was pulled from a central RBI files repository.
5
+ # Please run `bin/tapioca annotations` to update it.
6
+
7
+ module ActiveSupport::Testing::Declarative
8
+ sig { params(name: String, block: T.proc.bind(T.untyped).void).void }
9
+ def test(name, &block); end
10
+ end
11
+
12
+ class ActiveSupport::EnvironmentInquirer
13
+ sig { returns(T::Boolean) }
14
+ def development?; end
15
+
16
+ sig { returns(T::Boolean) }
17
+ def production?; end
18
+
19
+ sig { returns(T::Boolean) }
20
+ def test?; end
21
+
22
+ # @method_missing: delegated to String through ActiveSupport::StringInquirer
23
+ sig { returns(T::Boolean) }
24
+ def staging?; end
25
+ end
26
+
27
+ module ActiveSupport::Testing::SetupAndTeardown::ClassMethods
28
+ sig { params(args: T.untyped, block: T.nilable(T.proc.bind(T.untyped).void)).void }
29
+ def setup(*args, &block); end
30
+
31
+ sig { params(args: T.untyped, block: T.nilable(T.proc.bind(T.untyped).void)).void }
32
+ def teardown(*args, &block); end
33
+ end
34
+
35
+ class ActiveSupport::TestCase
36
+ sig { params(args: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).void)).void }
37
+ def self.setup(*args, &block); end
38
+
39
+ sig { params(args: T.untyped, block: T.nilable(T.proc.bind(T.attached_class).void)).void }
40
+ def self.teardown(*args, &block); end
41
+
42
+ sig { params(name: String, block: T.proc.bind(T.attached_class).void).void }
43
+ def self.test(name, &block); end
44
+ end
45
+
46
+ class Object
47
+ sig { returns(T::Boolean) }
48
+ def blank?; end
49
+
50
+ sig { returns(T::Boolean) }
51
+ def present?; end
52
+ end
53
+
54
+ class Hash
55
+ sig { returns(T::Boolean) }
56
+ def extractable_options?; end
57
+ end
58
+
59
+ class Array
60
+ sig { params(position: Integer).returns(T.self_type) }
61
+ def from(position); end
62
+
63
+ sig { params(position: Integer).returns(T.self_type) }
64
+ def to(position); end
65
+
66
+ sig { params(elements: T.untyped).returns(T::Array[T.untyped]) }
67
+ def including(*elements); end
68
+
69
+ sig { params(elements: T.untyped).returns(T.self_type) }
70
+ def excluding(*elements); end
71
+
72
+ sig { params(elements: T.untyped).returns(T.self_type) }
73
+ def without(*elements); end
74
+
75
+ sig { returns(T.nilable(Elem)) }
76
+ def second; end
77
+
78
+ sig { returns(T.nilable(Elem)) }
79
+ def third; end
80
+
81
+ sig { returns(T.nilable(Elem)) }
82
+ def fourth; end
83
+
84
+ sig { returns(T.nilable(Elem)) }
85
+ def fifth; end
86
+
87
+ sig { returns(T.nilable(Elem)) }
88
+ def forty_two; end
89
+
90
+ sig { returns(T.nilable(Elem)) }
91
+ def third_to_last; end
92
+
93
+ sig { returns(T.nilable(Elem)) }
94
+ def second_to_last; end
95
+
96
+ sig { params(options: T::Hash[T.untyped, T.untyped]).returns(String) }
97
+ def to_sentence(options = {}); end
98
+
99
+ sig { params(format: Symbol).returns(String) }
100
+ def to_fs(format = :default); end
101
+
102
+ sig { params(format: Symbol).returns(String) }
103
+ def to_formatted_s(format = :default); end
104
+
105
+ sig { returns(String) }
106
+ def to_xml; end
107
+
108
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
109
+ def extract_options!; end
110
+
111
+ sig { type_parameters(:FillType).params(number: Integer, fill_with: T.type_parameter(:FillType), block: T.nilable(T.proc.params(group: T::Array[T.any(Elem, T.type_parameter(:FillType))]).void)).returns(T::Array[T::Array[T.any(Elem, T.type_parameter(:FillType))]]) }
112
+ def in_groups(number, fill_with = T.unsafe(nil), &block); end
113
+
114
+ sig { type_parameters(:FillType).params(number: Integer, fill_with: T.type_parameter(:FillType), block: T.nilable(T.proc.params(group: T::Array[T.any(Elem, T.type_parameter(:FillType))]).void)).returns(T::Array[T::Array[T.any(Elem, T.type_parameter(:FillType))]]) }
115
+ def in_groups_of(number, fill_with = T.unsafe(nil), &block); end
116
+
117
+ sig { params(value: T.untyped, block: T.nilable(T.proc.params(element: Elem).returns(T.untyped))).returns(T::Array[T::Array[Elem]]) }
118
+ def split(value = nil, &block); end
119
+
120
+ sig { params(object: T.untyped).returns(T::Array[T.untyped]) }
121
+ def self.wrap(object); end
122
+
123
+ sig { returns(T.untyped) }
124
+ def extract!; end
125
+
126
+ sig { returns(ActiveSupport::ArrayInquirer) }
127
+ def inquiry; end
128
+ end