hierarchical_config 0.11 → 0.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/release.yml +41 -0
  3. data/.github/workflows/ruby.yml +55 -0
  4. data/.gitignore +2 -0
  5. data/.release-please-manifest.json +3 -0
  6. data/.rubocop.yml +68 -0
  7. data/.ruby-version +1 -0
  8. data/CHANGELOG.md +15 -0
  9. data/Gemfile +15 -2
  10. data/Gemfile.2.4 +19 -0
  11. data/Gemfile.2.4.lock +117 -0
  12. data/Gemfile.lock +126 -25
  13. data/README.md +0 -2
  14. data/bin/console +3 -3
  15. data/bin/tapioca +29 -0
  16. data/hierarchical_config.gemspec +14 -13
  17. data/lib/hierarchical_config/version.rb +3 -1
  18. data/lib/hierarchical_config.rb +195 -98
  19. data/release-please-config.json +14 -0
  20. data/sorbet/config +2 -0
  21. data/sorbet/rbi/annotations/activesupport.rbi +128 -0
  22. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  23. data/sorbet/rbi/gems/activesupport@7.0.4.2.rbi +16155 -0
  24. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  25. data/sorbet/rbi/gems/binding_of_caller@1.0.0.rbi +55 -0
  26. data/sorbet/rbi/gems/coderay@1.1.3.rbi +3426 -0
  27. data/sorbet/rbi/gems/concurrent-ruby@1.2.2.rbi +11545 -0
  28. data/sorbet/rbi/gems/debug_inspector@1.1.0.rbi +23 -0
  29. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1083 -0
  30. data/sorbet/rbi/gems/i18n@1.12.0.rbi +2296 -0
  31. data/sorbet/rbi/gems/interception@0.5.rbi +138 -0
  32. data/sorbet/rbi/gems/json@2.6.3.rbi +1541 -0
  33. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  34. data/sorbet/rbi/gems/minitest@5.17.0.rbi +1457 -0
  35. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  36. data/sorbet/rbi/gems/parallel@1.22.1.rbi +277 -0
  37. data/sorbet/rbi/gems/parser@3.2.1.0.rbi +7252 -0
  38. data/sorbet/rbi/gems/pry-rescue@1.5.2.rbi +186 -0
  39. data/sorbet/rbi/gems/pry-stack_explorer@0.6.1.rbi +295 -0
  40. data/sorbet/rbi/gems/pry@0.14.2.rbi +10081 -0
  41. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  42. data/sorbet/rbi/gems/rake@13.0.6.rbi +3018 -0
  43. data/sorbet/rbi/gems/rbi@0.0.16.rbi +3008 -0
  44. data/sorbet/rbi/gems/regexp_parser@2.7.0.rbi +3580 -0
  45. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
  46. data/sorbet/rbi/gems/rspec-core@3.12.1.rbi +10845 -0
  47. data/sorbet/rbi/gems/rspec-expectations@3.12.2.rbi +8100 -0
  48. data/sorbet/rbi/gems/rspec-mocks@3.12.3.rbi +5299 -0
  49. data/sorbet/rbi/gems/rspec-support@3.12.0.rbi +1611 -0
  50. data/sorbet/rbi/gems/rspec@3.12.0.rbi +82 -0
  51. data/sorbet/rbi/gems/rubocop-ast@1.27.0.rbi +6998 -0
  52. data/sorbet/rbi/gems/rubocop-performance@1.16.0.rbi +3004 -0
  53. data/sorbet/rbi/gems/rubocop@1.46.0.rbi +54549 -0
  54. data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +1239 -0
  55. data/sorbet/rbi/gems/spoom@1.1.15.rbi +2383 -0
  56. data/sorbet/rbi/gems/tapioca@0.11.1.rbi +3255 -0
  57. data/sorbet/rbi/gems/thor@1.2.1.rbi +3956 -0
  58. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5917 -0
  59. data/sorbet/rbi/gems/unicode-display_width@2.4.2.rbi +65 -0
  60. data/sorbet/rbi/gems/unparser@0.6.7.rbi +4524 -0
  61. data/sorbet/rbi/gems/webrick@1.7.0.rbi +2555 -0
  62. data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +441 -0
  63. data/sorbet/rbi/gems/yard@0.9.28.rbi +17841 -0
  64. data/sorbet/tapioca/config.yml +13 -0
  65. data/sorbet/tapioca/require.rb +4 -0
  66. metadata +75 -47
  67. data/.travis.yml +0 -6
