Skip to content

Commit d94da14

Browse files
URLCrazy version 0.1
1 parent 0f15ff5 commit d94da14

File tree

4 files changed

+1181
-0
lines changed

4 files changed

+1181
-0
lines changed

inflections.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
module ActiveSupport
2+
Inflector.inflections do |inflect|
3+
inflect.plural(/$/, 's')
4+
inflect.plural(/s$/i, 's')
5+
inflect.plural(/(ax|test)is$/i, '\1es')
6+
inflect.plural(/(octop|vir)us$/i, '\1i')
7+
inflect.plural(/(alias|status)$/i, '\1es')
8+
inflect.plural(/(bu)s$/i, '\1ses')
9+
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
10+
inflect.plural(/([ti])um$/i, '\1a')
11+
inflect.plural(/sis$/i, 'ses')
12+
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
13+
inflect.plural(/(hive)$/i, '\1s')
14+
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
15+
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
16+
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
17+
inflect.plural(/([m|l])ouse$/i, '\1ice')
18+
inflect.plural(/^(ox)$/i, '\1en')
19+
inflect.plural(/(quiz)$/i, '\1zes')
20+
21+
inflect.singular(/s$/i, '')
22+
inflect.singular(/(n)ews$/i, '\1ews')
23+
inflect.singular(/([ti])a$/i, '\1um')
24+
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
25+
inflect.singular(/(^analy)ses$/i, '\1sis')
26+
inflect.singular(/([^f])ves$/i, '\1fe')
27+
inflect.singular(/(hive)s$/i, '\1')
28+
inflect.singular(/(tive)s$/i, '\1')
29+
inflect.singular(/([lr])ves$/i, '\1f')
30+
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
31+
inflect.singular(/(s)eries$/i, '\1eries')
32+
inflect.singular(/(m)ovies$/i, '\1ovie')
33+
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
34+
inflect.singular(/([m|l])ice$/i, '\1ouse')
35+
inflect.singular(/(bus)es$/i, '\1')
36+
inflect.singular(/(o)es$/i, '\1')
37+
inflect.singular(/(shoe)s$/i, '\1')
38+
inflect.singular(/(cris|ax|test)es$/i, '\1is')
39+
inflect.singular(/(octop|vir)i$/i, '\1us')
40+
inflect.singular(/(alias|status)es$/i, '\1')
41+
inflect.singular(/^(ox)en/i, '\1')
42+
inflect.singular(/(vert|ind)ices$/i, '\1ex')
43+
inflect.singular(/(matr)ices$/i, '\1ix')
44+
inflect.singular(/(quiz)zes$/i, '\1')
45+
46+
inflect.irregular('person', 'people')
47+
inflect.irregular('man', 'men')
48+
inflect.irregular('child', 'children')
49+
inflect.irregular('sex', 'sexes')
50+
inflect.irregular('move', 'moves')
51+
inflect.irregular('cow', 'kine')
52+
53+
inflect.uncountable(%w(equipment information rice money species series fish sheep))
54+
end
55+
end

