Skip to content

Commit cd449f4

Browse files
authored
Merge pull request #1 from devnote-dev/dev
feat: push v2
2 parents 71706e2 + 117405e commit cd449f4

24 files changed

+1410
-718
lines changed

README.md

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,44 @@
11
# CLI.cr
2-
Yet another Crystal command line interface library.
2+
3+
Yet another command line interface library for Crystal. Based on [spf13/cobra](https://github.com/spf13/cobra), CLI.cr is built to be almost entirely modular, giving you absolute control over almost everything without the need for embedded macros - there isn't even a default help command or flag!
34

45
## Installation
6+
57
1. Add the dependency to your `shard.yml`:
68
```yaml
79
dependencies:
810
cli:
911
github: devnote-dev/cli.cr
12+
branch: stable
1013
```
1114
1215
2. Run `shards install`
1316

1417
## Usage
18+
1519
```crystal
1620
require "cli"
1721
1822
class MainCmd < CLI::Command
19-
def setup
23+
def setup : Nil
2024
@name = "greet"
21-
@description = "Greets a person"
25+
description = "Greets a person"
2226
add_argument "name", desc: "the name of person to greet", required: true
23-
add_option "caps", short: "c", desc: "greet with capitals"
27+
add_option 'c', "caps", desc: "greet with capitals"
28+
add_option 'h', "help", desc: "sends help information"
29+
end
30+
31+
def pre_run(args, options)
32+
if options.has? "help"
33+
puts help_template # generated using CLI::Formatter
34+
35+
false
36+
else
37+
true
38+
end
2439
end
2540
26-
def execute(args, options) : Nil
41+
def run(args, options) : Nil
2742
msg = "Hello, #{args.get("name")}!"
2843
2944
if options.has? "caps"
@@ -34,23 +49,21 @@ class MainCmd < CLI::Command
3449
end
3550
end
3651
37-
app = CLI::Application.new
38-
app.add_command MainCmd, default: true
52+
main = MainCmd.new
53+
main.execute ARGV
54+
```
3955

40-
app.run ARGV
4156
```
42-
```shell
4357
$ crystal greet.cr -h
44-
Greets a person
45-
4658
Usage:
4759
greet <arguments> [options]
4860

4961
Arguments:
50-
person the person to greet
62+
name the name of person to greet (required)
5163

5264
Options:
5365
-c, --caps greet with capitals
66+
-h, --help sends help information
5467

5568
$ crystal greet.cr Dev
5669
Hello, Dev!

shard.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: cli
2-
description: expandable command line interface library
3-
version: 1.0.0
2+
description: a modular, non-macro-based command line interface library
3+
version: 2.0.0
44
crystal: 1.5.0
55

66
repository: https://github.com/devnote-dev/cli.cr

spec/argument_spec.cr

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require "./spec_helper"
2+
3+
describe CLI::Argument do
4+
it "parses an argument" do
5+
arg = CLI::Argument.new "spec", "a test argument"
6+
7+
arg.name.should eq "spec"
8+
arg.description.should eq "a test argument"
9+
arg.required?.should be_false
10+
arg.value.should be_nil
11+
end
12+
end

spec/cli_spec.cr

Lines changed: 0 additions & 9 deletions
This file was deleted.

spec/helper_spec.cr

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
require "./spec_helper"
2+
3+
# Inspired by Clim
4+
5+
private class ContextCmd < CLI::Command
6+
def setup : Nil
7+
@name = "context"
8+
description = "Runs the Crystal context tool"
9+
end
10+
11+
def run(args, options) : Nil
12+
stdout.puts "Fake crystal context command!"
13+
end
14+
end
15+
16+
private class FormatCmd < CLI::Command
17+
def setup : Nil
18+
@name = "format"
19+
description = "Runs the Crystal format tool"
20+
end
21+
22+
def run(args, options) : Nil
23+
stdout.puts "Fake crystal format command!"
24+
end
25+
end
26+
27+
private class CrystalCmd < CLI::MainCommand
28+
def setup : Nil
29+
super
30+
31+
description = "Runs some Crystal commands"
32+
end
33+
34+
def run(args, options) : Nil
35+
end
36+
end
37+
38+
command = CrystalCmd.new
39+
command.add_command ContextCmd.new
40+
command.add_command FormatCmd.new
41+
42+
describe CLI do
43+
it "prints the help message" do
44+
io = IO::Memory.new
45+
command.stdout = io
46+
command.execute ""
47+
48+
io.to_s.should eq "Usage:\n\tmain [options]\n\n" \
49+
"Commands:\n\tcontext \n\tformat \n\n" \
50+
"Options:\n\t-h, --help sends help information\n" \
51+
"\t-v, --version sends the app version\n\n"
52+
end
53+
54+
it "runs the context command" do
55+
io = IO::Memory.new
56+
command.children["context"].stdout = io
57+
command.execute "context"
58+
59+
io.to_s.should eq "Fake crystal context command!\n"
60+
end
61+
62+
it "runs the format command" do
63+
io = IO::Memory.new
64+
command.children["format"].stdout = io
65+
command.execute "format"
66+
67+
io.to_s.should eq "Fake crystal format command!\n"
68+
end
69+
end

spec/main_spec.cr

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
require "./spec_helper"
2+
3+
private class Greet < CLI::Command
4+
getter io : IO::Memory
5+
6+
def initialize
7+
super
8+
9+
@io = IO::Memory.new
10+
end
11+
12+
def setup : Nil
13+
@name = "greet"
14+
@description = "Greets a person"
15+
16+
add_argument "name", desc: "the name of the person", required: true
17+
add_option 'c', "caps", desc: "greet with caps"
18+
end
19+
20+
def pre_run(args, options)
21+
unless args.has? "name"
22+
io.puts CLI::Formatter.new(self).generate
23+
24+
false
25+
end
26+
end
27+
28+
def run(args, options) : Nil
29+
msg = %(Hello, #{args.get! "name"}!)
30+
31+
if options.has? "caps"
32+
io.puts msg.upcase
33+
else
34+
io.puts msg
35+
end
36+
end
37+
end
38+
39+
describe CLI do
40+
it "tests the help command" do
41+
cmd = Greet.new
42+
cmd.execute %w()
43+
44+
cmd.io.to_s.should eq "Greets a person\n\n" \
45+
"Usage:\n\tgreet <arguments> [options]\n\n" \
46+
"Arguments:\n\tname the name of the person (required)\n\n" \
47+
"Options:\n\t-c, --caps greet with caps\n\n"
48+
end
49+
50+
it "tests the main command" do
51+
cmd = Greet.new
52+
cmd.execute %w(Dev)
53+
54+
cmd.io.to_s.should eq "Hello, Dev!\n"
55+
end
56+
57+
it "tests the main command with flag" do
58+
cmd = Greet.new
59+
cmd.execute %w(-c Dev)
60+
61+
cmd.io.to_s.should eq "HELLO, DEV!\n"
62+
end
63+
end

spec/option_spec.cr

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require "./spec_helper"
2+
3+
describe CLI::Option do
4+
it "parses a long option" do
5+
opt = CLI::Option.new "spec"
6+
opt.long.should eq "spec"
7+
opt.short.should be_nil
8+
9+
opt.is?("spec").should be_true
10+
end
11+
12+
it "parses a short option" do
13+
opt = CLI::Option.new "spec", 's'
14+
opt.long.should eq "spec"
15+
opt.short.should eq 's'
16+
17+
opt.is?("s").should be_true
18+
end
19+
20+
it "compares options" do
21+
opt1 = CLI::Option.new "spec", 's'
22+
opt2 = CLI::Option.new "flag", 'f'
23+
24+
opt1.should_not eq opt2
25+
opt1.should eq opt1.dup
26+
end
27+
end

spec/parser_spec.cr

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require "./spec_helper"
2+
3+
describe CLI::Parser do
4+
it "parses standard input arguments" do
5+
parser = CLI::Parser.new %(these --are "some" -c arguments)
6+
results = parser.parse
7+
8+
results[0].kind.should eq CLI::Parser::ResultKind::Argument
9+
results[1].kind.should eq CLI::Parser::ResultKind::LongFlag
10+
results[2].kind.should eq CLI::Parser::ResultKind::Argument
11+
results[3].kind.should eq CLI::Parser::ResultKind::ShortFlag
12+
results[4].kind.should eq CLI::Parser::ResultKind::Argument
13+
end
14+
15+
it "parses custom flag input arguments" do
16+
opts = CLI::Parser::Options.new option_delim: '+'
17+
parser = CLI::Parser.new %(these ++are "some" +c arguments), opts
18+
results = parser.parse
19+
20+
results[0].kind.should eq CLI::Parser::ResultKind::Argument
21+
results[1].kind.should eq CLI::Parser::ResultKind::LongFlag
22+
results[2].kind.should eq CLI::Parser::ResultKind::Argument
23+
results[3].kind.should eq CLI::Parser::ResultKind::ShortFlag
24+
results[4].kind.should eq CLI::Parser::ResultKind::Argument
25+
end
26+
27+
it "parses a string argument" do
28+
parser = CLI::Parser.new %w(--test "string argument" -t)
29+
results = parser.parse
30+
31+
results[0].kind.should eq CLI::Parser::ResultKind::LongFlag
32+
results[1].kind.should eq CLI::Parser::ResultKind::Argument
33+
results[1].value.should eq "string argument"
34+
results[2].kind.should eq CLI::Parser::ResultKind::ShortFlag
35+
end
36+
37+
it "parses an option argument" do
38+
parser = CLI::Parser.new %(--name=foo -k=bar)
39+
results = parser.parse
40+
41+
results[0].kind.should eq CLI::Parser::ResultKind::LongFlag
42+
results[0].value.should eq "name=foo"
43+
results[0].parse_value.should eq "name"
44+
45+
results[1].kind.should eq CLI::Parser::ResultKind::ShortFlag
46+
results[1].value.should eq "k=bar"
47+
results[1].parse_value.should eq "k"
48+
end
49+
end

spec/value_spec.cr

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require "./spec_helper"
2+
3+
describe CLI::Value do
4+
it "parses a value" do
5+
CLI::Value.new("foo").should be_a CLI::Value
6+
CLI::Value.new(123_i64).should be_a CLI::Value
7+
CLI::Value.new(4.56).should be_a CLI::Value
8+
CLI::Value.new(true).should be_a CLI::Value
9+
CLI::Value.new(nil).should be_a CLI::Value
10+
11+
# currently broken
12+
# CLI::Value.new(%w[foo bar baz]).should be_a CLI::Value
13+
end
14+
15+
it "compares values" do
16+
CLI::Value.new("foo").should eq "foo"
17+
CLI::Value.new(123).should eq 123
18+
CLI::Value.new(4.56).should eq 4.56
19+
CLI::Value.new(true).should eq true
20+
CLI::Value.new(nil).should eq nil
21+
22+
# also broken
23+
# CLI::Value.new(%[foo bar baz]).should eq ["foo", "bar", "baz"]
24+
end
25+
26+
it "asserts types" do
27+
CLI::Value.new("foo").as_s.should be_a String
28+
CLI::Value.new(123).as_i32.should be_a Int32
29+
CLI::Value.new(4.56).as_f64.should be_a Float64
30+
CLI::Value.new(true).as_bool.should be_true
31+
CLI::Value.new(nil).as_nil.should be_nil
32+
end
33+
end

0 commit comments

Comments
 (0)