Ruby Language
Raffinements
Recherche…
Remarques
Les raffinements ont une portée lexicale, ce qui signifie qu'ils sont actifs à partir du moment où ils sont activés (avec le mot-clé using
) jusqu'à ce que le contrôle change. Généralement, le contrôle est modifié à la fin d'un module, d'une classe ou d'un fichier.
Patch de singe à portée limitée
Le principal problème de la correction des singes est qu’elle pollue la portée globale. Votre code de travail est à la merci de tous les modules que vous utilisez sans marcher sur les pieds les uns des autres. La solution Ruby à cela est des améliorations, qui sont essentiellement des patchs de singe dans une portée limitée.
module Patches
refine Fixnum do
def plus_one
self + 1
end
def plus(num)
self + num
end
def concat_one
self.to_s + '1'
end
end
end
class RefinementTest
# has access to our patches
using Patches
def initialize
puts 1.plus_one
puts 3.concat_one
end
end
# Main scope doesn't have changes
1.plus_one
# => undefined method `plus_one' for 1:Fixnum (NoMethodError)
RefinementTest.new
# => 2
# => '31'
Modules à double usage (améliorations ou correctifs globaux)
Il est recommandé de définir les correctifs à l'aide de Refinements, mais il est parfois utile de les charger globalement (par exemple, en développement ou en test).
Supposons, par exemple, que vous souhaitiez démarrer une console, que vous ayez besoin de votre bibliothèque et que vous ayez les méthodes corrigées disponibles dans la portée globale. Vous ne pouvez pas le faire avec des améliorations car l' using
doit être appelée dans une définition de classe / module. Mais il est possible d’écrire le code de manière à ce qu’il ait un double objectif:
module Patch
def patched?; true; end
refine String do
include Patch
end
end
# globally
String.include Patch
"".patched? # => true
# refinement
class LoadPatch
using Patch
"".patched? # => true
end
Raffinements dynamiques
Les raffinements ont des limitations spéciales.
refine
ne peut être utilisé que dans une portée de module, mais peut être programmé en utilisant send :refine
.
using
est plus limitée. Il ne peut être appelé que dans une définition de classe / module. Cependant, il peut accepter une variable pointant vers un module et être appelé en boucle.
Un exemple montrant ces concepts:
module Patch
def patched?; true; end
end
Patch.send(:refine, String) { include Patch }
patch_classes = [Patch]
class Patched
patch_classes.each { |klass| using klass }
"".patched? # => true
end
Puisque using
est si statique, il est possible de générer un ordre de chargement si les fichiers de raffinement ne sont pas chargés en premier. Un moyen de résoudre ce problème consiste à envelopper la définition de classe / module corrigée dans un processus. Par exemple:
module Patch
refine String do
def patched; true; end
end
end
class Foo
end
# This is a proc since methods can't contain class definitions
create_patched_class = Proc.new do
Foo.class_exec do
class Bar
using Patch
def self.patched?; ''.patched == true; end
end
end
end
create_patched_class.call
Foo::Bar.patched? # => true
L'appel du proc crée la classe corrigée Foo::Bar
. Cela peut être retardé jusqu'à ce que tout le code ait été chargé.