inflector.rb

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
require 'singleton'
2+
3+
module ActiveSupport
4+
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
5+
# and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
6+
# in inflections.rb.
7+
#
8+
# The Rails core team has stated patches for the inflections library will not be accepted
9+
# in order to avoid breaking legacy applications which may be relying on errant inflections.
10+
# If you discover an incorrect inflection and require it for your application, you'll need
11+
# to correct it yourself (explained below).
12+
module Inflector
13+
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
14+
# inflection rules. Examples:
15+
#
16+
# Inflector.inflections do |inflect|
17+
# inflect.plural /^(ox)$/i, '\1\2en'
18+
# inflect.singular /^(ox)en/i, '\1'
19+
#
20+
# inflect.irregular 'octopus', 'octopi'
21+
#
22+
# inflect.uncountable "equipment"
23+
# end
24+
#
25+
# New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
26+
# pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
27+
# already have been loaded.
28+
class Inflections
29+
include Singleton
30+
31+
attr_reader :plurals, :singulars, :uncountables
32+
33+
def initialize
34+
@plurals, @singulars, @uncountables = [], [], []
35+
end
36+
37+
# Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
38+
# The replacement should always be a string that may include references to the matched data from the rule.
39+
def plural(rule, replacement)
40+
@plurals.insert(0, [rule, replacement])
41+
end
42+
43+
# Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
44+
# The replacement should always be a string that may include references to the matched data from the rule.
45+
def singular(rule, replacement)
46+
@singulars.insert(0, [rule, replacement])
47+
end
48+
49+
# Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
50+
# for strings, not regular expressions. You simply pass the irregular in singular and plural form.
51+
#
52+
# Examples:
53+
# irregular 'octopus', 'octopi'
54+
# irregular 'person', 'people'
55+
def irregular(singular, plural)
56+
if singular[0,1].upcase == plural[0,1].upcase
57+
plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
58+
singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
59+
else
60+
plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
61+
plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
62+
singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
63+
singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
64+
end
65+
end
66+
67+
# Add uncountable words that shouldn't be attempted inflected.
68+
#
69+
# Examples:
70+
# uncountable "money"
71+
# uncountable "money", "information"
72+
# uncountable %w( money information rice )
73+
def uncountable(*words)
74+
(@uncountables << words).flatten!
75+
end
76+
77+
# Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
78+
# Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
79+
# <tt>:singulars</tt>, <tt>:uncountables</tt>.
80+
#
81+
# Examples:
82+
# clear :all
83+
# clear :plurals
84+
def clear(scope = :all)
85+
case scope
86+
when :all
87+
@plurals, @singulars, @uncountables = [], [], []
88+
else
89+
instance_variable_set "@#{scope}", []
90+
end
91+
end
92+
end
93+
94+
extend self
95+
96+
# Yields a singleton instance of Inflector::Inflections so you can specify additional
97+
# inflector rules.
98+
#
99+
# Example:
100+
# Inflector.inflections do |inflect|
101+
# inflect.uncountable "rails"
102+
# end
103+
def inflections
104+
if block_given?
105+
yield Inflections.instance
106+
else
107+
Inflections.instance
108+
end
109+
end
110+
111+
# Returns the plural form of the word in the string.
112+
#
113+
# Examples:
114+
# "post".pluralize # => "posts"
115+
# "octopus".pluralize # => "octopi"
116+
# "sheep".pluralize # => "sheep"
117+
# "words".pluralize # => "words"
118+
# "the blue mailman".pluralize # => "the blue mailmen"
119+
# "CamelOctopus".pluralize # => "CamelOctopi"
120+
def pluralize(word)
121+
result = word.to_s.dup
122+
123+
if word.empty? || inflections.uncountables.include?(result.downcase)
124+
result
125+
else
126+
inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
127+
result
128+
end
129+
end
130+
131+
# The reverse of +pluralize+, returns the singular form of a word in a string.
132+
#
133+
# Examples:
134+
# "posts".singularize # => "post"
135+
# "octopi".singularize # => "octopus"
136+
# "sheep".singluarize # => "sheep"
137+
# "word".singluarize # => "word"
138+
# "the blue mailmen".singularize # => "the blue mailman"
139+
# "CamelOctopi".singularize # => "CamelOctopus"
140+
def singularize(word)
141+
result = word.to_s.dup
142+
143+
if inflections.uncountables.include?(result.downcase)
144+
result
145+
else
146+
inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
147+
result
148+
end
149+
end
150+
151+
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
152+
# is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
153+
#
154+
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
155+
#
156+
# Examples:
157+
# "active_record".camelize # => "ActiveRecord"
158+
# "active_record".camelize(:lower) # => "activeRecord"
159+
# "active_record/errors".camelize # => "ActiveRecord::Errors"
160+
# "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
161+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
162+
if first_letter_in_uppercase
163+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
164+
else
165+
lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
166+
end
167+
end
168+
169+
# Capitalizes all the words and replaces some characters in the string to create
170+
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
171+
# used in the Rails internals.
172+
#
173+
# +titleize+ is also aliased as as +titlecase+.
174+
#
175+
# Examples:
176+
# "man from the boondocks".titleize # => "Man From The Boondocks"
177+
# "x-men: the last stand".titleize # => "X Men: The Last Stand"
178+
def titleize(word)
179+
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
180+
end
181+
182+
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
183+
#
184+
# Changes '::' to '/' to convert namespaces to paths.
185+
#
186+
# Examples:
187+
# "ActiveRecord".underscore # => "active_record"
188+
# "ActiveRecord::Errors".underscore # => active_record/errors
189+
def underscore(camel_cased_word)
190+
camel_cased_word.to_s.gsub(/::/, '/').
191+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
192+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
193+
tr("-", "_").
194+
downcase
195+
end
196+
197+
# Replaces underscores with dashes in the string.
198+
#
199+
# Example:
200+
# "puni_puni" # => "puni-puni"
201+
def dasherize(underscored_word)
202+
underscored_word.gsub(/_/, '-')
203+
end
204+
205+
# Capitalizes the first word and turns underscores into spaces and strips a
206+
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
207+
#
208+
# Examples:
209+
# "employee_salary" # => "Employee salary"
210+
# "author_id" # => "Author"
211+
def humanize(lower_case_and_underscored_word)
212+
lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
213+
end
214+
215+
# Removes the module part from the expression in the string.
216+
#
217+
# Examples:
218+
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
219+
# "Inflections".demodulize # => "Inflections"
220+
def demodulize(class_name_in_module)
221+
class_name_in_module.to_s.gsub(/^.*::/, '')
222+
end
223+
224+
# Create the name of a table like Rails does for models to table names. This method
225+
# uses the +pluralize+ method on the last word in the string.
226+
#
227+
# Examples
228+
# "RawScaledScorer".tableize # => "raw_scaled_scorers"
229+
# "egg_and_ham".tableize # => "egg_and_hams"
230+
# "fancyCategory".tableize # => "fancy_categories"
231+
def tableize(class_name)
232+
pluralize(underscore(class_name))
233+
end
234+
235+
# Create a class name from a plural table name like Rails does for table names to models.
236+
# Note that this returns a string and not a Class. (To convert to an actual class
237+
# follow +classify+ with +constantize+.)
238+
#
239+
# Examples:
240+
# "egg_and_hams".classify # => "EggAndHam"
241+
# "posts".classify # => "Post"
242+
#
243+
# Singular names are not handled correctly:
244+
# "business".classify # => "Busines"
245+
def classify(table_name)
246+
# strip out any leading schema name
247+
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
248+
end
249+
250+
# Creates a foreign key name from a class name.
251+
# +separate_class_name_and_id_with_underscore+ sets whether
252+
# the method should put '_' between the name and 'id'.
253+
#
254+
# Examples:
255+
# "Message".foreign_key # => "message_id"
256+
# "Message".foreign_key(false) # => "messageid"
257+
# "Admin::Post".foreign_key # => "post_id"
258+
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
259+
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
260+
end
261+
262+
# Tries to find a constant with the name specified in the argument string:
263+
#
264+
# "Module".constantize # => Module
265+
# "Test::Unit".constantize # => Test::Unit
266+
#
267+
# The name is assumed to be the one of a top-level constant, no matter whether
268+
# it starts with "::" or not. No lexical context is taken into account:
269+
#
270+
# C = 'outside'
271+
# module M
272+
# C = 'inside'
273+
# C # => 'inside'
274+
# "C".constantize # => 'outside', same as ::C
275+
# end
276+
#
277+
# NameError is raised when the name is not in CamelCase or the constant is
278+
# unknown.
279+
def constantize(camel_cased_word)
280+
names = camel_cased_word.split('::')
281+
names.shift if names.empty? || names.first.empty?
282+
283+
constant = Object
284+
names.each do |name|
285+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
286+
end
287+
constant
288+
end
289+
290+
# Turns a number into an ordinal string used to denote the position in an
291+
# ordered sequence such as 1st, 2nd, 3rd, 4th.
292+
#
293+
# Examples:
294+
# ordinalize(1) # => "1st"
295+
# ordinalize(2) # => "2nd"
296+
# ordinalize(1002) # => "1002nd"
297+
# ordinalize(1003) # => "1003rd"
298+
def ordinalize(number)
299+
if (11..13).include?(number.to_i % 100)
300+
"#{number}th"
301+
else
302+
case number.to_i % 10
303+
when 1; "#{number}st"
304+
when 2; "#{number}nd"
305+
when 3; "#{number}rd"
306+
else "#{number}th"
307+
end
308+
end
309+
end
310+
end
311+
end
312+
313+
require File.dirname(__FILE__) + '/inflections'

0 commit comments

Comments
 (0)