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 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