@@ -1,84 +1,204 @@
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)
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 do
28
+ type_parameters(:A, :B).
29
+ params(
30
+ blk: T.nilable(
31
+ T.proc.params(name: Symbol, value: T.untyped).
32
+ returns([T.type_parameter(:A), T.type_parameter(:B)]),
33
+ ),
34
+ ).
35
+ returns(
36
+ T.any(
37
+ T::Hash[T.type_parameter(:A), T.type_parameter(:B)],
38
+ T::Hash[Symbol, T.untyped],
39
+ ),
40
+ )
41
+ end
42
+ def to_h(&blk)
43
+ hash = self.class.props.keys.map{|key| [key, send(key)]}
44
+ if blk
45
+ # copied from https://github.com/marcandre/backports/blob/36572870cbdc0cda30e5bab81af8ba390a6cf7c7/lib/backports/2.6.0/hash/to_h.rb#L3C39-L3C39
46
+ # to implement to_h with block for ruby < 2.6.0
47
+ if {n: true}.to_h{[:ok, true]}[:n]
48
+ T.unsafe(hash).map(&blk).to_h
49
+ else
50
+ hash.to_h(&blk)
22
51
  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]
28
52
  else
29
- raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
53
+ hash.to_h
30
54
  end
31
55
  end
32
56
 
33
- def [](attribute)
34
- send(attribute)
57
+ sig{params(key: T.any(String, Symbol)).returns(T.untyped)}
58
+ def [](key)
59
+ send(key)
35
60
  end
36
61
 
37
- alias :each :each_pair
62
+ private
38
63
 
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
64
+ sig{params(item: BasicObject).returns(T.any(BasicObject, T::Hash[T.untyped, T.untyped]))}
65
+ def item_to_hash(item)
66
+ case item
67
+ when ConfigStruct
68
+ item.to_hash
69
+ when Array
70
+ item.map{|i| item_to_hash(i)}
71
+ else
72
+ item
44
73
  end
45
74
  end
75
+ end
46
76
 
47
- private
77
+ @@root_index = T.let(0, Integer) # rubocop:disable Style/ClassVars
78
+
79
+ class << self
80
+ extend T::Sig
48
81
 
49
- def item_to_hash(value)
82
+ sig{params(value: T.untyped, path: String).returns(T::Array[String])}
83
+ def detect_errors(value, path)
84
+ errors = T.let([], T::Array[String])
50
85
  case value
86
+ when Hash
87
+ value.each do |key, item|
88
+ errors += detect_errors(item, "#{path}.#{key}")
89
+ end
90
+ when Array
91
+ value.each_with_index do |item, index|
92
+ errors += detect_errors(item, "#{path}[#{index}]")
93
+ end
94
+ when REQUIRED
95
+ errors << "#{path} is REQUIRED"
96
+ end
97
+ errors
98
+ end
99
+
100
+ sig{params(current_item: Object, name: String, parent_class: ClassOrModule).returns(T.any(Class, T::Types::Base))}
101
+ def build_types(current_item, name, parent_class)
102
+ case current_item
103
+ when Hash
104
+ new_type_name = inflect_typename(name)
105
+
106
+ return Hash if current_item.keys.to_a.any?{|k| k =~ /^[0-9]/ || k =~ /[- ]/}
107
+
108
+ new_type =
109
+ if parent_class.const_defined?(new_type_name, false)
110
+ parent_class.const_get(new_type_name, false)
111
+ else
112
+ parent_class.const_set(new_type_name, Class.new(T::Struct).tap{|c| c.include ConfigStruct})
113
+ end
114
+
115
+ current_item.each do |key, value|
116
+ next if new_type.props.key?(key.to_sym)
117
+
118
+ new_type.const key.to_sym, build_types(value, key, new_type)
119
+ new_type.send(:define_method, "#{key}?") do
120
+ !!send(key)
121
+ end
122
+ end
123
+
124
+ new_type
51
125
  when Array
