Created
November 13, 2017 12:48
-
-
Save actsasflinn/13311177d55c882e05b5ca46d0515ac5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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