Skip to content

Instantly share code, notes, and snippets.

@actsasflinn
Created November 13, 2017 12:48
Show Gist options
  • Save actsasflinn/13311177d55c882e05b5ca46d0515ac5 to your computer and use it in GitHub Desktop.
Save actsasflinn/13311177d55c882e05b5ca46d0515ac5 to your computer and use it in GitHub Desktop.
module Inflector
extend self
class Inflections
getter :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
def initialize
@plurals = {
/$/ => "s",
/s$/i => "s",
/^(ax|test)is$/i => "\1es",
/(octop|vir)us$/i => "\1i",
/(octop|vir)i$/i => "\1i",
/(alias|status)$/i => "\1es",
/(bu)s$/i => "\1ses",
/(buffal|tomat)o$/i => "\1oes",
/([ti])um$/i => "\1a",
/([ti])a$/i => "\1a",
/sis$/i => "ses",
/(?:([^f])fe|([lr])f)$/i => "\1\2ves",
/(hive)$/i => "\1s",
/([^aeiouy]|qu)y$/i => "\1ies",
/(x|ch|ss|sh)$/i => "\1es",
/(matr|vert|ind)(?:ix|ex)$/i => "\1ices",
/^(m|l)ouse$/i => "\1ice",
/^(m|l)ice$/i => "\1ice",
/^(ox)$/i => "\1en",
/^(oxen)$/i => "\1",
/(quiz)$/i => "\1zes"
}
@singulars = {
/s$/i => "",
/(ss)$/i => "\1",
/(n)ews$/i => "\1ews",
/([ti])a$/i => "\1um",
/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i => "\1sis",
/(^analy)(sis|ses)$/i => "\1sis",
/([^f])ves$/i => "\1fe",
/(hive)s$/i => "\1",
/(tive)s$/i => "\1",
/([lr])ves$/i => "\1f",
/([^aeiouy]|qu)ies$/i => "\1y",
/(s)eries$/i => "\1eries",
/(m)ovies$/i => "\1ovie",
/(x|ch|ss|sh)es$/i => "\1",
/^(m|l)ice$/i => "\1ouse",
/(bus)(es)?$/i => "\1",
/(o)es$/i => "\1",
/(shoe)s$/i => "\1",
/(cris|test)(is|es)$/i => "\1is",
/^(a)x[ie]s$/i => "\1xis",
/(octop|vir)(us|i)$/i => "\1us",
/(alias|status)(es)?$/i => "\1",
/^(ox)en/i => "\1",
/(vert|ind)ices$/i => "\1ex",
/(matr)ices$/i => "\1ix",
/(quiz)zes$/i => "\1",
/(database)s$/i => "\1"
}
@uncountables = %w(equipment information rice money species series fish sheep jeans police)
@humans = Array(String).new
@acronyms = Hash(String, String).new
@acronym_regex = /(?=a)b/
end
def acronym(word)
@acronyms[word.downcase] = word
@acronym_regex = /#{acronyms.values.join("|")}/
end
end
@@inflections = Inflections.new
def inflections
@@inflections
end
end
require "./inflections"
module Inflector
extend self
def pluralize(word)
apply_inflections(word, inflections.plurals)
end
def singularize(word)
apply_inflections(word, inflections.singulars)
end
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.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
end
string = string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{inflections.acronyms[$2]? || $2.capitalize}" }
string = string.gsub("/", "::")
string
end
def underscore(camel_cased_word)
return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
word = camel_cased_word.to_s.gsub("::", "/")
word = word.gsub(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && "_" }#{$2.downcase}" }
word = word.gsub(/([A-Z\d]+)([A-Z][a-z])/, "\\1_\\2")
word = word.gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
word = word.tr("-", "_")
word = word.downcase
word
end
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 = result.sub(rule, replacement) }
result = result.sub(/\A_+/, "")
unless keep_id_suffix
result = result.sub(/_id\z/, "")
end
result = result.tr("_", " ")
result = result.gsub(/([a-z\d]*)/i) do |match|
"#{inflections.acronyms[match.downcase]? || match.downcase}"
end
if capitalize
result = result.sub(/\A\w/) { |match| match.upcase }
end
result
end
def upcase_first(string)
string.size > 0 ? string[0].to_s.upcase + string[1..-1] : ""
end
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
def tableize(class_name)
pluralize(underscore(class_name))
end
def classify(table_name)
# strip out any leading schema name
camelize(singularize(table_name.to_s.sub(/.*\./, "")))
end
def dasherize(underscored_word)
underscored_word.tr("_", "-")
end
def demodulize(path)
path = path.to_s
if i = path.rindex("::")
path[(i + 2)..-1]
else
path
end
end
def deconstantize(path)
path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
end
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
#TODO: implement constantize
#TODO: implement safe_constantize
def ordinal(number)
abs_number = number.to_i.abs
if (11..13).includes?(abs_number % 100)
"th"
else
case abs_number % 10
when 1; "st"
when 2; "nd"
when 3; "rd"
else "th"
end
end
end
def ordinalize(number)
"#{number}#{ordinal(number)}"
end
private def apply_inflections(word, rules)
result = word.to_s.dup
if result.empty? || inflections.uncountables.includes?(result)
result
else
rules.each { |(rule, replacement)| break if result = result.sub(rule, replacement) }
result
end
end
end
require "./inflector.cr"
puts Inflector.pluralize("post") # => "posts"
puts Inflector.pluralize("octopus") # => "octopi"
puts Inflector.pluralize("sheep") # => "sheep"
puts Inflector.pluralize("words") # => "words"
puts Inflector.pluralize("CamelOctopus") # => "CamelOctopi"
#puts Inflector.pluralize("ley", :es) # => "leyes"
puts Inflector.singularize("posts") # => "post"
puts Inflector.singularize("octopi") # => "octopus"
puts Inflector.singularize("sheep") # => "sheep"
puts Inflector.singularize("word") # => "word"
puts Inflector.singularize("CamelOctopi") # => "CamelOctopus"
#puts Inflector.singularize("leyes", :es) # => "ley"
puts Inflector.camelize("active_model") # => "ActiveModel"
puts Inflector.camelize("active_model", false) # => "activeModel"
puts Inflector.camelize("active_model/errors") # => "ActiveModel::Errors"
puts Inflector.camelize("active_model/errors", false) # => "activeModel::Errors"
puts Inflector.camelize(Inflector.underscore("SSLError")) # => "SslError"
puts Inflector.humanize("employee_salary") # => "Employee salary"
puts Inflector.humanize("author_id") # => "Author"
puts Inflector.humanize("author_id", capitalize: false) # => "author"
puts Inflector.humanize("_id") # => "Id"
puts Inflector.humanize("author_id", keep_id_suffix: true) # => "Author id"
puts Inflector.humanize("ssl_error") # => "SSL error"
puts Inflector.upcase_first("what a Lovely Day") # => "What a Lovely Day"
puts Inflector.upcase_first("w") # => "W"
puts Inflector.upcase_first("") # => ""
puts Inflector.titleize("man from the boondocks") # => "Man From The Boondocks"
puts Inflector.titleize("x-men: the last stand") # => "X Men: The Last Stand"
puts Inflector.titleize("TheManWithoutAPast") # => "The Man Without A Past"
puts Inflector.titleize("raiders_of_the_lost_ark") # => "Raiders Of The Lost Ark"
puts Inflector.titleize("string_ending_with_id", keep_id_suffix: true) # => "String Ending With Id"
puts Inflector.tableize("RawScaledScorer") # => "raw_scaled_scorers"
puts Inflector.tableize("ham_and_egg") # => "ham_and_eggs"
puts Inflector.tableize("fancyCategory") # => "fancy_categories"
puts Inflector.classify("ham_and_eggs") # => "HamAndEgg"
puts Inflector.classify("posts") # => "Post"
puts Inflector.classify("calculus") # => "Calculus"
puts Inflector.dasherize("puni_puni") # => "puni-puni"
puts Inflector.demodulize("ActiveSupport::Inflector::Inflections") # => "Inflections"
puts Inflector.demodulize("Inflections") # => "Inflections"
puts Inflector.demodulize("::Inflections") # => "Inflections"
puts Inflector.demodulize("") # => ""
puts Inflector.deconstantize("Net::HTTP") # => "Net"
puts Inflector.deconstantize("::Net::HTTP") # => "::Net"
puts Inflector.deconstantize("String") # => ""
puts Inflector.deconstantize("::String") # => ""
puts Inflector.deconstantize("") # => ""
puts Inflector.foreign_key("Message") # => "message_id"
puts Inflector.foreign_key("Message", false) # => "messageid"
puts Inflector.foreign_key("Admin::Post") # => "post_id"
#puts Inflector.constantize("Module") # => Module
#puts Inflector.constantize("Foo::Bar") # => Foo::Bar
puts Inflector.ordinal(1) # => "st"
puts Inflector.ordinal(2) # => "nd"
puts Inflector.ordinal(1002) # => "nd"
puts Inflector.ordinal(1003) # => "rd"
puts Inflector.ordinal(-11) # => "th"
puts Inflector.ordinal(-1021) # => "st"
puts Inflector.ordinalize(1) # => "1st"
puts Inflector.ordinalize(2) # => "2nd"
puts Inflector.ordinalize(1002) # => "1002nd"
puts Inflector.ordinalize(1003) # => "1003rd"
puts Inflector.ordinalize(-11) # => "-11th"
puts Inflector.ordinalize(-1021) # => "-1021st"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment