thor 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/{CHANGELOG.rdoc → CHANGELOG.md} +30 -36
  2. data/README.md +10 -3
  3. data/lib/thor.rb +205 -180
  4. data/lib/thor/actions.rb +5 -5
  5. data/lib/thor/actions/create_link.rb +3 -0
  6. data/lib/thor/actions/directory.rb +2 -0
  7. data/lib/thor/actions/file_manipulation.rb +1 -1
  8. data/lib/thor/base.rb +84 -95
  9. data/lib/thor/{task.rb → command.rb} +17 -13
  10. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  11. data/lib/thor/error.rb +4 -7
  12. data/lib/thor/group.rb +30 -33
  13. data/lib/thor/invocation.rb +28 -26
  14. data/lib/thor/parser/options.rb +3 -1
  15. data/lib/thor/runner.rb +21 -20
  16. data/lib/thor/shell/basic.rb +5 -1
  17. data/lib/thor/shell/color.rb +4 -0
  18. data/lib/thor/shell/html.rb +4 -0
  19. data/lib/thor/util.rb +214 -210
  20. data/lib/thor/version.rb +1 -1
  21. data/spec/actions/create_file_spec.rb +1 -1
  22. data/spec/actions/create_link_spec.rb +15 -1
  23. data/spec/actions/directory_spec.rb +18 -5
  24. data/spec/actions/empty_directory_spec.rb +1 -1
  25. data/spec/actions/file_manipulation_spec.rb +13 -13
  26. data/spec/actions/inject_into_file_spec.rb +1 -1
  27. data/spec/actions_spec.rb +1 -1
  28. data/spec/base_spec.rb +40 -24
  29. data/spec/command_spec.rb +80 -0
  30. data/spec/core_ext/hash_with_indifferent_access_spec.rb +1 -1
  31. data/spec/core_ext/ordered_hash_spec.rb +1 -1
  32. data/spec/exit_condition_spec.rb +3 -3
  33. data/spec/fixtures/{task.thor → command.thor} +0 -0
  34. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  35. data/spec/fixtures/doc/COMMENTER +11 -0
  36. data/spec/fixtures/doc/README +3 -0
  37. data/spec/fixtures/doc/block_helper.rb +3 -0
  38. data/spec/fixtures/doc/config.rb +1 -0
  39. data/spec/fixtures/doc/config.yaml.tt +1 -0
  40. data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
  41. data/spec/fixtures/group.thor +24 -10
  42. data/spec/fixtures/invoke.thor +3 -3
  43. data/spec/fixtures/script.thor +40 -15
  44. data/spec/fixtures/subcommand.thor +17 -0
  45. data/spec/group_spec.rb +13 -13
  46. data/spec/{spec_helper.rb → helper.rb} +11 -7
  47. data/spec/invocation_spec.rb +16 -16
  48. data/spec/parser/argument_spec.rb +4 -4
  49. data/spec/parser/arguments_spec.rb +1 -1
  50. data/spec/parser/option_spec.rb +1 -1
  51. data/spec/parser/options_spec.rb +6 -1
  52. data/spec/rake_compat_spec.rb +1 -1
  53. data/spec/register_spec.rb +12 -12
  54. data/spec/runner_spec.rb +36 -36
  55. data/spec/shell/basic_spec.rb +9 -10
  56. data/spec/shell/color_spec.rb +16 -2
  57. data/spec/shell/html_spec.rb +1 -1
  58. data/spec/shell_spec.rb +1 -1
  59. data/spec/subcommand_spec.rb +30 -0
  60. data/spec/thor_spec.rb +81 -78
  61. data/spec/util_spec.rb +10 -10
  62. data/thor.gemspec +22 -18
  63. metadata +49 -38
  64. data/.gitignore +0 -44
  65. data/.rspec +0 -3
  66. data/.travis.yml +0 -8
  67. data/Gemfile +0 -19
  68. data/bin/rake2thor +0 -86
  69. data/lib/thor/core_ext/file_binary_read.rb +0 -9
  70. data/spec/task_spec.rb +0 -80
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/parser'
3
3
 
4
4
  describe Thor::Arguments do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/parser'
3
3
 
4
4
  describe Thor::Option do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/parser'
3
3
 
4
4
  describe Thor::Options do
@@ -223,6 +223,11 @@ describe Thor::Options do
223
223
  expect(remaining).to eq(["--bar", "--verbose"])
224
224
  end
225
225
 
226
+ it "retains -- after it has stopped parsing" do
227
+ expect(parse(%w[--bar -- whatever])).to eq({})
228
+ expect(remaining).to eq(["--bar", "--", "whatever"])
229
+ end
230
+
226
231
  it "still accepts options that are given before non-options" do
227
232
  expect(parse(%w[--verbose foo])).to eq({"verbose" => true})
228
233
  expect(remaining).to eq(["foo"])
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'helper'
2
2
  require 'thor/rake_compat'
3
3
  require 'rake/tasklib'
4
4
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'helper'
2
2
 
3
3
  class BoringVendorProvidedCLI < Thor
4
4
  desc "boring", "do boring stuff"
@@ -20,7 +20,7 @@ class ExcitingPluginCLI < Thor
20
20
  end
21
21
 
22
22
  class SuperSecretPlugin < Thor
23
- default_task :squirrel
23
+ default_command :squirrel
24
24
 
25
25
  desc "squirrel", "All of secret squirrel's secrets"
26
26
  def squirrel
@@ -60,7 +60,7 @@ class PluginWithDefault < Thor
60
60
  puts msg
61
61
  end
62
62
 
63
- default_task :say
63
+ default_command :say
64
64
  end
65
65
 
66
66
  class PluginWithDefaultMultipleArguments < Thor
@@ -69,17 +69,17 @@ class PluginWithDefaultMultipleArguments < Thor
69
69
  puts args
70
70
  end
71
71
 
72
- default_task :say
72
+ default_command :say
73
73
  end
74
74
 
75
- class PluginWithDefaultTaskAndDeclaredArgument < Thor
75
+ class PluginWithDefaultcommandAndDeclaredArgument < Thor
76
76
  desc "say MSG [MSG]", "print multiple messages"
77
77
  argument :msg
78
78
  def say
79
79
  puts msg
80
80
  end
81
81
 
82
- default_task :say
82
+ default_command :say
83
83
  end
84
84
 
85
85
  BoringVendorProvidedCLI.register(
@@ -120,7 +120,7 @@ BoringVendorProvidedCLI.register(
120
120
  'subcommands ftw')
121
121
 
122
122
  BoringVendorProvidedCLI.register(
123
- PluginWithDefaultTaskAndDeclaredArgument,
123
+ PluginWithDefaultcommandAndDeclaredArgument,
124
124
  'say_argument',
125
125
  'say message',
126
126
  'subcommands ftw')
@@ -136,18 +136,18 @@ describe ".register-ing a Thor subclass" do
136
136
  expect(help_output).to include('do exciting things')
137
137
  end
138
138
 
139
- it "invokes the default task correctly" do
139
+ it "invokes the default command correctly" do
140
140
  output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say hello]) }
141
141
  expect(output).to include("hello")
142
142
  end
143
143
 
144
- it "invokes the default task correctly with multiple args" do
144
+ it "invokes the default command correctly with multiple args" do
145
145
  output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say_multiple hello adam]) }
146
146
  expect(output).to include("hello")
147
147
  expect(output).to include("adam")
148
148
  end
149
149
 
150
- it "invokes the default task correctly with a declared argument" do
150
+ it "invokes the default command correctly with a declared argument" do
151
151
  output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say_argument hello]) }
152
152
  expect(output).to include("hello")
153
153
  end
@@ -185,12 +185,12 @@ describe ".register-ing a Thor::Group subclass" do
185
185
  end
186
186
 
187
187
  describe "1.8 and 1.9 syntax compatibility" do
188
- it "is compatible with both 1.8 and 1.9 syntax w/o task options" do
188
+ it "is compatible with both 1.8 and 1.9 syntax w/o command options" do
189
189
  group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[zoo]) }
190
190
  expect(group_output).to match(/zebra/)
191
191
  end
192
192
 
193
- it "is compatible with both 1.8 and 1.9 syntax w/task options" do
193
+ it "is compatible with both 1.8 and 1.9 syntax w/command options" do
194
194
  group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[zoo -w lion]) }
195
195
  expect(group_output).to match(/lion/)
196
196
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
1
+ require 'helper'
2
2
  require 'thor/runner'
3
3
 
4
4
  describe Thor::Runner do
@@ -14,13 +14,13 @@ describe Thor::Runner do
14
14
 
15
15
  describe "#help" do
16
16
  it "shows information about Thor::Runner itself" do
17
- expect(capture(:stdout) { Thor::Runner.start(["help"]) }).to match(/List the available thor tasks/)
17
+ expect(capture(:stdout) { Thor::Runner.start(["help"]) }).to match(/List the available thor commands/)
18
18
  end
19
19
 
20
- it "shows information about an specific Thor::Runner task" do
20
+ it "shows information about a specific Thor::Runner command" do
21
21
  content = capture(:stdout) { Thor::Runner.start(["help", "list"]) }
22
- expect(content).to match(/List the available thor tasks/)
23
- expect(content).not_to match(/help \[TASK\]/)
22
+ expect(content).to match(/List the available thor commands/)
23
+ expect(content).not_to match(/help \[COMMAND\]/)
24
24
  end
25
25
 
26
26
  it "shows information about a specific Thor class" do
@@ -28,10 +28,10 @@ describe Thor::Runner do
28
28
  expect(content).to match(/zoo\s+# zoo around/m)
29
29
  end
30
30
 
31
- it "shows information about an specific task from an specific Thor class" do
31
+ it "shows information about an specific command from an specific Thor class" do
32
32
  content = capture(:stdout) { Thor::Runner.start(["help", "my_script:zoo"]) }
33
33
  expect(content).to match(/zoo around/)
34
- expect(content).not_to match(/help \[TASK\]/)
34
+ expect(content).not_to match(/help \[COMMAND\]/)
35
35
  end
36
36
 
37
37
  it "shows information about a specific Thor group class" do
@@ -39,63 +39,63 @@ describe Thor::Runner do
39
39
  expect(content).to match(/my_counter N/)
40
40
  end
41
41
 
42
- it "raises error if a class/task cannot be found" do
42
+ it "raises error if a class/command cannot be found" do
43
43
  content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) }
44
- expect(content.strip).to eq('Could not find task "unknown" in "default" namespace.')
44
+ expect(content.strip).to eq('Could not find command "unknown" in "default" namespace.')
45
45
  end
46
46
 
47
- it "raises error if a class/task cannot be found for a setup without thorfiles" do
47
+ it "raises error if a class/command cannot be found for a setup without thorfiles" do
48
48
  when_no_thorfiles_exist do
49
49
  Thor::Runner.should_receive :exit
50
50
  content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) }
51
- expect(content.strip).to eq('Could not find task "unknown".')
51
+ expect(content.strip).to eq('Could not find command "unknown".')
52
52
  end
53
53
  end
54
54
  end
55
55
 
56
56
  describe "#start" do
57
- it "invokes a task from Thor::Runner" do
57
+ it "invokes a command from Thor::Runner" do
58
58
  ARGV.replace ["list"]
59
59
  expect(capture(:stdout) { Thor::Runner.start }).to match(/my_counter N/)
60
60
  end
61
61
 
62
- it "invokes a task from a specific Thor class" do
62
+ it "invokes a command from a specific Thor class" do
63
63
  ARGV.replace ["my_script:zoo"]
64
64
  expect(Thor::Runner.start).to be_true
65
65
  end
66
66
 
67
- it "invokes the default task from a specific Thor class if none is specified" do
67
+ it "invokes the default command from a specific Thor class if none is specified" do
68
68
  ARGV.replace ["my_script"]
69
- expect(Thor::Runner.start).to eq("default task")
69
+ expect(Thor::Runner.start).to eq("default command")
70
70
  end
71
71
 
72
- it "forwads arguments to the invoked task" do
72
+ it "forwads arguments to the invoked command" do
73
73
  ARGV.replace ["my_script:animal", "horse"]
74
74
  expect(Thor::Runner.start).to eq(["horse"])
75
75
  end
76
76
 
77
- it "invokes tasks through shortcuts" do
77
+ it "invokes commands through shortcuts" do
78
78
  ARGV.replace ["my_script", "-T", "horse"]
79
79
  expect(Thor::Runner.start).to eq(["horse"])
80
80
  end
81
81
 
82
82
  it "invokes a Thor::Group" do
83
83
  ARGV.replace ["my_counter", "1", "2", "--third", "3"]
84
- expect(Thor::Runner.start).to eq([1, 2, 3])
84
+ expect(Thor::Runner.start).to eq([1, 2, 3, nil, nil, nil])
85
85
  end
86
86
 
87
- it "raises an error if class/task can't be found" do
87
+ it "raises an error if class/command can't be found" do
88
88
  ARGV.replace ["unknown"]
89
89
  content = capture(:stderr){ Thor::Runner.start }
90
- expect(content.strip).to eq('Could not find task "unknown" in "default" namespace.')
90
+ expect(content.strip).to eq('Could not find command "unknown" in "default" namespace.')
91
91
  end
92
92
 
93
- it "raises an error if class/task can't be found in a setup without thorfiles" do
93
+ it "raises an error if class/command can't be found in a setup without thorfiles" do
94
94
  when_no_thorfiles_exist do
95
95
  ARGV.replace ["unknown"]
96
96
  Thor::Runner.should_receive :exit
97
97
  content = capture(:stderr){ Thor::Runner.start }
98
- expect(content.strip).to eq('Could not find task "unknown".')
98
+ expect(content.strip).to eq('Could not find command "unknown".')
99
99
  end
100
100
  end
101
101
 
@@ -112,13 +112,13 @@ describe Thor::Runner do
112
112
  it "does not swallow Thor InvocationError" do
113
113
  ARGV.replace ["my_script:animal"]
114
114
  content = capture(:stderr) { Thor::Runner.start }
115
- expect(content.strip).to eq('thor animal requires at least 1 argument: "thor my_script:animal TYPE".')
115
+ expect(content.strip).to eq(%Q'ERROR: thor animal was called with no arguments\nUsage: "thor my_script:animal TYPE".')
116
116
  end
117
117
  end
118
118
 
119
- describe "tasks" do
119
+ describe "commands" do
120
120
  before do
