module ActiveSupport::Inflector
def apply_inflections(word, rules)
apply_inflections('post', inflections.plurals) # => "posts"
Applies inflection rules for +singularize+ and +pluralize+.
def apply_inflections(word, rules) result = word.to_s.dup if word.empty? || inflections.uncountables.uncountable?(result) result else rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) } result end end
def camelize(term, uppercase_first_letter = true)
#underscore, though there are cases where that does not hold:
As a rule of thumb you can think of +camelize+ as the inverse of
camelize('active_model/errors', false) # => "activeModel::Errors"
camelize('active_model/errors') # => "ActiveModel::Errors"
camelize('active_model', false) # => "activeModel"
camelize('active_model') # => "ActiveModel"
paths to namespaces.
Also converts '/' to '::' which is useful for converting
lowerCamelCase.
If the +uppercase_first_letter+ parameter is set to false, then produces
Converts strings to UpperCamelCase.
def camelize(term, uppercase_first_letter = true) string = term.to_s if uppercase_first_letter string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize } else string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase } end string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" } string.gsub!('/'.freeze, '::'.freeze) string end
def classify(table_name)
Singular names are not handled correctly:
classify('posts') # => "Post"
classify('ham_and_eggs') # => "HamAndEgg"
convert to an actual class follow +classify+ with #constantize).
names to models. Note that this returns a string and not a Class (To
Creates a class name from a plural table name like Rails does for table
def classify(table_name) # strip out any leading schema name camelize(singularize(table_name.to_s.sub(/.*\./, ''.freeze))) end
def const_regexp(camel_cased_word) #:nodoc:
const_regexp("::") # => "::"
const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
that will match part by part the given constant.
Mounts a regular expression, returned as a string to ease interpolation,
def const_regexp(camel_cased_word) #:nodoc: parts = camel_cased_word.split("::".freeze) return Regexp.escape(camel_cased_word) if parts.blank? last = parts.pop parts.reverse.inject(last) do |acc, part| part.empty? ? acc : "#{part}(::#{acc})?" end end
def constantize(camel_cased_word)
NameError is raised when the name is not in CamelCase or the constant is
end
'C'.constantize # => 'outside', same as ::C
C # => 'inside'
C = 'inside'
module M
C = 'outside'
account:
whether it starts with "::" or not. No lexical context is taken into
The name is assumed to be the one of a top-level constant, no matter
'Foo::Bar'.constantize # => Foo::Bar
'Module'.constantize # => Module
Tries to find a constant with the name specified in the argument string.
def constantize(camel_cased_word) names = camel_cased_word.split('::'.freeze) # 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(constant) 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
def dasherize(underscored_word)
Replaces underscores with dashes in the string.
def dasherize(underscored_word) underscored_word.tr('_'.freeze, '-'.freeze) end
def deconstantize(path)
deconstantize('') # => ""
deconstantize('::String') # => ""
deconstantize('String') # => ""
deconstantize('::Net::HTTP') # => "::Net"
deconstantize('Net::HTTP') # => "Net"
Removes the rightmost segment from the constant expression in the string.
def deconstantize(path) path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename end
def demodulize(path)
demodulize('') # => ""
demodulize('::Inflections') # => "Inflections"
demodulize('Inflections') # => "Inflections"
demodulize('ActiveRecord::CoreExtensions::String::Inflections') # => "Inflections"
Removes the module part from the expression in the string.
def demodulize(path) path = path.to_s if i = path.rindex('::') path[(i+2)..-1] else path end end
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
foreign_key('Message', false) # => "messageid"
foreign_key('Message') # => "message_id"
the method should put '_' between the name and 'id'.
+separate_class_name_and_id_with_underscore+ sets whether
Creates a foreign key name from a class name.
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
def humanize(lower_case_and_underscored_word, options = {})
humanize('ssl_error') # => "SSL error"
If "SSL" was defined to be an acronym:
humanize('_id') # => "Id"
humanize('author_id', capitalize: false) # => "author"
humanize('author_id') # => "Author"
humanize('employee_salary') # => "Employee salary"
+:capitalize+ option to false (default is true).
The capitalization of the first word can be turned off by setting the
* Capitalizes the first word.
* Downcases all words except acronyms.
* Replaces underscores with spaces, if any.
* Removes a "_id" suffix if present.
* Deletes leading underscores, if any.
* Applies human inflection rules to the argument.
Specifically, performs these transformations:
Tweaks an attribute name for display to end users.
def humanize(lower_case_and_underscored_word, options = {}) result = lower_case_and_underscored_word.to_s.dup inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) } result.sub!(/\A_+/, ''.freeze) result.sub!(/_id\z/, ''.freeze) result.tr!('_'.freeze, ' '.freeze) result.gsub!(/([a-z\d]*)/i) do |match| "#{inflections.acronyms[match] || match.downcase}" end if options.fetch(:capitalize, true) result.sub!(/\A\w/) { |match| match.upcase } end result end
def inflections(locale = :en)
inflect.uncountable 'rails'
ActiveSupport::Inflector.inflections(:en) do |inflect|
Only rules for English are provided.
languages can be specified. If not specified, defaults to :en.
additional inflector rules. If passed an optional locale, rules for other
Yields a singleton instance of Inflector::Inflections so you can specify
def inflections(locale = :en) if block_given? yield Inflections.instance(locale) else Inflections.instance(locale) end end
def ordinal(number)
ordinal(-11) # => "th"
ordinal(1003) # => "rd"
ordinal(1002) # => "nd"
ordinal(2) # => "nd"
ordinal(1) # => "st"
in an ordered sequence such as 1st, 2nd, 3rd, 4th.
Returns the suffix that should be added to a number to denote the position
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
def ordinalize(number)
ordinalize(-11) # => "-11th"
ordinalize(1003) # => "1003rd"
ordinalize(1002) # => "1002nd"
ordinalize(2) # => "2nd"
ordinalize(1) # => "1st"
ordered sequence such as 1st, 2nd, 3rd, 4th.
Turns a number into an ordinal string used to denote the position in an
def ordinalize(number) "#{number}#{ordinal(number)}" end
def parameterize(string, sep = :unused, separator: '-', preserve_case: false)
parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie"
parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
To preserve the case of the characters in a string, use the `preserve_case` argument.
parameterize("^trés|Jolie-- ", separator: '_') # => "tres_jolie"
parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
To use a custom separator, override the `separator` argument.
parameterize("^trés|Jolie-- ") # => "tres-jolie"
parameterize("Donald E. Knuth") # => "donald-e-knuth"
a 'pretty' URL.
Replaces special characters in a string so that it may be used as part of
def parameterize(string, sep = :unused, separator: '-', preserve_case: false) unless sep == :unused ActiveSupport::Deprecation.warn("Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '#{sep}'` instead.") separator = sep end # Replace accented chars with their ASCII equivalents. parameterized_string = transliterate(string) # Turn unwanted chars into the separator. parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator) unless separator.nil? || separator.empty? if separator == "-".freeze re_duplicate_separator = /-{2,}/ re_leading_trailing_separator = /^-|-$/i else re_sep = Regexp.escape(separator) re_duplicate_separator = /#{re_sep}{2,}/ re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i end # No more than one of the separator in a row. parameterized_string.gsub!(re_duplicate_separator, separator) # Remove leading/trailing separator. parameterized_string.gsub!(re_leading_trailing_separator, ''.freeze) end parameterized_string.downcase! unless preserve_case parameterized_string end
def pluralize(word, locale = :en)
pluralize('CamelOctopus') # => "CamelOctopi"
pluralize('words') # => "words"
pluralize('sheep') # => "sheep"
pluralize('octopus') # => "octopi"
pluralize('post') # => "posts"
this parameter is set to :en.
pluralized using rules defined for that language. By default,
If passed an optional +locale+ parameter, the word will be
Returns the plural form of the word in the string.
def pluralize(word, locale = :en) apply_inflections(word, inflections(locale).plurals) end
def safe_constantize(camel_cased_word)
safe_constantize('UnknownModule') # => nil
safe_constantize('blargle') # => nil
part of it) is unknown.
+nil+ is returned when the name is not in CamelCase or the constant (or
end
safe_constantize('C') # => 'outside', same as ::C
C # => 'inside'
C = 'inside'
module M
C = 'outside'
account:
whether it starts with "::" or not. No lexical context is taken into
The name is assumed to be the one of a top-level constant, no matter
safe_constantize('Foo::Bar') # => Foo::Bar
safe_constantize('Module') # => Module
Tries to find a constant with the name specified in the argument string.
def safe_constantize(camel_cased_word) constantize(camel_cased_word) rescue NameError => e raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) || e.name.to_s == camel_cased_word.to_s) rescue ArgumentError => e raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/ end
def singularize(word, locale = :en)
singularize('CamelOctopi') # => "CamelOctopus"
singularize('word') # => "word"
singularize('sheep') # => "sheep"
singularize('octopi') # => "octopus"
singularize('posts') # => "post"
this parameter is set to :en.
singularized using rules defined for that language. By default,
If passed an optional +locale+ parameter, the word will be
string.
The reverse of #pluralize, returns the singular form of a word in a
def singularize(word, locale = :en) apply_inflections(word, inflections(locale).singulars) end
def tableize(class_name)
tableize('ham_and_egg') # => "ham_and_eggs"
tableize('RawScaledScorer') # => "raw_scaled_scorers"
This method uses the #pluralize method on the last word in the string.
Creates the name of a table like Rails does for models to table names.
def tableize(class_name) pluralize(underscore(class_name)) end
def titleize(word)
titleize('TheManWithoutAPast') # => "The Man Without A Past"
titleize('x-men: the last stand') # => "X Men: The Last Stand"
titleize('man from the boondocks') # => "Man From The Boondocks"
+titleize+ is also aliased as +titlecase+.
output. It is not used in the Rails internals.
create a nicer looking title. +titleize+ is meant for creating pretty
Capitalizes all the words and replaces some characters in the string to
def titleize(word) humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize } end
def transliterate(string, replacement = "?".freeze)
transliterate('Jürgen')
I18n.locale = :de
# => "Jurgen"
transliterate('Jürgen')
I18n.locale = :en
Now you can have different transliterations for each locale:
})
}
rule: ->(string) { MyTransliterator.transliterate(string) }
transliterate: {
I18n.backend.store_translations(:de, i18n: {
complex requirements, a Proc:
maps characters to ASCII approximations as shown above, or, for more
The value for i18n.transliterate.rule can be a simple Hash that
})
}
}
'ö' => 'oe'
'ü' => 'ue',
rule: {
transliterate: {
I18n.backend.store_translations(:de, i18n: {
# Or set them using Ruby
ö: "oe"
ü: "ue"
rule:
transliterate:
i18n:
# Store the transliterations in locales/de.yml
them as the i18n.transliterate.rule i18n key:
In order to make your custom transliterations available, you must set
to ASCII.
and "ö" to "ue" and "oe", or to add support for transliterating Russian
locale. This can be useful, for example, to transliterate German's "ü"
This method is I18n aware, so you can set up custom approximations for a
e.g, "ø", "ñ", "é", "ß", etc.
Default approximations are provided for Western/Latin characters,
# => "AEroskobing"
transliterate('Ærøskøbing')
exists, a replacement character which defaults to "?".
Replaces non-ASCII characters with an ASCII approximation, or if none
def transliterate(string, replacement = "?".freeze) I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize( ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c), :replacement => replacement) end
def underscore(camel_cased_word)
#camelize, though there are cases where that does not hold:
As a rule of thumb you can think of +underscore+ as the inverse of
underscore('ActiveModel::Errors') # => "active_model/errors"
underscore('ActiveModel') # => "active_model"
Changes '::' to '/' to convert namespaces to paths.
Makes an underscored, lowercase form from the expression in the string.
def underscore(camel_cased_word) return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/ word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze) word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" } word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze) word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze) word.tr!("-".freeze, "_".freeze) word.downcase! word end
def upcase_first(string)
upcase_first('w') # => "W"
upcase_first('what a Lovely Day') # => "What a Lovely Day"
Converts just the first character to uppercase.
def upcase_first(string) string.length > 0 ? string[0].upcase.concat(string[1..-1]) : '' end