LoginSignup
0
0

More than 5 years have passed since last update.

利用 Thor 来写 CLI

Last updated at Posted at 2015-09-17
explicitly |ɪkˈsplɪsɪtli|
adverb
①(clearly, precisely)明确地 míngquè de ‹warn, forbid, tell, deny›
②(openly)坦率地 tǎnshuài de ‹talk, admit›
③(graphically)生动地 shēngdòng de ‹depict, display›

入门

简单说,所有继承自Thor类的类都是可以执行的, 而且可以带很多子命令和参数.
在这样的类里, 所有的公开方法都是可以作为第一参数的形式来执行各种子命令的.
最后的效果就是能够像 git 或者 bundler 那样灵活使用.

看简单的例子:
我们创建一个文件: touch cli, 然后编辑这个文件, 创建一个类MyCLI, 然后继续了Thor.

class MyCLI < Thor
  desc "hello NAME", "say hello to NAME"
  def hello(name)
    puts "Hello #{name}"
  end
end

MyCLI.start(ARGV)

这个时候如果我们使用ruby来执行这个文件的话, 我们会发现这样的结果:

─> ruby cli                                                                                                                                                               
Commands:
  cli hello NAME      # say hello to NAME
  cli help [COMMAND]  # Describe available commands or one specific command

这里需要注意的就是在定义完这个类之后, 它会通过MyCLI.start(ARGV)里的一个start方法来触发当前文件中所有可以当作子命令来执行的类. 而且它还可以帮助解析从命令行传递的参数作为方法参数使用, 如果当前没有传递任何参数,就会默认输出帮助信息.

如上面的输出:

 cli hello NAME      # say hello to NAME

我们来把玩一下:

─> ruby cli hello "xiang"                                                                                                                                                     
Hello xiang

有点酷? 我们继续

关于命令的描述

默认 Thor 会使用desc来作为短描述使用, 如果想要使用长描述, 可以通过使用long_desc来完成.
在 长描述的场合,有时候需要强制换行, 可以通过如 markdown 一样的方式 用\x5 来实现.

class MyCLI < Thor
  desc "hello NAME", "say hello to NAME"
  long_desc <<-LONGDESC
    `cli hello` will print out a message to a person of your
    choosing.

    You can optionally specify a second parameter, which will print
    out a from message as well.

    > $ cli hello "Yehuda Katz" "Carl Lerche"
    \x5> from: Carl Lerche
  LONGDESC
  def hello(name, from=nil)
    puts "from: #{from}" if from
    puts "Hello #{name}"
  end
end

命令选修和标识

Thor 已经实现了轻松使用命令的选项和标识:

class MyCLI < Thor
  desc "hello NAME", "say hello to NAME"
  option :from
  def hello(name)
    puts "from: #{options[:from]}" if options[:from]
    puts "Hello #{name}"
  end
end

这个时候,可以这样玩:


$ ruby ./cli hello --from "Carl Lerche" Yehuda
from: Carl Lerche
Hello Yehuda

$ ruby ./cli hello Yehuda --from "Carl Lerche"
from: Carl Lerche
Hello Yehuda

$ ruby ./cli hello Yehuda --from="Carl Lerche"
from: Carl Lerche
Hello Yehuda

但是一般来说,所有的参数都是作为字符串来使用的, 不过也可以这样:

class MyCLI < Thor
  option :from
  option :yell, :type => :boolean
  desc "hello NAME", "say hello to NAME"
  def hello(name)
    output = []
    output << "from: #{options[:from]}" if options[:from]
    output << "Hello #{name}"
    output = output.join("\n")
    puts options[:yell] ? output.upcase : output
  end
end

这样玩:

$ ./cli hello --yell Yehuda --from "Carl Lerche"
FROM: CARL LERCHE
HELLO YEHUDA

$ ./cli hello Yehuda --from "Carl Lerche" --yell
FROM: CARL LERCHE
HELLO YEHUDA

同样,还有必选参数选修哦

class MyCLI < Thor
  option :from, :required => true
  option :yell, :type => :boolean
  desc "hello NAME", "say hello to NAME"
  def hello(name)
    output = []
    output << "from: #{options[:from]}" if options[:from]
    output << "Hello #{name}"
    output = output.join("\n")
    puts options[:yell] ? output.upcase : output
  end
end

这样的话,如果没有from选修:

$ ./cli hello Yehuda
No value provided for required options '--from'

下面是对于选修的解释:

:desc: A description for the option. When printing out full usage for a command using cli help hello, this description will appear next to the option.
:banner: The short description of the option, printed out in the usage description. By default, this is the upcase version of the flag (from=FROM).
:required: Indicates that an option is required
:default: The default value of this option if it is not provided. An option cannot be both :required and have a :default.
:type: :string, :hash, :array, :numeric, or :boolean
:aliases: A list of aliases for this option. Typically, you would use aliases to provide short versions of the option.

另外, 可以通过options这个参数来一次性搞定所有选修参数

class MyCLI < Thor
  desc "hello NAME", "say hello to NAME"
  options :from => :required, :yell => :boolean
  def hello(name)
    output = []
    output << "from: #{options[:from]}" if options[:from]
    output << "Hello #{name}"
    output = output.join("\n")
    puts options[:yell] ? output.upcase : output
  end
end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0