121
- @location = "#{File.dirname(__FILE__)}/fixtures/task.thor"
121
+ @location = "#{File.dirname(__FILE__)}/fixtures/command.thor"
122
122
  @original_yaml = {
123
123
  "random" => {
124
124
  :location => @location,
@@ -136,7 +136,7 @@ describe Thor::Runner do
136
136
  end
137
137
 
138
138
  describe "list" do
139
- it "gives a list of the available tasks" do
139
+ it "gives a list of the available commands" do
140
140
  ARGV.replace ["list"]
141
141
  content = capture(:stdout) { Thor::Runner.start }
142
142
  expect(content).to match(/amazing:describe NAME\s+# say that someone is amazing/m)
@@ -147,7 +147,7 @@ describe Thor::Runner do
147
147
  expect(capture(:stdout) { Thor::Runner.start }).to match(/my_counter N/)
148
148
  end
149
149
 
150
- it "can filter a list of the available tasks by --group" do
150
+ it "can filter a list of the available commands by --group" do
151
151
  ARGV.replace ["list", "--group", "standard"]
152
152
  expect(capture(:stdout) { Thor::Runner.start }).to match(/amazing:describe NAME/)
153
153
  ARGV.replace []
@@ -156,34 +156,34 @@ describe Thor::Runner do
156
156
  expect(capture(:stdout) { Thor::Runner.start }).to match(/my_script:animal TYPE/)
157
157
  end
158
158
 
159
- it "can skip all filters to show all tasks using --all" do
159
+ it "can skip all filters to show all commands using --all" do
160
160
  ARGV.replace ["list", "--all"]
161
161
  content = capture(:stdout) { Thor::Runner.start }
162
162
  expect(content).to match(/amazing:describe NAME/)
163
163
  expect(content).to match(/my_script:animal TYPE/)
164
164
  end
165
165
 
166
- it "doesn't list superclass tasks in the subclass" do
166
+ it "doesn't list superclass commands in the subclass" do
167
167
  ARGV.replace ["list"]
168
168
  expect(capture(:stdout) { Thor::Runner.start }).not_to match(/amazing:help/)
169
169
  end
170
170
 
171
- it "presents tasks in the default namespace with an empty namespace" do
171
+ it "presents commands in the default namespace with an empty namespace" do
172
172
  ARGV.replace ["list"]
173
173
  expect(capture(:stdout) { Thor::Runner.start }).to match(/^thor :cow\s+# prints 'moo'/m)
174
174
  end
175
175
 
176
- it "runs tasks with an empty namespace from the default namespace" do
177
- ARGV.replace [":task_conflict"]
178
- expect(capture(:stdout) { Thor::Runner.start }).to eq("task\n")
176
+ it "runs commands with an empty namespace from the default namespace" do
177
+ ARGV.replace [":command_conflict"]
178
+ expect(capture(:stdout) { Thor::Runner.start }).to eq("command\n")
179
179
  end
180
180
 
181
- it "runs groups even when there is a task with the same name" do
182
- ARGV.replace ["task_conflict"]
181
+ it "runs groups even when there is a command with the same name" do
182
+ ARGV.replace ["command_conflict"]
183
183
  expect(capture(:stdout) { Thor::Runner.start }).to eq("group\n")
184
184
  end
185
185
 
186
- it "runs tasks with no colon in the default namespace" do
186
+ it "runs commands with no colon in the default namespace" do
187
187
  ARGV.replace ["cow"]
188
188
  expect(capture(:stdout) { Thor::Runner.start }).to eq("moo\n")
189
189
  end
@@ -1,6 +1,5 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ # coding: UTF-8
2
+ require 'helper'
4
3
 
5
4
  describe Thor::Shell::Basic do
6
5
  def shell
@@ -93,8 +92,8 @@ describe Thor::Shell::Basic do
93
92
 
94
93
  describe "#say_status" do
95
94
  it "prints a message to the user with status" do
96
- $stdout.should_receive(:puts).with(" create ~/.thor/task.thor")
97
- shell.say_status(:create, "~/.thor/task.thor")
95
+ $stdout.should_receive(:puts).with(" create ~/.thor/command.thor")
96
+ shell.say_status(:create, "~/.thor/command.thor")
98
97
  end
99
98
 
100
99
  it "always use new line" do
@@ -107,7 +106,7 @@ describe Thor::Shell::Basic do
107
106
  $stdout.should_not_receive(:puts)
108
107
 
109
108
  shell.mute do
110
- shell.say_status(:created, "~/.thor/task.thor")
109
+ shell.say_status(:created, "~/.thor/command.thor")
111
110
  end
112
111
  end
113
112
 
@@ -117,18 +116,18 @@ describe Thor::Shell::Basic do
117
116
 
118
117
  $stdout.should_not_receive(:puts)
119
118
  shell.base = base
120
- shell.say_status(:created, "~/.thor/task.thor")
119
+ shell.say_status(:created, "~/.thor/command.thor")
121
120
  end
122
121
 
123
122
  it "does not print a message if log status is set to false" do
124
123
  $stdout.should_not_receive(:puts)
125
- shell.say_status(:created, "~/.thor/task.thor", false)
124
+ shell.say_status(:created, "~/.thor/command.thor", false)
126
125
  end
127
126
 
128
127
  it "uses padding to set messages left margin" do
129
128
  shell.padding = 2
130
- $stdout.should_receive(:puts).with(" create ~/.thor/task.thor")
131
- shell.say_status(:create, "~/.thor/task.thor")
129
+ $stdout.should_receive(:puts).with(" create ~/.thor/command.thor")
130
+ shell.say_status(:create, "~/.thor/command.thor")
132
131
  end
133
132
  end
134
133
 
@@ -1,12 +1,16 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
 
3
3
  describe Thor::Shell::Color do
4
4
  def shell
5
5
  @shell ||= Thor::Shell::Color.new
6
6
  end
7
7
 
8
+ before do
9
+ StringIO.any_instance.stub(:tty?).and_return(true)
10
+ end
11
+
8
12
  describe "#say" do
9
- it "set the color if specified" do
13
+ it "set the color if specified and tty?" do
10
14
  out = capture(:stdout) do
11
15
  shell.say "Wow! Now we have colors!", :green
12
16
  end
@@ -14,6 +18,15 @@ describe Thor::Shell::Color do
14
18
  expect(out.chomp).to eq("\e[32mWow! Now we have colors!\e[0m")
15
19
  end
16
20
 
21
+ it "does not set the color if output is not a tty" do
22
+ out = capture(:stdout) do
23
+ $stdout.should_receive(:tty?).and_return(false)
24
+ shell.say "Wow! Now we have colors!", :green
25
+ end
26
+
27
+ expect(out.chomp).to eq("Wow! Now we have colors!")
28
+ end
29
+
17
30
  it "does not use a new line even with colors" do
18
31
  out = capture(:stdout) do
19
32
  shell.say "Wow! Now we have colors! ", :green
@@ -68,6 +81,7 @@ describe Thor::Shell::Color do
68
81
  describe "when a block is given" do
69
82
  it "invokes the diff command" do
70
83
  $stdout.stub!(:print)
84
+ $stdout.stub!(:tty?).and_return(true)
71
85
  $stdin.should_receive(:gets).and_return('d')
72
86
  $stdin.should_receive(:gets).and_return('n')
73
87
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
 
3
3
  describe Thor::Shell::HTML do
4
4
  def shell
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'helper'
2
2
 
3
3
  describe Thor::Shell do
4
4
  def shell
@@ -0,0 +1,30 @@
1
+ require 'helper'
2
+
3
+ describe Thor do
4
+
5
+ describe "#subcommand" do
6
+
7
+ it "maps a given subcommand to another Thor subclass" do
8
+ barn_help = capture(:stdout) { Scripts::MyDefaults.start(%w[barn]) }
9
+ expect(barn_help).to include("barn help [COMMAND] # Describe subcommands or one specific subcommand")
10
+ end
11
+
12
+ it "passes commands to subcommand classes" do
13
+ expect(capture(:stdout) { Scripts::MyDefaults.start(%w[barn open]) }.strip).to eq("Open sesame!")
14
+ end
15
+
16
+ it "passes arguments to subcommand classes" do
17
+ expect(capture(:stdout) { Scripts::MyDefaults.start(%w[barn open shotgun]) }.strip).to eq("That's going to leave a mark.")
18
+ end
19
+
20
+ it "ignores unknown options (the subcommand class will handle them)" do
21
+ expect(capture(:stdout) { Scripts::MyDefaults.start(%w[barn paint blue --coats 4])}.strip).to eq("4 coats of blue paint")
22
+ end
23
+
24
+ it "passes parsed options to subcommands" do
25
+ output = capture(:stdout) { TestSubcommands::Parent.start(%w[sub print_opt --opt output]) }
26
+ expect(output).to eq("output")
27
+ end
28
+ end
29
+
30
+ end