hanami-utils 0.7.2 → 0.8.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.
@@ -10,7 +10,7 @@ module Hanami
10
10
  #
11
11
  # @see http://ruby-doc.org/core/Object.html#method-i-class
12
12
  def class
13
- (class << self; self end).superclass
13
+ (class << self; self; end).superclass
14
14
  end
15
15
 
16
16
  # Bare minimum inspect for debugging purposes.
@@ -21,7 +21,7 @@ module Hanami
21
21
  #
22
22
  # @see http://ruby-doc.org/core/Object.html#method-i-inspect
23
23
  def inspect
24
- "#<#{ self.class }:#{'%x' % (__id__ << 1)}#{ __inspect }>"
24
+ "#<#{self.class}:#{'%x' % (__id__ << 1)}#{__inspect}>" # rubocop:disable Style/FormatString
25
25
  end
26
26
 
27
27
  # Returns true if responds to the given method.
@@ -36,11 +36,12 @@ module Hanami
36
36
  end
37
37
 
38
38
  private
39
+
39
40
  # Must be overridden by descendants
40
41
  #
41
42
  # @since 0.3.5
42
43
  # @api private
43
- def respond_to_missing?(method_name, include_all)
44
+ def respond_to_missing?(_method_name, _include_all)
44
45
  ::Kernel.raise ::NotImplementedError
45
46
  end
46
47
 
@@ -0,0 +1,45 @@
1
+ module Hanami
2
+ module Utils
3
+ # Checks for blank
4
+ # @since 0.8.0
5
+ class Blank
6
+ # Matcher for blank strings
7
+ #
8
+ # @since 0.8.0
9
+ # @api private
10
+ STRING_MATCHER = /\A[[:space:]]*\z/
11
+
12
+ # Checks object is blank
13
+ #
14
+ # @example Basic Usage
15
+ # require 'hanami/utils/blank'
16
+ #
17
+ # Hanami::Utils::Blank.blank?(Hanami::Utils::String.new('')) # => true
18
+ # Hanami::Utils::Blank.blank?(' ') # => true
19
+ # Hanami::Utils::Blank.blank?(nil) # => true
20
+ # Hanami::Utils::Blank.blank?(Hanami::Utils::Hash.new({})) # => true
21
+ # Hanami::Utils::Blank.blank?(true) # => false
22
+ # Hanami::Utils::Blank.blank?(1) # => false
23
+ #
24
+ # @param object the argument
25
+ #
26
+ # @return [TrueClass,FalseClass]
27
+ #
28
+ # @since 0.8.0
29
+ def self.blank?(object) # rubocop:disable Metrics/MethodLength
30
+ case object
31
+ when String, ::String
32
+ STRING_MATCHER === object # rubocop:disable Style/CaseEquality
33
+ when Hash, ::Hash, ::Array
34
+ object.empty?
35
+ when TrueClass, Numeric
36
+ false
37
+ when FalseClass, NilClass
38
+ true
39
+ else
40
+ object.respond_to?(:empty?) ? !!object.empty? : !self
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -16,7 +16,7 @@ module Hanami
16
16
  #
17
17
  # @since 0.2.0
18
18
  def initialize
19
- @chain = Array.new
19
+ @chain = []
20
20
  end
21
21
 
22
22
  # Appends the given callbacks to the end of the chain.
@@ -168,14 +168,12 @@ module Hanami
168
168
  @chain.freeze
169
169
  end
170
170
 
171
-
172
171
  private
173
172
 
174
173
  def callables(callbacks, block)
175
174
  callbacks.push(block) if block
176
175
  callbacks.map { |c| Factory.fabricate(c) }
177
176
  end
178
-
179
177
  end
180
178
 
181
179
  # Callback factory
@@ -41,6 +41,37 @@ module Hanami
41
41
  namespace.const_get(name.to_s)
42
42
  end
43
43
 
