How to make two options for working with you?

With Thor, you can use method_option to set parameters for a specific task. To set parameters for all tasks in a class, you can use class_option . But what about the case when you want some tasks of a class, but not all, to share parameters?

In the following options, task1 and task2 exchanged, but they do not share all the options, and they do not have parameters with 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) 

The problem with specifying method_option :type, :type => :string, :required => true, :default => 'foo' task1 method_option :type, :type => :string, :required => true, :default => 'foo' for task1 and task2 is that it violates the DRY principle . Is there an idiomatic way to handle this?

+7
source share
5 answers

method_option defined in thor.rb , and according to the documentation, it takes the following parameters:

  • name<Symbol>:: The name of the argument.
  • options<Hash>:: Described below.

Knowing this, you can save the method_option parameters in an array and expand this array to separate parameters when calling method_option .

 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) 

I have no idea if this is idiomatic, and I don't think it is so elegant. However, this is better than violating the DRY principle.

+11
source

I would just use a superclass, for example:

 require 'thor' class CliBase < Thor def self.shared_options method_option :verbose, :aliases => '-v', :type => :boolean, :desc => 'Verbose', :default => false, :required => false end end 

... then subclass as follows:

 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) 
+3
source

I had the same problem, and I used what N.N. answered. But I found some problems:

If you want to share more than one option, as in the example, it does not work very well. Imagine what you want to share :value between task2 and task3. You can create another shared_options or create an array with shared parameters and access it with the name shared_option.

It works, but it is verbose and hard to read. I implemented something small to be able to exchange options.

 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 

This creates a hash with the parameter name as the key, and โ€œdefinitionโ€ (required, default, etc.) as the value (which is the hash). It is easily accessible after that.

With this, you can do the following:

 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) 

For me it looks more readable, and if the number of teams is more than 3 or 4, it will be a big improvement.

+2
source

To avoid typing "shared_options" all the time, you can also do this:

 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 

Or, if you don't want acrobatics with an alias for methods, you can simply define your own method "my_desc" and call it instead of "desc".

0
source

So, there is a good dry way to do this now, but it may not fall under idiomatic requirements, although I wanted to mention this for those who are looking for a more recent answer.

You can start by using class_options to set most common parameters between your methods:

 module MyModule class Hello < Thor class_option :name, :desc => "name", :required => true class_option :greet, :desc => "greeting to use", :required => true desc "Hello", "Saying hello" def say puts "#{options[:greet]}, #{options[:name]}!" end desc "Say", "Saying anything" remove_class_option :greet def hello puts "Hello, #{options[:name]}!" end def foo puts "Foo, #{options[:name]}!" end end end 

The best part about this is that it applies to all methods after the declaration. Given this set, you can see that the first method requires both a greeting and a name, but to say foo only a name is required.

0
source

All Articles