2013-01-15 15 views
7

Z Thorem można użyć method_option, aby ustawić opcje dla określonego zadania. Aby ustawić opcje dla wszystkich zadań w klasie, można użyć class_option. Ale co z przypadkiem, w którym ktoś chce wykonywać pewne zadania klasy, ale nie wszystkie, aby dzielić się opcjami?Jak wykonać dwa zadania thor współdzielić opcje?

W poniższych opcjach na akcje task1 i task2, ale nie udostępniają wszystkich opcji i nie udostępniają opcji z task3.

require 'thor' 

class Cli < Thor 
    desc 'task1', 'Task 1' 
    method_option :type, :type => :string, :required => true, :default => 'foo' 
    def task1 
    end 

    desc 'task2', 'Task 2' 
    method_option :type, :type => :string, :required => true, :default => 'foo' 
    method_option :value, :type => :numeric 
    def task2 
    end 

    desc 'task3', 'Task 3' 
    method_option :verbose, :type => :boolean, :aliases => '-v' 
    def task3 
    end 
end 

Cli.start(ARGV) 

Problem z podaniem method_option :type, :type => :string, :required => true, :default => 'foo' zarówno task1 i task2 jest to, że jest niezgodny the DRY principle. Czy istnieje idiomatyczny sposób radzenia sobie z tym?

Odpowiedz

11

method_option jest zdefiniowana w thor.rb i wykonuje następujące parametry zgodnie z dokumentacją:

  • name<Symbol>:: Nazwa argumentu.
  • options<Hash>:: Opisane poniżej.

Wiedząc o tym można przechowywać parametry do method_option w tablicy i expand that array into separate parameters jak method_option nazywa.

Nie mam pojęcia, czy to jest idiomatyczne i nie sądzę, że jest to takie eleganckie. Mimo to jest to lepsze rozwiązanie niż naruszenie zasady DRY.

+1

Dobry pomysł, ale czy możemy pójść dalej i może zdefiniować metodę klasy shared_options delegującą do method_option + łącząc wspólny skrót? – inger

3

Chciałbym po prostu użyć superklasę takiego:

require 'thor' 

class CliBase < Thor 
    def self.shared_options 

    method_option :verbose, 
        :aliases => '-v', 
        :type => :boolean, 
        :desc => 'Verbose', 
        :default => false, 
        :required => false 

    end 
end 

... wtedy podklasa następująco:

require 'cli_base' 

class Cli < CliBase 
    desc 'task1', 'Task 1' 
    shared_options 
    def task1 
    end 

    desc 'task2', 'Task 2' 
    shared_options 
    method_option :value, :type => :numeric 
    def task2 
    end 

    desc 'task3', 'Task 3' 
    method_option :colors, :type => :boolean, :aliases => '-c' 
    def task3 
    end 
end 

Cli.start(ARGV) 
+0

Czy naprawdę potrzebujesz tutaj podklasy? Dlaczego po prostu nie zdefiniować shared_options w klasie CLI? – inger

+3

Myślę, że istnieje wiele sposobów na odpowiedź na PO. Zdarza mi się pogardzać pisaniem kodu więcej niż jeden raz, więc przy pomocy mojego rozwiązania napisz klasę podstawową, umieść ją w klejnocie, a następnie użyj jej przy każdym kolejnym skrypcie Thora. W końcu -v dla verbose jest czymś, czego użyję ponownie poza pojedynczą instancją. Ale na pewno możesz to zrobić, jak chcesz. – Midwire

2

miałem ten sam problem, a ja co N.N. odpowiedział. Ale napotkałem pewne problemy:

Jeśli chcesz udostępnić więcej niż jedną opcję, tak jak w przykładzie, to nie działa zbyt dobrze. Wyobraź sobie, że chcesz udostępnić :value między task2 i task3. Możesz utworzyć kolejną shared_options lub utworzyć tablicę z opcjami współdzielonymi i uzyskać do niej dostęp za pomocą nazwy shared_option.

To działa, ale jest pełne i trudne do odczytania. Zaimplementowałem coś małego, aby móc udostępniać opcje.

Cli < Thor 
    class << self 
     def add_shared_option(name, options = {}) 
     @shared_options = {} if @shared_options.nil? 
     @shared_options[name] = options 
     end 

     def shared_options(*option_names) 
     option_names.each do |option_name| 
      opt = @shared_options[option_name] 
      raise "Tried to access shared option '#{option_name}' but it was not previously defined" if opt.nil? 
      option option_name, opt 
     end 
     end 
    end 
    #...commands 
end 

Stwarza to skrót z nazwą opcji w kluczu, a 'definicję' (wymagany, domyślny, etc) jako wartość (co jest hash). Jest to łatwo dostępne później.

Z tego, można wykonać następujące czynności:

require 'thor' 

class Cli < Thor 

    add_shared_option :type, :type => :string, :required => true, :default => 'foo' 
    add_shared_option :value, :type => :numeric 

    desc 'task1', 'Task 1' 
    shared_options :type 
    def task1 
    end 

    desc 'task2', 'Task 2' 
    shared_options :type, :value 
    def task2 
    end 

    desc 'task3', 'Task 3' 
    shared_options :value 
    def task3 
    end 
end 

Cli.start(ARGV) 

Dla mnie to wygląda bardziej czytelny, a jeżeli liczba poleceń jest większy niż 3 lub 4 to wielka poprawa.

0

Tak, aby nie wpisać „shared_options” cały czas, można też to zrobić:

require 'thor' 

class Cli < Thor 
    class << self 
    private 
    def shared_options! 
     # list your shared options here 
     method_option :opt1, type: :boolean 
     method_option :opt2, type: :numeric 
     # etc 
    end 

    # alias original desc so we can call it from inside new desc 
    alias_method :orig_desc, :desc 

    # redefine desc, calling original desc, and then applying shared_options! 
    def desc(*args) 
     orig_desc(*args) 
     shared_options! 
    end 
    end 

    desc 'task1', 'Task 1' 

    def task1 
    end 

    desc 'task2', 'Task 2' 

    def task2 
    end 

    desc 'task3', 'Task 3' 

    def task3 
    end 
end 

Lub jeśli nie chcesz akrobacji z metodą aliasingu, można po prostu zdefiniować własną metodę „my_desc "i wywołaj to zamiast" desc ".

Powiązane problemy