44
+ # Loads a class for the given name, only if it's defined.
45
+ #
46
+ # @param name [String, Class] the specific class name
47
+ # @param namespace [Class, Module] the Ruby namespace where we want to perform the lookup.
48
+ # @return [Class, Module, NilClass] the Ruby constant, or nil if not found.
49
+ #
50
+ # @since 0.8.0
51
+ #
52
+ # @example
53
+ # require 'hanami/utils/class'
54
+ #
55
+ # module App
56
+ # module Service
57
+ # class Endpoint
58
+ # end
59
+ # end
60
+ #
61
+ # class ServiceEndpoint
62
+ # end
63
+ # end
64
+ #
65
+ # # basic usage
66
+ # Hanami::Utils::Class.load('App::Service') # => App::Service
67
+ # Hanami::Utils::Class.load(App::Service) # => App::Service
68
+ #
69
+ # # with explicit namespace
70
+ # Hanami::Utils::Class.load('Service', App) # => App::Service
71
+ def self.load(name, namespace = Object)
72
+ load!(name, namespace) if namespace.const_defined?(name.to_s)
73
+ end
74
+
44
75
  # Loads a class from the given pattern name and namespace
45
76
  #
46
77
  # @param pattern [String] the class name pattern
@@ -82,12 +113,12 @@ module Hanami
82
113
  String.new(pattern).tokenize do |token|
83
114
  begin
84
115
  return namespace.const_get(token)
85
- rescue NameError
116
+ rescue NameError # rubocop:disable Lint/HandleExceptions
86
117
  end
87
118
  end
88
119
 
89
- full_name = [ (namespace == Object ? nil : namespace), pattern ].compact.join('::')
90
- raise NameError.new("uninitialized constant #{ full_name }")
120
+ full_name = [(namespace == Object ? nil : namespace), pattern].compact.join('::')
121
+ raise NameError.new("uninitialized constant #{full_name}")
91
122
  end
92
123
  end
93
124
  end
@@ -12,6 +12,8 @@ module Hanami
12
12
  base.extend ClassMethods
13
13
  end
14
14
 
15
+ # @since 0.1.0
16
+ # @api private
15
17
  module ClassMethods
16
18
  # Defines a class level accessor for the given attribute(s).
17
19
  #
@@ -64,13 +66,14 @@ module Hanami
64
66
  # SmallAirplane.wheels # => 8
65
67
  def class_attribute(*attributes)
66
68
  singleton_class.class_eval do
67
- attr_accessor *attributes
69
+ attr_accessor(*attributes)
68
70
  end
69
71
 
70
72
  class_attributes.merge(attributes)
71
73
  end
72
74
 
73
75
  protected
76
+
74
77
  # @see Class#inherited
75
78
  def inherited(subclass)
76
79
  class_attributes.each do |attr|
@@ -84,6 +87,7 @@ module Hanami
84
87
  end
85
88
 
86
89
  private
90
+
87
91
  # Class accessor for class attributes.
88
92
  # @private
89
93
  def class_attributes
@@ -58,10 +58,11 @@ module Hanami
58
58
  # # => old_method is deprecated, please use new_method - called from: test.rb:20:in `start'.
59
59
  # # => started
60
60
  def initialize(message)
61
- ::Kernel.warn("#{ message } - called from: #{ caller[caller_index] }.")
61
+ ::Kernel.warn("#{message} - called from: #{caller[caller_index]}.")
62
62
  end
63
63
 
64
64
  private
65
+
65
66
  def caller_index
66
67
  Utils.jruby? || Utils.rubinius? ? 1 : 2
67
68
  end
@@ -45,7 +45,7 @@ module Hanami
45
45
  # result = Hanami::Utils::Duplicable.dup(object)
46
46
  #
47
47
  # puts result # => "hello"
48
- # puts result.object_id # => 70172671467020 Different object
48
+ # puts result.object_id # => 70172671467020 - Different object
49
49
  #
50
50
  # @example Custom Logic
51
51
  # require 'hanami/utils/duplicable'
@@ -64,7 +64,7 @@ module Hanami
64
64
  # end
65
65
  #
66
66
  # puts result # => "{:a=>1}"
67
- # puts result.object_id # => 70207105185500 Different object
67
+ # puts result.object_id # => 70207105185500 - Different object
68
68
  def self.dup(value, &blk)
69
69
  case value
70
70
  when NilClass, FalseClass, TrueClass, Symbol, Numeric
@@ -11,7 +11,7 @@ module Hanami
11
11
  # @see https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
12
12
  # @see https://www.owasp.org/index.php/ESAPI
13
13
  # @see https://github.com/ESAPI/esapi-java-legacy
14
- module Escape
14
+ module Escape # rubocop:disable Metrics/ModuleLength
15
15
  # Hex base for base 10 integer conversion
16
16
  #
17
17
  # @since 0.4.0
@@ -30,14 +30,14 @@ module Hanami
30
30
  #
31
31
  # @since 0.4.0
