跳至内容 跳至搜索

Active Support Inflector

The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept in inflections.rb.

The Rails core team has stated patches for the inflections library will not be accepted in order to avoid breaking legacy applications which may be relying on errant inflections. If you discover an incorrect inflection and require it for your application or wish to define rules for languages other than English, please correct or add them yourself (explained below).

命名空间
方法
C
D
F
H
I
O
P
S
T
U

常量

ALLOWED_ENCODINGS_FOR_TRANSLITERATE = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030].freeze
 

实例公共方法

camelize(term, uppercase_first_letter = true)

将字符串转换为 UpperCamelCase。如果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 70
def camelize(term, uppercase_first_letter = true)
  string = term.to_s
  # String#camelize takes a symbol (:upper or :lower), so here we also support :lower to keep the methods consistent.
  if !uppercase_first_letter || uppercase_first_letter == :lower
    string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase! || match }
  elsif string.match?(/\A[a-z\d]*\z/)
    return inflections.acronyms[string]&.dup || string.capitalize
  else
    string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize! || match }
  end
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
    word = $2
    substituted = inflections.acronyms[word] || word.capitalize! || word
    $1 ? "::#{substituted}" : substituted
  end
  string
end

classify(table_name)

从复数表名创建类名,就像 Rails 对表名到模型所做的那样。请注意,这将返回一个字符串,而不是一个 。(要转换为实际的类,请在classify 后面使用 constantize。)

classify('ham_and_eggs') # => "HamAndEgg"
classify('posts')        # => "Post"

单数名称处理不正确

classify('calculus')     # => "Calculu"
# File activesupport/lib/active_support/inflector/methods.rb, line 218
def classify(table_name)
  # strip out any leading schema name
  camelize(singularize(table_name.to_s.sub(/.*\./, "")))
end

constantize(camel_cased_word)

尝试查找与参数字符串中指定名称相同的常量。

constantize('Module')   # => Module
constantize('Foo::Bar') # => Foo::Bar

无论名称是否以“::”开头,都假定它是顶级常量的名称。不考虑任何词法上下文

C = 'outside'
module M
  C = 'inside'
  C                # => 'inside'
  constantize('C') # => 'outside', same as ::C
end

当名称不是 CamelCase 或常量未知时,会引发 NameError

# File activesupport/lib/active_support/inflector/methods.rb, line 289
def constantize(camel_cased_word)
  Object.const_get(camel_cased_word)
end

dasherize(underscored_word)

将字符串中的下划线替换为连字符。

dasherize('puni_puni') # => "puni-puni"
# File activesupport/lib/active_support/inflector/methods.rb, line 226
def dasherize(underscored_word)
  underscored_word.tr("_", "-")
end

deconstantize(path)

从字符串中的常量表达式中删除最右边的段。

deconstantize('Net::HTTP')   # => "Net"
deconstantize('::Net::HTTP') # => "::Net"
deconstantize('String')      # => ""
deconstantize('::String')    # => ""
deconstantize('')            # => ""

另请参见 demodulize

# File activesupport/lib/active_support/inflector/methods.rb, line 256
def deconstantize(path)
  path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
end

demodulize(path)

从字符串中的表达式中删除模块部分。

demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
demodulize('Inflections')                           # => "Inflections"
demodulize('::Inflections')                         # => "Inflections"
demodulize('')                                      # => ""

另请参见 deconstantize

# File activesupport/lib/active_support/inflector/methods.rb, line 238
def demodulize(path)
  path = path.to_s
  if i = path.rindex("::")
    path[(i + 2), path.length]
  else
    path
  end
end

downcase_first(string)

将字符串中的第一个字符转换为小写。

downcase_first('If they enjoyed The Matrix') # => "if they enjoyed The Matrix"
downcase_first('I')                          # => "i"
downcase_first('')                           # => ""
# File activesupport/lib/active_support/inflector/methods.rb, line 175
def downcase_first(string)
  string.length > 0 ? string[0].downcase.concat(string[1..-1]) : +""
end

foreign_key(class_name, separate_class_name_and_id_with_underscore = true)

从类名创建外键名称。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 267
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, capitalize: true, keep_id_suffix: false)

调整属性名称以供最终用户显示。

具体来说,执行以下转换

  • 将人类变格规则应用于参数。

  • 删除任何前导下划线。

  • 如果存在,删除“_id”后缀。

  • 将任何下划线替换为空格。

  • 将所有单词(除缩略词外)转换为小写。

  • 将第一个单词的首字母大写。

可以通过将:capitalize 选项设置为 false 来关闭第一个单词的首字母大写(默认为 true)。

