Module: Philiprehberger::Inflector

Defined in:
lib/philiprehberger/inflector.rb,
lib/philiprehberger/inflector/rules.rb,
lib/philiprehberger/inflector/version.rb,
lib/philiprehberger/inflector/string_refinements.rb

Defined Under Namespace

Modules: Rules, StringRefinements Classes: Error

Constant Summary collapse

VERSION =
'0.5.0'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.custom_pluralsObject (readonly)

Returns the value of attribute custom_plurals.



16
17
18
# File 'lib/philiprehberger/inflector.rb', line 16

def custom_plurals
  @custom_plurals
end

.custom_singularsObject (readonly)

Returns the value of attribute custom_singulars.



16
17
18
# File 'lib/philiprehberger/inflector.rb', line 16

def custom_singulars
  @custom_singulars
end

.custom_uncountablesObject (readonly)

Returns the value of attribute custom_uncountables.



16
17
18
# File 'lib/philiprehberger/inflector.rb', line 16

def custom_uncountables
  @custom_uncountables
end

Class Method Details

.add_irregular(singular, plural) ⇒ Object

Register an irregular singular/plural pair

Parameters:

  • singular (String)

    the singular form (e.g. “person”)

  • plural (String)

    the plural form (e.g. “people”)



254
255
256
257
258
259
260
261
262
# File 'lib/philiprehberger/inflector.rb', line 254

def self.add_irregular(singular, plural)
  s0 = singular[0]
  srest = singular[1..]
  p0 = plural[0]
  prest = plural[1..]

  add_plural_rule(/(#{s0})#{srest}$/i, "\\1#{prest}")
  add_singular_rule(/(#{p0})#{prest}$/i, "\\1#{srest}")
end

.add_plural_rule(pattern, replacement) ⇒ Object

Register a custom pluralization rule

Parameters:

  • pattern (Regexp, String)

    the pattern to match

  • replacement (String)

    the replacement string



212
213
214
215
# File 'lib/philiprehberger/inflector.rb', line 212

def self.add_plural_rule(pattern, replacement)
  pattern = /#{pattern}/i if pattern.is_a?(String)
  @custom_plurals.unshift([pattern, replacement])
end

.add_singular_rule(pattern, replacement) ⇒ Object

Register a custom singularization rule

Parameters:

  • pattern (Regexp, String)

    the pattern to match

  • replacement (String)

    the replacement string



221
222
223
224
# File 'lib/philiprehberger/inflector.rb', line 221

def self.add_singular_rule(pattern, replacement)
  pattern = /#{pattern}/i if pattern.is_a?(String)
  @custom_singulars.unshift([pattern, replacement])
end

.add_uncountable(*words) ⇒ Object

Register additional uncountable words

Parameters:

  • words (Array<String>)

    the words to add



267
268
269
# File 'lib/philiprehberger/inflector.rb', line 267

def self.add_uncountable(*words)
  @custom_uncountables.push(*words.flatten.map(&:downcase))
end

.camelize(str, uppercase_first: true) ⇒ String

Convert a snake_case or dashed string to CamelCase

Parameters:

  • str (String)

    the string to camelize

  • uppercase_first (Boolean) (defaults to: true)

    whether to uppercase the first letter

Returns:

  • (String)

    the CamelCase string



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/philiprehberger/inflector.rb', line 135

def self.camelize(str, uppercase_first: true)
  result = str.to_s.dup
  result = if uppercase_first
             result.sub(/^[a-z\d]*/, &:capitalize)
           else
             result.sub(/^(?:(?=\b|[A-Z_])|\w)/, &:downcase)
           end
  result.gsub!(%r{(?:_|(/))([a-z\d]*)}) { "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}" }
  result.gsub!('/', '::')
  result
end

.classify(table_name) ⇒ String

Convert a table name to a class name

Parameters:

  • table_name (String)

    the table name (e.g. “user_accounts”)

Returns:

  • (String)

    the class name (e.g. “UserAccount”)



89
90
91
# File 'lib/philiprehberger/inflector.rb', line 89

def self.classify(table_name)
  camelize(singularize(table_name.to_s.sub(/.*\./, '')))
end

.count(n, word) ⇒ String

Format a count with singular/plural word agreement

Uses the existing singularize/pluralize logic to choose the correct form based on n. When n.abs equals exactly 1, the singular form of word is used; otherwise the plural form. The original n (Integer or Float) is preserved in the returned string.

Examples:

Philiprehberger::Inflector.count(1, 'apple')   # => "1 apple"
Philiprehberger::Inflector.count(0, 'apple')   # => "0 apples"
Philiprehberger::Inflector.count(5, 'goose')   # => "5 geese"
Philiprehberger::Inflector.count(-1, 'mouse')  # => "-1 mouse"
Philiprehberger::Inflector.count(1.5, 'apple') # => "1.5 apples"

Parameters:

  • n (Integer, Float)

    the count value

  • word (String)

    the word to inflect

Returns:

  • (String)

    the formatted string, e.g. “1 apple” or “5 geese”



72
73
74
75
# File 'lib/philiprehberger/inflector.rb', line 72

def self.count(n, word)
  form = n.abs == 1 ? singularize(word) : pluralize(word)
  "#{n} #{form}"
end

.dasherize(str) ⇒ String

Convert underscores to dashes in a string

Parameters:

  • str (String)

    the underscored string

Returns:

  • (String)

    the dashed string



191
192
193
# File 'lib/philiprehberger/inflector.rb', line 191

def self.dasherize(str)
  str.to_s.tr('_', '-')
end

.deconstantize(str) ⇒ String

Remove the rightmost segment from a fully qualified constant name

Parameters:

  • str (String)

    the fully qualified constant name (e.g. “Admin::User”)

Returns:

  • (String)

    the constant without the rightmost segment (e.g. “Admin”)



230
231
232
233
234
235
236
237
# File 'lib/philiprehberger/inflector.rb', line 230

def self.deconstantize(str)
  path = str.to_s
  if (index = path.rindex('::'))
    path[0, index]
  else
    ''
  end
end

.demodulize(str) ⇒ String

Remove the module namespace from a fully qualified class name

Parameters:

  • str (String)

    the fully qualified class name (e.g. “Admin::User”)

Returns:

  • (String)

    the class name without namespace (e.g. “User”)



199
200
201
202
203
204
205
206
# File 'lib/philiprehberger/inflector.rb', line 199

def self.demodulize(str)
  path = str.to_s
  if (index = path.rindex('::'))
    path[(index + 2)..]
  else
    path
  end
end

.foreign_key(class_name, separate_id: true) ⇒ String

Create a foreign key name from a class name

Parameters:

  • class_name (String)

    the class name (e.g. “User”)

  • separate_id (Boolean) (defaults to: true)

    whether to separate with underscore

Returns:

  • (String)

    the foreign key (e.g. “user_id”)



98
99
100
101
# File 'lib/philiprehberger/inflector.rb', line 98

def self.foreign_key(class_name, separate_id: true)
  key = underscore(class_name.to_s.gsub('::', '/').split('/').last)
  "#{key}#{separate_id ? '_id' : 'id'}"
end

.humanize(str) ⇒ String

Convert an underscored or CamelCase string to a human-readable form

Parameters:

  • str (String)

    the string to humanize

Returns:

  • (String)

    the humanized string



151
152
153
154
155
156
157
158
# File 'lib/philiprehberger/inflector.rb', line 151

def self.humanize(str)
  result = str.to_s.dup
  result.sub!(/_id$/, '')
  result.tr!('_', ' ')
  result.gsub!(/([a-z\d])([A-Z])/, '\1 \2')
  result.gsub!(/\b([a-z])/) { Regexp.last_match(1).capitalize }
  result.strip
end

.ordinalize(number) ⇒ String

Convert a number to its ordinal string

Parameters:

  • number (Integer)

    the number to ordinalize

Returns:

  • (String)

    the ordinal string (e.g. “1st”, “2nd”, “3rd”)



172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/philiprehberger/inflector.rb', line 172

def self.ordinalize(number)
  abs_number = number.to_i.abs
  suffix = if (11..13).cover?(abs_number % 100)
             'th'
           else
             case abs_number % 10
             when 1 then 'st'
             when 2 then 'nd'
             when 3 then 'rd'
             else 'th'
             end
           end
  "#{number}#{suffix}"
end

.parameterize(str, separator: '-') ⇒ String

Convert a string to a URL-friendly parameterized form

Parameters:

  • str (String)

    the string to parameterize

  • separator (String) (defaults to: '-')

    the separator character

Returns:

  • (String)

    the parameterized string



108
109
110
111
112
113
114
# File 'lib/philiprehberger/inflector.rb', line 108

def self.parameterize(str, separator: '-')
  result = str.to_s.dup
  result.gsub!(/[^a-zA-Z0-9\-_]+/, separator)
  result.gsub!(/#{Regexp.escape(separator)}{2,}/, separator) unless separator.empty?
  result.gsub!(/^#{Regexp.escape(separator)}|#{Regexp.escape(separator)}$/, '') unless separator.empty?
  result.downcase
end

.pluralize(word) ⇒ String

Return the plural form of a word

Parameters:

  • word (String)

    the word to pluralize

Returns:

  • (String)

    the pluralized word



23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/philiprehberger/inflector.rb', line 23

def self.pluralize(word)
  return word if word.empty?
  return word if @custom_uncountables.include?(word.downcase) || Rules::UNCOUNTABLES.include?(word.downcase)

  @custom_plurals.each do |pattern, replacement|
    return word.sub(pattern, replacement) if word.match?(pattern)
  end

  Rules::PLURALS.each do |pattern, replacement|
    return word.sub(pattern, replacement) if word.match?(pattern)
  end

  word
end

.singularize(word) ⇒ String

Return the singular form of a word

Parameters:

  • word (String)

    the word to singularize

Returns:

  • (String)

    the singularized word



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/philiprehberger/inflector.rb', line 42

def self.singularize(word)
  return word if word.empty?
  return word if @custom_uncountables.include?(word.downcase) || Rules::UNCOUNTABLES.include?(word.downcase)

  @custom_singulars.each do |pattern, replacement|
    return word.sub(pattern, replacement) if word.match?(pattern)
  end

  Rules::SINGULARS.each do |pattern, replacement|
    return word.sub(pattern, replacement) if word.match?(pattern)
  end

  word
end

.tableize(class_name) ⇒ String

Convert a class name to a table name

Parameters:

  • class_name (String)

    the class name (e.g. “UserAccount”)

Returns:

  • (String)

    the table name (e.g. “user_accounts”)



81
82
83
# File 'lib/philiprehberger/inflector.rb', line 81

def self.tableize(class_name)
  pluralize(underscore(class_name))
end

.titleize(str) ⇒ String

Convert a string to title case

Parameters:

  • str (String)

    the string to titleize

Returns:

  • (String)

    the titleized string



164
165
166
# File 'lib/philiprehberger/inflector.rb', line 164

def self.titleize(str)
  humanize(underscore(str))
end

.underscore(str) ⇒ String

Convert a CamelCase string to snake_case

Parameters:

  • str (String)

    the CamelCase string

Returns:

  • (String)

    the snake_case string



120
121
122
123
124
125
126
127
128
# File 'lib/philiprehberger/inflector.rb', line 120

def self.underscore(str)
  result = str.to_s.dup
  result.gsub!('::', '/')
  result.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
  result.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
  result.tr!('-', '_')
  result.downcase!
  result
end

.upcase_first(str) ⇒ String

Uppercase only the first character of a string

Parameters:

  • str (String)

    the string to modify

Returns:

  • (String)

    the string with the first character uppercased



243
244
245
246
247
248
# File 'lib/philiprehberger/inflector.rb', line 243

def self.upcase_first(str)
  str = str.to_s
  return str if str.empty?

  str[0].upcase + str[1..]
end