hanami-utils 0.7.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -2
- data/README.md +5 -1
- data/hanami-utils.gemspec +5 -6
- data/lib/hanami-utils.rb +1 -1
- data/lib/hanami/interactor.rb +7 -5
- data/lib/hanami/logger.rb +178 -30
- data/lib/hanami/utils/attributes.rb +11 -2
- data/lib/hanami/utils/basic_object.rb +4 -3
- data/lib/hanami/utils/blank.rb +45 -0
- data/lib/hanami/utils/callbacks.rb +1 -3
- data/lib/hanami/utils/class.rb +34 -3
- data/lib/hanami/utils/class_attribute.rb +5 -1
- data/lib/hanami/utils/deprecation.rb +2 -1
- data/lib/hanami/utils/duplicable.rb +2 -2
- data/lib/hanami/utils/escape.rb +53 -52
- data/lib/hanami/utils/hash.rb +9 -9
- data/lib/hanami/utils/inflector.rb +84 -62
- data/lib/hanami/utils/io.rb +2 -2
- data/lib/hanami/utils/json.rb +51 -0
- data/lib/hanami/utils/kernel.rb +12 -11
- data/lib/hanami/utils/load_paths.rb +4 -2
- data/lib/hanami/utils/path_prefix.rb +4 -3
- data/lib/hanami/utils/string.rb +12 -11
- data/lib/hanami/utils/version.rb +1 -1
- metadata +7 -20
@@ -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
|
-
"#<#{
|
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?(
|
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 =
|
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
|
data/lib/hanami/utils/class.rb
CHANGED
@@ -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 = [
|
90
|
-
raise NameError.new("uninitialized constant #{
|
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
|
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("#{
|
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
|
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
|
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
|
data/lib/hanami/utils/escape.rb
CHANGED
@@ -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
|
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 =
|
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.
|
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
|
-
|
522
|
-
|
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
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
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
|
-
|
550
|
-
|
551
|
-
|
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
|
-
|
554
|
-
hex
|
555
|
-
|
551
|
+
code = char.ord
|
552
|
+
hex = hex_for_non_alphanumeric_code(code)
|
553
|
+
return char if hex.nil?
|
556
554
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
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
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
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
|
data/lib/hanami/utils/hash.rb
CHANGED
@@ -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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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 `#{
|
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
|
-
|
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'
|
199
|
-
'child'
|
200
|
-
'corpus'
|
201
|
-
'foot'
|
202
|
-
'genus'
|
203
|
-
'goose'
|
204
|
-
'
|
205
|
-
'
|
206
|
-
'
|
207
|
-
'
|
208
|
-
'
|
209
|
-
'
|
210
|
-
'
|
211
|
-
'
|
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'
|
214
|
-
'equipment'
|
215
|
-
'fish'
|
216
|
-
'information'
|
217
|
-
'means'
|
218
|
-
'money'
|
219
|
-
'news'
|
220
|
-
'offspring'
|
221
|
-
'rice'
|
222
|
-
'series'
|
223
|
-
'sheep'
|
224
|
-
'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'
|
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'
|
238
|
-
'children'=> 'child',
|
239
|
-
'corpora'
|
240
|
-
'feet'
|
241
|
-
'genera'
|
242
|
-
'geese'
|
243
|
-
'
|
244
|
-
'
|
245
|
-
'
|
246
|
-
'
|
247
|
-
'
|
248
|
-
'
|
249
|
-
'
|
250
|
-
'
|
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
|
-
|
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
|
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
|
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
|