# title.rb
module Title
def name
'Mr. ' + self.first_name + ' ' + self.last_name
end
end
# person.rb
class Person < ActiveRecord::Base
def name
self.first_name + ' ' + self.last_name
end
end
# ====
# Without mixin, expect 'Joe Bloggs'
p = Person.new(:first_name => 'Joe', :last_name => 'Bloggs')
p.name
=> 'Joe Bloggs' # pass
# ====
# Directly insert into class, expect 'Mr. Joe Bloggs'
Person.class_eval do
def name
'Mr. ' + self.first_name + ' ' + self.last_name
end
end
p = Person.new(:first_name => 'Joe', :last_name => 'Bloggs')
p.name
=> 'Mr. Joe Bloggs' # pass
# ====
# With mixin, expect 'Mr. Joe Bloggs'
Person.class_eval do
include Title
end
p = Person.new(:first_name => 'Joe', :last_name => 'Bloggs')
p.name
=> 'Joe Bloggs' # fail
Refactorings
No refactoring yet !
Adam
March 18, 2009, March 18, 2009 02:40, permalink
module Title
def self.included(klass)
klass.alias_method_chain(:name, :mr)
end
def name_with_mr
[ 'Mr.', first_name, last_name ].join(' ')
end
end
class Person < ActiveRecord::Base
def name
[ first_name, last_name ].join(' ')
end
end
Person.send(:include, Title)
Chris Blunt
March 18, 2009, March 18, 2009 11:06, permalink
Thanks for your code Adam, I tried putting it into my plugin my still could not get the expected output. It might be worth me saying that my application (at runtime) will not know which form of the Person class it will be using, i.e. if my plugin is installed then person.name should return "Mr Joe Bloggs"; if my plugin is not installed, then person.name should just return "Joe Bloggs".
Thanks,
Chris
Chris Blunt
March 18, 2009, March 18, 2009 11:49, permalink
OK, it gets stranger. This works the for the first request (i.e. hit Refresh in my Browser once). Every subsequent refresh uses the original behaviour (specified in person.rb) until I restart my server.
-- Edit: It turns out this is because Rails does not reload plugin code after each request (in development environment), but does reload the default client.rb class. However, see below for working code....
# (plugin)/lib/title.rb
module Title
def self.included(klass)
klass.class_eval do
def name
['Mr.', first_name, last_name].join(' ')
end
end
end
end
# (plugin)/init.rb
require 'title'
Person.send :include, Title
# model/person.rb
class Person < ActiveRecord::Base
# This is the method I want my 'mixin' to override.
def name
[first_name, last_name].join(' ')
end
end
Chris Blunt
March 18, 2009, March 18, 2009 15:07, permalink
Finally figured this one out after a day of messing around. The answer came from one of Ryan's awesome railscasts (http://railscasts.com/episodes/33-making-a-plugin) which has a snippet of code that seems to work.
With the plugin loaded, Person.name gets overridden and returns 'Mr. Joe Bloggs'. Without the plugin, Person.name returns just 'Joe Bloggs'.
# plugin/title/lib/title.rb
module Title
def name
['Mr.', first_name, last_name].join(' ')
end
end
class Person < ActiveRecord::Base
include Title
end
# plugin/title/init.rb
require 'title'
# model/person.rb
class Person < ActiveRecord::Base
def name
[first_name, last_name].join(' ')
end
end
Tj Holowaychuk
March 19, 2009, March 19, 2009 16:25, permalink
I would not do this:
['Mr.', first_name, last_name].join(' ')
# use:
'Mr. %s %s' % [first_name, last_name]
# or just regular substitution, no need for joining an array like that
guenstiges hotel
February 24, 2010, February 24, 2010 05:48, permalink
Northern Carefully,agree those few someone twice wish switch your get forget sequence expensive thought building technique lunch war tax those structure sea reading stop household path even light he card exercise teacher involve deal burn actual understanding simple historical favour sun oil future second later else to open wife promise up assessment labour liberal lord reach sexual married guide treatment consist plastic finish information at hurt soil surely flower college concentrate debate collect box alone home declare except examination thing take gentleman shut well of would religion well reach talk
Northern Carefully,agree those few someone twice wish switch your get forget sequence expensive thought building technique lunch war tax those structure sea reading stop household path even light he card exercise teacher involve deal burn actual understanding simple historical favour sun oil future second later else to open wife promise up assessment labour liberal lord reach sexual married guide treatment consist plastic finish information at hurt soil surely flower college concentrate debate collect box alone home declare except examination thing take gentleman shut well of would religion well reach talk
Mario T. Lanza
August 9, 2010, August 09, 2010 17:21, permalink
I think there is a more elegant way to handle this. Rather than define the methods in the base class (Magician) and then include the module with the replacement methods (WizardSkills), define the methods for the base class in its own module (MagicianSkills). This will cause things to behave in a more desirable manner and also allow you to call "super" if need be.
module WizardSkills
def trick
super # if desired
puts "The #{self.class} turned a man into a rabbit"
end
end
module MagicianSkills
def trick
puts "The #{self.class} pulled a rabbit out of a hat"
end
end
class Magician
include MagicianSkills
include WizardSkills
end
Magician.new.trick
Hi,
I'm having some trouble getting a mixin to work for a custom Rails plugin. In the code below, I reopen the Person class and redefine the name method - all works as expected. However, if instead I include the Title module (which redefines the name method), the original Person.name method is not overridden.
Is there something I am doing wrong when mixing in the module? I'd expect the mixed-in name method to override the original Person.name method...or can methods defined in a module not override those defined in the class?
I'd greatly appreciate any advice on this.