2010-07-29 8 views
9

Piszę małą aplikację wiersza polecenia w języku Ruby, która używa fileutils ze standardowej biblioteki do operacji na plikach. W zależności od tego, w jaki sposób użytkownik wywołuje aplikację, będę chciał dołączyć: FileUtils, FileUtils::DryRun lub FileUtils::Verbose.Jak mogę wybrać, która wersja modułu ma być dołączana dynamicznie w Ruby?

Ponieważ include jest prywatny, nie mogę jednak umieścić logiki w metodzie obiektu initialize. (To była moja pierwsza myśl, od tego czasu mogłem podać informacje o wyborze użytkownika jako parametr do new.) Wymyśliłem dwie opcje, które wydają się działać, ale nie jestem z nich zadowolony:

  1. Ustaw zmienną globalną w przestrzeni nazw aplikacji w oparciu o wybrane przez użytkownika, a następnie wykonaj warunkowe obejmują w klasie:

    class Worker 
        case App::OPTION 
        when "dry-run" 
        include FileUtils::DryRun 
        etc. 
    
  2. Tworzenie podklas, gdzie jedyną różnicą jest to, która wersja FileUtils obejmują one. Wybierz odpowiedni, w zależności od wyboru użytkownika.

    class Worker 
        include FileUtils 
        # shared Worker methods go here 
    end 
    class Worker::DryRun < Worker 
        include FileUtils::DryRun 
    end 
    class Worker::Verbose < Worker 
        include FileUtils::Verbose 
    end 
    

Pierwsza metoda wydaje DRY-ER, ale mam nadzieję, że coś jest prostsze, że nie myśli.

Odpowiedz

7

A co jeśli jest prywatny?

class Worker 
    def initialize(verbose=false) 
    if verbose 
     (class <<self; include FileUtils::Verbose; end) 
    else 
     (class <<self; include FileUtils; end) 
    end 
    touch "test" 
    end 
end 

Obejmuje FileUtils::something w metaklasą szczególności na Worker „s - w głównej Worker klasy nie. Różni pracownicy mogą w ten sposób używać różnych FileUtils.

+0

„Więc co” brzmi prosto do mnie. (To była * dokładnie * bardziej bezpośrednia rzecz, której nie widziałem.) Dzięki. – Telemachus

+0

Ups, mój błąd. Kod, który wcześniej podałem, modyfikuje klasę 'Worker', więc wszystkie' Worker' będą używać tych samych ustawień. Teraz faktycznie używa metaklasy i pozwala na ustawienia sprzed pracownika. – taw

+1

zamiast '(class << self; include FileUtils; end)' możesz również użyć 'extend FileUtils'. –

0

Jeśli chcesz uniknąć „przełącznik” i wstrzyknąć moduł Składnia

def initialize(injected_module) 
    class << self 
     include injected_module 
    end 
end 

nie zadziała (zmienna injected_module jest poza zakresem). Można użyć sztuczki self.class.send, ale za obiekt instancji rozszerzenie wydaje się bardziej rozsądne dla mnie, nie tylko dlatego, że jest krótszy napisać:

def initialize(injected_module = MyDefaultModule) 
    extend injected_module 
end 

ale również minimalizuje skutki uboczne - wspólne i łatwo zmienny stan klasy, co może spowodować nieoczekiwane zachowanie w większym projekcie. W Ruby nie ma prawdziwej "prywatności", ale niektóre metody są oznaczone jako prywatne, nie bez powodu.

0

Warunkowo tym module za pomocą metod wysyłania działa na mnie jak na poniższym przykładzie badanej:

class Artefact 
    include HPALMGenericApi 
    # the initializer just sets the server name we will be using ans also the 'transport' method : Rest or OTA (set in the opt parameter) 
    def initialize server, opt = {} 
    # conditionally include the Rest or OTA module 
    self.class.send(:include, HPALMApiRest) if (opt.empty? || (opt && opt[:using] opt[:using] == :Rest)) 
    self.class.send(:include, HPALMApiOTA) if (opt && opt[:using] opt[:using] == :OTA)  
    # ... rest of initialization code 
    end 
end 
Powiązane problemy