tabry 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|