Zoeken…


Opmerkingen

Verfijningen scope lexicaal, wat betekent dat ze in werking vanaf het moment dat ze worden geactiveerd (met using trefwoord) tot controle verschuivingen. Meestal wordt de besturing gewijzigd aan het einde van een module, klasse of bestand.

Monkey-patching met beperkte reikwijdte

Het belangrijkste probleem van Monkey Patching is dat het de wereldwijde scope vervuilt. Uw code werkt is overgeleverd aan alle modules die u gebruikt en treden niet op elkaars tenen. De Ruby-oplossing hiervoor zijn verfijningen, die in feite aap-pleisters met een beperkte reikwijdte zijn.

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'

Dual-purpose modules (verfijningen of wereldwijde patches)

Het is een goede gewoonte om patches te reiken met behulp van Verfijningen, maar soms is het leuk om het wereldwijd te laden (bijvoorbeeld in ontwikkeling of testen).

Stel bijvoorbeeld dat u een console wilt starten, uw bibliotheek nodig hebt en vervolgens de gepatchte methoden beschikbaar wilt hebben in de globale scope. U kunt dit niet doen met verfijningen, omdat het using moet worden aangeroepen in een klasse / modeldefinitie. Maar het is mogelijk om de code zo te schrijven dat deze twee doelen heeft:

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

Dynamische verfijningen

Verfijningen hebben speciale beperkingen.

refine kan alleen worden gebruikt in een modulebereik, maar kan worden geprogrammeerd met behulp van send :refine .

using is beperkter. Het kan alleen worden opgeroepen in een klasse / modeldefinitie. Toch kan het een variabele accepteren die naar een module verwijst en in een lus worden opgeroepen.

Een voorbeeld van deze concepten:

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

Omdat het using zo statisch is, kan er een laadvolgorde worden uitgegeven als de verfijningsbestanden niet eerst worden geladen. Een manier om dit aan te pakken is om de gepatchte klasse / module-definitie in een proc te wikkelen. Bijvoorbeeld:

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

Door de proc aan te roepen wordt de gepatchte klasse Foo::Bar . Dit kan worden uitgesteld tot nadat alle code is geladen.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow