samovar 1.10.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/lib/samovar/command.rb +42 -31
- data/lib/samovar/{command/system.rb → error.rb} +26 -26
- data/lib/samovar/many.rb +16 -5
- data/lib/samovar/nested.rb +21 -10
- data/lib/samovar/one.rb +15 -4
- data/lib/samovar/option.rb +107 -0
- data/lib/samovar/options.rb +40 -77
- data/lib/samovar/output.rb +1 -151
- data/lib/samovar/{command/track_time.rb → output/columns.rb} +18 -13
- data/lib/samovar/{command/terminal.rb → output/header.rb} +12 -7
- data/lib/samovar/output/row.rb +38 -0
- data/lib/samovar/output/rows.rb +86 -0
- data/lib/samovar/output/usage_formatter.rb +87 -0
- data/lib/samovar/split.rb +15 -4
- data/lib/samovar/table.rb +61 -13
- data/lib/samovar/version.rb +1 -1
- data/spec/samovar/coerce_spec.rb +19 -1
- data/spec/samovar/command_spec.rb +21 -11
- data/spec/samovar/many_spec.rb +49 -0
- data/spec/samovar/nested_spec.rb +78 -9
- data/spec/samovar/one_spec.rb +49 -0
- data/spec/samovar/options_spec.rb +22 -3
- data/spec/samovar/split_spec.rb +49 -0
- data/spec/samovar/table_spec.rb +40 -0
- data/spec/spec_helper.rb +19 -1
- metadata +17 -6
- data/lib/samovar/output/line_wrapper.rb +0 -87
@@ -0,0 +1,87 @@
|
|
1
|
+
# Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'mapping/model'
|
22
|
+
require 'event/terminal'
|
23
|
+
|
24
|
+
require_relative '../error'
|
25
|
+
|
26
|
+
require_relative 'header'
|
27
|
+
|
28
|
+
require_relative 'row'
|
29
|
+
require_relative 'rows'
|
30
|
+
|
31
|
+
module Samovar
|
32
|
+
module Output
|
33
|
+
class UsageFormatter < Mapping::Model
|
34
|
+
def self.print(rows, output)
|
35
|
+
formatter = self.new(rows, output)
|
36
|
+
|
37
|
+
yield formatter if block_given?
|
38
|
+
|
39
|
+
formatter.print
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(rows, output)
|
43
|
+
@rows = rows
|
44
|
+
@output = output
|
45
|
+
@width = 80
|
46
|
+
|
47
|
+
@terminal = Event::Terminal.for(@output)
|
48
|
+
@terminal[:header] = @terminal.style(nil, nil, :bright)
|
49
|
+
@terminal[:description] = @terminal.style(:blue)
|
50
|
+
@terminal[:error] = @terminal.style(:red)
|
51
|
+
end
|
52
|
+
|
53
|
+
map(InvalidInputError) do |error|
|
54
|
+
# This is a little hack which avoids printing out "--help" if it was part of an incomplete parse. In the future I'd prefer if this was handled explicitly.
|
55
|
+
@terminal.puts("#{error.message} in:", style: :error) unless error.help?
|
56
|
+
end
|
57
|
+
|
58
|
+
map(MissingValueError) do |error|
|
59
|
+
@terminal.puts("#{error.message} in:", style: :error)
|
60
|
+
end
|
61
|
+
|
62
|
+
map(Header) do |header, rows|
|
63
|
+
@terminal.puts unless header == @rows.first
|
64
|
+
|
65
|
+
command_line = header.object.command_line(header.name)
|
66
|
+
@terminal.puts "#{rows.indentation}#{command_line}", style: :header
|
67
|
+
|
68
|
+
if description = header.object.description
|
69
|
+
@terminal.puts "#{rows.indentation}\t#{description}", style: :description
|
70
|
+
@terminal.puts
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
map(Row) do |row, rows|
|
75
|
+
@terminal.puts "#{rows.indentation}#{row.align(rows.columns)}"
|
76
|
+
end
|
77
|
+
|
78
|
+
map(Rows) do |items|
|
79
|
+
items.collect{|row, rows| map(row, rows)}
|
80
|
+
end
|
81
|
+
|
82
|
+
def print
|
83
|
+
map(@rows)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/samovar/split.rb
CHANGED
@@ -20,14 +20,19 @@
|
|
20
20
|
|
21
21
|
module Samovar
|
22
22
|
class Split
|
23
|
-
def initialize(key, description, marker: '--', default: nil)
|
23
|
+
def initialize(key, description, marker: '--', default: nil, required: false)
|
24
24
|
@key = key
|
25
25
|
@description = description
|
26
26
|
@marker = marker
|
27
27
|
@default = default
|
28
|
+
@required = required
|
28
29
|
end
|
29
30
|
|
30
31
|
attr :key
|
32
|
+
attr :description
|
33
|
+
attr :marker
|
34
|
+
attr :default
|
35
|
+
attr :required
|
31
36
|
|
32
37
|
def to_s
|
33
38
|
"#{@marker} <#{@key}...>"
|
@@ -37,16 +42,22 @@ module Samovar
|
|
37
42
|
usage = [to_s, @description]
|
38
43
|
|
39
44
|
if @default
|
40
|
-
usage << "
|
45
|
+
usage << "(default: #{@default.inspect})"
|
46
|
+
elsif @required
|
47
|
+
usage << "(required)"
|
41
48
|
end
|
42
49
|
|
43
50
|
return usage
|
44
51
|
end
|
45
52
|
|
46
|
-
def parse(input, default =
|
53
|
+
def parse(input, parent = nil, default = nil)
|
47
54
|
if offset = input.index(@marker)
|
48
55
|
input.pop(input.size - offset).tap(&:shift)
|
49
|
-
|
56
|
+
elsif default ||= @default
|
57
|
+
return default
|
58
|
+
elsif @required
|
59
|
+
raise MissingValueError.new(parent, self)
|
60
|
+
end
|
50
61
|
end
|
51
62
|
end
|
52
63
|
end
|
data/lib/samovar/table.rb
CHANGED
@@ -20,31 +20,79 @@
|
|
20
20
|
|
21
21
|
module Samovar
|
22
22
|
class Table
|
23
|
-
def
|
24
|
-
|
25
|
-
|
23
|
+
def self.nested(klass, parent = nil)
|
24
|
+
if klass.superclass.respond_to?(:table)
|
25
|
+
parent = klass.superclass.table
|
26
|
+
end
|
27
|
+
|
28
|
+
self.new(parent, name: klass.name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(parent = nil, name: nil)
|
32
|
+
@parent = parent
|
33
|
+
@name = name
|
34
|
+
@rows = {}
|
26
35
|
end
|
27
36
|
|
28
|
-
|
37
|
+
def freeze
|
38
|
+
return self if frozen?
|
39
|
+
|
40
|
+
@rows.freeze
|
41
|
+
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def [] key
|
46
|
+
@rows[key]
|
47
|
+
end
|
48
|
+
|
49
|
+
def each(&block)
|
50
|
+
@rows.each_value(&block)
|
51
|
+
end
|
29
52
|
|
30
53
|
def << row
|
31
|
-
@rows
|
54
|
+
if existing_row = @rows[row.key] and existing_row.respond_to?(:merge!)
|
55
|
+
existing_row.merge!(row)
|
56
|
+
else
|
57
|
+
# In the above case where there is an existing row, but it doensn't support being merged, we overwrite it. This preserves order.
|
58
|
+
@rows[row.key] = row.dup
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def empty?
|
63
|
+
@rows.empty? && @parent&.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def merge_into(table)
|
67
|
+
@parent&.merge_into(table)
|
32
68
|
|
33
|
-
|
34
|
-
|
69
|
+
@rows.each_value do |row|
|
70
|
+
table << row
|
71
|
+
end
|
72
|
+
|
73
|
+
return table
|
74
|
+
end
|
75
|
+
|
76
|
+
def merged
|
77
|
+
if @parent.nil? or @parent.empty?
|
78
|
+
return self
|
79
|
+
else
|
80
|
+
merge_into(self.class.new)
|
35
81
|
end
|
36
82
|
end
|
37
83
|
|
38
84
|
def usage
|
39
|
-
@rows.collect(&:to_s).join(' ')
|
85
|
+
@rows.each_value.collect(&:to_s).reject(&:empty?).join(' ')
|
40
86
|
end
|
41
87
|
|
42
|
-
def parse(input,
|
43
|
-
@
|
44
|
-
|
88
|
+
def parse(input, parent)
|
89
|
+
@rows.each do |key, row|
|
90
|
+
next unless row.respond_to?(:parse)
|
91
|
+
|
92
|
+
current = parent.send(key)
|
45
93
|
|
46
|
-
if result = row.parse(input, current)
|
47
|
-
|
94
|
+
if result = row.parse(input, parent, current)
|
95
|
+
parent.send("#{row.key}=", result)
|
48
96
|
end
|
49
97
|
end
|
50
98
|
end
|
data/lib/samovar/version.rb
CHANGED
data/spec/samovar/coerce_spec.rb
CHANGED
@@ -1,6 +1,24 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
1
20
|
|
2
21
|
require 'samovar'
|
3
|
-
require 'stringio'
|
4
22
|
|
5
23
|
module Samovar::CoerceSpec
|
6
24
|
class Coerce < Samovar::Command
|
@@ -1,6 +1,24 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
1
20
|
|
2
21
|
require 'samovar'
|
3
|
-
require 'stringio'
|
4
22
|
|
5
23
|
module Samovar::CommandSpec
|
6
24
|
class Bottom < Samovar::Command
|
@@ -22,7 +40,7 @@ module Samovar::CommandSpec
|
|
22
40
|
option '-v/--version', "Print out the application version."
|
23
41
|
end
|
24
42
|
|
25
|
-
nested
|
43
|
+
nested :command, {
|
26
44
|
'bottom' => Bottom
|
27
45
|
}
|
28
46
|
end
|
@@ -55,17 +73,9 @@ module Samovar::CommandSpec
|
|
55
73
|
it "should generate documentation" do
|
56
74
|
top = Top[]
|
57
75
|
buffer = StringIO.new
|
58
|
-
top.print_usage(
|
76
|
+
top.print_usage(output: buffer)
|
59
77
|
|
60
78
|
expect(buffer.string).to be_include(Top.description)
|
61
79
|
end
|
62
|
-
|
63
|
-
it "can run commands" do
|
64
|
-
expect(subject.system("ls")).to be_truthy
|
65
|
-
expect(subject.system!("ls")).to be_truthy
|
66
|
-
|
67
|
-
expect(subject.system("fail")).to be_falsey
|
68
|
-
expect{subject.system!("fail")}.to raise_error(Samovar::SystemError)
|
69
|
-
end
|
70
80
|
end
|
71
81
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'samovar/many'
|
22
|
+
|
23
|
+
RSpec.describe Samovar::Many do
|
24
|
+
let(:default) {["1", "2", "3"]}
|
25
|
+
let(:input) {["2", "3", "--else"]}
|
26
|
+
|
27
|
+
subject{described_class.new(:items, "some items", default: default)}
|
28
|
+
|
29
|
+
it "has string representation" do
|
30
|
+
expect(subject.to_s).to be == "<items...>"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have default" do
|
34
|
+
expect(subject.default).to be == default
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should use default" do
|
38
|
+
expect(subject.parse([])).to be == default
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should use specified default" do
|
42
|
+
expect(subject.parse([], nil, ["2"])).to be == ["2"]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should not use default if input specified" do
|
46
|
+
expect(subject.parse(input)).to be == ["2", "3"]
|
47
|
+
expect(input).to be == ["--else"]
|
48
|
+
end
|
49
|
+
end
|
data/spec/samovar/nested_spec.rb
CHANGED
@@ -1,39 +1,108 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
1
20
|
|
2
21
|
require 'samovar'
|
3
|
-
|
22
|
+
|
23
|
+
RSpec.describe Samovar::Nested do
|
24
|
+
let(:commands) do
|
25
|
+
{
|
26
|
+
'inner-a' => Class.new(Samovar::Command),
|
27
|
+
'inner-b' => Class.new(Samovar::Command),
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:default) {'inner-a'}
|
32
|
+
let(:input) {['inner-a']}
|
33
|
+
subject{described_class.new(:command, commands, default: default)}
|
34
|
+
|
35
|
+
it "has string representation" do
|
36
|
+
expect(subject.to_s).to be == "<command>"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should have default" do
|
40
|
+
expect(subject.default).to be == default
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should use default" do
|
44
|
+
expect(subject.parse([])).to be_kind_of commands[default]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should use specified default" do
|
48
|
+
command = commands['inner-b'].new
|
49
|
+
|
50
|
+
expect(subject.parse([], nil, command)).to be command
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should not use default if input specified" do
|
54
|
+
expect(subject.parse(input)).to be_kind_of commands['inner-a']
|
55
|
+
end
|
56
|
+
end
|
4
57
|
|
5
58
|
module Samovar::NestedSpec
|
6
59
|
class InnerA < Samovar::Command
|
60
|
+
options
|
7
61
|
end
|
8
62
|
|
9
|
-
class InnerB <
|
63
|
+
class InnerB < InnerA
|
10
64
|
options do
|
11
|
-
option '--help'
|
65
|
+
option '--help', "Do you need it?"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class InnerC < InnerB
|
70
|
+
options do
|
71
|
+
option '--frobulate', "Zork is waiting for you."
|
12
72
|
end
|
13
73
|
end
|
14
74
|
|
15
75
|
class Outer < Samovar::Command
|
16
76
|
options do
|
17
|
-
option '--help'
|
18
77
|
end
|
19
|
-
|
20
|
-
nested
|
78
|
+
|
79
|
+
nested :command, {
|
21
80
|
'inner-a' => InnerA,
|
22
81
|
'inner-b' => InnerB,
|
82
|
+
'inner-c' => InnerC,
|
23
83
|
}, default: 'inner-b'
|
24
84
|
end
|
25
|
-
|
85
|
+
|
26
86
|
RSpec.describe Samovar::Nested do
|
27
87
|
it "should select default nested command" do
|
28
88
|
outer = Outer[]
|
29
89
|
expect(outer.command).to be_kind_of(InnerB)
|
90
|
+
|
91
|
+
outer.print_usage
|
30
92
|
end
|
31
|
-
|
93
|
+
|
32
94
|
it "should select explicitly named nested command" do
|
33
95
|
outer = Outer['inner-a']
|
34
96
|
expect(outer.command).to be_kind_of(InnerA)
|
35
97
|
end
|
36
|
-
|
98
|
+
|
99
|
+
it "can parse derived options" do
|
100
|
+
outer = Outer['inner-c', '--help']
|
101
|
+
expect(outer.command).to be_kind_of(InnerC)
|
102
|
+
expect(outer.command.options).to include(help: true)
|
103
|
+
expect(outer.command.parent).to be outer
|
104
|
+
end
|
105
|
+
|
37
106
|
xit "should parse help option at outer level" do
|
38
107
|
outer = Outer['inner-a', '--help']
|
39
108
|
expect(outer.options[:help]).to_be truthy
|