tabry 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/tabry/cli/base.rb +5 -2
- data/lib/tabry/cli/builder.rb +5 -1
- data/lib/tabry/result.rb +5 -1
- data/lib/tabry/shell_splitter.rb +2 -0
- data/spec/fixtures/vehicles.tabry +5 -0
- data/spec/fixtures/vehicles.yaml +5 -0
- data/spec/lib/tabry/cli/util_spec.rb +26 -24
- data/spec/tabry/cli/builder_spec.rb +46 -15
- data/spec/tabry/config_loader_spec.rb +2 -2
- data/spec/tabry/options_finder_spec.rb +1 -1
- data/spec/tabry/result_spec.rb +4 -0
- data/spec/tabry/shell_splitter_spec.rb +7 -7
- data/tabry.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe7c5c94f54f4d6fb603a73a8ec6b8364c63b6247a9dc3f095d4359fe96b5018
|
4
|
+
data.tar.gz: 4a29c24624360099c44dfd5c8c1420ef701bb21ee6535dc1e7a3540315002811
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9b58e549801135afe35686048bd3b84b76c9e41cb5ecd5dc49bff3a1c3ea3ac26f10a2187eca5775ce4aa2d02d46157c559cefc2d376267ff62ebcf630b721c
|
7
|
+
data.tar.gz: a21e775881c48a511363cce6690e99cd13ccb32a3b829670bf049f3de63ab6e590b351a591717bb8137f112eb0e81514f548cefb8e3a0f4a79e5339118fafb33
|
data/lib/tabry/cli/base.rb
CHANGED
@@ -14,8 +14,11 @@ module Tabry
|
|
14
14
|
@internals = internals
|
15
15
|
end
|
16
16
|
|
17
|
-
def self.sub_route(
|
18
|
-
(
|
17
|
+
def self.sub_route(*prefixes, to:, **opts)
|
18
|
+
prefixes.map(&:to_s).each do |prefix|
|
19
|
+
(@sub_route_clis ||= {})[prefix] = to
|
20
|
+
(@sub_route_clis_opts ||= {})[prefix] = opts
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
24
|
def self.after_action(*method_names, only: nil, except: nil, &blk)
|
data/lib/tabry/cli/builder.rb
CHANGED
@@ -72,8 +72,12 @@ module Tabry
|
|
72
72
|
sub_name, rest = met.split("__", 2)
|
73
73
|
|
74
74
|
if sub_route_clis&.dig(sub_name)
|
75
|
+
# Instantiate CLI if it hasn't already (and store instantiation)
|
75
76
|
sub_route_clis[sub_name] = instantiate_cli(sub_route_clis[sub_name], internals)
|
76
|
-
|
77
|
+
opts = cli.class.instance_variable_get(:@sub_route_clis_opts)[sub_name]
|
78
|
+
|
79
|
+
new_met_name = opts[:full_method_name] ? met : rest
|
80
|
+
get_cli_object_and_met(sub_route_clis[sub_name], new_met_name, internals)
|
77
81
|
else
|
78
82
|
[cli, met]
|
79
83
|
end
|
data/lib/tabry/result.rb
CHANGED
@@ -35,7 +35,11 @@ module Tabry
|
|
35
35
|
|
36
36
|
def wrong_number_of_args?
|
37
37
|
n_args = state.args.length
|
38
|
-
|
38
|
+
# This could use some tweaking. I'm not at which point a subcommand
|
39
|
+
# should be considered to be invalid with no arguments... if it has a
|
40
|
+
# subcommand it's a good sign, but if it also has an optional argument
|
41
|
+
# that means it's OK. Probably need another "no_args" construct.
|
42
|
+
if n_args == 0 && current_sub.subs.any? && !(current_sub.args.any? && current_sub.min_args == 0)
|
39
43
|
if current_sub.args.any?
|
40
44
|
"missing subcommand or arg(s)"
|
41
45
|
else
|
data/lib/tabry/shell_splitter.rb
CHANGED
data/spec/fixtures/vehicles.yaml
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative "../../../../lib/tabry/cli/util"
|
2
4
|
|
3
5
|
describe Tabry::CLI::Util do
|
4
6
|
describe ".make_cmdline" do
|
5
7
|
it "escapes a single string" do
|
6
8
|
expect(described_class.make_cmdline(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
"hello %s", "world;abc"
|
10
|
+
)).to eq(
|
11
|
+
"hello world\\;abc"
|
12
|
+
)
|
11
13
|
end
|
12
14
|
|
13
15
|
it "escapes multiple string arguments" do
|
14
16
|
expect(described_class.make_cmdline(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
"hello %s hi %s", "world;abc", "def;ghi"
|
18
|
+
)).to eq(
|
19
|
+
"hello world\\;abc hi def\\;ghi"
|
20
|
+
)
|
19
21
|
end
|
20
22
|
|
21
23
|
it "escapes array arguments" do
|
22
24
|
# args is an array of strings and arrays
|
23
25
|
expect(described_class.make_cmdline(
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
"hello %s hi %s", "world;abc", ["foo;bar", "waz;ok"]
|
27
|
+
)).to eq(
|
28
|
+
"hello world\\;abc hi foo\\;bar waz\\;ok"
|
29
|
+
)
|
28
30
|
end
|
29
31
|
|
30
32
|
it "treats a single array of strings as a list of args, not as one array arg" do
|
@@ -32,27 +34,27 @@ describe Tabry::CLI::Util do
|
|
32
34
|
# an improvement might be to figure out how
|
33
35
|
# many '%s's there are in the cmdline
|
34
36
|
expect(described_class.make_cmdline(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
"hello %s hi %s", ["world;abc", "foo;bar"]
|
38
|
+
)).to eq(
|
39
|
+
"hello world\\;abc hi foo\\;bar"
|
40
|
+
)
|
39
41
|
end
|
40
42
|
|
41
43
|
it "escapes arrays if given one array of mixed strings and arrays" do
|
42
44
|
expect(described_class.make_cmdline(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
"hello %s hi %s", [["world;abc", "foo;bar"], "waz;ok"]
|
46
|
+
)).to eq(
|
47
|
+
"hello world\\;abc foo\\;bar hi waz\\;ok"
|
48
|
+
)
|
47
49
|
end
|
48
50
|
|
49
51
|
it "can send a single array argument by wrapping in an array" do
|
50
52
|
# args is an array with one array of one array
|
51
53
|
expect(described_class.make_cmdline(
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
"hello %s hi", [["foo;bar", "waz;ok"]]
|
55
|
+
)).to eq(
|
56
|
+
"hello foo\\;bar waz\\;ok hi"
|
57
|
+
)
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
@@ -28,7 +28,33 @@ class Tabry::CLI::Builder
|
|
28
28
|
end
|
29
29
|
|
30
30
|
class TestCliFoo < Tabry::CLI::Base
|
31
|
-
sub_route :bar, TestCliFooBar
|
31
|
+
sub_route :bar, to: TestCliFooBar
|
32
|
+
end
|
33
|
+
|
34
|
+
class TestCli2 < Tabry::CLI::Base
|
35
|
+
class << self
|
36
|
+
attr_accessor :actions_run, :cli_object
|
37
|
+
end
|
38
|
+
|
39
|
+
after_action :my_after_action, except: :build2
|
40
|
+
|
41
|
+
def build2
|
42
|
+
self.class.actions_run << :build2
|
43
|
+
end
|
44
|
+
|
45
|
+
def sub__sub_sub
|
46
|
+
self.class.actions_run << :sub__sub_sub
|
47
|
+
end
|
48
|
+
|
49
|
+
def my_before_action
|
50
|
+
self.class.actions_run << :before_action
|
51
|
+
self.class.cli_object = self
|
52
|
+
end
|
53
|
+
|
54
|
+
def my_after_action
|
55
|
+
self.class.actions_run << :after_action
|
56
|
+
self.class.cli_object = self
|
57
|
+
end
|
32
58
|
end
|
33
59
|
|
34
60
|
class TestCli < Tabry::CLI::Base
|
@@ -37,9 +63,10 @@ class Tabry::CLI::Builder
|
|
37
63
|
end
|
38
64
|
|
39
65
|
before_action :my_before_action, only: :build
|
40
|
-
after_action :my_after_action
|
66
|
+
after_action :my_after_action
|
41
67
|
|
42
|
-
sub_route :foo, TestCliFoo
|
68
|
+
sub_route :foo, to: TestCliFoo
|
69
|
+
sub_route :sub, :build2, to: TestCli2, full_method_name: true
|
43
70
|
|
44
71
|
def main
|
45
72
|
self.class.actions_run << :main
|
@@ -49,18 +76,10 @@ class Tabry::CLI::Builder
|
|
49
76
|
self.class.actions_run << :build
|
50
77
|
end
|
51
78
|
|
52
|
-
def build2
|
53
|
-
self.class.actions_run << :build2
|
54
|
-
end
|
55
|
-
|
56
79
|
def my_action
|
57
80
|
self.class.actions_run << :my_action
|
58
81
|
end
|
59
82
|
|
60
|
-
def sub__sub_sub
|
61
|
-
self.class.actions_run << :sub__sub_sub
|
62
|
-
end
|
63
|
-
|
64
83
|
def my_before_action
|
65
84
|
self.class.actions_run << :before_action
|
66
85
|
self.class.cli_object = self
|
@@ -99,7 +118,8 @@ describe Tabry::CLI::Builder do
|
|
99
118
|
let(:builder) { described_class.new("theconfigname", Tabry::CLI::Builder::TestCli) }
|
100
119
|
|
101
120
|
let(:cli_class) { Tabry::CLI::Builder::TestCli }
|
102
|
-
let(:cli_class2) { Tabry::CLI::Builder::
|
121
|
+
let(:cli_class2) { Tabry::CLI::Builder::TestCli2 }
|
122
|
+
let(:cli_class_foo_bar) { Tabry::CLI::Builder::TestCliFooBar }
|
103
123
|
let(:actions_run) { cli_class.actions_run }
|
104
124
|
let(:cli_object) { cli_class.cli_object }
|
105
125
|
|
@@ -108,6 +128,8 @@ describe Tabry::CLI::Builder do
|
|
108
128
|
cli_class.cli_object = nil
|
109
129
|
cli_class2.actions_run = []
|
110
130
|
cli_class2.cli_object = nil
|
131
|
+
cli_class_foo_bar.actions_run = []
|
132
|
+
cli_class_foo_bar.cli_object = nil
|
111
133
|
end
|
112
134
|
|
113
135
|
describe "successful runs" do
|
@@ -132,7 +154,7 @@ describe Tabry::CLI::Builder do
|
|
132
154
|
describe "except on actions" do
|
133
155
|
let(:state) { { subcommand_stack: %w[build2] } }
|
134
156
|
|
135
|
-
it { expect(actions_run).to eq %i[build2] }
|
157
|
+
it { expect(cli_class2.actions_run).to eq %i[build2] }
|
136
158
|
end
|
137
159
|
|
138
160
|
describe "handling multiple levels of subcommand routing" do
|
@@ -143,14 +165,23 @@ describe Tabry::CLI::Builder do
|
|
143
165
|
end
|
144
166
|
|
145
167
|
it "maps to the sub-CLI" do
|
146
|
-
expect(
|
168
|
+
expect(cli_class_foo_bar.actions_run).to eq(%i[before_action waz])
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "subcommand routing with full_method_name: true" do
|
173
|
+
let(:state) { { subcommand_stack: %i[sub sub_sub] } }
|
174
|
+
|
175
|
+
it "preserves the full method name" do
|
176
|
+
expect(cli_class.actions_run).to eq(%i[])
|
177
|
+
expect(cli_class2.actions_run).to eq(%i[sub__sub_sub after_action])
|
147
178
|
end
|
148
179
|
end
|
149
180
|
|
150
181
|
describe "handling multiple levels of subcommand routing (main method)" do
|
151
182
|
let(:state) { { subcommand_stack: %i[foo bar] } }
|
152
183
|
|
153
|
-
it { expect(
|
184
|
+
it { expect(cli_class_foo_bar.actions_run).to eq(%i[before_action main]) }
|
154
185
|
end
|
155
186
|
|
156
187
|
describe "providing an ArgProxy in TestCLI#args" do
|
@@ -17,7 +17,7 @@ describe Tabry::ConfigLoader do
|
|
17
17
|
config = described_class.load(name: "#{__dir__}/../fixtures/vehicles.yaml")
|
18
18
|
expect(config).to be_a(Tabry::Models::Config)
|
19
19
|
expect(config.main.subs.map(&:name)).to eq(
|
20
|
-
%w[build list-vehicles move sub-with-sub-or-arg sub-with-mandatory-flag]
|
20
|
+
%w[build list-vehicles move sub-with-sub-or-arg sub-with-sub-or-opt-arg sub-with-mandatory-flag]
|
21
21
|
)
|
22
22
|
end
|
23
23
|
|
@@ -27,7 +27,7 @@ describe Tabry::ConfigLoader do
|
|
27
27
|
it "looks for yaml files in directories in TABRY_IMPORTS_PATH" do
|
28
28
|
config = described_class.load(name: "vehicles")
|
29
29
|
expect(config.main.subs.map(&:name)).to eq(
|
30
|
-
%w[build list-vehicles move sub-with-sub-or-arg sub-with-mandatory-flag]
|
30
|
+
%w[build list-vehicles move sub-with-sub-or-arg sub-with-sub-or-opt-arg sub-with-mandatory-flag]
|
31
31
|
)
|
32
32
|
end
|
33
33
|
|
@@ -12,7 +12,7 @@ describe Tabry::OptionsFinder do
|
|
12
12
|
|
13
13
|
examples = {
|
14
14
|
"lists possible subcommands of the main command" => [
|
15
|
-
%w[build list-vehicles move sub-with-sub-or-arg sub-with-mandatory-flag],
|
15
|
+
%w[build list-vehicles move sub-with-sub-or-arg sub-with-sub-or-opt-arg sub-with-mandatory-flag],
|
16
16
|
{}
|
17
17
|
],
|
18
18
|
"lists possible subcommands of a subcommand" => [
|
data/spec/tabry/result_spec.rb
CHANGED
@@ -70,6 +70,10 @@ describe Tabry::Result do
|
|
70
70
|
)).to be_nil
|
71
71
|
end
|
72
72
|
|
73
|
+
it "doesn't complain if given no args to a subcommand with subcommands and optional args" do
|
74
|
+
expect(reason(subcommand_stack: %w[sub-with-sub-or-opt-arg])).to be_nil
|
75
|
+
end
|
76
|
+
|
73
77
|
it "complains if mandatory varargs are not given" do
|
74
78
|
expect(reason(
|
75
79
|
subcommand_stack: %w[build]
|
@@ -3,18 +3,18 @@
|
|
3
3
|
require_relative "../../lib/tabry/shell_splitter"
|
4
4
|
|
5
5
|
describe Tabry::ShellSplitter do
|
6
|
-
describe
|
6
|
+
describe ".split" do
|
7
7
|
it "returns the command basename, argument, and last argument" do
|
8
|
-
str =
|
8
|
+
str = "foo/bar abc def ghi"
|
9
9
|
expect(described_class.split(str, 13)).to eq(["bar", ["abc"], "def"])
|
10
10
|
end
|
11
11
|
|
12
|
-
it "
|
12
|
+
it "handles quotes and backslashes like a shell" do
|
13
13
|
expect(described_class.split('"/foo bar/waz" a\'b \'c\\ d "ef g" "hi jk" lmn', 38)).to eq([
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
"waz",
|
15
|
+
["ab c d", "ef g"],
|
16
|
+
"hi jk"
|
17
|
+
])
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
data/tabry.gemspec
CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "tabry"
|
9
|
-
s.version = "0.1.
|
9
|
+
s.version = "0.1.5"
|
10
10
|
s.summary = "Tab completion and CLIs extraordinaire"
|
11
11
|
s.authors = ["Evan Battaglia"]
|
12
12
|
s.email = "battaglia.evan@gmail.com"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Battaglia
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry-byebug
|
@@ -225,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
225
225
|
- !ruby/object:Gem::Version
|
226
226
|
version: '0'
|
227
227
|
requirements: []
|
228
|
-
rubygems_version: 3.2.
|
228
|
+
rubygems_version: 3.2.22
|
229
229
|
signing_key:
|
230
230
|
specification_version: 4
|
231
231
|
summary: Tab completion and CLIs extraordinaire
|