2009-03-10 8 views
4

Existe-t-il un moyen d'inclure un module dans une classe de sorte que les méthodes du module remplacent les méthodes de la classe? Par exemple:Comment inclure un module dans une classe afin que le module remplace la classe?

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' 

Dans l'exemple ci-dessus, est u.name 'john', pas 'JOHN'. Je sais que si je tends l'objet utilisateur au lieu d'inclure le module à la classe, cette travaillera

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' 

Cependant, je veux inclure le module au niveau de la classe, pas de niveau objet.

Répondre

3

À l'heure actuelle, il existe plusieurs approches pour ce faire. Eh bien la première et la plus fondamentale serait d'utiliser alias_method_chain de 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 

L'approche que vous avez publié est en fait similaire à l'approche affichée par la méthode de Bruce Williams ici: http://www.codefluency.com/articles/2009/01/03/wrapping-a-method-in-ruby

Si vous êtes vraiment hardcore à propos de vous pouvez suivre les approches publiées par Yehuda Katz ici: http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method/

+0

Merci pour la référence. Cependant, le base.alias_method_chain: nom,: upcase doit être changé pour base.alias_method_chain: nom,: name_with_upcase – gsmendoza

2

L'inclusion est similaire à l'héritage d'une autre classe, dans le sens où les méthodes de la classe auxquelles vous incluez un module ont la priorité sur les méthodes incluses. Vous pouvez même appeler super dans votre classe pour accéder à la méthode à partir du module:

class User 
    attr_accessor :name 
    def name 
    super 
    end 
    include UpcasedName 
end 

u = User.new 
u.name = 'john' 
puts u.name # outputs 'JOHN' 

Voici un article sur le sujet: include vs. extend in Ruby

0

Sur la base de la réponse de ucron, il est possible de le faire sans activesupport comme suit:

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

le problème ici est que attr_accessor crée une méthode de User.name qui remplacent la méthode UpcasedName.name, donc une solution serait d'utiliser attr_writer:

0

Ce n'est peut-être pas toujours une option, mais je pense qu'il est préférable de simplement déplacer les méthodes de la classe dans son propre module et de réintégrer ce module dans la classe. Cela me semble plus propre.

http://gist.github.com/515856

Questions connexes