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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow