strings-inflection 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +424 -0
- data/Rakefile +8 -0
- data/lib/strings-inflection.rb +1 -0
- data/lib/strings/inflection.rb +202 -0
- data/lib/strings/inflection/combined_noun.rb +70 -0
- data/lib/strings/inflection/configuration.rb +84 -0
- data/lib/strings/inflection/extensions.rb +33 -0
- data/lib/strings/inflection/noun.rb +70 -0
- data/lib/strings/inflection/nouns.rb +715 -0
- data/lib/strings/inflection/parser.rb +99 -0
- data/lib/strings/inflection/term.rb +78 -0
- data/lib/strings/inflection/verb.rb +54 -0
- data/lib/strings/inflection/verbs.rb +130 -0
- data/lib/strings/inflection/version.rb +7 -0
- data/strings-inflection.gemspec +32 -0
- data/tasks/console.rake +11 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +34 -0
- metadata +111 -0
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "strings/inflection"
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "inflection/combined_noun"
|
3
|
+
require_relative "inflection/configuration"
|
4
|
+
require_relative "inflection/noun"
|
5
|
+
require_relative "inflection/parser"
|
6
|
+
require_relative "inflection/verb"
|
7
|
+
require_relative "inflection/version"
|
8
|
+
|
9
|
+
module Strings
|
10
|
+
module Inflection
|
11
|
+
# Error raised by this inflector
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
# Create a noun object
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def Noun(word)
|
18
|
+
Noun[word]
|
19
|
+
end
|
20
|
+
module_function :Noun
|
21
|
+
|
22
|
+
# Create a verb object
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def Verb(word)
|
26
|
+
Verb[word]
|
27
|
+
end
|
28
|
+
module_function :Verb
|
29
|
+
|
30
|
+
# A configuration object
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def configuration
|
34
|
+
@configuration ||= Configuration.new
|
35
|
+
end
|
36
|
+
module_function :configuration
|
37
|
+
|
38
|
+
# Reset configuration and remove loaded inflections
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def reset(scope = :all)
|
42
|
+
configuration.reset(scope)
|
43
|
+
end
|
44
|
+
module_function :reset
|
45
|
+
|
46
|
+
# Configure custom inflections
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# configure do |config|
|
50
|
+
# config.plural "index", "indexes"
|
51
|
+
# config.singular "axes", "ax"
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
def configure
|
56
|
+
if block_given?
|
57
|
+
yield configuration
|
58
|
+
else
|
59
|
+
configuration
|
60
|
+
end
|
61
|
+
end
|
62
|
+
module_function :configure
|
63
|
+
|
64
|
+
# Check if word is uncountable
|
65
|
+
#
|
66
|
+
# @param [String] word
|
67
|
+
# the word to check
|
68
|
+
#
|
69
|
+
# @return [Boolean]
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
def uncountable?(word)
|
73
|
+
Noun[word].uncountable?
|
74
|
+
end
|
75
|
+
module_function :uncountable?
|
76
|
+
|
77
|
+
# Inflect a noun into a correct form
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# Strings::Inflection.inflect("error", 3)
|
81
|
+
# # => "errors"
|
82
|
+
#
|
83
|
+
# @param [String] word
|
84
|
+
# the word to inflect
|
85
|
+
# @param [Integer] count
|
86
|
+
# the count of items
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
def inflect(word, count, term: :noun)
|
90
|
+
template = !!(word =~ /\{\{([^\}]+)\}\}/)
|
91
|
+
|
92
|
+
Parser.parse(template ? word : "{{#{term.upcase[0]}:#{word}}}", count)
|
93
|
+
end
|
94
|
+
module_function :inflect
|
95
|
+
|
96
|
+
# Inflect a pural word to its singular form
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# Strings::Inflection.singularize("errors")
|
100
|
+
# # => "error"
|
101
|
+
#
|
102
|
+
# @param [String] word
|
103
|
+
# the noun to inflect to singular form
|
104
|
+
#
|
105
|
+
# @api public
|
106
|
+
def singularize(word, term: :noun)
|
107
|
+
case term
|
108
|
+
when :noun, :n
|
109
|
+
Noun[word].singular
|
110
|
+
when :verb, :v
|
111
|
+
Verb[word].singular
|
112
|
+
end
|
113
|
+
end
|
114
|
+
module_function :singularize
|
115
|
+
|
116
|
+
# Inflect a word to its plural form
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# Strings::Inflection.pluralize("error")
|
120
|
+
# # => "errors"
|
121
|
+
#
|
122
|
+
# @param [String] word
|
123
|
+
# noun to inflect to plural form
|
124
|
+
#
|
125
|
+
# @api public
|
126
|
+
def pluralize(word, term: :noun)
|
127
|
+
case term
|
128
|
+
when :noun, :n
|
129
|
+
Noun[word].plural
|
130
|
+
when :verb, :v
|
131
|
+
Verb[word].plural
|
132
|
+
end
|
133
|
+
end
|
134
|
+
module_function :pluralize
|
135
|
+
|
136
|
+
# Check if noun is in singular form
|
137
|
+
#
|
138
|
+
# @example
|
139
|
+
# Strings::Inflection.singular?("error")
|
140
|
+
# # => true
|
141
|
+
#
|
142
|
+
# @return [Boolean]
|
143
|
+
#
|
144
|
+
# @api public
|
145
|
+
def singular?(word, term: :noun)
|
146
|
+
case term.to_sym
|
147
|
+
when :noun, :n
|
148
|
+
Noun[word].singular?
|
149
|
+
when :verb, :v
|
150
|
+
Verb[word].singular?
|
151
|
+
else
|
152
|
+
raise Error, "Unknown option '#{term}' as a term"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
module_function :singular?
|
156
|
+
|
157
|
+
# Check if noun is in plural form
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# Strings::Inflection.plural?("errors")
|
161
|
+
# # => true
|
162
|
+
#
|
163
|
+
# @return [Boolean]
|
164
|
+
#
|
165
|
+
# @api public
|
166
|
+
def plural?(word, term: :noun)
|
167
|
+
case term.to_sym
|
168
|
+
when :noun, :n
|
169
|
+
Noun[word].plural?
|
170
|
+
when :verb, :v
|
171
|
+
Verb[word].plural?
|
172
|
+
else
|
173
|
+
raise Error, "Unknown option '#{term}' as a term"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
module_function :plural?
|
177
|
+
|
178
|
+
|
179
|
+
# Join a list of words into a single sentence
|
180
|
+
#
|
181
|
+
# @example
|
182
|
+
# Strings::Inflection.join_words("one", "two", "three")
|
183
|
+
# # => "one, two and three"
|
184
|
+
#
|
185
|
+
# @param [Array[String]] words
|
186
|
+
# the words to join
|
187
|
+
# @param [String] separator
|
188
|
+
# the character to use to join words, defaults to `,`
|
189
|
+
# @param [String] final_separator
|
190
|
+
# the separator used before joining the last word
|
191
|
+
# @param [String] conjunctive
|
192
|
+
# the word used for combining the last word with the rest
|
193
|
+
#
|
194
|
+
# @return [String]
|
195
|
+
#
|
196
|
+
# @api public
|
197
|
+
def join_words(*words, **options)
|
198
|
+
CombinedNoun.new(words).join_words(**options)
|
199
|
+
end
|
200
|
+
module_function :join_words
|
201
|
+
end # Inflection
|
202
|
+
end # Strings
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strings
|
4
|
+
module Inflection
|
5
|
+
class CombinedNoun
|
6
|
+
WHITESPACE_REGEX = /(\s)\s+/.freeze
|
7
|
+
|
8
|
+
attr_reader :words
|
9
|
+
|
10
|
+
# Create a combined noun
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def initialize(words = [])
|
14
|
+
@words = words
|
15
|
+
end
|
16
|
+
|
17
|
+
# Combined with another noun
|
18
|
+
#
|
19
|
+
# @param [String] word
|
20
|
+
# the word to combine with
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
def +(word)
|
24
|
+
CombinedNoun.new(@words + [word])
|
25
|
+
end
|
26
|
+
|
27
|
+
# Join a list of words into a single sentence
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# CombinedNoun(["one", "two", "three"]).join_words
|
31
|
+
# # => "one, two and three"
|
32
|
+
#
|
33
|
+
# @param [Array[String]] words
|
34
|
+
# the words to join
|
35
|
+
# @param [String] separator
|
36
|
+
# the character to use to join words, defaults to `,`
|
37
|
+
# @param [String] final_separator
|
38
|
+
# the separator used before joining the last word
|
39
|
+
# @param [String] conjunctive
|
40
|
+
# the word used for combining the last word with the rest
|
41
|
+
#
|
42
|
+
# @return [String]
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def join_words(separator: ", ", conjunctive: "and", final_separator: nil)
|
46
|
+
oxford_comma = final_separator || separator
|
47
|
+
|
48
|
+
case words.length
|
49
|
+
when 0
|
50
|
+
""
|
51
|
+
when 1, 2
|
52
|
+
words.join(" #{conjunctive} ").gsub(WHITESPACE_REGEX, "\\1")
|
53
|
+
else
|
54
|
+
((words[0...-1]).join(separator.to_s) +
|
55
|
+
"#{oxford_comma} #{conjunctive} " + words[-1])
|
56
|
+
.gsub(WHITESPACE_REGEX, "\\1").gsub(/\s*(,)/, "\\1")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# The combined words
|
61
|
+
#
|
62
|
+
# @return [Array[String]]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def to_ary
|
66
|
+
@words.to_ary
|
67
|
+
end
|
68
|
+
end # CombinedNoun
|
69
|
+
end # Inflection
|
70
|
+
end # Strings
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strings
|
4
|
+
module Inflection
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
attr_reader :plurals, :singulars, :uncountables
|
8
|
+
|
9
|
+
# Initialize a configuration
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
def initialize
|
13
|
+
reset
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add a singular rule form for a term. By default noun.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# singular "axes", "ax"
|
20
|
+
#
|
21
|
+
# @param [String,Regex] rule
|
22
|
+
# @param [String] replacement
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def singular(rule, replacement, term: :noun)
|
26
|
+
@singulars[term] << [Regexp.new(rule, "i"), replacement]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add a plural rule form for a term. By default noun.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# plural "index", "indexes"
|
33
|
+
#
|
34
|
+
# @param [String,Regex] rule
|
35
|
+
# @param [String] replacement
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def plural(rule, replacement, term: :noun)
|
39
|
+
@plurals[term] << [Regexp.new(rule, "i"), replacement]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Add a rule that provides transformation for both singular and
|
43
|
+
# plural inflections.
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# rule "ax", "axes"
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
def rule(sing, plur, term: :noun)
|
50
|
+
plural(sing, plur, term: term)
|
51
|
+
singular(plur, sing, term: term)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add an uncountable word. By default noun.
|
55
|
+
#
|
56
|
+
# @api public
|
57
|
+
def uncountable(word, term: :noun)
|
58
|
+
@uncountables[term] << word
|
59
|
+
end
|
60
|
+
|
61
|
+
# Reset configuration and remove loaded inflections. By default
|
62
|
+
# all scopes are reset. The available scopes are: :plurals,
|
63
|
+
# :singulars, :uncountable
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# reset(:all)
|
67
|
+
# reset(:plurals)
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def reset(scope = :all)
|
71
|
+
case scope
|
72
|
+
when :all
|
73
|
+
%i[singulars plurals uncountables].map {|s| clear(s) }
|
74
|
+
else
|
75
|
+
clear(scope)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def clear(name)
|
80
|
+
instance_variable_set "@#{name}", { noun: [], verb: [] }
|
81
|
+
end
|
82
|
+
end # Configuration
|
83
|
+
end # Inflection
|
84
|
+
end # Strings
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Strings
|
4
|
+
module Inflection
|
5
|
+
module Extensions
|
6
|
+
refine String do
|
7
|
+
def inflect(*args, **options)
|
8
|
+
Strings::Inflection.inflect(self, *args, **options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def singularize(**options)
|
12
|
+
Strings::Inflection.singularize(self, **options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def pluralize(**options)
|
16
|
+
Strings::Inflection.pluralize(self, **options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def singular?(**options)
|
20
|
+
Strings::Inflection.singular?(self, **options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def plural?(**options)
|
24
|
+
Strings::Inflection.plural?(self, **options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def join_words(*words, **options)
|
28
|
+
Strings::Inflection.join_words(self, *words, **options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end # Extensions
|
32
|
+
end # Inflection
|
33
|
+
end # Strings
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "combined_noun"
|
4
|
+
require_relative "term"
|
5
|
+
require_relative "nouns"
|
6
|
+
|
7
|
+
module Strings
|
8
|
+
module Inflection
|
9
|
+
class Noun < Term
|
10
|
+
# Check if word is uncountable
|
11
|
+
#
|
12
|
+
# @param [String] word
|
13
|
+
# the word to check
|
14
|
+
#
|
15
|
+
# @return [Boolean]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def uncountable?
|
19
|
+
Inflection.configuration.uncountables[:noun].include?(word.downcase) ||
|
20
|
+
Nouns.uncountable.include?(word.downcase)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Inflect a word to its singular form
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# Strings::Inflection::Noun.new("errors").singular
|
27
|
+
# # => "error"
|
28
|
+
#
|
29
|
+
# @param [String] word
|
30
|
+
# the noun to inflect to singular form
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def singular
|
34
|
+
return word if word.to_s.empty?
|
35
|
+
|
36
|
+
find_match(Inflection.configuration.singulars[:noun]) ||
|
37
|
+
(uncountable? && word) || find_match(Nouns.singulars) || word
|
38
|
+
end
|
39
|
+
|
40
|
+
# Inflect a word to its plural form
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# Strings::Inflection::Noun.new("error").plural
|
44
|
+
# # => "errors"
|
45
|
+
#
|
46
|
+
# @param [String] word
|
47
|
+
# noun to inflect to plural form
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def plural
|
51
|
+
return word if word.to_s.empty?
|
52
|
+
|
53
|
+
find_match(Inflection.configuration.plurals[:noun]) ||
|
54
|
+
(uncountable? && word) || find_match(Nouns.plurals) || word
|
55
|
+
end
|
56
|
+
|
57
|
+
# Combine this noun with another word
|
58
|
+
#
|
59
|
+
# @param [String] other_word
|
60
|
+
# the other word to combined with
|
61
|
+
#
|
62
|
+
# @return [CombinedNoun]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def +(other_word)
|
66
|
+
CombinedNoun.new([word, other_word])
|
67
|
+
end
|
68
|
+
end # Noun
|
69
|
+
end # Inflection
|
70
|
+
end # Strings
|