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é.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow