How to include a module in a class so that the module redefines the class?

Is there a way to include a module in a class so that module methods override class methods? For instance:

module UpcasedName def name @name.upcase end end class User attr_accessor :name include UpcasedName end u = User.new u.name = 'john' puts u.name # outputs 'john', not 'JOHN' 

In the above example, u.name is "john" and not "JOHN". I know that if I expand the user object and not include the module in the class, this will work

 module UpcasedName def name @name.upcase end end class User attr_accessor :name end u = User.new u.name = 'john' u.extend UpcasedName puts u.name # outputs 'JOHN' 

However, I want to enable the module at the class level, not at the object level.

+4
source share
5 answers

There are currently several approaches to this. Well, the first and most basic would be to use alias_method_chain from ActiveSupport

 require 'activesupport' module UpcasedName def self.included( base ) base.alias_method_chain :name, :upcase end def name_with_upcase @name.upcase end end class User attr_accessor :name include UpcasedName end u = User.new u.name = 'john' puts u.name 

The approach you posted is actually similar to the approach proposed here by the Bruce Williams method: http://www.codefluency.com/articles/2009/01/03/wrapping-a-method-in-ruby

If you are really hardcore about this, you can follow the approaches posted by Yehuda Katz here: http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method/

+3
source

Include is similar to inheriting from another class in the sense that the methods of the class in which you include the module take precedence over the included methods. You can even call super in your class to access the method from the module:

 class User attr_accessor :name def name super end include UpcasedName end u = User.new u.name = 'john' puts u.name # outputs 'JOHN' 

Here's an article about it: include vs. extend in Ruby

+2
source

Based on ucron's answer, you can do this without activesupport as follows:

 module UpcasedName def self.included(base) base.send :alias_method, :name_without_feature, :name base.send :alias_method, :name, :name_with_upcase end def name_with_upcase @name.upcase end end class User attr_accessor :name include UpcasedName end u = User.new u.name = 'john' puts u.name 
0
source

The problem is that attr_accessor creates a User.name method that overrides the UpcasedName.name method, so one solution will use attr_writer :

 module UpcasedName def name @name.upcase end end class User attr_writer :name include UpcasedName end u = User.new u.name = 'john' puts u.name # outputs 'JOHN' 
0
source

This may not always be an option, but I think it’s better if you simply move the class methods into your own module and mix this module with the class. It seems to me cleaner.

http://gist.github.com/515856

0
source

All Articles