Ruby Language
perfezionamenti
Ricerca…
Osservazioni
I perfezionamenti sono di tipo lessicale, il che significa che sono in vigore dal momento in cui sono attivati (con la parola chiave using
) fino a quando il controllo non cambia. Di solito il controllo viene modificato dalla fine di un modulo, classe o file.
Patch per scimmie con portata limitata
Il problema principale di Monkey Patching è che inquina l'ambito globale. Il tuo codice di lavoro è in balia di tutti i moduli che usi non calpestare le dita degli altri. La soluzione Ruby a questo è raffinatezza, che sono fondamentalmente patch scimmia in un ambito limitato.
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'
Moduli dual-purpose (perfezionamenti o patch globali)
È buona norma applicare le patch con i perfezionamenti, ma a volte è bello caricarlo globalmente (ad esempio in fase di sviluppo o di test).
Ad esempio, per esempio, si desidera avviare una console, richiedere la libreria e quindi disporre dei metodi con patch disponibili nell'ambito globale. Non è possibile farlo con i perfezionamenti perché using
deve essere chiamato in una definizione di classe / modulo. Ma è possibile scrivere il codice in modo tale che abbia un duplice scopo:
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
Affinamenti dinamici
I perfezionamenti hanno limitazioni speciali.
refine
può essere usato solo nell'ambito di un modulo, ma può essere programmato usando send :refine
.
using
è più limitato. Può essere chiamato solo in una definizione di classe / modulo. Tuttavia, può accettare una variabile che punta a un modulo e può essere invocata in un ciclo.
Un esempio che mostra questi concetti:
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
Poiché l' using
è statico, è possibile che venga emesso un ordine di caricamento se i file di perfezionamento non vengono caricati per primi. Un modo per affrontare questo è quello di avvolgere la definizione di classi / moduli patchato in un proc. Per esempio:
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
Chiamando il proc crea la classe patchato Foo::Bar
. Questo può essere ritardato fino a dopo che tutto il codice è stato caricato.