Ruby Language
verfijningen
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.