Class: Inflections
Overview
Copyright © 2005-2014 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Instance Attribute Summary collapse
-
#acronym_regex ⇒ Object
readonly
Returns the value of attribute acronym_regex.
-
#acronyms ⇒ Object
readonly
Returns the value of attribute acronyms.
-
#humans ⇒ Object
readonly
Returns the value of attribute humans.
-
#plurals ⇒ Object
readonly
Returns the value of attribute plurals.
-
#singulars ⇒ Object
readonly
Returns the value of attribute singulars.
-
#uncountables ⇒ Object
readonly
Returns the value of attribute uncountables.
Class Method Summary collapse
-
.camelize(term, uppercase_first_letter = true) ⇒ Object
By default,
camelize
converts strings to UpperCamelCase. -
.classify(table_name) ⇒ Object
Create a class name from a plural table name like Rails does for table names to models.
-
.constantize(camel_cased_word) ⇒ Object
Tries to find a constant with the name specified in the argument string.
-
.dasherize(underscored_word) ⇒ Object
Replaces underscores with dashes in the string.
-
.deconstantize(path) ⇒ Object
Removes the rightmost segment from the constant expression in the string.
-
.demodulize(path) ⇒ Object
Removes the module part from the expression in the string.
-
.foreign_key(class_name, separate_class_name_and_id_with_underscore = true) ⇒ Object
Creates a foreign key name from a class name.
-
.humanize(lower_case_and_underscored_word, options = {}) ⇒ Object
Tweaks an attribute name for display to end users.
-
.inflections(locale = :en) ⇒ Object
Yields a singleton instance of Inflections so you can specify additional Inflections rules.
- .instance(locale = :en) ⇒ Object
-
.ordinal(number) ⇒ Object
Returns the suffix that should be added to a number to denote the position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
.ordinalize(number) ⇒ Object
Turns a number into an ordinal string used to denote the position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
.pluralize(word, locale = :en) ⇒ Object
Returns the plural form of the word in the string.
-
.safe_constantize(camel_cased_word) ⇒ Object
Tries to find a constant with the name specified in the argument string.
-
.singularize(word, locale = :en) ⇒ Object
The reverse of
pluralize
, returns the singular form of a word in a string. -
.tableize(class_name) ⇒ Object
Create the name of a table like Rails does for models to table names.
-
.titleize(word) ⇒ Object
Capitalizes all the words and replaces some characters in the string to create a nicer looking title.
-
.underscore(camel_cased_word) ⇒ Object
Makes an underscored, lowercase form from the expression in the string.
Instance Method Summary collapse
-
#acronym(word) ⇒ Object
Specifies a new acronym.
-
#clear(scope = :all) ⇒ Object
Clears the loaded inflections within a given scope (default is
:all
). -
#human(rule, replacement) ⇒ Object
Specifies a humanized form of a string by a regular expression rule or by a string mapping.
-
#initialize(locale = :en) ⇒ Inflections
constructor
A new instance of Inflections.
-
#irregular(singular, plural) ⇒ Object
Specifies a new irregular that applies to both pluralization and singularization at the same time.
-
#plural(rule, replacement) ⇒ Object
Specifies a new pluralization rule and its replacement.
-
#singular(rule, replacement) ⇒ Object
Specifies a new singularization rule and its replacement.
-
#uncountable(*words) ⇒ Object
Add uncountable words that shouldn’t be attempted inflected.
Constructor Details
#initialize(locale = :en) ⇒ Inflections
Returns a new instance of Inflections.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/build/ExtendedString.rb', line 33 def initialize(locale = :en) @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/ if locale == :en plural(/$/, 's') plural(/s$/i, 's') plural(/^(ax|test)is$/i, '\1es') plural(/(octop|vir)us$/i, '\1i') plural(/(octop|vir)i$/i, '\1i') plural(/(alias|status)$/i, '\1es') plural(/(bu)s$/i, '\1ses') plural(/(buffal|tomat)o$/i, '\1oes') plural(/([ti])um$/i, '\1a') plural(/([ti])a$/i, '\1a') plural(/sis$/i, 'ses') plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves') plural(/(hive)$/i, '\1s') plural(/([^aeiouy]|qu)y$/i, '\1ies') plural(/(x|ch|ss|sh)$/i, '\1es') plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') plural(/^(m|l)ouse$/i, '\1ice') plural(/^(m|l)ice$/i, '\1ice') plural(/^(ox)$/i, '\1en') plural(/^(oxen)$/i, '\1') plural(/(quiz)$/i, '\1zes') singular(/s$/i, '') singular(/(ss)$/i, '\1') singular(/(n)ews$/i, '\1ews') singular(/([ti])a$/i, '\1um') singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis') singular(/(^analy)(sis|ses)$/i, '\1sis') singular(/([^f])ves$/i, '\1fe') singular(/(hive)s$/i, '\1') singular(/(tive)s$/i, '\1') singular(/([lr])ves$/i, '\1f') singular(/([^aeiouy]|qu)ies$/i, '\1y') singular(/(s)eries$/i, '\1eries') singular(/(m)ovies$/i, '\1ovie') singular(/(x|ch|ss|sh)es$/i, '\1') singular(/^(m|l)ice$/i, '\1ouse') singular(/(bus)(es)?$/i, '\1') singular(/(o)es$/i, '\1') singular(/(shoe)s$/i, '\1') singular(/(cris|test)(is|es)$/i, '\1is') singular(/^(a)x[ie]s$/i, '\1xis') singular(/(octop|vir)(us|i)$/i, '\1us') singular(/(alias|status)(es)?$/i, '\1') singular(/^(ox)en/i, '\1') singular(/(vert|ind)ices$/i, '\1ex') singular(/(matr)ices$/i, '\1ix') singular(/(quiz)zes$/i, '\1') singular(/(database)s$/i, '\1') irregular('person', 'people') irregular('man', 'men') irregular('child', 'children') irregular('sex', 'sexes') irregular('move', 'moves') irregular('zombie', 'zombies') uncountable(%w(equipment information rice money species series fish sheep jeans police)) end end |
Instance Attribute Details
#acronym_regex ⇒ Object (readonly)
Returns the value of attribute acronym_regex.
31 32 33 |
# File 'lib/build/ExtendedString.rb', line 31 def acronym_regex @acronym_regex end |
#acronyms ⇒ Object (readonly)
Returns the value of attribute acronyms.
31 32 33 |
# File 'lib/build/ExtendedString.rb', line 31 def acronyms @acronyms end |
#humans ⇒ Object (readonly)
Returns the value of attribute humans.
31 32 33 |
# File 'lib/build/ExtendedString.rb', line 31 def humans @humans end |
#plurals ⇒ Object (readonly)
Returns the value of attribute plurals.
31 32 33 |
# File 'lib/build/ExtendedString.rb', line 31 def plurals @plurals end |
#singulars ⇒ Object (readonly)
Returns the value of attribute singulars.
31 32 33 |
# File 'lib/build/ExtendedString.rb', line 31 def singulars @singulars end |
#uncountables ⇒ Object (readonly)
Returns the value of attribute uncountables.
31 32 33 |
# File 'lib/build/ExtendedString.rb', line 31 def uncountables @uncountables end |
Class Method Details
.camelize(term, uppercase_first_letter = true) ⇒ Object
By default, camelize
converts strings to UpperCamelCase. If the argument to camelize
is set to :lower
then camelize
produces lowerCamelCase.
camelize
will also convert ‘/’ to ‘::’ which is useful for converting paths to namespaces.
232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/build/ExtendedString.rb', line 232 def camelize(term, uppercase_first_letter = true) string = term.to_s if uppercase_first_letter string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize } else string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase } end string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" } string.gsub!('/', '::') string end |
.classify(table_name) ⇒ Object
Create a class name from a plural table name like Rails does for table names to models. Note that this returns a string and not a Class (To convert to an actual class follow classify
with constantize
).
318 319 320 321 |
# File 'lib/build/ExtendedString.rb', line 318 def classify(table_name) # strip out any leading schema name camelize(singularize(table_name.to_s.sub(/.*\./, ''))) end |
.constantize(camel_cased_word) ⇒ Object
Tries to find a constant with the name specified in the argument string.
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/build/ExtendedString.rb', line 352 def constantize(camel_cased_word) names = camel_cased_word.split('::') # Trigger a built-in NameError exception including the ill-formed constant in the message. Object.const_get(camel_cased_word) if names.empty? # Remove the first blank element in case of '::ClassName' notation. names.shift if names.size > 1 && names.first.empty? names.inject(Object) do |constant, name| if constant == Object constant.const_get(name) else candidate = constant.const_get(name) next candidate if constant.const_defined?(name, false) next candidate unless Object.const_defined?(name) # Go down the ancestors to check if it is owned directly. The check # stops when we reach Object or the end of ancestors tree. constant = constant.ancestors.inject do |const, ancestor| break const if ancestor == Object break ancestor if ancestor.const_defined?(name, false) const end # owner is in Object, so raise constant.const_get(name, false) end end end |
.dasherize(underscored_word) ⇒ Object
Replaces underscores with dashes in the string.
324 325 326 |
# File 'lib/build/ExtendedString.rb', line 324 def dasherize(underscored_word) underscored_word.tr('_', '-') end |
.deconstantize(path) ⇒ Object
Removes the rightmost segment from the constant expression in the string. See also demodulize
.
340 341 342 |
# File 'lib/build/ExtendedString.rb', line 340 def deconstantize(path) path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename end |
.demodulize(path) ⇒ Object
Removes the module part from the expression in the string.
329 330 331 332 333 334 335 336 |
# File 'lib/build/ExtendedString.rb', line 329 def demodulize(path) path = path.to_s if i = path.rindex('::') path[(i+2)..-1] else path end end |
.foreign_key(class_name, separate_class_name_and_id_with_underscore = true) ⇒ Object
Creates a foreign key name from a class name. separate_class_name_and_id_with_underscore
sets whether the method should put ‘_’ between the name and ‘id’.
347 348 349 |
# File 'lib/build/ExtendedString.rb', line 347 def foreign_key(class_name, separate_class_name_and_id_with_underscore = true) underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id") end |
.humanize(lower_case_and_underscored_word, options = {}) ⇒ Object
Tweaks an attribute name for display to end users.
Specifically, humanize
performs these transformations:
* Applies human inflection rules to the argument.
* Deletes leading underscores, if any.
* Removes a "_id" suffix if present.
* Replaces underscores with spaces, if any.
* Downcases all words except acronyms.
* Capitalizes the first word.
The capitalization of the first word can be turned off by setting the :capitalize
option to false (default is true).
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/build/ExtendedString.rb', line 279 def humanize(lower_case_and_underscored_word, = {}) result = lower_case_and_underscored_word.to_s.dup inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) } result.sub!(/\A_+/, '') result.sub!(/_id\z/, '') result.tr!('_', ' ') result.gsub!(/([a-z\d]*)/i) do |match| "#{inflections.acronyms[match] || match.downcase}" end if .fetch(:capitalize, true) result.sub!(/\A\w/) { |match| match.upcase } end result end |
.inflections(locale = :en) ⇒ Object
Yields a singleton instance of Inflections so you can specify additional Inflections rules. If passed an optional locale, rules for other languages can be specified. If not specified, defaults to :en
. Only rules for English are provided.
197 198 199 200 201 202 203 |
# File 'lib/build/ExtendedString.rb', line 197 def inflections(locale = :en) if block_given? yield self.instance(locale) else self.instance(locale) end end |
.instance(locale = :en) ⇒ Object
27 28 29 |
# File 'lib/build/ExtendedString.rb', line 27 def self.instance(locale = :en) @__instance__[locale] ||= new(locale) end |
.ordinal(number) ⇒ Object
Returns the suffix that should be added to a number to denote the position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
# File 'lib/build/ExtendedString.rb', line 396 def ordinal(number) abs_number = number.to_i.abs if (11..13).include?(abs_number % 100) "th" else case abs_number % 10 when 1; "st" when 2; "nd" when 3; "rd" else "th" end end end |
.ordinalize(number) ⇒ Object
Turns a number into an ordinal string used to denote the position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
413 414 415 |
# File 'lib/build/ExtendedString.rb', line 413 def ordinalize(number) "#{number}#{ordinal(number)}" end |
.pluralize(word, locale = :en) ⇒ Object
Returns the plural form of the word in the string.
If passed an optional locale
parameter, the word will be pluralized using rules defined for that language. By default, this parameter is set to :en
.
210 211 212 |
# File 'lib/build/ExtendedString.rb', line 210 def pluralize(word, locale = :en) apply_inflections(word, inflections(locale).plurals) end |
.safe_constantize(camel_cased_word) ⇒ Object
Tries to find a constant with the name specified in the argument string.
384 385 386 387 388 389 390 391 |
# File 'lib/build/ExtendedString.rb', line 384 def safe_constantize(camel_cased_word) constantize(camel_cased_word) rescue NameError => e raise unless e. =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ || e.name.to_s == camel_cased_word.to_s rescue ArgumentError => e raise unless e. =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/ end |
.singularize(word, locale = :en) ⇒ Object
The reverse of pluralize
, returns the singular form of a word in a string.
If passed an optional locale
parameter, the word will be singularized using rules defined for that language. By default, this parameter is set to :en
.
221 222 223 |
# File 'lib/build/ExtendedString.rb', line 221 def singularize(word, locale = :en) apply_inflections(word, inflections(locale).singulars) end |
.tableize(class_name) ⇒ Object
Create the name of a table like Rails does for models to table names. This method uses the pluralize
method on the last word in the string.
310 311 312 |
# File 'lib/build/ExtendedString.rb', line 310 def tableize(class_name) pluralize(underscore(class_name)) end |
.titleize(word) ⇒ Object
Capitalizes all the words and replaces some characters in the string to create a nicer looking title. titleize
is meant for creating pretty output. It is not used in the Rails internals.
titleize
is also aliased as titlecase
.
304 305 306 |
# File 'lib/build/ExtendedString.rb', line 304 def titleize(word) humanize(underscore(word)).gsub(/\b(?<!['`])[a-z]/) { $&.capitalize } end |
.underscore(camel_cased_word) ⇒ Object
Makes an underscored, lowercase form from the expression in the string.
Changes ‘::’ to ‘/’ to convert namespaces to paths.
'ActiveModel'.underscore # => "active_model"
'ActiveModel::Errors'.underscore # => "active_model/errors"
As a rule of thumb you can think of underscore
as the inverse of camelize
, though there are cases where that does not hold:
'SSLError'.underscore.camelize # => "SslError"
255 256 257 258 259 260 261 262 263 264 |
# File 'lib/build/ExtendedString.rb', line 255 def underscore(camel_cased_word) return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/ word = camel_cased_word.to_s.gsub('::', '/') word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" } word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2') word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') word.tr!("-", "_") word.downcase! word end |
Instance Method Details
#acronym(word) ⇒ Object
Specifies a new acronym. An acronym must be specified as it will appear in a camelized string. An underscore string that contains the acronym will retain the acronym when passed to camelize
, humanize
, or titleize
. A camelized string that contains the acronym will maintain the acronym when titleized or humanized, and will convert the acronym into a non-delimited single lowercase word when passed to underscore
.
104 105 106 107 |
# File 'lib/build/ExtendedString.rb', line 104 def acronym(word) @acronyms[word.downcase] = word @acronym_regex = /#{@acronyms.values.join("|")}/ end |
#clear(scope = :all) ⇒ Object
Clears the loaded inflections within a given scope (default is :all
). Give the scope as a symbol of the inflection type, the options are: :plurals
, :singulars
, :uncountables
, :humans
.
180 181 182 183 184 185 186 187 |
# File 'lib/build/ExtendedString.rb', line 180 def clear(scope = :all) case scope when :all @plurals, @singulars, @uncountables, @humans = [], [], [], [] else instance_variable_set "@#{scope}", [] end end |
#human(rule, replacement) ⇒ Object
Specifies a humanized form of a string by a regular expression rule or by a string mapping. When using a regular expression based replacement, the normal humanize formatting is called after the replacement. When a string is used, the human form should be specified as desired (example: ‘The name’, not ‘the_name’).
172 173 174 |
# File 'lib/build/ExtendedString.rb', line 172 def human(rule, replacement) @humans.unshift([rule, replacement]) end |
#irregular(singular, plural) ⇒ Object
Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used for strings, not regular expressions. You simply pass the irregular in singular and plural form.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/build/ExtendedString.rb', line 133 def irregular(singular, plural) @uncountables.delete(singular) @uncountables.delete(plural) s0 = singular[0] srest = singular[1..-1] p0 = plural[0] prest = plural[1..-1] if s0.upcase == p0.upcase plural(/(#{s0})#{srest}$/i, '\1' + prest) plural(/(#{p0})#{prest}$/i, '\1' + prest) singular(/(#{s0})#{srest}$/i, '\1' + srest) singular(/(#{p0})#{prest}$/i, '\1' + srest) else plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest) plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest) plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest) plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest) singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest) singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest) singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest) singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest) end end |
#plural(rule, replacement) ⇒ Object
Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression. The replacement should always be a string that may include references to the matched data from the rule.
113 114 115 116 117 |
# File 'lib/build/ExtendedString.rb', line 113 def plural(rule, replacement) @uncountables.delete(rule) if rule.is_a?(String) @uncountables.delete(replacement) @plurals.unshift([rule, replacement]) end |
#singular(rule, replacement) ⇒ Object
Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression. The replacement should always be a string that may include references to the matched data from the rule.
123 124 125 126 127 |
# File 'lib/build/ExtendedString.rb', line 123 def singular(rule, replacement) @uncountables.delete(rule) if rule.is_a?(String) @uncountables.delete(replacement) @singulars.unshift([rule, replacement]) end |
#uncountable(*words) ⇒ Object
Add uncountable words that shouldn’t be attempted inflected.
163 164 165 |
# File 'lib/build/ExtendedString.rb', line 163 def uncountable(*words) @uncountables += words.flatten.map(&:downcase) end |