Chaque fois que j'essaie d'étendre un module Ruby, je perds les méthodes du module. Ni inclure ni étendre ne le fera. Considérons l'extrait de code:
module A
def self.say_hi
puts "hi"
end
end
module B
include A
end
B.say_hi #undefined_method
Que B inclue ou étende A, say_hi ne sera pas défini.
Y a-t-il un moyen d'accomplir quelque chose comme ça?
Si vous êtes l'auteur de module A
et que vous en avez fréquemment besoin, vous pouvez ré-écrire A comme suit:
module A
module ClassMethods
def say_hi
puts "hi"
end
end
extend ClassMethods
def self.included( other )
other.extend( ClassMethods )
end
end
module B
include A
end
A.say_hi #=> "hi"
B.say_hi #=> "hi"
Je ne pense pas qu'il existe un moyen simple de le faire.
Donc, voici une manière complexe:
module B
class << self
A.singleton_methods.each do |m|
define_method m, A.method(m).to_proc
end
end
end
Vous pouvez le mettre dans une méthode d'assistance comme celle-ci:
class Module
def include_module_methods(mod)
mod.singleton_methods.each do |m|
(class << self; self; end).send :define_method, m, mod.method(m).to_proc
end
end
end
module B
include_module_methods A
end
Utilisez include_complete
gem install include_complete
module A
def self.say_hi
puts "hi"
end
end
module B
include_complete A
end
B.say_hi #=> "hi"
Johnathan, Je ne suis pas sûr que vous vous posiez encore des questions à ce sujet, mais il existe deux manières différentes d'utiliser des modules en Ruby. A.) vous utilisez des modules sous leur forme autonome Base :: Tree.entity (params) directement dans votre code, ou B.) vous utilisez des modules en tant que méthodes mixins ou auxiliaires.
A. Vous permettra d'utiliser des modules en tant que modèle d'espace de noms. C'est bon pour les grands projets où il y a une chance pour des conflits de nom de méthode
module Base
module Tree
def self.entity(params={},&block)
# some great code goes here
end
end
end
Vous pouvez maintenant l'utiliser pour créer une sorte d'arborescence dans votre code, sans avoir à instancier une nouvelle classe pour chaque appel à Base :: Tree.entity.
Une autre façon de faire en utilisant Namespace-ing est de classe par classe.
module Session
module Live
class Actor
attr_accessor :type, :uuid, :name, :status
def initialize(params={},&block)
# check params, insert init values for vars..etc
# save your callback as a class variable, and use it sometime later
@block = block
end
def hit_rock_bottom
end
def has_hit_rock_bottom?
end
...
end
end
class Actor
attr_accessor :id,:scope,:callback
def initialize(params={},&block)
self.callback = block if block_given?
end
def respond
if self.callback.is_a? Proc
# do some real crazy things...
end
end
end
end
Nous avons maintenant le potentiel de chevauchement dans nos classes. Nous voulons savoir que lorsque nous créons une classe Actor, il s'agit de la bonne classe, c'est donc ici que les espaces de noms sont utiles.
Session::Live::Actor.new(params) do |res|...
Session::Actor.new(params)
B. Mix-Ins Ce sont vos amis. Utilisez-les chaque fois que vous pensez devoir faire quelque chose plus d'une fois dans votre code.
module Friendly
module Formatter
def to_hash(xmlstring)
#parsing methods
return hash
end
def remove_trailing_whitespace(string,&block)
# remove trailing white space from that idiot who pasted from textmate
end
end
end
Maintenant, chaque fois que vous avez besoin de formater une chaîne xml en tant que hachage, ou de supprimer les espaces blancs de fin dans votre futur code, mélangez-les simplement.
module Fun
class Ruby
include Friendly::Formatter
attr_accessor :string
def initialize(params={})
end
end
end
Vous pouvez maintenant formater la chaîne dans votre classe.
fun_Ruby = Fun::Ruby.new(params)
fun_Ruby.string = "<xml><why><do>most</do><people></people><use>this</use><it>sucks</it></why></xml>"
fun_Ruby_hash = fun_Ruby.to_hash(fun_Ruby.string)
J'espère que c'est une bonne explication. Les points soulevés ci-dessus sont de bons exemples de moyens d'étendre les classes, mais avec les modules, le plus difficile est de savoir quand utiliser le mot-clé self . Il fait référence à la portée de l'objet dans la hiérarchie des objets de Ruby. Donc, si vous voulez utiliser un module en tant que mix-in et ne voulez rien déclarer de singleton, n'utilisez pas le mot-clé self. Cependant, si vous souhaitez conserver l'état dans l'objet, utilisez simplement une classe et dans les modules que vous voulez.
Je n'aime pas que tout le monde utilise self.included
. J'ai une solution plus simple:
module A
module ClassMethods
def a
'a1'
end
end
def a
'a2'
end
end
module B
include A
module ClassMethods
include A::ClassMethods
def b
'b1'
end
end
def b
'b2'
end
end
class C
include B
extend B::ClassMethods
end
class D < C; end
puts D.a
puts D.b
puts D.new.a
puts D.new.b