module Fusu::String::Methods

Constants

ACRONYM_REGEX

Public Instance Methods

camelize(term, uppercase_first_letter = true) click to toggle source

Converts strings to UpperCamelCase. If the uppercase_first_letter parameter is set to false, then produces lowerCamelCase.

Also converts '/' to '::' which is useful for converting paths to namespaces.

camelize('active_model')                # => "ActiveModel"
camelize('active_model', false)         # => "activeModel"
camelize('active_model/errors')         # => "ActiveModel::Errors"
camelize('active_model/errors', false)  # => "activeModel::Errors"

As a rule of thumb you can think of camelize as the inverse of underscore, though there are cases where that does not hold:

camelize(underscore('SSLError'))        # => "SslError"
# File lib/fusu/string/methods.rb, line 33
def camelize(term, uppercase_first_letter = true)
  string = term.to_s
  if uppercase_first_letter
    string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
  else
    string = string.sub(/^(?:#{ACRONYM_REGEX}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
  end
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
  string.gsub!("/".freeze, "::".freeze)
  string
end
constantize(camel_cased_word) click to toggle source

Tries to find a constant with the name specified in the argument string.

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

The name is assumed to be the one of a top-level constant, no matter whether it starts with “::” or not. No lexical context is taken into account:

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

NameError is raised when the name is not in CamelCase or the constant is unknown.

# File lib/fusu/string/methods.rb, line 211
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
dasherize(underscored_word) click to toggle source

Replaces underscores with dashes in the string.

dasherize('puni_puni') # => "puni-puni"
# File lib/fusu/string/methods.rb, line 148
def dasherize(underscored_word)
  underscored_word.tr("_".freeze, "-".freeze)
end
deconstantize(path) click to toggle source

Removes the rightmost segment from the constant expression in the string.

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

See also demodulize.

# File lib/fusu/string/methods.rb, line 178
def deconstantize(path)
  path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
end
demodulize(path) click to toggle source

Removes the module part from the expression in the string.

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

See also deconstantize.

# File lib/fusu/string/methods.rb, line 160
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) click to toggle source

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'.

foreign_key('Message')        # => "message_id"
foreign_key('Message', false) # => "messageid"
foreign_key('Admin::Post')    # => "post_id"
# File lib/fusu/string/methods.rb, line 189
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) click to toggle source

Tweaks an attribute name for display to end users.

Specifically, 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).

The trailing '_id' can be kept and capitalized by setting the optional parameter keep_id_suffix to true (default is 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"

If “SSL” was defined to be an acronym:

humanize('ssl_error') # => "SSL error"
# File lib/fusu/string/methods.rb, line 102
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_+/, "".freeze)
  unless keep_id_suffix
    result.sub!(/_id\z/, "".freeze)
  end
  result.tr!("_".freeze, " ".freeze)

  result.gsub!(/([a-z\d]*)/i) do |match|
    "#{match.downcase}"
  end

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

  result
end
ordinal(number) click to toggle source

Returns the suffix that should be added to a number to denote the position in an ordered sequence such as 1st, 2nd, 3rd, 4th.

ordinal(1)     # => "st"
ordinal(2)     # => "nd"
ordinal(1002)  # => "nd"
ordinal(1003)  # => "rd"
ordinal(-11)   # => "th"
ordinal(-1021) # => "st"
# File lib/fusu/string/methods.rb, line 282
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) click to toggle source

Turns a number into an ordinal string used to denote the position in an ordered sequence such as 1st, 2nd, 3rd, 4th.

ordinalize(1)     # => "1st"
ordinalize(2)     # => "2nd"
ordinalize(1002)  # => "1002nd"
ordinalize(1003)  # => "1003rd"
ordinalize(-11)   # => "-11th"
ordinalize(-1021) # => "-1021st"
# File lib/fusu/string/methods.rb, line 306
def ordinalize(number)
  "#{number}#{ordinal(number)}"
end
safe_constantize(camel_cased_word) click to toggle source

Tries to find a constant with the name specified in the argument string.

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

The name is assumed to be the one of a top-level constant, no matter whether it starts with “::” or not. No lexical context is taken into account:

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

nil is returned when the name is not in CamelCase or the constant (or part of it) is unknown.

safe_constantize('blargle')                  # => nil
safe_constantize('UnknownModule')            # => nil
safe_constantize('UnknownModule::Foo::Bar')  # => nil
# File lib/fusu/string/methods.rb, line 264
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)
end
titleize(word, keep_id_suffix: false) click to toggle source

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.

The trailing '_id','Id'.. can be kept and capitalized by setting the optional parameter keep_id_suffix to true. By default, this parameter is false.

titleize is also aliased as 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 lib/fusu/string/methods.rb, line 139
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
underscore(camel_cased_word) click to toggle source

Makes an underscored, lowercase form from the expression in the string.

Changes '::' to '/' to convert namespaces to paths.

underscore('ActiveModel')         # => "active_model"
underscore('ActiveModel::Errors') # => "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:

camelize(underscore('SSLError'))  # => "SslError"
# File lib/fusu/string/methods.rb, line 56
def underscore(camel_cased_word)
  return camel_cased_word unless Fusu::Regexp.match?(/[A-Z-]|::/, camel_cased_word)
  word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
  word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{ACRONYM_REGEX})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }" }
  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
upcase_first(string) click to toggle source

Converts just the first character to uppercase.

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

Private Instance Methods

const_regexp(camel_cased_word) click to toggle source

Mounts a regular expression, returned as a string to ease interpolation, that will match part by part the given constant.

const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
const_regexp("::")            # => "::"
# File lib/fusu/string/methods.rb, line 317
def const_regexp(camel_cased_word)
  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