модуль ActiveSupport::Inflector
Класс Inflector преобразует слова от единственного к множественному числу, имена классов в имена таблиц, имена модульных классов в имена без модулей и имена классов в внешние ключи. Стандартные склонения для множественного, единственного числа и несчётных слов хранятся в inflections.rb.
Команда разработчиков Rails заявила, что исправления для библиотеки склонений не принимаются, чтобы избежать разрыва совместимости с устаревшими приложениями, которые могут полагаться на неправильные склонения. Если вы обнаружите неверное склонение, необходимое для вашего приложения, или захотите определить правила для языков помимо английского, пожалуйста, исправьте или добавьте их самостоятельно (объяснено ниже).
Общедоступные методы экземпляров
# File activesupport/lib/active_support/inflector/methods.rb, line 68
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.acronyms_camelize_regex) { |match| match.downcase }
end
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
string.gsub!("/", "::")
string
end Преобразует строки в ВерхнийCamelCase. Если параметр uppercase_first_letter установлен в false, то генерируется lowerCamelCase.
Также преобразует '/' в '::', что полезно для преобразования путей в имена пространств имён.
camelize('active_model') # => "ActiveModel"
camelize('active_model', false) # => "activeModel"
camelize('active_model/errors') # => "ActiveModel::Errors"
camelize('active_model/errors', false) # => "activeModel::Errors"
Как правило, можно считать, что camelize является обратным методом underscore, хотя есть случаи, когда это не так:
camelize(underscore('SSLError')) # => "SslError"
# File activesupport/lib/active_support/inflector/methods.rb, line 200 def classify(table_name) # strip out any leading schema name camelize(singularize(table_name.to_s.sub(/.*\./, ""))) end
Создаёт имя класса из имени таблицы в множественном числе, как это делает Rails для имён таблиц и моделей. Обратите внимание, что это возвращает строку, а не Класс (чтобы преобразовать в фактический класс, выполните classify с constantize).
classify('ham_and_eggs') # => "HamAndEgg"
classify('posts') # => "Post"
Имена в единственном числе не обрабатываются корректно:
classify('calculus') # => "Calculus"
# File activesupport/lib/active_support/inflector/methods.rb, line 271
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(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 Попытка найти константу с именем, указанным в строке аргумента.
constantize('Module') # => Module
constantize('Foo::Bar') # => Foo::Bar
Имя предполагается именем константы верхнего уровня, независимо от того, начинается ли оно с «::» или нет. Никакой лексический контекст не учитывается:
C = 'outside'
module M
C = 'inside'
C # => 'inside'
constantize('C') # => 'outside', same as ::C
end
NameError генерируется, когда имя не в формате CamelCase или константа неизвестна.
# File activesupport/lib/active_support/inflector/methods.rb, line 208
def dasherize(underscored_word)
underscored_word.tr("_", "-")
end Заменяет все символы подчеркивания на дефисы в строке.
dasherize('puni_puni') # => "puni-puni"
# File activesupport/lib/active_support/inflector/methods.rb, line 238
def deconstantize(path)
path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
end Удаляет правую самую часть константного выражения из строки.
deconstantize('Net::HTTP') # => "Net"
deconstantize('::Net::HTTP') # => "::Net"
deconstantize('String') # => ""
deconstantize('::String') # => ""
deconstantize('') # => ""
См. также demodulize.
# File activesupport/lib/active_support/inflector/methods.rb, line 220
def demodulize(path)
path = path.to_s
if i = path.rindex("::")
path[(i + 2)..-1]
else
path
end
end Удаляет часть модуля из выражения в строке.
demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
demodulize('Inflections') # => "Inflections"
demodulize('::Inflections') # => "Inflections"
demodulize('') # => ""
См. также deconstantize.
# File activesupport/lib/active_support/inflector/methods.rb, line 249 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
Создаёт имя внешнего ключа из имени класса. separate_class_name_and_id_with_underscore указывает, следует ли методу вставлять '_' между именем и 'id'.
foreign_key('Message') # => "message_id"
foreign_key('Message', false) # => "messageid"
foreign_key('Admin::Post') # => "post_id"
# File activesupport/lib/active_support/inflector/methods.rb, line 128
def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
result = lower_case_and_underscored_word.to_s.dup
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
result.sub!(/\A_+/, "")
unless keep_id_suffix
result.sub!(/_id\z/, "")
end
result.tr!("_", " ")
result.gsub!(/([a-z\d]*)/i) do |match|
"#{inflections.acronyms[match.downcase] || match.downcase}"
end
if capitalize
result.sub!(/\A\w/) { |match| match.upcase }
end
result
end Настраивает имя атрибута для отображения пользователям.
В частности, выполняет следующие преобразования:
-
Применяет правила склонения к аргументу.
-
Удаляет начальные символы подчеркивания, если они есть.
-
Удаляет суффикс «_id», если он есть.
-
Заменяет символы подчеркивания пробелами, если они есть.
-
Приводит все слова к нижнему регистру, кроме аббревиатур.
-
Приводит первое слово к верхнему регистру.
Регистр первого слова можно отключить, установив значение :capitalize в false (по умолчанию true).
Завершающий «_id» можно сохранить и привести к верхнему регистру, установив необязательный параметр keep_id_suffix в true (по умолчанию false).
humanize('employee_salary') # => "Employee salary"
humanize('author_id') # => "Author"
humanize('author_id', capitalize: false) # => "author"
humanize('_id') # => "Id"
humanize('author_id', keep_id_suffix: true) # => "Author Id"
Если «SSL» определено как аббревиатура:
humanize('ssl_error') # => "SSL error"
# File activesupport/lib/active_support/inflector/inflections.rb, line 249
def inflections(locale = :en)
if block_given?
yield Inflections.instance(locale)
else
Inflections.instance(locale)
end
end Возвращает единственный экземпляр Inflector::Inflections, позволяющий указать дополнительные правила склонения. Если передан необязательный параметр locale, можно указать правила для других языков. Если не указан, по умолчанию используется :en. Предоставлены только правила для английского языка.
ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.uncountable 'rails' end
# File activesupport/lib/active_support/inflector/methods.rb, line 344
def ordinal(number)
I18n.translate("number.nth.ordinals", number: number)
end Возвращает суффикс, который следует добавить к числу, чтобы обозначить позицию в упорядоченной последовательности, например, 1-й, 2-й, 3-й, 4-й.
ordinal(1) # => "st" ordinal(2) # => "nd" ordinal(1002) # => "nd" ordinal(1003) # => "rd" ordinal(-11) # => "th" ordinal(-1021) # => "st"
# File activesupport/lib/active_support/inflector/methods.rb, line 357
def ordinalize(number)
I18n.translate("number.nth.ordinalized", number: number)
end Преобразует число в строку порядкового числа, используемую для обозначения позиции в упорядоченной последовательности, например, 1-й, 2-й, 3-й, 4-й.
ordinalize(1) # => "1st" ordinalize(2) # => "2nd" ordinalize(1002) # => "1002nd" ordinalize(1003) # => "1003rd" ordinalize(-11) # => "-11th" ordinalize(-1021) # => "-1021st"
# File activesupport/lib/active_support/inflector/transliterate.rb, line 121
def parameterize(string, separator: "-", preserve_case: false, locale: nil)
# Replace accented chars with their ASCII equivalents.
parameterized_string = transliterate(string, locale: locale)
# Turn unwanted chars into the separator.
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
unless separator.nil? || separator.empty?
if separator == "-"
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, "")
end
parameterized_string.downcase! unless preserve_case
parameterized_string
end Заменяет специальные символы в строке, чтобы её можно было использовать в качестве части «красивого» URL.
parameterize("Donald E. Knuth") # => "donald-e-knuth"
parameterize("^très|Jolie-- ") # => "tres-jolie"
Чтобы использовать пользовательский разделитель, переопределите аргумент separator.
parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
Чтобы сохранить регистр символов в строке, используйте аргумент preserve_case.
parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
parameterize("^très|Jolie-- ", preserve_case: true) # => "tres-Jolie"
Сохраняются дефисы и подчеркивания, если они не используются в качестве разделителей:
parameterize("^très|Jolie__ ") # => "tres-jolie__"
parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
Если указан необязательный параметр locale, слово будет параметризовано как слово этого языка. По умолчанию этот параметр устанавливается в nil, и он будет использовать настроенный <tt>I18n.locale<tt>.
# File activesupport/lib/active_support/inflector/methods.rb, line 31 def pluralize(word, locale = :en) apply_inflections(word, inflections(locale).plurals, locale) end
Возвращает форму множественного числа слова в строке.
Если передан необязательный параметр locale, слово будет сведено к форме множественного числа с использованием правил, определённых для этого языка. По умолчанию этот параметр устанавливается в :en.
pluralize('post') # => "posts"
pluralize('octopus') # => "octopi"
pluralize('sheep') # => "sheep"
pluralize('words') # => "words"
pluralize('CamelOctopus') # => "CamelOctopi"
pluralize('ley', :es) # => "leyes"
# File activesupport/lib/active_support/inflector/methods.rb, line 324
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 /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
rescue LoadError => e
raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
end Попытка найти константу с именем, указанным в строке аргумента.
safe_constantize('Module') # => Module
safe_constantize('Foo::Bar') # => Foo::Bar
Имя предполагается именем константы верхнего уровня, независимо от того, начинается ли оно с «::» или нет. Никакой лексический контекст не учитывается:
C = 'outside'
module M
C = 'inside'
C # => 'inside'
safe_constantize('C') # => 'outside', same as ::C
end
Возвращается nil, если имя не в формате CamelCase или константа (или часть её) неизвестна.
safe_constantize('blargle') # => nil
safe_constantize('UnknownModule') # => nil
safe_constantize('UnknownModule::Foo::Bar') # => nil
# File activesupport/lib/active_support/inflector/methods.rb, line 48 def singularize(word, locale = :en) apply_inflections(word, inflections(locale).singulars, locale) end
Обратная функция pluralize, возвращает форму единственного числа слова в строке.
Если передан необязательный параметр locale, слово будет приведено к форме единственного числа с использованием правил, определённых для этого языка. По умолчанию этот параметр устанавливается в :en.
singularize('posts') # => "post"
singularize('octopi') # => "octopus"
singularize('sheep') # => "sheep"
singularize('word') # => "word"
singularize('CamelOctopi') # => "CamelOctopus"
singularize('leyes', :es) # => "ley"
# File activesupport/lib/active_support/inflector/methods.rb, line 186 def tableize(class_name) pluralize(underscore(class_name)) end
Создаёт имя таблицы, как Rails создаёт имена таблиц для моделей. Этот метод использует метод pluralize для последнего слова в строке.
tableize('RawScaledScorer') # => "raw_scaled_scorers"
tableize('ham_and_egg') # => "ham_and_eggs"
tableize('fancyCategory') # => "fancy_categories"
# File activesupport/lib/active_support/inflector/methods.rb, line 174
def titleize(word, keep_id_suffix: false)
humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`])[a-z]/) do |match|
match.capitalize
end
end Преобразует все слова в заглавные и заменяет некоторые символы в строке для создания более красивого заголовка. titleize предназначен для создания красивого вывода. Он не используется во внутренних компонентах Rails.
Конечные символы «_id», «Id» и т. д. могут быть сохранены и приведены к заглавным буквам, установив необязательный параметр keep_id_suffix в значение true. По умолчанию этот параметр равен false.
titleize также имеет псевдоним titlecase.
titleize('man from the boondocks') # => "Man From The Boondocks"
titleize('x-men: the last stand') # => "X Men: The Last Stand"
titleize('TheManWithoutAPast') # => "The Man Without A Past"
titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
# File activesupport/lib/active_support/inflector/transliterate.rb, line 62
def transliterate(string, replacement = "?", locale: nil)
string = string.dup if string.frozen?
raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
allowed_encodings = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030]
raise ArgumentError, "Can not transliterate strings with #{string.encoding} encoding" unless allowed_encodings.include?(string.encoding)
input_encoding = string.encoding
# US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
# US-ASCII is given. This way we can let tidy_bytes handle the string
# in the same way as we do for UTF-8
string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII
# GB18030 is Unicode compatible but is not a direct mapping so needs to be
# transcoded. Using invalid/undef :replace will result in loss of data in
# the event of invalid characters, but since tidy_bytes will replace
# invalid/undef with a "?" we're safe to do the same beforehand
string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030
transliterated = I18n.transliterate(
ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
replacement: replacement,
locale: locale
)
# Restore the string encoding of the input if it was not UTF-8.
# Apply invalid/undef :replace as tidy_bytes does
transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding
transliterated
end Заменяет символы, не являющиеся ASCII, на их приближённые ASCII-аналоги, или, если таких аналогов нет, на символ замены, по умолчанию равный «?».
transliterate('Ærøskøbing')
# => "AEroskobing"
Предоставляются приближения по умолчанию для западных/латинских символов, например, «ø», «ñ», «é», «ß» и т. д.
Этот метод учитывает I18n, поэтому вы можете настроить пользовательские приближения для конкретного языка. Это может быть полезно, например, для транслитерации немецких «ü» и «ö» в «ue» и «oe» или для добавления поддержки транслитерации русского языка в ASCII.
Чтобы ваши пользовательские транслитерации были доступны, вы должны установить их в качестве ключа i18n.transliterate.rule в i18n:
# Store the transliterations in locales/de.yml
i18n:
transliterate:
rule:
ü: "ue"
ö: "oe"
# Or set them using Ruby
I18n.backend.store_translations(:de, i18n: {
transliterate: {
rule: {
'ü' => 'ue',
'ö' => 'oe'
}
}
}) Значение для i18n.transliterate.rule может быть простым словарём, который сопоставляет символы с их ASCII-аналогами, как показано выше, или, для более сложных требований, функцией:
I18n.backend.store_translations(:de, i18n: {
transliterate: {
rule: ->(string) { MyTransliterator.transliterate(string) }
}
})
Теперь у вас могут быть разные транслитерации для каждого языка:
transliterate('Jürgen', locale: :en)
# => "Jurgen"
transliterate('Jürgen', locale: :de)
# => "Juergen"
Транслитерация ограничена строками UTF-8, US-ASCII и GB18030. Другие кодировки вызовут исключение ArgumentError.
# File activesupport/lib/active_support/inflector/methods.rb, line 91
def underscore(camel_cased_word)
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
word = camel_cased_word.to_s.gsub("::", "/")
word.gsub!(inflections.acronyms_underscore_regex) { "#{$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 Создаёт нижнестрочную версию выражения в строке, используя нижние подчёркивания.
Заменяет «::» на «/» для преобразования пространств имён в пути.
underscore('ActiveModel') # => "active_model"
underscore('ActiveModel::Errors') # => "active_model/errors"
В общем случае, можно считать, что underscore является обратным преобразованием к camelize, хотя есть случаи, когда это не так:
camelize(underscore('SSLError')) # => "SslError"
# File activesupport/lib/active_support/inflector/methods.rb, line 155 def upcase_first(string) string.length > 0 ? string[0].upcase.concat(string[1..-1]) : "" end
Преобразует только первый символ в верхний регистр.
upcase_first('what a Lovely Day') # => "What a Lovely Day"
upcase_first('w') # => "W"
upcase_first('') # => ""
© 2004–2019 David Heinemeier Hansson
Licensed under the MIT License.