Correct vendoring of inflector

This commit is contained in:
Markus Schirp 2012-12-07 13:08:55 +01:00
parent 49dba174ad
commit 8ef816d012
6 changed files with 278 additions and 425 deletions

7
lib/inflector.rb Normal file
View file

@ -0,0 +1,7 @@
# Library namespace
module Inflector
end
require 'inflector/inflections'
require 'inflector/defaults'
require 'inflector/methods'

62
lib/inflector/defaults.rb Normal file
View file

@ -0,0 +1,62 @@
module Inflector
Inflector::Inflections.instance.instance_eval do
plural(/$/, 's')
plural(/s$/i, 's')
plural(/us$/i, 'i')
plural(/(ax|test)is$/i, '\1es')
plural(/(octop|vir)us$/i, '\1i')
plural(/(octop|vir)i$/i, '\1i')
plural(/(alias|status)$/i, '\1es')
plural(/(bu)s$/i, '\1ses')
plural(/(buffal|tomat)o$/i, '\1oes')
plural(/([ti])um$/i, '\1a')
plural(/([ti])a$/i, '\1a')
plural(/sis$/i, 'ses')
plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
plural(/(hive)$/i, '\1s')
plural(/([^aeiouy]|qu)y$/i, '\1ies')
plural(/(x|ch|ss|sh)$/i, '\1es')
plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
plural(/([m|l])ouse$/i, '\1ice')
plural(/([m|l])ice$/i, '\1ice')
plural(/^(ox)$/i, '\1en')
plural(/^(oxen)$/i, '\1')
plural(/(quiz)$/i, '\1zes')
singular(/s$/i, '')
singular(/i$/i, 'us')
singular(/(n)ews$/i, '\1ews')
singular(/([ti])a$/i, '\1um')
singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
singular(/(^analy)ses$/i, '\1sis')
singular(/([^f])ves$/i, '\1fe')
singular(/(hive)s$/i, '\1')
singular(/(tive)s$/i, '\1')
singular(/([lr])ves$/i, '\1f')
singular(/([^aeiouy]|qu)ies$/i, '\1y')
singular(/(s)eries$/i, '\1eries')
singular(/(m)ovies$/i, '\1ovie')
singular(/(x|ch|ss|sh)es$/i, '\1')
singular(/([m|l])ice$/i, '\1ouse')
singular(/(bus)es$/i, '\1')
singular(/(o)es$/i, '\1')
singular(/(shoe)s$/i, '\1')
singular(/(cris|ax|test)es$/i, '\1is')
singular(/(octop|vir)i$/i, '\1us')
singular(/(alias|status)es$/i, '\1')
singular(/^(ox)en/i, '\1')
singular(/(vert|ind)ices$/i, '\1ex')
singular(/(matr)ices$/i, '\1ix')
singular(/(quiz)zes$/i, '\1')
singular(/(database)s$/i, '\1')
irregular('person', 'people')
irregular('man', 'men')
irregular('child', 'children')
irregular('sex', 'sexes')
irregular('move', 'moves')
irregular('cow', 'kine')
uncountable(%w(grass equipment information rice money species series fish sheep jeans))
end
end

View file

@ -0,0 +1,209 @@
module Inflector
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
# inflection rules. Examples:
#
# Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1\2en'
# inflect.singular /^(ox)en/i, '\1'
#
# inflect.irregular 'octopus', 'octopi'
#
# inflect.uncountable "equipment"
# end
#
# New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
# pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
# already have been loaded.
class Inflections
def self.instance
@__instance__ ||= new
end
attr_reader :plurals, :singulars, :uncountables, :humans
def initialize
@plurals, @singulars, @uncountables, @humans = [], [], [], []
end
# Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
# The replacement should always be a string that may include references to the matched data from the rule.
def plural(rule, replacement)
@uncountables.delete(rule) if rule.is_a?(String)
@uncountables.delete(replacement)
@plurals.insert(0, [rule, replacement])
end
# Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
# The replacement should always be a string that may include references to the matched data from the rule.
def singular(rule, replacement)
@uncountables.delete(rule) if rule.is_a?(String)
@uncountables.delete(replacement)
@singulars.insert(0, [rule, replacement])
end
# Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
# for strings, not regular expressions. You simply pass the irregular in singular and plural form.
#
# Examples:
# irregular 'octopus', 'octopi'
# irregular 'person', 'people'
def irregular(singular, plural)
@uncountables.delete(singular)
@uncountables.delete(plural)
if singular[0,1].upcase == plural[0,1].upcase
plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
else
plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
end
end
# Add uncountable words that shouldn't be attempted inflected.
#
# Examples:
# uncountable "money"
# uncountable "money", "information"
# uncountable %w( money information rice )
def uncountable(*words)
(@uncountables << words).flatten!
end
# Specifies a humanized form of a string by a regular expression rule or by a string mapping.
# When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
# When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
#
# Examples:
# human /_cnt$/i, '\1_count'
# human "legacy_col_person_name", "Name"
def human(rule, replacement)
@humans.insert(0, [rule, replacement])
end
# Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
# Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
# <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
#
# Examples:
# clear :all
# clear :plurals
def clear(scope = :all)
case scope
when :all
@plurals, @singulars, @uncountables = [], [], []
else
instance_variable_set "@#{scope}", []
end
end
end
# Yields a singleton instance of Inflector::Inflections so you can specify additional
# inflector rules.
#
# Example:
# Inflector.inflections do |inflect|
# inflect.uncountable "rails"
# end
def inflections
if block_given?
yield Inflections.instance
else
Inflections.instance
end
end
# Returns the plural form of the word in the string.
#
# Examples:
# "post".pluralize # => "posts"
# "octopus".pluralize # => "octopi"
# "sheep".pluralize # => "sheep"
# "words".pluralize # => "words"
# "CamelOctopus".pluralize # => "CamelOctopi"
def pluralize(word)
result = word.to_s.dup
if word.empty? || inflections.uncountables.include?(result.downcase)
result
else
inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
result
end
end
# The reverse of +pluralize+, returns the singular form of a word in a string.
#
# Examples:
# "posts".singularize # => "post"
# "octopi".singularize # => "octopus"
# "sheep".singularize # => "sheep"
# "word".singularize # => "word"
# "CamelOctopi".singularize # => "CamelOctopus"
def singularize(word)
result = word.to_s.dup
if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
result
else
inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
result
end
end
# 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.
#
# Examples:
# "employee_salary" # => "Employee salary"
# "author_id" # => "Author"
def humanize(lower_case_and_underscored_word)
result = lower_case_and_underscored_word.to_s.dup
inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
end
# 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.
#
# +titleize+ is also aliased as as +titlecase+.
#
# Examples:
# "man from the boondocks".titleize # => "Man From The Boondocks"
# "x-men: the last stand".titleize # => "X Men: The Last Stand"
def titleize(word)
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
end
# 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.
#
# Examples
# "RawScaledScorer".tableize # => "raw_scaled_scorers"
# "egg_and_ham".tableize # => "egg_and_hams"
# "fancyCategory".tableize # => "fancy_categories"
def tableize(class_name)
pluralize(underscore(class_name))
end
# 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:
# "egg_and_hams".classify # => "EggAndHam"
# "posts".classify # => "Post"
#
# Singular names are not handled correctly:
# "business".classify # => "Busines"
def classify(table_name)
# strip out any leading schema name
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
end
end

View file