52
- value.map{|item| item_to_hash(item)}
53
- when OpenStruct
54
- value.to_hash
126
+ types = current_item.each_with_index.map do |item, index|
127
+ build_types(item, "#{name}_#{index}", parent_class)
128
+ end
129
+ case types.size
130
+ when 0
131
+ T.untyped
132
+ when 1
133
+ T::Array[types.first]
134
+ else
135
+ T::Array[T.unsafe(T).any(*types)]
136
+ end
55
137
  else
56
- value
138
+ current_item.class
57
139
  end
58
140
  end
59
- end
60
141
 
61
- class << self
62
- def load_config( name, dir, environment, preprocess_with=:erb )
142
+ sig{params(current_item: Object, name: String, parent_class: ClassOrModule).returns(T.untyped)}
143
+ def build_config(current_item, name, parent_class)
144
+ case current_item
145
+ when Hash
146
+ return current_item.symbolize_keys if current_item.keys.to_a.any?{|k| k =~ /^[0-9]/ || k =~ /[- ]/}
147
+
148
+ current_type = parent_class.const_get(inflect_typename(name))
149
+ current_type.new(Hash[current_item.map{|key, value| [key.to_sym, build_config(value, key, current_type)]}]) # rubocop:disable Style/HashConversion
150
+ when Array
151
+ current_item.each_with_index.map do |item, index|
152
+ build_config(item, "#{name}_#{index}", parent_class)
153
+ end.freeze
154
+ else
155
+ current_item.freeze
156
+ end
157
+ end
158
+
159
+ sig{returns(Class)}
160
+ def build_new_root
161
+ @@root_index += 1 # rubocop:disable Style/ClassVars
162
+ const_set("ConfigRoot#{@@root_index}", Class.new)
163
+ end
164
+
165
+ sig do
166
+ params(
167
+ name: String,
168
+ dir: String,
169
+ environment: String,
170
+ preprocess_with: T.nilable(Symbol),
171
+ root_class: ClassOrModule,
172
+ ).returns(T::Struct)
173
+ end
174
+ def load_config(name, dir, environment, preprocess_with = :erb, root_class = build_new_root)
63
175
  primary_config_file = "#{dir}/#{name}.yml"
64
176
  overrides_config_file = "#{dir}/#{name}-overrides.yml"
65
177
 
66
- config_hash = load_hash_for_env( primary_config_file, environment, preprocess_with )
178
+ config_hash = load_hash_for_env(primary_config_file, environment, preprocess_with)
67
179
 
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 )
180
+ if File.exist?(overrides_config_file)
181
+ overrides_config_hash = load_hash_for_env(overrides_config_file, environment, preprocess_with)
182
+ config_hash = deep_merge(config_hash, overrides_config_hash)
71
183
  end
72
184
 
73
- config_hash, errors = lock_down_and_ostructify!( config_hash, name, environment )
185
+ errors = detect_errors(config_hash, name)
186
+ raise errors.map{|error| "#{error} for #{environment}"}.inspect unless errors.empty?
74
187
 
75
- raise errors.inspect unless errors.empty?
188
+ build_types(config_hash, name, root_class)
76
189
 
77
- config_hash
190
+ build_config(config_hash, name, root_class)
78
191
  end
79
192
 
80
- def load_hash_for_env( file, environment, preprocess_with )
81
- file_contents = IO.read(file)
193
+ sig do
194
+ params(
195
+ file: String,
196
+ environment: String,
197
+ preprocess_with: T.nilable(Symbol),
198
+ ).returns(T::Hash[String, BasicObject])
199
+ end
200
+ def load_hash_for_env(file, environment, preprocess_with)
201
+ file_contents = File.read(file)
82
202
  yaml_contents = case preprocess_with
83
203
  when :erb
84
204
  ERB.new(file_contents).result
@@ -87,103 +207,80 @@ module HierarchicalConfig
87
207
  else
88
208
  raise "Unknown preprocessor <#{preprocess_with}>"
89
209
  end
90
- yaml_config = YAML::load(yaml_contents)
210
+ yaml_config = YAML.safe_load(yaml_contents)
91
211
 