32
32
  # @api private
33
- REPLACEMENT_HEX = "fffd".freeze
33
+ REPLACEMENT_HEX = 'fffd'.freeze
34
34
 
35
35
  # Low hex codes lookup table
36
36
  #
37
37
  # @since 0.4.0
38
38
  # @api private
39
39
  HEX_CODES = (0..255).each_with_object({}) do |c, codes|
40
- if (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A)
40
+ if (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A) # rubocop:disable Style/ConditionalAssignment
41
41
  codes[c] = nil
42
42
  else
43
43
  codes[c] = c.to_s(HEX_BASE)
@@ -362,7 +362,7 @@ module Hanami
362
362
  # @api private
363
363
  #
364
364
  # @see Hanami::Utils::Escape.url
365
- DEFAULT_URL_SCHEMES = ['http', 'https', 'mailto'].freeze
365
+ DEFAULT_URL_SCHEMES = %w(http https mailto).freeze
366
366
 
367
367
  # The output of an escape.
368
368
  #
@@ -511,65 +511,66 @@ module Hanami
511
511
  return input if input.is_a?(SafeString)
512
512
 
513
513
  SafeString.new(
514
- URI.extract(
515
- URI.decode(input),
514
+ URI::Parser.new.extract(
515
+ URI.decode_www_form_component(input),
516
516
  schemes
517
517
  ).first.to_s
518
518
  )
519
519
  end
520
520
 
521
- private
522
- # Encode the given string into UTF-8
523
- #
524
- # @param input [String] the input
525
- #
526
- # @return [String] an UTF-8 encoded string
527
- #
528
- # @since 0.4.0
529
- # @api private
530
- def self.encode(input)
531
- return '' if input.nil?
532
- input.to_s.encode(Encoding::UTF_8)
533
- rescue Encoding::UndefinedConversionError
534
- input.dup.force_encoding(Encoding::UTF_8)
535
- end
521
+ class << self
522
+ private
536
523
 
537
- # Encode the given UTF-8 char.
538
- #
539
- # @param char [String] an UTF-8 char
540
- # @param safe_chars [Hash] a table of safe chars
541
- #
542
- # @return [String] an HTML encoded string
543
- #
544
- # @since 0.4.0
545
- # @api private
546
- def self.encode_char(char, safe_chars = {})
547
- return char if safe_chars[char]
524
+ # Encode the given string into UTF-8
525
+ #
526
+ # @param input [String] the input
527
+ #
528
+ # @return [String] an UTF-8 encoded string
529
+ #
530
+ # @since 0.4.0
531
+ # @api private
532
+ def encode(input)
533
+ return '' if input.nil?
534
+ input.to_s.encode(Encoding::UTF_8)
535
+ rescue Encoding::UndefinedConversionError
536
+ input.dup.force_encoding(Encoding::UTF_8)
537
+ end
548
538
 
549
- code = char.ord
550
- hex = hex_for_non_alphanumeric_code(code)
551
- return char if hex.nil?
539
+ # Encode the given UTF-8 char.
540
+ #
541
+ # @param char [String] an UTF-8 char
542
+ # @param safe_chars [Hash] a table of safe chars
543
+ #
544
+ # @return [String] an HTML encoded string
545
+ #
546
+ # @since 0.4.0
547
+ # @api private
548
+ def encode_char(char, safe_chars = {})
549
+ return char if safe_chars[char]
552
550
 
553
- if NON_PRINTABLE_CHARS[code]
554
- hex = REPLACEMENT_HEX
555
- end
551
+ code = char.ord
552
+ hex = hex_for_non_alphanumeric_code(code)
553
+ return char if hex.nil?
556
554
 
557
- if entity = HTML_ENTITIES[code]
558
- "&#{ entity };"
559
- else
560
- "&#x#{ hex };"
555
+ hex = REPLACEMENT_HEX if NON_PRINTABLE_CHARS[code]
556
+
557
+ if entity = HTML_ENTITIES[code] # rubocop:disable Lint/AssignmentInCondition
558
+ "&#{entity};"
559
+ else
560
+ "&#x#{hex};"
561
+ end
561
562
  end
562
- end
563
563
 
564
- # Transforms the given char code
565
- #
566
- # @since 0.4.0
567
- # @api private
568
- def self.hex_for_non_alphanumeric_code(input)
569
- if input < LOW_HEX_CODE_LIMIT
570
- HEX_CODES[input]
571
- else
572
- input.to_s(HEX_BASE)
564
+ # Transforms the given char code
565
+ #
566
+ # @since 0.4.0
567
+ # @api private
568
+ def hex_for_non_alphanumeric_code(input)
569
+ if input < LOW_HEX_CODE_LIMIT
570
+ HEX_CODES[input]
571
+ else
572
+ input.to_s(HEX_BASE)
573
+ end
573
574
  end
574
575
  end
575
576
  end
@@ -10,7 +10,7 @@ module Hanami
10
10
  #
11
11
  # @see Hanami::Utils::Hash#deep_dup
12
12
  # @see Hanami::Utils::Duplicable
13
- DUPLICATE_LOGIC = Proc.new do |value|
13
+ DUPLICATE_LOGIC = proc do |value|
14
14
  case value
15
15
  when Hash
16
16
  value.deep_dup
@@ -65,7 +65,7 @@ module Hanami
65
65
  def symbolize!
66
66
  keys.each do |k|
67
67
  v = delete(k)
68
- v = Hash.new(v).symbolize! if v.is_a?(::Hash)
68
+ v = self.class.new(v).symbolize! if v.is_a?(::Hash) || v.is_a?(self.class)
69
69
 
70
70
  self[k.to_sym] = v
71
71
  end
@@ -90,7 +90,7 @@ module Hanami
90
90
  def stringify!
91
91
  keys.each do |k|
92
92
  v = delete(k)
93
- v = Hash.new(v).stringify! if v.is_a?(::Hash)
93
+ v = self.class.new(v).stringify! if v.is_a?(::Hash) || v.is_a?(self.class)
94
94
 
95
95
  self[k.to_s] = v
96
96
  end
@@ -160,8 +160,8 @@ module Hanami
160
160
  # # it deeply duplicates Hanami::Utils::Hash, by preserving the class
161
161
  # duped['u_hash'].class # => Hanami::Utils::Hash
162
162
  def deep_dup
163
- Hash.new.tap do |result|
164
- @hash.each {|k, v| result[k] = Duplicable.dup(v, &DUPLICATE_LOGIC) }
163
+ self.class.new.tap do |result|
164
+ @hash.each { |k, v| result[k] = Duplicable.dup(v, &DUPLICATE_LOGIC) }
165
165
  end
166
166
  end
167
167
 
@@ -229,7 +229,7 @@ module Hanami
229
229
  end
230
230
  end
231
231
 
232
- alias_method :to_hash, :to_h
232
+ alias to_hash to_h
233
233
 
234
234
  # Converts into a nested array of [ key, value ] arrays.
235
235
  #
@@ -251,7 +251,7 @@ module Hanami
251
251
  @hash == other.to_h
252
252
  end
253
253
 
254
- alias_method :eql?, :==
254
+ alias eql? ==
255
255
 
256
256
  # Returns the hash of the internal @hash
257
257
  #
@@ -283,7 +283,7 @@ module Hanami
283
283
  h = self.class.new(h) if h.is_a?(::Hash)
284
284
  h
285
285
  else
286
- raise NoMethodError.new(%(undefined method `#{ m }' for #{ @hash }:#{ self.class }))
286
+ raise NoMethodError.new(%(undefined method `#{m}' for #{@hash}:#{self.class}))
287
287
  end
288
288
  end
289
289
 
@@ -291,7 +291,7 @@ module Hanami
291
291
  #
292
292
  # @api private
293
293
  # @since 0.3.0
294
- def respond_to_missing?(m, include_private=false)
294
+ def respond_to_missing?(m, include_private = false)
295
295
  @hash.respond_to?(m, include_private)
296
296
  end
297
297
  end
@@ -1,11 +1,14 @@
1
1
  require 'hanami/utils/class_attribute'
2
+ require 'hanami/utils/blank'
2
3
 
3
4
  module Hanami
4
5
  module Utils
5
6
  # String inflector
6
7
  #
7
8
  # @since 0.4.1
8
- module Inflector
9
+ #
10
+ # rubocop:disable Style/PerlBackrefs
11
+ module Inflector # rubocop:disable Metrics/ModuleLength
9
12
  # Rules for irregular plurals
10
13
  #
11
14
  # @since 0.6.0
@@ -40,12 +43,6 @@ module Hanami
40
43
  end
41
44
  end
42
45
 
43
- # Matcher for blank strings
44
- #
45
- # @since 0.4.1
46
- # @api private
47
- BLANK_STRING_MATCHER = /\A[[:space:]]*\z/.freeze
48
-
49
46
  # @since 0.4.1
50
47
  # @api private
51
48
  A = 'a'.freeze
@@ -142,6 +139,14 @@ module Hanami
142
139
  # @api private
143
140
  OUSE = 'ouse'.freeze
144
141
 
142
+ # @since 0.4.1
143
+ # @api private
144
+ RSE = 'rse'.freeze
145
+
146
+ # @since 0.4.1
147
+ # @api private
148
+ RSES = 'rses'.freeze
149
+
145
150
  # @since 0.4.1
146
151
  # @api private
147
152
  S = 's'.freeze
@@ -193,61 +198,66 @@ module Hanami
193
198
  # @since 0.6.0
194
199
  # @api private
195
200
  class_attribute :plurals
196
- self.plurals = IrregularRules.new({
201
+ self.plurals = IrregularRules.new(
197
202
  # irregular
198
- 'cactus' => 'cacti',
199
- 'child' => 'children',
200
- 'corpus' => 'corpora',
201
- 'foot' => 'feet',
202
- 'genus' => 'genera',
203
- 'goose' => 'geese',
204
- 'man' => 'men',
205
- 'ox' => 'oxen',
206
- 'person' => 'people',
207
- 'quiz' => 'quizzes',
208
- 'sex' => 'sexes',
209
- 'testis' => 'testes',
210
- 'tooth' => 'teeth',
211
- 'woman' => 'women',
203
+ 'cactus' => 'cacti',
204
+ 'child' => 'children',
205
+ 'corpus' => 'corpora',
206
+ 'foot' => 'feet',
207
+ 'genus' => 'genera',
208
+ 'goose' => 'geese',
209
+ 'louse' => 'lice',
210
+ 'man' => 'men',
211
+ 'mouse' => 'mice',
212
+ 'ox' => 'oxen',
213
+ 'person' => 'people',
214
+ 'quiz' => 'quizzes',
215
+ 'sex' => 'sexes',
216
+ 'testis' => 'testes',
217
+ 'tooth' => 'teeth',
218
+ 'woman' => 'women',
212
219
  # uncountable
213
- 'deer' => 'deer',
214
- 'equipment' => 'equipment',
215
- 'fish' => 'fish',
216
- 'information' => 'information',
217
- 'means' => 'means',
218
- 'money' => 'money',
219
- 'news' => 'news',
220
- 'offspring' => 'offspring',
221
- 'rice' => 'rice',
222
- 'series' => 'series',
223
- 'sheep' => 'sheep',
224
- 'species' => 'species',
220
+ 'deer' => 'deer',
221
+ 'equipment' => 'equipment',
222
+ 'fish' => 'fish',
223
+ 'information' => 'information',
224
+ 'means' => 'means',
225
+ 'money' => 'money',
226
+ 'news' => 'news',
227
+ 'offspring' => 'offspring',
228
+ 'rice' => 'rice',
229
+ 'series' => 'series',
230
+ 'sheep' => 'sheep',
231
+ 'species' => 'species',
232
+ 'police' => 'police',
225
233
  # regressions
226
234
  # https://github.com/hanami/utils/issues/106
227
- 'album' => 'albums',
228
- })
235
+ 'album' => 'albums'
236
+ )
229
237
 
230
238
  # Irregular rules for singulars
231
239
  #
232
240
  # @since 0.6.0
233
241
  # @api private
234
242
  class_attribute :singulars
235
- self.singulars = IrregularRules.new({
243
+ self.singulars = IrregularRules.new(
236
244
  # irregular
237
- 'cacti' => 'cactus',
238
- 'children'=> 'child',
239
- 'corpora' => 'corpus',
240
- 'feet' => 'foot',
241
- 'genera' => 'genus',
242
- 'geese' => 'goose',
243
- 'men' => 'man',
244
- 'oxen' => 'ox',
245
- 'people' => 'person',
246
- 'quizzes' => 'quiz',
247
- 'sexes' => 'sex',
248
- 'testes' => 'testis',
249
- 'teeth' => 'tooth',
250
- 'women' => 'woman',
245
+ 'cacti' => 'cactus',
246
+ 'children' => 'child',
247
+ 'corpora' => 'corpus',
248
+ 'feet' => 'foot',
249
+ 'genera' => 'genus',
250
+ 'geese' => 'goose',
251
+ 'lice' => 'louse',
252
+ 'men' => 'man',
253
+ 'mice' => 'mouse',
254
+ 'oxen' => 'ox',
255
+ 'people' => 'person',
256
+ 'quizzes' => 'quiz',
257
+ 'sexes' => 'sex',
258
+ 'testes' => 'testis',
259
+ 'teeth' => 'tooth',
260
+ 'women' => 'woman',
251
261
  # uncountable
252
262
  'deer' => 'deer',
253
263
  'equipment' => 'equipment',
@@ -263,9 +273,8 @@ module Hanami
263
273
  'species' => 'species',
264
274
  'police' => 'police',
265
275
  # fallback
266
- 'hives' => 'hive',
267
- 'horses' => 'horse',
268
- })
276
+ 'hives' => 'hive'
277
+ )
269
278
 
270
279
  # Block for custom inflection rules.
271
280
  #
@@ -338,8 +347,12 @@ module Hanami
338
347
  #
339
348
  # @api private
340
349
  # @since 0.4.1
350
+ #
351
+ # rubocop:disable Metrics/AbcSize
352
+ # rubocop:disable Metrics/CyclomaticComplexity
353
+ # rubocop:disable Metrics/MethodLength
341
354
  def self.pluralize(string)
342
- return string if string.nil? || string.match(BLANK_STRING_MATCHER)
355
+ return string if string.nil? || string =~ Utils::Blank::STRING_MATCHER
343
356
 
344
357
  case string
345
358
  when plurals
@@ -358,8 +371,6 @@ module Hanami
358
371
  string + TA
359
372
  when /\A(.*)(um|#{ A })\z/
360
373
  $1 + A
361
- when /\A(.*)(ouse|#{ ICE })\z/
362
- $1 + ICE
363
374
  when /\A(buffal|domin|ech|embarg|her|mosquit|potat|tomat)#{ O }\z/i
364
375
  $1 + OES
365
376
  when /\A(.*)(en|#{ INA })\z/
@@ -380,6 +391,9 @@ module Hanami
380
391
  string + S
381
392
  end
382
393
  end
394
+ # rubocop:enable Metrics/AbcSize
395
+ # rubocop:enable Metrics/CyclomaticComplexity
396
+ # rubocop:enable Metrics/MethodLength
383
397
 
384
398
  # Singularize the given string
385
399
  #
@@ -389,8 +403,13 @@ module Hanami
389
403
  #
390
404
  # @api private
391
405
  # @since 0.4.1
406
+ #
407
+ # rubocop:disable Metrics/AbcSize
408
+ # rubocop:disable Metrics/CyclomaticComplexity
409
+ # rubocop:disable Metrics/MethodLength
410
+ # rubocop:disable Metrics/PerceivedComplexity
392
411
  def self.singularize(string)
393
- return string if string.nil? || string.match(BLANK_STRING_MATCHER)
412
+ return string if string.nil? || string =~ Utils::Blank::STRING_MATCHER
394
413
 
395
414
  case string
396
415
  when singulars
@@ -399,18 +418,16 @@ module Hanami
399
418
  string.sub(CHES, CH)
400
419
  when /\A.*[^aeiou]#{IES}\z/
401
420
  string.sub(IES, Y)
402
- when /\A(.*)#{ICE}\z/
403
- $1 + OUSE
404
421
  when /\A.*#{EAUX}\z/
405
422
  string.chop
406
423
  when /\A(.*)#{IDES}\z/
407
424
  $1 + IS
408
425
  when /\A(.*)#{US}\z/
409
426
  $1 + I
427
+ when /\A(.*)#{RSES}\z/
428
+ $1 + RSE
410
429
  when /\A(.*)#{SES}\z/
411
430
  $1 + S
412
- when /\A(.*)#{OUSE}\z/
413
- $1 + ICE
414
431
  when /\A(.*)#{MATA}\z/
415
432
  $1 + MA
416
433
  when /\A(.*)#{OES}\z/
@@ -437,6 +454,11 @@ module Hanami
437
454
  string.chop
438
455
  end
439
456
  end
457
+ # rubocop:enable Metrics/AbcSize
458
+ # rubocop:enable Metrics/CyclomaticComplexity
459
+ # rubocop:enable Metrics/MethodLength
460
+ # rubocop:enable Metrics/PerceivedComplexity
440
461
  end
462
+ # rubocop:enable Style/PerlBackrefs
441
463
  end
442
464
  end