@ -1,425 +0,0 @@
# = English Nouns Number Inflection.
#
# This module provides english singular <-> plural noun inflections.
module Inflector
# Take an underscored name and make it into a camelized name
#
# @example
# "egg_and_hams".classify #=> "EggAndHam"
# "enlarged_testes".classify #=> "EnlargedTestis"
# "post".classify #=> "Post"
#
def self.classify(name)
words = name.to_s.sub(/.*\./, '').split('_')
words[-1] = singularize(words[-1])
words.collect { |word| word.capitalize }.join
end
# By default, camelize converts strings to UpperCamelCase.
#
# camelize will also convert '/' to '::' which is useful for converting paths to namespaces
#
# @example
# "active_record".camelize #=> "ActiveRecord"
# "active_record/errors".camelize #=> "ActiveRecord::Errors"
#
def self.camelize(lower_case_and_underscored_word, *args)
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
end
# The reverse of +camelize+. Makes an underscored form from the expression in the string.
#
# Changes '::' to '/' to convert namespaces to paths.
#
# @example
# "ActiveRecord".underscore #=> "active_record"
# "ActiveRecord::Errors".underscore #=> active_record/errors
#
def self.underscore(camel_cased_word)
camel_cased_word.to_const_path
end
# Capitalizes the first word and turns underscores into spaces and strips _id.
# Like titleize, this is meant for creating pretty output.
#
# @example
# "employee_salary" #=> "Employee salary"
# "author_id" #=> "Author"
def self.humanize(lower_case_and_underscored_word)
lower_case_and_underscored_word.to_s.gsub(/_id$/, '').tr('_', ' ').capitalize
end
# Removes the module part from the expression in the string
#
# @example
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
# "Inflections".demodulize #=> "Inflections"
def self.demodulize(class_name_in_module)
class_name_in_module.to_s.gsub(/^.*::/, '')
end
# 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
# "RawScaledScorer".tableize #=> "raw_scaled_scorers"
# "EnlargedTestis".tableize #=> "enlarged_testes"
# "egg_and_ham".tableize #=> "egg_and_hams"
# "fancyCategory".tableize #=> "fancy_categories"
def self.tableize(class_name)
words = class_name.to_const_path.tr('/', '_').split('_')
words[-1] = pluralize(words[-1])
words.join('_')
end
# Creates a foreign key name from a class name.
#
# @example
# "Message".foreign_key #=> "message_id"
# "Admin::Post".foreign_key #=> "post_id"
def self.foreign_key(class_name, key = "id")
underscore(demodulize(class_name.to_s)) << "_" << key.to_s
end
# Constantize tries to find a declared constant with the name specified
# in the string. It raises a NameError when the name is not in CamelCase
# or is not initialized.
#
# @example
# "Module".constantize #=> Module
# "Class".constantize #=> Class
def self.constantize(camel_cased_word)
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
end
Object.module_eval("::#{$1}", __FILE__, __LINE__)
end
end
@singular_of = {}
@plural_of = {}
@singular_rules = []
@plural_rules = []
class << self
# Defines a general inflection exception case.
#
# ==== Parameters
# singular<String>::
# singular form of the word
# plural<String>::
# plural form of the word
#
# ==== Examples
#
# Here we define erratum/errata exception case:
#
# English::Inflect.word "erratum", "errata"
#
# In case singular and plural forms are the same omit
# second argument on call:
#
# English::Inflect.word 'information'
def self.word(singular, plural=nil)
plural = singular unless plural
singular_word(singular, plural)
plural_word(singular, plural)
end
def self.clear(type = :all)
if type == :singular || type == :all
@singular_of = {}
@singular_rules = []
@singularization_rules, @singularization_regex = nil, nil
end
if type == :plural || type == :all
@singular_of = {}
@singular_rules = []
@singularization_rules, @singularization_regex = nil, nil
end
end
# Define a singularization exception.
#
# ==== Parameters
# singular<String>::
# singular form of the word
# plural<String>::
# plural form of the word
def self.singular_word(singular, plural)
@singular_of[plural] = singular
@singular_of[plural.capitalize] = singular.capitalize
end
# Define a pluralization exception.
#
# ==== Parameters
# singular<String>::
# singular form of the word
# plural<String>::
# plural form of the word
def self.plural_word(singular, plural)
@plural_of[singular] = plural
@plural_of[singular.capitalize] = plural.capitalize
end
# Define a general rule.
#
# ==== Parameters
# singular<String>::
# ending of the word in singular form
# plural<String>::
# ending of the word in plural form
# whole_word<Boolean>::
# for capitalization, since words can be
# capitalized (Man => Men) #
# ==== Examples
# Once the following rule is defined:
# English::Inflect.rule 'y', 'ies'
#
# You can see the following results:
# irb> "fly".plural
# => flies
# irb> "cry".plural
# => cries
# Define a general rule.
def self.rule(singular, plural, whole_word = false)
singular_rule(singular, plural)
plural_rule(singular, plural)
word(singular, plural) if whole_word
end
# Define a singularization rule.
#
# ==== Parameters
# singular<String>::
# ending of the word in singular form
# plural<String>::
# ending of the word in plural form
#
# ==== Examples
# Once the following rule is defined:
# English::Inflect.singular_rule 'o', 'oes'
#
# You can see the following results:
# irb> "heroes".singular
# => hero
def self.singular_rule(singular, plural)
@singular_rules << [singular, plural]
end
# Define a plurualization rule.
#
# ==== Parameters
# singular<String>::
# ending of the word in singular form
# plural<String>::
# ending of the word in plural form
#
# ==== Examples
# Once the following rule is defined:
# English::Inflect.singular_rule 'fe', 'ves'
#
# You can see the following results:
# irb> "wife".plural
# => wives
def self.plural_rule(singular, plural)
@plural_rules << [singular, plural]
end
# Read prepared singularization rules.
def self.singularization_rules
if defined?(@singularization_regex) && @singularization_regex
return [@singularization_regex, @singularization_hash]
end
# No sorting needed: Regexen match on longest string
@singularization_regex = Regexp.new("(" + @singular_rules.map {|s,p| p}.join("|") + ")$", "i")
@singularization_hash = Hash[*@singular_rules.flatten].invert
[@singularization_regex, @singularization_hash]
end
# Read prepared pluralization rules.
def self.pluralization_rules
if defined?(@pluralization_regex) && @pluralization_regex
return [@pluralization_regex, @pluralization_hash]
end
@pluralization_regex = Regexp.new("(" + @plural_rules.map {|s,p| s}.join("|") + ")$", "i")
@pluralization_hash = Hash[*@plural_rules.flatten]
[@pluralization_regex, @pluralization_hash]
end
attr_reader :singular_of, :plural_of
# Convert an English word from plural to singular.
#
# "boys".singular #=> boy
# "tomatoes".singular #=> tomato
#
# ==== Parameters
# word<String>:: word to singularize
#
# ==== Returns
# <String>:: singularized form of word
#
# ==== Notes
# Aliased as singularize (a Railism)
def self.singular(word)
if result = singular_of[word]
return result.dup
end
result = word.dup
regex, hash = singularization_rules
result.sub!(regex) {|m| hash[m]}
singular_of[word] = result
return result
end
# Alias for #singular (a Railism).
#
alias_method(:singularize, :singular)
# Convert an English word from singular to plural.
#
# "boy".plural #=> boys
# "tomato".plural #=> tomatoes
#
# ==== Parameters
# word<String>:: word to pluralize
#
# ==== Returns
# <String>:: pluralized form of word
#
# ==== Notes
# Aliased as pluralize (a Railism)
def self.plural(word)
# special exceptions
return "" if word == ""
if result = plural_of[word]
return result.dup
end
result = word.dup
regex, hash = pluralization_rules
result.sub!(regex) {|m| hash[m]}
plural_of[word] = result
return result
end
# Alias for #plural (a Railism).
alias_method(:pluralize, :plural)
end
# One argument means singular and plural are the same.
word 'equipment'
word 'fish'
word 'grass'
word 'hovercraft'
word 'information'
word 'milk'
word 'money'
word 'moose'
word 'plurals'
word 'postgres'
word 'rain'
word 'rice'
word 'series'
word 'sheep'
word 'species'
word 'status'
# Two arguments defines a singular and plural exception.
word 'alias' , 'aliases'
word 'analysis' , 'analyses'
word 'axis' , 'axes'
word 'basis' , 'bases'
word 'buffalo' , 'buffaloes'
word 'cactus' , 'cacti'
word 'crisis' , 'crises'
word 'criterion' , 'criteria'
word 'cross' , 'crosses'
word 'datum' , 'data'
word 'diagnosis' , 'diagnoses'
word 'drive' , 'drives'
word 'erratum' , 'errata'
word 'goose' , 'geese'
word 'index' , 'indices'
word 'life' , 'lives'
word 'louse' , 'lice'
word 'matrix' , 'matrices'
word 'medium' , 'media'
word 'mouse' , 'mice'
word 'movie' , 'movies'
word 'octopus' , 'octopi'
word 'ox' , 'oxen'
word 'phenomenon' , 'phenomena'
word 'plus' , 'plusses'
word 'potato' , 'potatoes'
word 'quiz' , 'quizzes'
word 'status' , 'status'
word 'status' , 'statuses'
word 'Swiss' , 'Swiss'
word 'testis' , 'testes'
word 'thesaurus' , 'thesauri'
word 'thesis' , 'theses'
word 'thief' , 'thieves'
word 'tomato' , 'tomatoes'
word 'torpedo' , 'torpedoes'
word 'vertex' , 'vertices'
word 'wife' , 'wives'
# One-way singularization exception (convert plural to singular).
# General rules.
rule 'person' , 'people', true
rule 'shoe' , 'shoes', true
rule 'hive' , 'hives', true
rule 'man' , 'men', true
rule 'child' , 'children', true
rule 'news' , 'news', true
rule 'rf' , 'rves'
rule 'af' , 'aves'
rule 'ero' , 'eroes'
rule 'man' , 'men'
rule 'ch' , 'ches'
rule 'sh' , 'shes'
rule 'ss' , 'sses'
rule 'ta' , 'tum'
rule 'ia' , 'ium'
rule 'ra' , 'rum'
rule 'ay' , 'ays'
rule 'ey' , 'eys'
rule 'oy' , 'oys'
rule 'uy' , 'uys'
rule 'y' , 'ies'
rule 'x' , 'xes'
rule 'lf' , 'lves'
rule 'ffe' , 'ffes'
rule 'afe' , 'aves'
rule 'ouse' , 'ouses'
# more cases of words ending in -oses not being singularized properly
# than cases of words ending in -osis
# rule 'osis' , 'oses'
rule 'ox' , 'oxes'
rule 'us' , 'uses'
rule '' , 's'
# One-way singular rules.
singular_rule 'of' , 'ofs' # proof
singular_rule 'o' , 'oes' # hero, heroes
singular_rule 'f' , 'ves'
# One-way plural rules.
#plural_rule 'fe' , 'ves' # safe, wife
plural_rule 's' , 'ses'
plural_rule 'ive' , 'ives' # don't want to snag wife
plural_rule 'fe' , 'ves' # don't want to snag perspectives
end