strings-inflection 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ FileList["tasks/**/*.rake"].each(&method(:import))
4
+
5
+ desc "Run all specs"
6
+ task ci: %w[ spec ]
7
+
8
+ task default: :spec
@@ -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