What is Inflector and how does it work
I am using Rails 2.3.2 and all the code mentioned in this article are tested with Rails 2.3.2 .
What is Inflector
Inflector is the backbone of the Rails code that converts a string from singular to plural and from plural to singular. This functionality is most commonly needed to find out the table name of a given model. It is Inflector that says that the table name for the model ‘Person’ will be ‘People’.
How do I use this feature
If you have a special case where you want to control the mapping from singular to plural then Rails provides you a hook. You will find a file called config/initializers/inflections.rb . This file looks something like this
# Add new inflection rules using the following format # (all these examples are active by default): # ActiveSupport::Inflector.inflections do |inflect| # inflect.plural /^(ox)$/i, '\1en' # inflect.singular /^(ox)en/i, '\1' # inflect.irregular 'person', 'people' # inflect.uncountable %w( fish sheep ) # end
If I want the plural of ‘car’ to be ‘carz’ then this is the place to make that change.
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'car', 'carz'
end
You can test it in script/console .
>> 'car'.pluralize => "carz"
How is Inflector implemented
In short inflector.rb has following code
module ActiveSupport
module Inflector
extend self
class Inflections
include Singleton
def plural(rule, replacement)
end
def singular(rule, replacement)
end
end
def pluralize(word)
end
def singularize(word)
end
end
end
A few things to notice here. First is the usage of extend self. Secondly module Singleton is being included.
What does ‘extend self’ do ?
A module can have instance methods and class methods.
module Foo def hi puts 'hi' end def self.hello puts 'hello' end end
You already know that a module can not be instantiated.
f = Foo.new unndefined method ‘new’ for Foo:Module
However you can invoke a class method directly on a module. Invoking an instance method will raise error.
module Foo def hi puts 'hi' end def self.hello puts 'hello' end end Foo.hello #=> 'hello' Foo.hi #=> undefined method ‘hi’ for Foo:Module
You can see that invocation of instance method hi resulted in a error. What is the solution to this problem. Solution is extend self. Inside the module self is the module Foo itself and if that module is extended then the instance methods become directly available on Foo object.
module Foo def hi puts 'hi' end def self.hello puts 'hello' end extend self end Foo.hello #=> 'hello' Foo.hi #=> 'hi'
In the above case extend self was added and both the methods ‘hi’ and ‘hello’ worked fine. Back to the main topic. The inflector code is this
module ActiveSupport
module Inflector
extend self
class Inflections
include Singleton
def plural(rule, replacement)
end
def singular(rule, replacement)
end
end
def pluralize(word)
end
def singularize(word)
end
end
end
In the script/console try this
>> ActiveSupport::Inflector.pluralize('cat')
=> "cats"
Notice that ‘Inflector’ is a module and method pluralize is an instance method. Invocation of instance method on a module results in error. However in this case extend self was done so that instance methods are directly available to the module.
What does ‘include Singleton’ do ?
The rule to find plural of a string or singular value of a string is defined in active_support/inflections.rb. Click here to look at the rules.
You will notice that the default rule to add ‘s’ to add any string is defined at the very top. The way it works is that the rules defined at the bottom override the rules defined at the top. So if all rules matching fails then the default rule to add ‘s’ to the string will kick in.
In this case inflections.rb is like a configuration file. The class that will read this file is a good candidate for singleton. You don’t want two instances of a class to read a configuration file. In this case methods plural, singular, irregular and uncountable are being called on Inflector.inflections.
def inflections
if block_given?
yield Inflections.instance
else
Inflections.instance
end
end
As you can see method inflections returns Inflections.instance. Class Inflections has include Singleton and hence every single time Inflections.instance is called same instance of the class is returned.
Changing the singular/plural rule
Changing the rule of how singular or plural value is calculated is pretty easy.
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'car', 'carz'
end
Add another rule for ‘car’.
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'car', 'carz'
inflect.irregular 'car', 'carzz'
end
In script/console
>> 'car'.pluralize => "carzz"
You can see that the rules that are defined at the top are overridden by the rules defined below them. Just keep that in mind if you are changing the rule.