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.
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/
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
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.
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
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'
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.