strings-case 0.3.0 → 0.4.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.
data/lib/strings/case.rb CHANGED
@@ -1,287 +1,389 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
3
4
  require "strscan"
4
5
 
6
+ require_relative "case/acronyms"
7
+ require_relative "case/configuration"
5
8
  require_relative "case/version"
6
9
 
7
10
  module Strings
8
- module Case
9
- DIGITS = ("0".."9").freeze
10
- UP_LETTERS = ("A".."Z").freeze
11
- DOWN_LETTERS = ("a".."z").freeze
11
+ # Responsible for converting strings to common cases
12
+ #
13
+ # @api public
14
+ class Case
15
+ # The word delimiters
16
+ #
17
+ # @return [Array<String>]
18
+ #
19
+ # @api private
12
20
  DELIMITERS = [" ", "\n", "\t", "_", ".", "-", "#", "?", "!"].freeze
21
+ private_constant :DELIMITERS
22
+
23
+ # The pattern to detect word delimiters
24
+ #
25
+ # @return [Regexp]
26
+ #
27
+ # @api private
13
28
  DELIMS = Regexp.union(DELIMITERS)
14
- NONALPHANUMERIC = (32..127).map(&:chr) -
15
- (DIGITS.to_a + DOWN_LETTERS.to_a + UP_LETTERS.to_a + DELIMITERS)
16
- NONALPHAS = Regexp.union(NONALPHANUMERIC)
29
+ private_constant :DELIMS
30
+
31
+ # The pattern to detect uppercase characters
32
+ #
33
+ # @return [Regexp]
34
+ #
35
+ # @api private
17
36
  UPPERCASE = /^(\p{Ll}|\p{Digit})\p{Lu}/.freeze
37
+ private_constant :UPPERCASE
38
+
39
+ # The pattern to detect lowercase characters
40
+ #
41
+ # @return [Regexp]
42
+ #
43
+ # @api private
18
44
  LOWERCASE = /\p{Lu}(?=\p{Ll})/.freeze
45
+ private_constant :LOWERCASE
19
46
 
20
47
  class Error < StandardError; end
21
48
 
22
- # Prevent changing case
23
- module NullCase
24
- def downcase
25
- self
26
- end
27
- alias upcase downcase
28
- alias capitalize downcase
49
+ # Global instance
50
+ #
51
+ # @return [Strings::Case]
52
+ #
53
+ # @api private
54
+ def self.__instance__
55
+ @__instance__ ||= Case.new
29
56
  end
57
+ private_class_method :__instance__
58
+
59
+ class << self
60
+ extend Forwardable
30
61
 
31
- # Convert string to camel case:
32
- # * start with a lowercase character
33
- # * every subsequent word has its first character uppercased
34
- # * all words are compounded together
62
+ delegate %i[camelcase constcase constantcase dashcase
63
+ headercase kebabcase lower_camelcase
64
+ pascalcase pathcase sentencecase snakecase
65
+ titlecase underscore upper_camelcase] => :__instance__
66
+ end
67
+
68
+ # Create a Strings::Case instance
35
69
  #
36
70
  # @example
37
- # camelcase("foo bar baz") # => "fooBarBaz"
71
+ # strings = Strings::Case.new(acronyms: %w[HTTP XML])
38
72
  #
39
- # @param [String] string
40
- # the string to camelcase
41
- # @param [Array[String]] acronyms
73
+ # @param [Array<String>] acronyms
42
74
  # the acronyms to use to prevent modifications
43
- # @param [String] separator
44
- # the separator for linking words, by default none
45
75
  #
46
76
  # @api public
47
- def camelcase(string, acronyms: [], separator: "")
48
- res = parsecase(string, acronyms: acronyms, sep: separator, casing: :capitalize)
77
+ def initialize(acronyms: nil)
78
+ configure(acronyms: acronyms)
79
+ end
49
80
 
