Sök…


Anmärkningar

Förfiningar omfattar lexiskt, vilket innebär att de gäller från den tid de aktiveras (med det using nyckelordet) tills kontrollen skiftas. Vanligtvis ändras kontrollen i slutet av en modul, klass eller fil.

Apaplackning med begränsad omfattning

Monkey patchings huvudsakliga fråga är att den förorenar det globala räckvidden. Ditt kod fungerar är priset för alla moduler du använder inte kliver på varandras tår. Ruby-lösningen på detta är förfiningar, som i princip är apapatcher i ett begränsat omfång.

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'

Moduler med dubbla ändamål (förfining eller globala korrigeringar)

Det är en bra praxis att utjämna fläckar med förfining, men ibland är det trevligt att ladda det globalt (till exempel i utveckling eller testning).

Säg till exempel att du vill starta en konsol, kräva ditt bibliotek och sedan ha de korrigerade metoderna tillgängliga i det globala omfånget. Du kan inte göra detta med förfiningar eftersom using måste kallas i en klass / moduldefinition. Men det är möjligt att skriva koden på ett sådant sätt att det är dubbelt syfte:

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

Dynamiska förbättringar

Förfiningar har speciella begränsningar.

refine kan endast användas i en modulomfång, men kan programmeras med send :refine .

using är mer begränsad. Det kan bara kallas i en klass / moduldefinition. Det kan fortfarande acceptera en variabel som pekar på en modul och kan åberopas i en slinga.

Ett exempel som visar dessa begrepp:

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

Eftersom using är så statisk kan det utfärdas en belastningsordning om förädlingsfilerna inte laddas först. Ett sätt att ta itu med detta är att linda in den korrigerade klass / moduldefinitionen i en proc. Till exempel:

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

Att ringa proc skapar den lappade klassen Foo::Bar . Detta kan försenas tills alla koder har laddats.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow