圧倒亭グランパのブログ

30年後の自分にもわかるように書くブログ

clim 0.1.3 released

Crystalの「スリムなCLIビルダー clim」の version 0.1.3 をリリースしました。

github.com

以下の記事でも紹介しましたが、自作ライブラリのリリースをブログでも公開してみたかったのです。

at-grandpa.hatenablog.jp

clim

climは、

  • スリムな実装
  • 直感的なコード

を目標として作っています。CLIツールを作る上で、必要最低限の機能を有し、かつ直感的に書けることを目指しています。そのため、以下の機能だけが実装されています。

  • CLIツールのオプションの型は以下の3つ
    • String
    • Bool
    • Array
  • 入れ子が可能なサブコマンド
  • 整形されたヘルプを表示する--help をデフォルトで実装

サンプルコードは以下です。

require "clim"

module Hello
  class Cli < Clim

    # Following is difinition of main command.
    #
    main_command
    desc   "Hello CLI tool."
    usage  "hello [options] [arguments] ..."
    array  "-n NAME",  "--name=NAME",      desc: "Target name.",        default: [] of String
    string "-g WORDS", "--greeting=WORDS", desc: "Words of greetings.", default: "Hello"
    run do |opts, args|
      print "#{opts["greeting"].as(String)}, "
      print "#{opts["name"].as(Array(String)).join(", ")}!"
      print "\n"
    end

  end
end

Hello::Cli.start(ARGV)

clim/README.md at master · at-grandpa/clim · GitHub より

dslは、直感でわかりやすい記述を心がけました。サブコマンドの例は以下です。

require "clim"

module Sample
  class Cli < Clim
    main_command
    desc "Main command."
    usage "sample [sub_command]"
    run do |opts, args|
      puts opts["help"]
    end

    sub do
      command "sub_cmd1"
      desc "Sub command 1."
      usage "sample sub_cmd1 [sub_command]"
      run do |opts, args|
        puts "sub command 1"
      end

      command "sub_cmd2"
      desc "Sub command 2."
      usage "sample sub_cmd2 [sub_command]"
      run do |opts, args|
        puts "sub command 2"
      end

      sub do
        command "sub_sub_cmd1"
        desc "Sub sub command 1."
        usage "sample sub_cmd2 sub_sub_cmd1"
        run do |opts, args|
          puts "sub sub command 1"
        end

        command "sub_sub_cmd2"
        desc "Sub sub command 2."
        usage "sample sub_cmd2 sub_sub_cmd2"
        run do |opts, args|
          puts "sub sub command 2"
        end
      end
    end
  end
end

Sample::Cli.start(ARGV)

ぶら下げたい親コマンドの定義の下に、sub do ... end でコマンドを定義すれば、その親コマンドのサブコマンドになります。実行してみましょう。

$ ./sample

  Main command.

  Usage:

    sample [sub_command]

  Options:

    --help                           Show this help.

  Sub Commands:

    sub_cmd1   Sub command 1.
    sub_cmd2   Sub command 2.

ちゃんと、メインのコマンドの下に、サブコマンドが二つできていますね。sub_cmd2には、さらにサブコマンドがあるので、そちらも見てみます。

$ ./sample sub_cmd2 --help

  Sub command 2.

  Usage:

    sample sub_cmd2 [sub_command]

  Options:

    --help                           Show this help.

  Sub Commands:

    sub_sub_cmd1   Sub sub command 1.
    sub_sub_cmd2   Sub sub command 2.

サブサブコマンドが存在しています。実行するとちゃんと run do ... end ブロックの中が実行されます。

$ ./sample sub_cmd2 sub_sub_cmd2
sub sub command 2

このように、サブコマンドの無限ネストが可能です。(需要はなさそうですが)

もっと詳細を見たい方は、githubのREADMEを御覧ください。

 

作ってみて

作ってみて思ったのは、

  • 自作ライブラリを公開するのは本当に楽しい
  • 使い勝手が良いものを自分で作れるので気持ちいい
  • crystalの「DSLの表現力」と「バイナリを吐ける」という組み合わせは、簡単にCLIツールを作る上ではとても良さそう
  • 下記の記事でも紹介したdesというCLIツールでも使っているが、直感的に書けて良い

at-grandpa.hatenablog.jp

という感じです。しかし、「自分の使いやすいもの」を作ったので良く感じてしまうのはあたりまえですね。これが大多数の人に受け入れられるかは、今後使って貰えるかが指標になりそうです。