332 lines
7.8 KiB
Ruby
332 lines
7.8 KiB
Ruby
require 'set'
|
|
|
|
# The Inflecto 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, you'll need
|
|
# to correct it yourself (explained below).
|
|
module Inflecto
|
|
|
|
# Convert input to UpperCamelCase
|
|
#
|
|
# Will also convert '/' to '::' which is useful for converting paths to namespaces.
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# @example
|
|
# Inflecto.camelize("data_mapper") # => "DataMapper"
|
|
# Inflecto.camelize("data_mapper/errors") # => "DataMapper::Errors"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.camelize(input)
|
|
input.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:\A|_)(.)/) { $1.upcase }
|
|
end
|
|
|
|
# Convert input to underscored, lowercase string
|
|
#
|
|
# Changes '::' to '/' to convert namespaces to paths.
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# @example
|
|
# Inflecto.underscore("DataMapper") # => "data_mapper"
|
|
# Inflecto.underscore("DataMapper::Errors") # => "data_mapper/errors"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.underscore(input)
|
|
word = input.gsub(/::/, '/')
|
|
underscorize(word)
|
|
end
|
|
|
|
# Convert input underscores to dashes
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# @example
|
|
# Inflecto.dasherize("foo_bar") # => "foo-bar"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.dasherize(input)
|
|
input.tr('_', '-')
|
|
end
|
|
|
|
# Return unscoped constant name
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.demodulize("DataMapper::Error") # => "Error"
|
|
# Inflecto.demodulize("DataMapper") # => "DataMapper"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.demodulize(input)
|
|
input.split('::').last
|
|
end
|
|
|
|
# Creates a foreign key name
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.foreign_key("Message") => "message_id"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.foreign_key(input)
|
|
"#{underscorize(demodulize(input))}_id"
|
|
end
|
|
|
|
# Find a constant with the name specified in the argument string
|
|
#
|
|
# The name is assumed to be the one of a top-level constant, constant scope of caller is ignored
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.constantize("Module") # => Module
|
|
# Inflecto.constantize("DataMapper::Error") # => DataMapper::Error
|
|
#
|
|
# @return [Class, Module]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.constantize(input)
|
|
names = input.split('::')
|
|
names.shift if names.first.empty?
|
|
|
|
names.inject(Object) do |constant, name|
|
|
if constant.const_defined?(name, false)
|
|
constant.const_get(name)
|
|
else
|
|
constant.const_missing(name)
|
|
end
|
|
end
|
|
end
|
|
|
|
ORDINALIZE_TH = (4..16).to_set.freeze
|
|
|
|
# Convert a number into an ordinal string
|
|
#
|
|
# @param [Fixnum] number
|
|
#
|
|
# @example
|
|
#
|
|
# ordinalize(1) # => "1st"
|
|
# ordinalize(2) # => "2nd"
|
|
# ordinalize(1002) # => "1002nd"
|
|
# ordinalize(1003) # => "1003rd"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.ordinalize(number)
|
|
abs_value = number.abs
|
|
|
|
if ORDINALIZE_TH.include?(abs_value % 100)
|
|
"#{number}th"
|
|
else
|
|
case abs_value % 10
|
|
when 1; "#{number}st"
|
|
when 2; "#{number}nd"
|
|
when 3; "#{number}rd"
|
|
end
|
|
end
|
|
end
|
|
|
|
# Yields a singleton instance of Inflecto::Inflections
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.inflections do |inflect|
|
|
# inflect.uncountable "rails"
|
|
# end
|
|
#
|
|
# @return [Inflecto::Inflections]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.inflections
|
|
instance = Inflections.instance
|
|
block_given? ? yield(instance) : instance
|
|
end
|
|
|
|
# Convert input word string to plural
|
|
#
|
|
# @param [String] word
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.pluralize("post") # => "posts"
|
|
# Inflecto.pluralize("octopus") # => "octopi"
|
|
# Inflecto.pluralize("sheep") # => "sheep"
|
|
# Inflecto.pluralize("words") # => "words"
|
|
# Inflecto.pluralize("CamelOctopus") # => "CamelOctopi"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.pluralize(word)
|
|
return word if uncountable?(word)
|
|
inflections.plurals.apply_to(word)
|
|
end
|
|
|
|
# Convert word to singular
|
|
#
|
|
# @param [String] word
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.singularize("posts") # => "post"
|
|
# Inflecto.singularize("octopi") # => "octopus"
|
|
# Inflecto.singularize("sheep") # => "sheep"
|
|
# Inflecto.singularize("word") # => "word"
|
|
# Inflecto.singularize("CamelOctopi") # => "CamelOctopus"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.singularize(word)
|
|
return word if uncountable?(word)
|
|
inflections.singulars.apply_to(word)
|
|
end
|
|
|
|
# Humanize string
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# capitalizes the first word and turns underscores into spaces and strips a # trailing "_id", if any.
|
|
# Like +titleize+, this is meant for creating pretty output.
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.humanize("employee_salary") # => "Employee salary"
|
|
# Inflecto.humanize("author_id") # => "Author"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.humanize(input)
|
|
result = inflections.humans.apply_to(input)
|
|
result.gsub!(/_id\z/, "")
|
|
result.tr!('_', " ")
|
|
result.capitalize!
|
|
result
|
|
end
|
|
|
|
# Tabelize input string
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# 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.
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.tableize("RawScaledScorer") # => "raw_scaled_scorers"
|
|
# Inflecto.tableize("egg_and_ham") # => "egg_and_hams"
|
|
# Inflecto.tableize("fancyCategory") # => "fancy_categories"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.tableize(input)
|
|
word = input.gsub(/::/, '_')
|
|
pluralize(underscorize(word))
|
|
end
|
|
|
|
# Classify input
|
|
#
|
|
# 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+.
|
|
#
|
|
# @examples:
|
|
#
|
|
# Inflecto.classify("egg_and_hams") # => "EggAndHam"
|
|
# Inflecto.classify("posts") # => "Post"
|
|
#
|
|
# # Singular names are not handled correctly:
|
|
# Inflecto.classify("business") # => "Busines"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api public
|
|
#
|
|
def self.classify(table_name)
|
|
# strip out any leading schema name
|
|
camelize(singularize(table_name.sub(/.*\./, '')))
|
|
end
|
|
|
|
# Test if word is uncountable
|
|
#
|
|
# @example
|
|
#
|
|
# Inflecto.uncountable?('rice') #=> true
|
|
# Inflecto.uncountable?('apple') #=> false
|
|
#
|
|
# @param [String] word
|
|
#
|
|
# @return [Boolean]
|
|
# true, if word is uncountable
|
|
#
|
|
# @api public
|
|
#
|
|
def self.uncountable?(word)
|
|
word.empty? || inflections.uncountables.include?(word.downcase)
|
|
end
|
|
|
|
# Convert input to underscored, lowercase string
|
|
#
|
|
# Contains main logic for .underscore and .tableize
|
|
# Does nothing with '::' divider
|
|
#
|
|
# @param [String] input
|
|
#
|
|
# @example
|
|
# Inflecto.underscorize("DataMapper") # => "data_mapper"
|
|
# Inflecto.underscorize("DataMapper::Errors") # => "data_mapper::errors"
|
|
#
|
|
# @return [String]
|
|
#
|
|
# @api private
|
|
#
|
|
def self.underscorize(word)
|
|
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
|
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
|
word.tr!('-', '_')
|
|
word.downcase!
|
|
word
|
|
end
|
|
private_class_method :underscorize
|
|
end
|
|
|
|
require 'inflecto/rules_collection'
|
|
require 'inflecto/inflections'
|
|
require 'inflecto/defaults'
|