6 # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
7 # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
10 # The Rails core team has stated patches for the inflections library will not be accepted
11 # in order to avoid breaking legacy applications which may be relying on errant inflections.
12 # If you discover an incorrect inflection and require it for your application, you'll need
13 # to correct it yourself (explained below).
17 # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
18 # inflection rules. Examples:
20 # ActiveSupport::Inflector.inflections do |inflect|
21 # inflect.plural /^(ox)$/i, '\1\2en'
22 # inflect.singular /^(ox)en/i, '\1'
24 # inflect.irregular 'octopus', 'octopi'
26 # inflect.uncountable "equipment"
29 # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
30 # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
31 # already have been loaded.
35 attr_reader
:plurals, :singulars, :uncountables, :humans
38 @plurals, @singulars, @uncountables, @humans = [], [], [], []
41 # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
42 # The replacement should always be a string that may include references to the matched data from the rule.
43 def plural(rule
, replacement
)
44 @uncountables.delete(rule
) if rule
.is_a
?(String
)
45 @uncountables.delete(replacement
)
46 @plurals.insert(0, [rule
, replacement
])
49 # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
50 # The replacement should always be a string that may include references to the matched data from the rule.
51 def singular(rule
, replacement
)
52 @uncountables.delete(rule
) if rule
.is_a
?(String
)
53 @uncountables.delete(replacement
)
54 @singulars.insert(0, [rule
, replacement
])
57 # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
58 # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
61 # irregular 'octopus', 'octopi'
62 # irregular 'person', 'people'
63 def irregular(singular
, plural
)
64 @uncountables.delete(singular
)
65 @uncountables.delete(plural
)
66 if singular
[0,1].upcase
== plural
[0,1].upcase
67 plural(Regexp
.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural
[1..-1])
68 singular(Regexp
.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular
[1..-1])
70 plural(Regexp
.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural
[0,1].upcase
+ plural
[1..-1])
71 plural(Regexp
.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural
[0,1].downcase
+ plural
[1..-1])
72 singular(Regexp
.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular
[0,1].upcase
+ singular
[1..-1])
73 singular(Regexp
.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular
[0,1].downcase
+ singular
[1..-1])
77 # Add uncountable words that shouldn't be attempted inflected.
81 # uncountable "money", "information"
82 # uncountable %w( money information rice )
83 def uncountable(*words
)
84 (@uncountables << words
).flatten
!
87 # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
88 # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
89 # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
92 # human /_cnt$/i, '\1_count'
93 # human "legacy_col_person_name", "Name"
94 def human(rule
, replacement
)
95 @humans.insert(0, [rule
, replacement
])
98 # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
99 # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
100 # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
105 def clear(scope
= :all)
108 @plurals, @singulars, @uncountables = [], [], []
110 instance_variable_set
"@#{scope}", []
115 # Yields a singleton instance of Inflector::Inflections so you can specify additional
119 # ActiveSupport::Inflector.inflections do |inflect|
120 # inflect.uncountable "rails"
124 yield Inflections
.instance
130 # Returns the plural form of the word in the string.
133 # "post".pluralize # => "posts"
134 # "octopus".pluralize # => "octopi"
135 # "sheep".pluralize # => "sheep"
136 # "words".pluralize # => "words"
137 # "CamelOctopus".pluralize # => "CamelOctopi"
139 result
= word
.to_s
.dup
141 if word
.empty
? || inflections
.uncountables
.include?(result
.downcase
)
144 inflections
.plurals
.each
{ |(rule
, replacement
)| break if result
.gsub
!(rule
, replacement
) }
149 # The reverse of +pluralize+, returns the singular form of a word in a string.
152 # "posts".singularize # => "post"
153 # "octopi".singularize # => "octopus"
154 # "sheep".singluarize # => "sheep"
155 # "word".singularize # => "word"
156 # "CamelOctopi".singularize # => "CamelOctopus"
157 def singularize(word
)
158 result
= word
.to_s
.dup
160 if inflections
.uncountables
.include?(result
.downcase
)
163 inflections
.singulars
.each
{ |(rule
, replacement
)| break if result
.gsub
!(rule
, replacement
) }
168 # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
169 # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
171 # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
174 # "active_record".camelize # => "ActiveRecord"
175 # "active_record".camelize(:lower) # => "activeRecord"
176 # "active_record/errors".camelize # => "ActiveRecord::Errors"
177 # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
178 def camelize(lower_case_and_underscored_word
, first_letter_in_uppercase
= true)
179 if first_letter_in_uppercase
180 lower_case_and_underscored_word
.to_s
.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^
|_
)(.)/) { $1.upcase
}
182 lower_case_and_underscored_word
.first
.downcase
+ camelize(lower_case_and_underscored_word
)[1..-1]
186 # Capitalizes all the words and replaces some characters in the string to create
187 # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
188 # used in the Rails internals.
190 # +titleize+ is also aliased as as +titlecase+.
193 # "man from the boondocks".titleize # => "Man From The Boondocks"
194 # "x-men: the last stand".titleize # => "X Men: The Last Stand"
196 humanize(underscore(word
)).gsub(/\b('?[a-z])/) { $1.capitalize
}
199 # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
201 # Changes '::' to '/' to convert namespaces to paths.
204 # "ActiveRecord".underscore # => "active_record"
205 # "ActiveRecord::Errors".underscore # => active_record/errors
206 def underscore(camel_cased_word
)
207 camel_cased_word
.to_s
.gsub(/::/, '/').
208 gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
209 gsub(/([a-z\d])([A-Z])/,'\1_\2').
214 # Replaces underscores with dashes in the string.
217 # "puni_puni" # => "puni-puni"
218 def dasherize(underscored_word
)
219 underscored_word
.gsub(/_/, '-')
222 # Capitalizes the first word and turns underscores into spaces and strips a
223 # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
226 # "employee_salary" # => "Employee salary"
227 # "author_id" # => "Author"
228 def humanize(lower_case_and_underscored_word
)
229 result
= lower_case_and_underscored_word
.to_s
.dup
231 inflections
.humans
.each
{ |(rule
, replacement
)| break if result
.gsub
!(rule
, replacement
) }
232 result
.gsub(/_id$/, "").gsub(/_/, " ").capitalize
235 # Removes the module part from the expression in the string.
238 # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
239 # "Inflections".demodulize # => "Inflections"
240 def demodulize(class_name_in_module
)
241 class_name_in_module
.to_s
.gsub(/^.*::/, '')
244 # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
250 # "#{id}-#{name.parameterize}"
254 # @person = Person.find(1)
255 # # => #<Person id: 1, name: "Donald E. Knuth">
257 # <%= link_to(@person.name, person_path %>
258 # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
259 def parameterize(string
, sep
= '-')
260 re_sep
= Regexp
.escape(sep
)
261 # replace accented chars with ther ascii equivalents
262 parameterized_string
= transliterate(string
)
263 # Turn unwanted chars into the seperator
264 parameterized_string
.gsub
!(/[^a-z0-9\-_\+]+/i
, sep
)
265 # No more than one of the separator in a row.
266 parameterized_string
.squeeze
!(sep
)
267 # Remove leading/trailing separator.
268 parameterized_string
.gsub
!(/^#{re_sep}|#{re_sep}$/i
, '')
269 parameterized_string
.downcase
273 # Replaces accented characters with their ascii equivalents.
274 def transliterate(string
)
275 Iconv
.iconv('ascii//ignore//translit', 'utf-8', string
).to_s
278 # The iconv transliteration code doesn't function correctly
279 # on some platforms, but it's very fast where it does function.
280 if "foo" != Inflector
.transliterate("föö")
281 undef_method
:transliterate
282 def transliterate(string
)
283 string
.mb_chars
.normalize(:kd). # Decompose accented characters
284 gsub(/[^\x00-\x7F]+/, '') # Remove anything non-ASCII entirely (e.g. diacritics).
288 # Create the name of a table like Rails does for models to table names. This method
289 # uses the +pluralize+ method on the last word in the string.
292 # "RawScaledScorer".tableize # => "raw_scaled_scorers"
293 # "egg_and_ham".tableize # => "egg_and_hams"
294 # "fancyCategory".tableize # => "fancy_categories"
295 def tableize(class_name
)
296 pluralize(underscore(class_name
))
299 # Create a class name from a plural table name like Rails does for table names to models.
300 # Note that this returns a string and not a Class. (To convert to an actual class
301 # follow +classify+ with +constantize+.)
304 # "egg_and_hams".classify # => "EggAndHam"
305 # "posts".classify # => "Post"
307 # Singular names are not handled correctly:
308 # "business".classify # => "Busines"
309 def classify(table_name
)
310 # strip out any leading schema name
311 camelize(singularize(table_name
.to_s
.sub(/.*\./, '')))
314 # Creates a foreign key name from a class name.
315 # +separate_class_name_and_id_with_underscore+ sets whether
316 # the method should put '_' between the name and 'id'.
319 # "Message".foreign_key # => "message_id"
320 # "Message".foreign_key(false) # => "messageid"
321 # "Admin::Post".foreign_key # => "post_id"
322 def foreign_key(class_name
, separate_class_name_and_id_with_underscore
= true)
323 underscore(demodulize(class_name
)) + (separate_class_name_and_id_with_underscore
? "_id" : "id")
326 # Ruby 1.9 introduces an inherit argument for Module#const_get and
327 # #const_defined? and changes their default behavior.
328 if Module
.method(:const_get).arity
== 1
329 # Tries to find a constant with the name specified in the argument string:
331 # "Module".constantize # => Module
332 # "Test::Unit".constantize # => Test::Unit
334 # The name is assumed to be the one of a top-level constant, no matter whether
335 # it starts with "::" or not. No lexical context is taken into account:
341 # "C".constantize # => 'outside', same as ::C
344 # NameError is raised when the name is not in CamelCase or the constant is
346 def constantize(camel_cased_word
)
347 names
= camel_cased_word
.split('::')
348 names
.shift
if names
.empty
? || names
.first
.empty
?
352 constant
= constant
.const_defined
?(name
) ? constant
.const_get(name
) : constant
.const_missing(name
)
357 def constantize(camel_cased_word
) #:nodoc:
358 names
= camel_cased_word
.split('::')
359 names
.shift
if names
.empty
? || names
.first
.empty
?
363 constant
= constant
.const_get(name
, false) || constant
.const_missing(name
)
369 # Turns a number into an ordinal string used to denote the position in an
370 # ordered sequence such as 1st, 2nd, 3rd, 4th.
373 # ordinalize(1) # => "1st"
374 # ordinalize(2) # => "2nd"
375 # ordinalize(1002) # => "1002nd"
376 # ordinalize(1003) # => "1003rd"
377 def ordinalize(number
)
378 if (11..13).include?(number
.to_i
% 100)
381 case number
.to_i
% 10
382 when 1; "#{number}st"
383 when 2; "#{number}nd"
384 when 3; "#{number}rd"
392 # in case active_support/inflector is required without the rest of active_support
393 require 'active_support/inflections'
394 require 'active_support/core_ext/string/inflections'
395 unless String
.included_modules
.include?(ActiveSupport
::CoreExtensions::String::Inflections)
396 String
.send
:include, ActiveSupport
::CoreExtensions::String::Inflections