50
- return res if res.to_s.empty?
81
+ # Access configuration
82
+ #
83
+ # @example
84
+ # strings.config
85
+ #
86
+ # @return [Strings::Case::Configuration]
87
+ #
88
+ # @api public
89
+ def config
90
+ @config ||= Configuration.new
91
+ end
51
92
 
52
- acronyms_regex = /^(#{acronyms.join("|")})/
53
- if !acronyms.empty? && (res =~ acronyms_regex)
54
- res
93
+ # Configure acronyms
94
+ #
95
+ # @example
96
+ # strings = Strings::Case.new
97
+ # strings.configure do |config|
98
+ # config.acronym "HTTP"
99
+ # end
100
+ #
101
+ # @example
102
+ # strings = Strings::Case.new
103
+ # strings.configure(acronyms: %w[HTTP XML])
104
+ #
105
+ # @param [Array<String>] acronyms
106
+ # the acronyms to use to prevent modifications
107
+ #
108
+ # @yield [Strings::Case::Configuration]
109
+ #
110
+ # @return [void]
111
+ #
112
+ # @api public
113
+ def configure(acronyms: nil)
114
+ if block_given?
115
+ yield config
55
116
  else
56
- res[0].downcase + res[1..-1]
117
+ config.acronym(*acronyms)
57
118
  end
58
119
  end
59
- module_function :camelcase
60
120
 
121
+ # Convert string to camel case
122
+ #
123
+ # @example
124
+ # camelcase("foo bar baz") # => "fooBarBaz"
125
+ #
126
+ # @param [String] string
127
+ # the string to convert to camel case
128
+ # @param [Array<String>] acronyms
129
+ # the acronyms to use to prevent modifications
130
+ # @param [String] separator
131
+ # the words separator, by default an empty string
132
+ #
133
+ # @return [String]
134
+ #
135
+ # @api public
136
+ def camelcase(string, acronyms: config.acronyms, separator: "")
137
+ acronyms = Acronyms.from(acronyms)
138
+ parsecase(string, acronyms: acronyms, sep: separator) do |word, i|
139
+ acronyms.fetch(word) || (i.zero? ? word.downcase : word.capitalize)
140
+ end
141
+ end
61
142
  alias lower_camelcase camelcase
62
- module_function :lower_camelcase
63
143
 
64
- # Converts string to a constant
144
+ # Convert string to constant case
65
145
  #
66
146
  # @example
67
147
  # constantcase("foo bar baz") # => "FOO_BAR_BAZ"
68
148
  #
69
149
  # @param [String] string
70
- # the string to turn into constant
71
- # @param [Array[String]] acronyms
150
+ # the string to convert to constant case
151
+ # @param [Array<String>] acronyms
72
152
  # the acronyms to use to prevent modifications
73
153
  # @param [String] separator
74
- # the words separator, by default "_"
154
+ # the words separator, by default an underscore "_"
155
+ #
156
+ # @return [String]
75
157
  #
76
158
  # @api public
77
- def constcase(string, separator: "_")
78
- parsecase(string, sep: separator, casing: :upcase)
159
+ def constcase(string, acronyms: config.acronyms, separator: "_")
160
+ acronyms = Acronyms.from(acronyms)
161
+ parsecase(string, acronyms: acronyms, sep: separator, &:upcase)
79
162
  end
80
- module_function :constcase
81
-
82
163
  alias constantcase constcase
83
- module_function :constantcase
84
164
 
85
- # Convert string to a HTTP Header
165
+ # Convert string to header case
86
166
  #
87
167
  # @example
88
168
  # headercase("foo bar baz") # = "Foo-Bar-Baz"
89
169
  #
90
170
  # @param [String] string
91
- # the string to turn into header
92
- # @param [Array[String]] acronyms
171
+ # the string to convert to header case
172
+ # @param [Array<String>] acronyms
93
173
  # the acronyms to use to prevent modifications
94
174
  # @param [String] separator
95
- # the words separator, by default "-"
175
+ # the words separator, by default a hyphen "-"
176
+ #
177
+ # @return [String]
96
178
  #
97
179
  # @api public
98
- def headercase(string, acronyms: [], separator: "-")
99
- parsecase(string, acronyms: acronyms, sep: separator, casing: :capitalize)
180
+ def headercase(string, acronyms: config.acronyms, separator: "-")
181
+ acronyms = Acronyms.from(acronyms)
182
+ parsecase(string, acronyms: acronyms, sep: separator) do |word|
183
+ (acronym = acronyms.fetch(word)) ? acronym : word.capitalize
184
+ end
100
185
  end
101
- module_function :headercase
102
186
 
103
- # Converts string to lower case words linked by hyphenes
187
+ # Convert string to kebab case
104
188
  #
105
189
  # @example
106
190
  # kebabcase("fooBarBaz") # => "foo-bar-baz"
107
191
  #
192
+ # @example
108
193
  # kebabcase("__FOO_BAR__") # => "foo-bar"
109
194
  #
110
195
  # @param [String] string
111
- # the string to convert to dashed string
112
- # @param [Array[String]] acronyms
196
+ # the string to convert to kebab case
197
+ # @param [Array<String>] acronyms
113
198
  # the acronyms to use to prevent modifications
114
199
  # @param [String] separator
115
- # the separator for linking words, by default hyphen
200
+ # the words separator, by default a hyphen "-"
116
201
  #
117
202
  # @return [String]
118
203
  #
119
204
  # @api public
120
- def kebabcase(string, acronyms: [], separator: "-")
121
- parsecase(string, acronyms: acronyms, sep: separator)
205
+ def kebabcase(string, acronyms: config.acronyms, separator: "-")
206
+ acronyms = Acronyms.from(acronyms)
207
+ parsecase(string, acronyms: acronyms, sep: separator, &:downcase)
122
208
  end
123
- module_function :kebabcase
124
-
125
209
  alias dashcase kebabcase
126
- module_function :dashcase
127
210
 
128
- # Convert string to pascal case:
129
- # * every word has its first character uppercased
130
- # * all words are compounded together
211
+ # Convert string to Pascal case
131
212
  #
132
213
  # @example
133
214
  # pascalcase("foo bar baz") # => "FooBarBaz"
134
215
  #
135
216
  # @param [String] string
136
- # the string to convert to camel case with capital letter
137
- # @param [Array[String]] acronyms
217
+ # the string to convert to Pascal case
218
+ # @param [Array<String>] acronyms
138
219
  # the acronyms to use to prevent modifications
139
220
  # @param [String] separator
140
- # the separator for linking words, by default none
221
+ # the words separator, by default an empty string
222
+ #
223
+ # @return [String]
141
224
  #
142
225
  # @api public
143
- def pascalcase(string, acronyms: [], separator: "")
144
- parsecase(string, acronyms: acronyms, sep: separator, casing: :capitalize)
226
+ def pascalcase(string, acronyms: config.acronyms, separator: "")
227
+ acronyms = Acronyms.from(acronyms)
228
+ parsecase(string, acronyms: acronyms, sep: separator) do |word|
229
+ acronyms.fetch(word) || word.capitalize
230
+ end
145
231
  end
146
- module_function :pascalcase
147
-
148
232
  alias upper_camelcase pascalcase
149
- module_function :upper_camelcase
150
233
 
151
- # Convert string into a file path.
152
- #
153
- # By default uses `/` as a path separator.
234
+ # Convert string to path case
154
235
  #
155
236
  # @example
156
237
  # pathcase("foo bar baz") # => "foo/bar/baz"
157
238
  #
239
+ # @example
158
240
  # pathcase("FooBarBaz") # => "foo/bar/baz"
159
241
  #
160
242
  # @param [String] string
161
- # the string to convert to file path
162
- # @param [Array[String]] acronyms
243
+ # the string to convert to path case
244
+ # @param [Array<String>] acronyms
163
245
  # the acronyms to use to prevent modifications
164
246
  # @param [String] separator
165
- # the separator for linking words, by default `/`
247
+ # the words separator, by default a forward slash "/"
248
+ #
249
+ # @return [String]
166
250
  #
167
251
  # @api public
168
- def pathcase(string, acronyms: [], separator: "/")
169
- parsecase(string, acronyms: acronyms, sep: separator)
252
+ def pathcase(string, acronyms: config.acronyms, separator: "/")
253
+ acronyms = Acronyms.from(acronyms)
254
+ parsecase(string, acronyms: acronyms, sep: separator, &:downcase)
170
255
  end
171
- module_function :pathcase
172
256
 
173
- # Convert string int a sentence
257
+ # Convert string to sentence case
174
258
  #
175
259
  # @example
176
260
  # sentencecase("foo bar baz") # => "Foo bar baz"
177
261
  #
178
262
  # @param [String] string
179
- # the string to convert to sentence
180
- # @param [Array[String]] acronyms
263
+ # the string to convert to sentence case
264
+ # @param [Array<String>] acronyms
181
265
  # the acronyms to use to prevent modifications
182
266
  # @param [String] separator
183
- # the separator for linking words, by default a space
267
+ # the words separator, by default a space
268
+ #
269
+ # @return [String]
184
270
  #
185
271
  # @api public
186
- def sentencecase(string, acronyms: [], separator: " ")
187
- res = parsecase(string, acronyms: acronyms, sep: separator, casing: :downcase)
188
-
189
- return res if res.to_s.empty?
190
-
191
- res[0].upcase + res[1..-1]
272
+ def sentencecase(string, acronyms: config.acronyms, separator: " ")
273
+ acronyms = Acronyms.from(acronyms)
274
+ parsecase(string, acronyms: acronyms, sep: separator) do |word, i|
275
+ acronyms.fetch(word) || (i.zero? ? word.capitalize : word.downcase)
276
+ end
192
277
  end
193
- module_function :sentencecase
194
278
 
195
- # Convert string into a snake_case
279
+ # Convert string to snake case
196
280
  #
197
281
  # @example
198
282
  # snakecase("foo bar baz") # => "foo_bar_baz"
199
283
  #
284
+ # @example
200
285
  # snakecase("ЗдравствуйтеПривет") # => "здравствуйте_привет"
201
286
  #
287
+ # @example
202
288
  # snakecase("HTTPResponse") # => "http_response"
203
289
  #
204
290
  # @param [String] string
205
291
  # the string to convert to snake case
206
- # @param [Array[String]] acronyms
292
+ # @param [Array<String>] acronyms
207
293
  # the acronyms to use to prevent modifications
208
294
  # @param [String] separator
209
- # the separator for linking words, by default `_`
295
+ # the words separator, by default an underscore "_"
296
+ #
297
+ # @return [String]
210
298
  #
211
299
  # @api public
212
- def snakecase(string, acronyms: [], separator: "_")
213
- parsecase(string, acronyms: acronyms, sep: separator)
300
+ def snakecase(string, acronyms: config.acronyms, separator: "_")
301
+ acronyms = Acronyms.from(acronyms)
302
+ parsecase(string, acronyms: acronyms, sep: separator, &:downcase)
214
303
  end
215
- module_function :snakecase
216
-
217
304
  alias underscore snakecase
218
- module_function :underscore
219
305
 
220
- # Convert string into a title case
306
+ # Convert string to title case
221
307
  #
222
308
  # @example
223
309
  # titlecase("foo bar baz") # => "Foo Bar Baz"
224
310
  #
225
311
  # @param [String] string
226
312
  # the string to convert to title case
227
- # @param [Array[String]] acronyms
313
+ # @param [Array<String>] acronyms
228
314
  # the acronyms to use to prevent modifications
229
315
  # @param [String] separator
230
- # the separator for linking words, by default a space
316
+ # the words separator, by default a space
317
+ #
318
+ # @return [String]
231
319
  #
232
320
  # @api public
233
- def titlecase(string, acronyms: [], separator: " ")
234
- parsecase(string, acronyms: acronyms, sep: separator, casing: :capitalize)
321
+ def titlecase(string, acronyms: config.acronyms, separator: " ")
322
+ acronyms = Acronyms.from(acronyms)
323
+ parsecase(string, acronyms: acronyms, sep: separator) do |word|
324
+ acronyms.fetch(word) || word.capitalize
325
+ end
235
326
  end
236
- module_function :titlecase
327
+
328
+ private
237
329
 
238
330
  # Parse string and transform to desired case
239
331
  #
332
+ # @param [String] string
333
+ # the string to convert to a given case
334
+ # @param [Strings::Case::Acronyms] acronyms
335
+ # the acronyms to use to parse words
336
+ # @param [String] sep
337
+ # the words separator, by default an underscore "_"
338
+ #
339
+ # @yield [word, index]
340
+ #
341
+ # @return [String]
342
+ #
240
343
  # @api private
241
- def parsecase(string, acronyms: [], sep: "_", casing: :downcase)
344
+ def parsecase(string, acronyms: nil, sep: "_", &conversion)
242
345
  return if string.nil?
243
346
 
244
- words = split_into_words(string, sep: sep)
245
-
246
- no_case = ->(w) { acronyms.include?(w) ? w.extend(NullCase) : w }
247
-
248
- words
249
- .map(&no_case)
250
- .map(&casing)
251
- .join(sep)
347
+ none_or_index = conversion.arity <= 1 ? :map : :with_index
348
+ split_into_words(string, acronyms: acronyms, sep: sep)
349
+ .map.send(none_or_index, &conversion).join(sep)
252
350
  end
253
- module_function :parsecase
254
- private_class_method :parsecase
255
351
 
256
352
  # Split string into words
257
353
  #
258
- # @return [Array[String]]
354
+ # @param [String] string
355
+ # the string to split into words
356
+ # @param [Strings::Case::Acronyms] acronyms
357
+ # the acronyms to use to split words
358
+ # @param [String] sep
359
+ # the separator to use to split words
360
+ #
361
+ # @return [Array<String>]
259
362
  # the split words
260
363
  #
261
364
  # @api private
262
- def split_into_words(string, sep: nil)
365
+ def split_into_words(string, acronyms: nil, sep: nil)
263
366
  words = []
264
367
  word = []
265
368
  scanner = StringScanner.new(string)
266
369
 
267
370
  while !scanner.eos?
268
- if scanner.match?(UPPERCASE)
269
- char = scanner.getch
270
- if word.size <= 1 # don't allow single letter words
271
- word << char
272
- else
273
- word << char
371
+ if scanner.match?(acronyms.pattern)
372
+ unless word.empty?
274
373
  words << word.join
275
- word = []
374
+ word.clear
276
375
  end
376
+ scanner.scan(acronyms.pattern)
377
+ words << scanner.matched
378
+ elsif scanner.match?(UPPERCASE)
379
+ char = scanner.getch
380
+ word << char
381
+ words << word.join
382
+ word.clear
277
383
  elsif scanner.match?(LOWERCASE)
278
384
  char = scanner.getch
279
- if word.size <= 1 # don't allow single letter words
280
- word << char
281
- else
282
- words << word.join
283
- word = [char]
284
- end
385
+ words << word.join unless word.empty?
386
+ word = [char]
285
387
  elsif scanner.match?(DELIMS)
286
388
  char = scanner.getch
287
389
  words << word.join unless word.empty?
@@ -290,10 +392,8 @@ module Strings
290
392
  elsif scanner.eos? && char == sep
291
393
  word = [""]
292
394
  else
293
- word = []
395
+ word.clear
294
396
  end
295
- elsif scanner.skip(NONALPHAS)
296
- # noop
297
397
  else
298
398
  word << scanner.getch
299
399
  end
@@ -303,6 +403,5 @@ module Strings
303
403
 
304
404
  words
305
405
  end
306
- module_function :split_into_words
307
406
  end # Case
308
407
  end # Strings
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strings-case
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-07 00:00:00.000000000 Z
11
+ date: 2023-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '1.5'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '1.5'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rake
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -42,57 +28,47 @@ dependencies:
42
28
  name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
- - - "~>"
31
+ - - ">="
46
32
  - !ruby/object:Gem::Version
47
33
  version: '3.0'
48
34
  type: :development
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
- - - "~>"
38
+ - - ">="
53
39
  - !ruby/object:Gem::Version
54
40
  version: '3.0'
55
41
  description: Convert strings to different cases
56
42
  email:
57
- - me@piotrmurach.com
43
+ - piotr@piotrmurach.com
58
44
  executables: []
59
45
  extensions: []
60
- extra_rdoc_files: []
46
+ extra_rdoc_files:
47
+ - README.md
48
+ - CHANGELOG.md
49
+ - LICENSE.txt
61
50
  files:
62
51
  - CHANGELOG.md
63
52
  - LICENSE.txt
64
53
  - README.md
65
- - Rakefile
66
54
  - lib/strings-case.rb
67
55
  - lib/strings/case.rb
56
+ - lib/strings/case/acronyms.rb
57
+ - lib/strings/case/configuration.rb
68
58
  - lib/strings/case/extensions.rb
69
59
  - lib/strings/case/version.rb
70
- - spec/perf/parsecase_spec.rb
71
- - spec/spec_helper.rb
72
- - spec/unit/camelcase_spec.rb
73
- - spec/unit/constcase_spec.rb
74
- - spec/unit/extensions_spec.rb
75
- - spec/unit/headercase_spec.rb
76
- - spec/unit/kebabcase_spec.rb
77
- - spec/unit/pascalcase_spec.rb
78
- - spec/unit/pathcase_spec.rb
79
- - spec/unit/sentencecase_spec.rb
80
- - spec/unit/snakecase_spec.rb
81
- - spec/unit/titlecase_spec.rb
82
- - strings-case.gemspec
83
- - tasks/console.rake
84
- - tasks/coverage.rake
85
- - tasks/spec.rake
86
60
  homepage: https://github.com/piotrmurach/strings-case
87
61
  licenses:
88
62
  - MIT
89
63
  metadata:
90
64
  allowed_push_host: https://rubygems.org
65
+ bug_tracker_uri: https://github.com/piotrmurach/strings-case/issues
91
66
  changelog_uri: https://github.com/piotrmurach/strings-case/blob/master/CHANGELOG.md
92
67
  documentation_uri: https://www.rubydoc.info/gems/strings-case
93
68
  homepage_uri: https://github.com/piotrmurach/strings-case
69
+ rubygems_mfa_required: 'true'
94
70
  source_code_uri: https://github.com/piotrmurach/strings-case
95
- post_install_message:
71
+ post_install_message:
96
72
  rdoc_options: []
97
73
  require_paths:
98
74
  - lib
@@ -107,8 +83,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
83
  - !ruby/object:Gem::Version
108
84
  version: '0'
109
85
  requirements: []
110
- rubygems_version: 3.0.6
111
- signing_key:
86
+ rubygems_version: 3.4.10
87
+ signing_key:
112
88
  specification_version: 4
113
89
  summary: Convert strings to different cases
114
90
  test_files: []
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
3
- FileList["tasks/**/*.rake"].each(&method(:import))
4
-
5
- desc "Run all specs"
6
- task ci: %w[ spec ]
7
-
8
- task default: :spec
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rspec-benchmark"
4
- require "active_support"
5
-
6
- RSpec.describe Strings::Case do
7
- include RSpec::Benchmark::Matchers
8
-
9
- it "changes case" do
10
- expect {
11
- Strings::Case.snakecase("fooBarBaz")
12
- }.to perform_slower_than {
13
- ActiveSupport::Inflector.underscore("fooBarBaz")
14
- }.at_most(2).times
15
- end
16
-
17
- it "allocates no more than 100 objects" do
18
- expect {
19
- Strings::Case.snakecase("fooBarBaz")
20
- }.to perform_allocation(25)
21
- end
22
- end