gsmendoza
gsmendoza

Reputation: 1394

How do I include a module to a class such that the module overrides the class?

Is there a way to include a module to a class such that the module's methods override the methods of the class? For example:

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 example above, u.name is 'john', not 'JOHN'. I know that if I extend the user object instead of including the module to 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 include the module at the class level, not object level.

Upvotes: 4

Views: 742

Answers (5)

M. Lanza
M. Lanza

Reputation: 6790

It may not always be an option, but I think it's better if you simply move the class's methods into its own module and mix that module back into the class. This feels cleaner to me.

http://gist.github.com/515856

Upvotes: 0

alib
alib

Reputation:

The problem here is that attr_accessor creates a User.name method that override the UpcasedName.name method, so one solution would be using 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'

Upvotes: 0

gsmendoza
gsmendoza

Reputation: 1394

Based on ucron's answer, it's possible to 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

Upvotes: 0

Firas Assaad
Firas Assaad

Reputation: 25750

Include is similar to inheriting from a another class, in the sense that the methods of the class you include a module into have 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

Upvotes: 2

ucron
ucron

Reputation: 2852

Right now there have been several approaches to doing 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 posted by Bruce Williams' method here : http://www.codefluency.com/articles/2009/01/03/wrapping-a-method-in-ruby

If you're 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/

Upvotes: 3

Related Questions