可以通过将可选参数keep_id_suffix 设置为 true 来保留尾部的“_id”并将其大写(默认为 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/methods.rb, line 135
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.tr!("_", " ")
  result.lstrip!
  if !keep_id_suffix && lower_case_and_underscored_word&.end_with?("_id")
    result.delete_suffix!(" id")
  end

  result.gsub!(/([a-z\d]+)/i) do |match|
    match.downcase!
    inflections.acronyms[match] || match
  end

  if capitalize
    result.sub!(/\A\w/) do |match|
      match.upcase!
      match
    end
  end

  result
end

inflections(locale = :en)

生成 Inflector::Inflections 的单例实例,以便您可以指定其他变格规则。如果传递了可选的语言环境,则可以指定其他语言的规则。如果未指定,则默认为:en。仅提供英语的规则。

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.uncountable 'rails'
end
# File activesupport/lib/active_support/inflector/inflections.rb, line 265
def inflections(locale = :en)
  if block_given?
    yield Inflections.instance(locale)
  else
    Inflections.instance_or_fallback(locale)
  end
end

ordinal(number)

返回应添加到数字后面的后缀,以表示在有序序列中的位置,例如第 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 334
def ordinal(number)
  I18n.translate("number.nth.ordinals", number: number)
end

ordinalize(number)

将数字转换为序数字符串,用于表示在有序序列中的位置,例如第 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/methods.rb, line 347
def ordinalize(number)
  I18n.translate("number.nth.ordinalized", number: number)
end

parameterize(string, separator: "-", preserve_case: false, locale: nil)

替换字符串中的特殊字符,使其可作为“漂亮”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,它将使用配置的I18n.locale

# File activesupport/lib/active_support/inflector/transliterate.rb, line 123
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

pluralize(word, locale = :en)

返回字符串中单词的复数形式。

如果传递了可选的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 33
def pluralize(word, locale = :en)
  apply_inflections(word, inflections(locale).plurals, locale)
end

safe_constantize(camel_cased_word)

尝试查找与参数字符串中指定名称相同的常量。

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

当名称不是 CamelCase 或常量(或其一部分)未知时,将返回nil

safe_constantize('blargle')                  # => nil
safe_constantize('UnknownModule')            # => nil
safe_constantize('UnknownModule::Foo::Bar')  # => nil
# File activesupport/lib/active_support/inflector/methods.rb, line 315
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 LoadError => e
  message = e.respond_to?(:original_message) ? e.original_message : e.message
  raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(message)
end

singularize(word, locale = :en)

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 50
def singularize(word, locale = :en)
  apply_inflections(word, inflections(locale).singulars, locale)
end

tableize(class_name)

创建表名,类似 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 204
def tableize(class_name)
  pluralize(underscore(class_name))
end

titleize(word, keep_id_suffix: false)

将所有单词首字母大写,并替换字符串中的一些字符,以创建更美观的标题。titleize 用于创建漂亮的输出,不会在 Rails 内部使用。

可以通过将可选参数 keep_id_suffix 设置为 true 来保留并大写结尾的 “_id”、"Id" 等。默认情况下,此参数为 false。

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/methods.rb, line 192
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

transliterate(string, replacement = "?", locale: nil)

用 ASCII 近似值替换非 ASCII 字符,如果不存在近似值,则用替换字符,默认为 “?”。

transliterate('Ærøskøbing')
# => "AEroskobing"

为西方/拉丁字符提供默认近似值,例如,"ø"、"ñ"、"é"、"ß" 等。

此方法支持国际化,因此您可以为区域设置设置自定义近似值。例如,这对于将德语的 "ü" 和 "ö" 转写为 "ue" 和 "oe" 很有用,或者为将俄语转写为 ASCII 添加支持。

为了使自定义转写可用,您必须将它们设置为 i18n.transliterate.rule 国际化键。

# 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 的值可以是简单的 Hash,它将字符映射到 ASCII 近似值,如上所示,或者,对于更复杂的要求,可以使用 Proc。

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/transliterate.rb, line 64
def transliterate(string, replacement = "?", locale: nil)
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
  raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)

  return string.dup if string.ascii_only?
  string = string.dup if string.frozen?

  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

underscore(camel_cased_word)

将字符串中的表达式转换为带下划线的小写形式。

将 "::" 更改为 "/",将命名空间转换为路径。

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 99
def underscore(camel_cased_word)
  return camel_cased_word.to_s.dup 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])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
  word.tr!("-", "_")
  word.downcase!
  word
end

upcase_first(string)

将字符串中第一个字符转换为大写。

upcase_first('what a Lovely Day') # => "What a Lovely Day"
upcase_first('w')                 # => "W"
upcase_first('')                  # => ""
# File activesupport/lib/active_support/inflector/methods.rb, line 166
def upcase_first(string)
  string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
end