hanami-utils 1.3.7 → 2.0.0.alpha1

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.
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Hanami
4
- module Utils
5
- # Safe dup logic
6
- #
7
- # @since 0.6.0
8
- module Duplicable
9
- # Duplicates the given value.
10
- #
11
- # It accepts a block to customize the logic.
12
- #
13
- # The following types aren't duped:
14
- #
15
- # * <tt>NilClass</tt>
16
- # * <tt>FalseClass</tt>
17
- # * <tt>TrueClass</tt>
18
- # * <tt>Symbol</tt>
19
- # * <tt>Numeric</tt>
20
- #
21
- # All the other types are duped via <tt>#dup</tt>
22
- #
23
- # @param value [Object] the value to duplicate
24
- # @param blk [Proc] the optional block to customize the logic
25
- #
26
- # @return [Object] the duped value
27
- #
28
- # @since 0.6.0
29
- #
30
- # @example Basic Usage With Types That Can't Be Duped
31
- # require 'hanami/utils/duplicable'
32
- #
33
- # object = 23
34
- # puts object.object_id # => 47
35
- #
36
- # result = Hanami::Utils::Duplicable.dup(object)
37
- #
38
- # puts result # => 23
39
- # puts result.object_id # => 47 - Same object, because numbers can't be duped
40
- #
41
- # @example Basic Usage With Types That Can Be Duped
42
- # require 'hanami/utils/duplicable'
43
- #
44
- # object = "hello"
45
- # puts object.object_id # => 70172661782360
46
- #
47
- # result = Hanami::Utils::Duplicable.dup(object)
48
- #
49
- # puts result # => "hello"
50
- # puts result.object_id # => 70172671467020 - Different object
51
- #
52
- # @example Custom Logic
53
- # require 'hanami/utils/duplicable'
54
- # require 'hanami/utils/hash'
55
- #
56
- # hash = { a: 1 }
57
- # puts hash.object_id # => 70207105061680
58
- #
59
- # result = Hanami::Utils::Duplicable.dup(hash) do |value|
60
- # case value
61
- # when Hanami::Utils::Hash
62
- # value.deep_dup
63
- # when ::Hash
64
- # Hanami::Utils::Hash.new(value).deep_dup.to_h
65
- # end
66
- # end
67
- #
68
- # puts result # => "{:a=>1}"
69
- # puts result.object_id # => 70207105185500 - Different object
70
- def self.dup(value, &blk)
71
- case value
72
- when NilClass, FalseClass, TrueClass, Symbol, Numeric
73
- value
74
- when v = blk&.call(value)
75
- v
76
- else
77
- value.dup
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,493 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "hanami/utils/class_attribute"
4
- require "hanami/utils/blank"
5
-
6
- module Hanami
7
- module Utils
8
- # String inflector
9
- #
10
- # @since 0.4.1
11
- module Inflector # rubocop:disable Metrics/ModuleLength
12
- # Rules for irregular plurals
13
- #
14
- # @since 0.6.0
15
- # @api private
16
- class IrregularRules
17
- # @since 0.6.0
18
- # @api private
19
- def initialize(rules)
20
- @rules = rules
21
- end
22
-
23
- # @since 0.6.0
24
- # @api private
25
- def add(key, value)
26
- @rules[key.downcase] = value.downcase
27
- end
28
-
29
- # @since 0.6.0
30
- # @api private
31
- def ===(other)
32
- key = extract_last_alphanumeric_token(other)
33
- @rules.key?(key) || @rules.value?(key)
34
- end
35
-
36
- # @since 0.6.0
37
- # @api private
38
- def apply(string)
39
- key = extract_last_alphanumeric_token(string)
40
- result = @rules[key] || @rules.rassoc(key).last
41
-
42
- prefix = if key == string.downcase
43
- string[0]
44
- else
45
- string[0..string.index(key)]
46
- end
47
-
48
- prefix + result[1..-1]
49
- end
50
-
51
- private
52
-
53
- # @since 1.3.3
54
- # @api private
55
- def extract_last_alphanumeric_token(string)
56
- if string.downcase =~ /_([[:alpha:]]*)\z/
57
- Regexp.last_match(1)
58
- else
59
- string.downcase
60
- end
61
- end
62
- end
63
-
64
- # @since 0.4.1
65
- # @api private
66
- A = "a"
67
-
68
- # @since 0.4.1
69
- # @api private
70
- CH = "ch"
71
-
72
- # @since 0.4.1
73
- # @api private
74
- CHES = "ches"
75
-
76
- # @since 0.4.1
77
- # @api private
78
- EAUX = "eaux"
79
-
80
- # @since 0.6.0
81
- # @api private
82
- ES = "es"
83
-
84
- # @since 0.4.1
85
- # @api private
86
- F = "f"
87
-
88
- # @since 0.4.1
89
- # @api private
90
- I = "i"
91
-
92
- # @since 0.4.1
93
- # @api private
94
- ICE = "ice"
95
-
96
- # @since 0.4.1
97
- # @api private
98
- ICES = "ices"
99
-
100
- # @since 0.4.1
101
- # @api private
102
- IDES = "ides"
103
-
104
- # @since 0.4.1
105
- # @api private
106
- IES = "ies"
107
-
108
- # @since 0.4.1
109
- # @api private
110
- IFE = "ife"
111
-
112
- # @since 0.4.1
113
- # @api private
114
- IS = "is"
115
-
116
- # @since 0.4.1
117
- # @api private
118
- IVES = "ives"
119
-
120
- # @since 0.4.1
121
- # @api private
122
- MA = "ma"
123
-
124
- # @since 0.4.1
125
- # @api private
126
- MATA = "mata"
127
-
128
- # @since 0.4.1
129
- # @api private
130
- MEN = "men"
131
-
132
- # @since 0.4.1
133
- # @api private
134
- MINA = "mina"
135
-
136
- # @since 0.6.0
137
- # @api private
138
- NA = "na"
139
-
140
- # @since 0.6.0
141
- # @api private
142
- NON = "non"
143
-
144
- # @since 0.4.1
145
- # @api private
146
- O = "o"
147
-
148
- # @since 0.4.1
149
- # @api private
150
- OES = "oes"
151
-
152
- # @since 0.4.1
153
- # @api private
154
- OUSE = "ouse"
155
-
156
- # @since 0.4.1
157
- # @api private
158
- RSE = "rse"
159
-
160
- # @since 0.4.1
161
- # @api private
162
- RSES = "rses"
163
-
164
- # @since 0.4.1
165
- # @api private
166
- S = "s"
167
-
168
- # @since 0.4.1
169
- # @api private
170
- SES = "ses"
171
-
172
- # @since 0.4.1
173
- # @api private
174
- SSES = "sses"
175
-
176
- # @since 0.6.0
177
- # @api private
178
- TA = "ta"
179
-
180
- # @since 0.4.1
181
- # @api private
182
- UM = "um"
183
-
184
- # @since 0.4.1
185
- # @api private
186
- US = "us"
187
-
188
- # @since 0.4.1
189
- # @api private
190
- USES = "uses"
191
-
192
- # @since 0.4.1
193
- # @api private
194
- VES = "ves"
195
-
196
- # @since 0.4.1
197
- # @api private
198
- X = "x"
199
-
200
- # @since 0.4.1
201
- # @api private
202
- XES = "xes"
203
-
204
- # @since 0.4.1
205
- # @api private
206
- Y = "y"
207
-
208
- include Utils::ClassAttribute
209
-
210
- # Irregular rules for plurals
211
- #
212
- # @since 0.6.0
213
- # @api private
214
- class_attribute :plurals
215
- self.plurals = IrregularRules.new(
216
- # irregular
217
- "cactus" => "cacti",
218
- "child" => "children",
219
- "corpus" => "corpora",
220
- "foot" => "feet",
221
- "genus" => "genera",
222
- "goose" => "geese",
223
- "louse" => "lice",
224
- "man" => "men",
225
- "mouse" => "mice",
226
- "ox" => "oxen",
227
- "person" => "people",
228
- "quiz" => "quizzes",
229
- "sex" => "sexes",
230
- "testis" => "testes",
231
- "tooth" => "teeth",
232
- "woman" => "women",
233
- # uncountable
234
- "deer" => "deer",
235
- "equipment" => "equipment",
236
- "fish" => "fish",
237
- "information" => "information",
238
- "means" => "means",
239
- "money" => "money",
240
- "news" => "news",
241
- "offspring" => "offspring",
242
- "rice" => "rice",
243
- "series" => "series",
244
- "sheep" => "sheep",
245
- "species" => "species",
246
- "police" => "police",
247
- # regressions
248
- # https://github.com/hanami/utils/issues/106
249
- "album" => "albums",
250
- "area" => "areas"
251
- )
252
-
253
- # Irregular rules for singulars
254
- #
255
- # @since 0.6.0
256
- # @api private
257
- class_attribute :singulars
258
- self.singulars = IrregularRules.new(
259
- # irregular
260
- "cacti" => "cactus",
261
- "children" => "child",
262
- "corpora" => "corpus",
263
- "feet" => "foot",
264
- "genera" => "genus",
265
- "geese" => "goose",
266
- "lice" => "louse",
267
- "men" => "man",
268
- "mice" => "mouse",
269
- "oxen" => "ox",
270
- "people" => "person",
271
- "quizzes" => "quiz",
272
- "sexes" => "sex",
273
- "testes" => "testis",
274
- "teeth" => "tooth",
275
- "women" => "woman",
276
- # uncountable
277
- "deer" => "deer",
278
- "equipment" => "equipment",
279
- "fish" => "fish",
280
- "information" => "information",
281
- "means" => "means",
282
- "money" => "money",
283
- "news" => "news",
284
- "offspring" => "offspring",
285
- "rice" => "rice",
286
- "series" => "series",
287
- "sheep" => "sheep",
288
- "species" => "species",
289
- "police" => "police",
290
- # fallback
291
- "areas" => "area",
292
- "hives" => "hive",
293
- "phases" => "phase",
294
- "exercises" => "exercise",
295
- "releases" => "release"
296
- )
297
-
298
- # Block for custom inflection rules.
299
- #
300
- # @param [Proc] blk custom inflections
301
- #
302
- # @since 0.6.0
303
- #
304
- # @see Hanami::Utils::Inflector.exception
305
- # @see Hanami::Utils::Inflector.uncountable
306
- #
307
- # @example
308
- # require 'hanami/utils/inflector'
309
- #
310
- # Hanami::Utils::Inflector.inflections do
311
- # exception 'analysis', 'analyses'
312
- # exception 'alga', 'algae'
313
- # uncountable 'music', 'butter'
314
- # end
315
- def self.inflections(&blk)
316
- class_eval(&blk)
317
- end
318
-
319
- # Adds a custom inflection exception
320
- #
321
- # @param [String] singular form
322
- # @param [String] plural form
323
- #
324
- # @since 0.6.0
325
- #
326
- # @see Hanami::Utils::Inflector.inflections
327
- # @see Hanami::Utils::Inflector.uncountable
328
- #
329
- # @example
330
- # require 'hanami/utils/inflector'
331
- #
332
- # Hanami::Utils::Inflector.inflections do
333
- # exception 'alga', 'algae'
334
- # end
335
- def self.exception(singular, plural)
336
- add_to_inflecto(singular, plural)
337
- singulars.add(plural, singular)
338
- plurals.add(singular, plural)
339
- end
340
-
341
- # Since ROM uses Inflecto for it inferences, we need to add an exception to it
342
- # when one is registered against our Inflector.
343
- # @api private
344
- def self.add_to_inflecto(singular, plural)
345
- return unless defined? Inflecto
346
-
347
- Inflecto.inflections.irregular(singular, plural)
348
- end
349
-
350
- # Adds an uncountable word
351
- #
352
- # @param [Array<String>] words
353
- #
354
- # @since 0.6.0
355
- #
356
- # @see Hanami::Utils::Inflector.inflections
357
- # @see Hanami::Utils::Inflector.exception
358
- #
359
- # @example
360
- # require 'hanami/utils/inflector'
361
- #
362
- # Hanami::Utils::Inflector.inflections do
363
- # uncountable 'music', 'art'
364
- # end
365
- def self.uncountable(*words)
366
- Array(words).each do |word|
367
- exception(word, word)
368
- end
369
- end
370
-
371
- # Pluralizes the given string
372
- #
373
- # @param string [String] a string to pluralize
374
- #
375
- # @return [String,NilClass] the pluralized string, if present
376
- #
377
- # @api private
378
- # @since 0.4.1
379
- #
380
- # rubocop:disable Metrics/AbcSize
381
- # rubocop:disable Metrics/CyclomaticComplexity
382
- # rubocop:disable Style/PerlBackrefs
383
- def self.pluralize(string)
384
- return string if string.nil? || string =~ Utils::Blank::STRING_MATCHER
385
-
386
- case string
387
- when plurals
388
- plurals.apply(string)
389
- when /\A((.*)[^aeiou])ch\z/
390
- $1 + CHES
391
- when /\A((.*)[^aeiou])y\z/
392
- $1 + IES
393
- when /\A(.*)(ex|ix)\z/
394
- $1 + ICES
395
- when /\A(.*)(eau|#{EAUX})\z/
396
- $1 + EAUX
397
- when /\A(.*)x\z/
398
- $1 + XES
399
- when /\A(.*)ma\z/
400
- string + TA
401
- when /\A(.*)(um|#{A})\z/
402
- $1 + A
403
- when /\A(buffal|domin|ech|embarg|her|mosquit|potat|tomat)#{O}\z/i
404
- $1 + OES
405
- when /\A(.*)(fee)\z/
406
- $1 + $2 + S
407
- when /\A(.*)(?:([^f]))f[e]*\z/
408
- $1 + $2 + VES
409
- when /\A(.*)us\z/
410
- $1 + USES
411
- when /\A(.*)non\z/
412
- $1 + NA
413
- when /\A((.*)[^aeiou])is\z/
414
- $1 + ES
415
- when /\A(.*)ss\z/
416
- $1 + SSES
417
- when /s\z/
418
- string
419
- else
420
- string + S
421
- end
422
- end
423
- # rubocop:enable Style/PerlBackrefs
424
- # rubocop:enable Metrics/AbcSize
425
- # rubocop:enable Metrics/CyclomaticComplexity
426
-
427
- # Singularizes the given string
428
- #
429
- # @param string [String] a string to singularize
430
- #
431
- # @return [String,NilClass] the singularized string, if present
432
- #
433
- # @api private
434
- # @since 0.4.1
435
- #
436
- # rubocop:disable Metrics/AbcSize
437
- # rubocop:disable Metrics/CyclomaticComplexity
438
- # rubocop:disable Metrics/PerceivedComplexity
439
- # rubocop:disable Style/PerlBackrefs
440
- def self.singularize(string)
441
- return string if string.nil? || string =~ Utils::Blank::STRING_MATCHER
442
-
443
- case string
444
- when singulars
445
- singulars.apply(string)
446
- when /\A.*[^aeiou]#{CHES}\z/
447
- string.sub(CHES, CH)
448
- when /\A.*[^aeiou]#{IES}\z/
449
- string.sub(IES, Y)
450
- when /\A.*#{EAUX}\z/
451
- string.chop
452
- when /\A(.*)#{IDES}\z/
453
- $1 + IS
454
- when /\A(.*)#{US}\z/
455
- $1 + I
456
- when /\A(.*)#{RSES}\z/
457
- $1 + RSE
458
- when /\A(.*)#{SES}\z/
459
- $1 + S
460
- when /\A(.*)#{MATA}\z/
461
- $1 + MA
462
- when /\A(.*)#{OES}\z/
463
- $1 + O
464
- when /\A(.*)#{MINA}\z/
465
- $1 + MEN
466
- when /\A(.*)#{XES}\z/
467
- $1 + X
468
- when /\A(.*)#{IVES}\z/
469
- $1 + IFE
470
- when /\A(.*)#{VES}\z/
471
- $1 + F
472
- when /\A(.*)#{I}\z/
473
- $1 + US
474
- when /\A(.*)ae\z/
475
- $1 + A
476
- when /\A(.*)na\z/
477
- $1 + NON
478
- when /\A(.*)#{A}\z/
479
- $1 + UM
480
- when /[^s]\z/
481
- string
482
- else
483
- string.chop
484
- end
485
- end
486
- # rubocop:enable Style/PerlBackrefs
487
- # rubocop:enable Metrics/AbcSize
488
- # rubocop:enable Metrics/CyclomaticComplexity
489
-
490
- # rubocop:enable Metrics/PerceivedComplexity
491
- end
492
- end
493
- end