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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 238e670dd915a52c21ff0af689fd3885261deba3caee10dae8972f06cbf90ccb
4
- data.tar.gz: ff161d2cda396309d243a8f1dd5f4a75a6b154f6bdacedb7ee7e59c4bae5d8f5
3
+ metadata.gz: fe7c5c94f54f4d6fb603a73a8ec6b8364c63b6247a9dc3f095d4359fe96b5018
4
+ data.tar.gz: 4a29c24624360099c44dfd5c8c1420ef701bb21ee6535dc1e7a3540315002811
5
5
  SHA512:
6
- metadata.gz: 66d5af76b745d7cb56c1905f095c7b137acfd3d816f4e8fb070c0ccf8c76c5be123c85ebc5b1fc29480e73485a7f9dacb438d98c0f514009830afabb5ec13426
7
- data.tar.gz: 30545a229fc13939596285535d84a6e69cc73c08e248f0d3a3bf18cbba18a379dbc26a1756c9f9266081503d277da15738d0608e573a219715b8d46211eb8772
6
+ metadata.gz: b9b58e549801135afe35686048bd3b84b76c9e41cb5ecd5dc49bff3a1c3ea3ac26f10a2187eca5775ce4aa2d02d46157c559cefc2d376267ff62ebcf630b721c
7
+ data.tar.gz: a21e775881c48a511363cce6690e99cd13ccb32a3b829670bf049f3de63ab6e590b351a591717bb8137f112eb0e81514f548cefb8e3a0f4a79e5339118fafb33
@@ -14,8 +14,11 @@ module Tabry
14
14
  @internals = internals
15
15
  end
16
16
 
17
- def self.sub_route(prefix, cli_class)
18
- (@sub_route_clis ||= {})[prefix.to_s] = cli_class
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)
@@ -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
- get_cli_object_and_met(sub_route_clis[sub_name], rest, internals)
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
- if n_args == 0 && current_sub.subs.any?
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Use Shellwords.split() to split a command line + comp point (index of the
2
4
  # cursor in the command line) into the args up to the current token plus
3
5
  # current token
@@ -45,6 +45,11 @@ sub sub-with-sub-or-arg {
45
45
  sub subsub
46
46
  }
47
47
 
48
+ sub sub-with-sub-or-opt-arg{
49
+ sub subsub
50
+ opt arg
51
+ }
52
+
48
53
  sub sub-with-mandatory-flag {
49
54
  opt arg { opts const (a b c) }
50
55
  reqd flagarg mandatory
@@ -101,6 +101,11 @@ main:
101
101
  value: z
102
102
  subs:
103
103
  - name: subsub
104
+ - name: sub-with-sub-or-opt-arg
105
+ subs:
106
+ - name: subsub
107
+ args:
108
+ - optional: true
104
109
  - name: sub-with-mandatory-flag
105
110
  args:
106
111
  - optional: true
@@ -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
- "hello %s", "world;abc"
8
- )).to eq(
9
- "hello world\\;abc"
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
- "hello %s hi %s", "world;abc", "def;ghi"
16
- )).to eq(
17
- "hello world\\;abc hi def\\;ghi"
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
- "hello %s hi %s", "world;abc", ["foo;bar", "waz;ok"]
25
- )).to eq(
26
- "hello world\\;abc hi foo\\;bar waz\\;ok"
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
- "hello %s hi %s", ["world;abc", "foo;bar"]
36
- )).to eq(
37
- "hello world\\;abc hi foo\\;bar"
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
- "hello %s hi %s", [["world;abc", "foo;bar"], "waz;ok"]
44
- )).to eq(
45
- "hello world\\;abc foo\\;bar hi waz\\;ok"
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
- "hello %s hi", [["foo;bar", "waz;ok"]]
53
- )).to eq(
54
- "hello foo\\;bar waz\\;ok hi"
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, except: :build2
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::TestCliFooBar }
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(cli_class2.actions_run).to eq(%i[before_action waz])
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(cli_class2.actions_run).to eq(%i[before_action main]) }
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" => [
@@ -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 '.split' do
6
+ describe ".split" do
7
7
  it "returns the command basename, argument, and last argument" do
8
- str = 'foo/bar abc def ghi'
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 "it handles quotes and backslashes like a shell" do
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
- "waz",
15
- ["ab c d", "ef g"],
16
- "hi jk"
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.4"
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
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-09-22 00:00:00.000000000 Z
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.15
228
+ rubygems_version: 3.2.22
229
229
  signing_key:
230
230
  specification_version: 4
231
231
  summary: Tab completion and CLIs extraordinaire