Szukaj…


Uwagi

Udoskonalenia mają zakres leksykalny, co oznacza, że obowiązują od momentu ich aktywacji (za using słowa kluczowego using ) do momentu zmiany kontroli. Zwykle kontrola zmienia się na końcu modułu, klasy lub pliku.

Łatka na małpy o ograniczonym zasięgu

Głównym problemem łatania małp jest to, że zanieczyszcza globalny zasięg. Twój kod działa na łasce wszystkich modułów, których używasz, nie nadepną na siebie. Rubinowym rozwiązaniem tego są udoskonalenia, które są w zasadzie małpimi łatkami w ograniczonym zakresie.

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'

Moduły podwójnego zastosowania (udoskonalenia lub poprawki globalne)

Dobrą praktyką jest stosowanie zakresu poprawek za pomocą udoskonaleń, ale czasem ładnie jest ładować je globalnie (na przykład podczas programowania lub testowania).

Powiedzmy na przykład, że chcesz uruchomić konsolę, potrzebujesz swojej biblioteki, a następnie masz dostępne łatki w zasięgu globalnym. Nie można tego zrobić z udoskonaleniami, ponieważ using musi być wywołane w definicji klasy / modułu. Ale możliwe jest napisanie kodu w taki sposób, aby był on podwójny:

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

Dynamiczne udoskonalenia

Udoskonalenia mają specjalne ograniczenia.

refine może być używana tylko w zakresie modułu, ale może być programowana za pomocą send :refine .

using jest bardziej ograniczone. Można go wywołać tylko w definicji klasy / modułu. Mimo to może akceptować zmienną wskazującą moduł i może być wywoływana w pętli.

Przykład pokazujący te pojęcia:

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

Ponieważ using jest tak statyczne, można wydać polecenie ładowania, jeśli pliki zawężania nie zostaną załadowane jako pierwsze. Sposobem na rozwiązanie tego jest zawinięcie poprawionej definicji klasy / modułu w proc. Na przykład:

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

Wywołanie proc tworzy łataną klasę Foo::Bar . Może to zostać opóźnione do momentu załadowania całego kodu.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow