strings-inflection 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|