2013-01-15 9 views
7

Con Thor si può usare method_option per impostare le opzioni per una particolare attività. Per impostare le opzioni per tutte le attività in una classe, è possibile utilizzare class_option. Ma per quanto riguarda il caso in cui si vogliono alcune attività di una classe, ma non tutte, per condividere le opzioni?Come rendere le due attività Thor condivisioni opzioni?

Nelle seguenti opzioni task1 e task2 condivide ma non condividono tutte le opzioni e non condividono alcuna opzione con 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) 

Il problema con affermando method_option :type, :type => :string, :required => true, :default => 'foo' sia per task1 e task2 è che esso viola the DRY principle. C'è un modo idiomatico di gestirlo?

risposta

11

method_option è definito in thor.rb e prende i seguenti parametri secondo la documentazione:

  • name<Symbol>:: Il nome dell'argomento.
  • options<Hash>:: Descritto di seguito.

Sapendo questo è possibile memorizzare i parametri per method_option in un array e expand that array into separate parameters come method_option si chiama.

require 'thor' 

class Cli < Thor 
    shared_options = [:type, {:type => :string, :required => true, :default => 'foo'}] 

    desc 'task1', 'Task 1' 
    method_option *shared_options 
    def task1 
    end 

    desc 'task2', 'Task 2' 
    method_option *shared_options 
    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) 

Non ho idea se questo sia idiomatico e non penso sia così elegante. Eppure, è meglio che violare il principio DRY.

+1

buona idea, ma possiamo andare oltre e magari definire il metodo di classe shared_options delegando a method_option + unendo l'hash comune? – inger

3

vorrei solo usare una superclasse come questo:

require 'thor' 

class CliBase < Thor 
    def self.shared_options 

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

    end 
end 

... poi sottoclasse come segue:

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

Hai davvero bisogno di sottoclassi qui? Perché non limitarsi a definire shared_options nella classe CLI? – inger

+3

Penso che ci siano molti modi per rispondere all'OP. Mi capita di disprezzare la scrittura di codice più di una volta così con la mia soluzione, scrivere la classe base, metterla in una gemma e poi usarla ogni volta che si scrive un altro script di Thor. Dopo tutto, -v per verbose è qualcosa che userò di nuovo al di fuori di una singola istanza. Ma certo, puoi farlo come preferisci. – Midwire

2

Ho avuto lo stesso problema e ho usato quello N.N. risposta. Ma ho riscontrato alcuni problemi:

Se si desidera condividere più di un'opzione come nell'esempio, non funziona molto bene. Immagina di voler condividere :value tra task2 e task3. Potresti creare un altro shared_options oppure potresti creare un array con le opzioni condivise e accedervi con il nome shared_option.

Questo funziona ma è prolisso e difficile da leggere. Ho implementato qualcosa di piccolo per poter condividere le opzioni.

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 

Questo crea un hash con il nome di opzione come chiave, e la 'definizione' (richiesta, di default, ecc) come valore (che è un hash). Questo è facilmente accessibile in seguito.

Con questo, è possibile effettuare le seguenti operazioni:

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) 

Per me sembra più leggibile, e se il numero di comandi è più grande di 3 o 4 E 'un grande miglioramento.

0

Per non TIPO "shared_options" tutto il tempo, si può anche fare questo:

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 

Oppure, se non si vuole acrobazie con il metodo aliasing, si può solo definire il proprio metodo di "my_desc "e chiamalo invece di" desc ".