92
212
  ordered_stanza_labels = []
93
213
  ordered_stanza_labels << 'defaults' if yaml_config.key? 'defaults'
94
- ordered_stanza_labels += yaml_config.keys.grep(/^defaults\[.*#{environment}/).sort_by{ |a| a.count(',') }
214
+ ordered_stanza_labels += yaml_config.keys.grep(/^defaults\[.*#{environment}/).sort_by{|a| a.count(',')}
95
215
  ordered_stanza_labels << environment if yaml_config.key? environment
96
216
 
97
217
  config = deep_merge_hashes_in_keys(ordered_stanza_labels, yaml_config)
98
218
 
99
219
  env_config_labels = []
100
220
  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(',') }
221
+ env_config_labels += yaml_config.keys.grep(/^env_vars\[.*#{environment}/).sort_by{|a| a.count(',')}
102
222
 
103
223
  env_config = deep_merge_hashes_in_keys(env_config_labels, yaml_config)
104
224
  env_config = fill_in_env_vars(env_config)
105
225
 
106
226
  deep_merge(config, env_config)
107
-
108
227
  rescue StandardError => e
109
228
  raise <<-ERROR
110
229
  Error loading config from file #{file}.
111
- #{$!.inspect}
112
- #{$@}
230
+ #{$ERROR_INFO.inspect}
231
+ #{$ERROR_POSITION}
232
+ #{e}
113
233
  ERROR
114
234
  end
115
235
 
116
236
  private
117
237
 
238
+ sig do
239
+ params(keys: T::Array[String],
240
+ root_hash: T::Hash[String,
241
+ T::Hash[String, T.untyped]]).returns(T::Hash[T.untyped, T.untyped])
242
+ end
118
243
  def deep_merge_hashes_in_keys(keys, root_hash)
119
244
  keys.inject({}) do |acc, label|
120
- deep_merge( acc, root_hash[label] )
245
+ deep_merge(acc, T.must(root_hash[label]))
121
246
  end
122
247
  end
123
248
 
249
+ sig{params(hash: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])}
124
250
  def fill_in_env_vars(hash)
125
251
  r = {}
126
- hash.each do |key,value|
252
+ hash.each do |key, value|
127
253
  if value.is_a? Hash
128
254
  leaf_hash = fill_in_env_vars(value)
129
- r[key]=leaf_hash unless leaf_hash.keys.empty?
255
+ r[key] = leaf_hash unless leaf_hash.keys.empty?
130
256
  elsif !value.nil? && ENV.key?(value)
131
- r[key]=ENV[value]
257
+ r[key] = ENV.fetch(value, nil)
132
258
  end
133
259
  end
134
260
  r
135
261
  end
136
262
 
137
263
  # merges two hashes with nested hashes if present
138
- def deep_merge( hash1, hash2 )
264
+ sig do
265
+ params(hash1: T::Hash[T.untyped, T.untyped],
266
+ hash2: T::Hash[T.untyped, T.untyped]).returns(T::Hash[T.untyped, T.untyped])
267
+ end
268
+ def deep_merge(hash1, hash2)
139
269
  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 )
270
+ (hash1.keys + hash2.keys).each do |key|
271
+ if hash1.key?(key) && hash2.key?(key) &&
272
+ hash1[key].is_a?(Hash) && hash2[key].is_a?(Hash)
273
+ hash1[key] = deep_merge(hash1[key], hash2[key])
274
+ elsif hash2.key?(key)
145
275
  hash1[key] = hash2[key]
146
276
  end
147
277
  end
148
278
  hash1
149
279
  end
150
280
 
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
281
+ sig{params(name: String).returns(String)}
282
+ def inflect_typename(name)
283
+ ActiveSupport::Inflector.camelize(name)
187
284
  end
188
285
  end
189
286
  end
@@ -0,0 +1,14 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "changelog-path": "CHANGELOG.md",
5
+ "release-type": "ruby",
6
+ "bump-minor-pre-major": true,
7
+ "bump-patch-for-minor-pre-major": true,
8
+ "draft": false,
9
+ "prerelease": false,
10
+ "version-file": "lib/hierarchical_config/version.rb"
11
+ }
12
+ },
13
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
14
+ }
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