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(@person)) %>
258 # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
259 def parameterize(string
, sep
= '-')
260 # replace accented chars with ther ascii equivalents
261 parameterized_string
= transliterate(string
)
262 # Turn unwanted chars into the seperator
263 parameterized_string
.gsub
!(/[^a-z0-9\-_\+]+/i
, sep
)
265 re_sep
= Regexp
.escape(sep
)
266 # No more than one of the separator in a row.
267 parameterized_string
.gsub
!(/#{re_sep}{2,}/, sep
)
268 # Remove leading/trailing separator.
269 parameterized_string
.gsub
!(/^#{re_sep}|#{re_sep}$/i
, '')
271 parameterized_string
.downcase
275 # Replaces accented characters with their ascii equivalents.
276 def transliterate(string
)
277 Iconv
.iconv('ascii//ignore//translit', 'utf-8', string
).to_s
280 if RUBY_VERSION >= '1.9'
281 undef_method
:transliterate
282 def transliterate(string
)
283 warn
"Ruby 1.9 doesn't support Unicode normalization yet"
287 # The iconv transliteration code doesn't function correctly
288 # on some platforms, but it's very fast where it does function.
289 elsif "foo" != (Inflector
.transliterate("föö") rescue nil)
290 undef_method
:transliterate
291 def transliterate(string
)
292 string
.mb_chars
.normalize(:kd). # Decompose accented characters
293 gsub(/[^\x00-\x7F]+/, '') # Remove anything non-ASCII entirely (e.g. diacritics).
297 # Create the name of a table like Rails does for models to table names. This method
298 # uses the +pluralize+ method on the last word in the string.
301 # "RawScaledScorer".tableize # => "raw_scaled_scorers"
302 # "egg_and_ham".tableize # => "egg_and_hams"
303 # "fancyCategory".tableize # => "fancy_categories"
304 def tableize(class_name
)
305 pluralize(underscore(class_name
))
308 # Create a class name from a plural table name like Rails does for table names to models.
309 # Note that this returns a string and not a Class. (To convert to an actual class
310 # follow +classify+ with +constantize+.)
313 # "egg_and_hams".classify # => "EggAndHam"
314 # "posts".classify # => "Post"
316 # Singular names are not handled correctly:
317 # "business".classify # => "Busines"
318 def classify(table_name
)
319 # strip out any leading schema name
320 camelize(singularize(table_name
.to_s
.sub(/.*\./, '')))
323 # Creates a foreign key name from a class name.
324 # +separate_class_name_and_id_with_underscore+ sets whether
325 # the method should put '_' between the name and 'id'.
328 # "Message".foreign_key # => "message_id"
329 # "Message".foreign_key(false) # => "messageid"
330 # "Admin::Post".foreign_key # => "post_id"
331 def foreign_key(class_name
, separate_class_name_and_id_with_underscore
= true)
332 underscore(demodulize(class_name
)) + (separate_class_name_and_id_with_underscore
? "_id" : "id")
335 # Ruby 1.9 introduces an inherit argument for Module#const_get and
336 # #const_defined? and changes their default behavior.
337 if Module
.method(:const_get).arity
== 1
338 # Tries to find a constant with the name specified in the argument string:
340 # "Module".constantize # => Module
341 # "Test::Unit".constantize # => Test::Unit
343 # The name is assumed to be the one of a top-level constant, no matter whether
344 # it starts with "::" or not. No lexical context is taken into account:
350 # "C".constantize # => 'outside', same as ::C
353 # NameError is raised when the name is not in CamelCase or the constant is
355 def constantize(camel_cased_word
)
356 names
= camel_cased_word
.split('::')
357 names
.shift
if names
.empty
? || names
.first
.empty
?
361 constant
= constant
.const_defined
?(name
) ? constant
.const_get(name
) : constant
.const_missing(name
)
366 def constantize(camel_cased_word
) #:nodoc:
367 names
= camel_cased_word
.split('::')
368 names
.shift
if names
.empty
? || names
.first
.empty
?
372 constant
= constant
.const_get(name
, false) || constant
.const_missing(name
)
378 # Turns a number into an ordinal string used to denote the position in an
379 # ordered sequence such as 1st, 2nd, 3rd, 4th.
382 # ordinalize(1) # => "1st"
383 # ordinalize(2) # => "2nd"
384 # ordinalize(1002) # => "1002nd"
385 # ordinalize(1003) # => "1003rd"
386 def ordinalize(number
)
387 if (11..13).include?(number
.to_i
% 100)
390 case number
.to_i
% 10
391 when 1; "#{number}st"
392 when 2; "#{number}nd"
393 when 3; "#{number}rd"
401 # in case active_support/inflector is required without the rest of active_support
402 require 'active_support/inflections'
403 require 'active_support/core_ext/string/inflections'
404 unless String
.included_modules
.include?(ActiveSupport
::CoreExtensions::String::Inflections)
405 String
.send
:include, ActiveSupport
::CoreExtensions::String::Inflections