thor_dleavitt 0.18.1
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 +7 -0
- data/.document +5 -0
- data/CHANGELOG.md +139 -0
- data/LICENSE.md +20 -0
- data/README.md +35 -0
- data/Thorfile +30 -0
- data/bin/thor_dleavitt +6 -0
- data/lib/thor/actions.rb +318 -0
- data/lib/thor/actions/create_file.rb +105 -0
- data/lib/thor/actions/create_link.rb +60 -0
- data/lib/thor/actions/directory.rb +119 -0
- data/lib/thor/actions/empty_directory.rb +137 -0
- data/lib/thor/actions/file_manipulation.rb +317 -0
- data/lib/thor/actions/inject_into_file.rb +109 -0
- data/lib/thor/base.rb +654 -0
- data/lib/thor/command.rb +136 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
- data/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/thor/core_ext/ordered_hash.rb +100 -0
- data/lib/thor/error.rb +32 -0
- data/lib/thor/group.rb +282 -0
- data/lib/thor/invocation.rb +172 -0
- data/lib/thor/parser.rb +4 -0
- data/lib/thor/parser/argument.rb +74 -0
- data/lib/thor/parser/arguments.rb +171 -0
- data/lib/thor/parser/option.rb +121 -0
- data/lib/thor/parser/options.rb +218 -0
- data/lib/thor/rake_compat.rb +72 -0
- data/lib/thor/runner.rb +322 -0
- data/lib/thor/shell.rb +88 -0
- data/lib/thor/shell/basic.rb +422 -0
- data/lib/thor/shell/color.rb +148 -0
- data/lib/thor/shell/html.rb +127 -0
- data/lib/thor/util.rb +270 -0
- data/lib/thor/version.rb +3 -0
- data/lib/thor_dleavitt.rb +473 -0
- data/spec/actions/create_file_spec.rb +170 -0
- data/spec/actions/create_link_spec.rb +95 -0
- data/spec/actions/directory_spec.rb +169 -0
- data/spec/actions/empty_directory_spec.rb +129 -0
- data/spec/actions/file_manipulation_spec.rb +382 -0
- data/spec/actions/inject_into_file_spec.rb +135 -0
- data/spec/actions_spec.rb +331 -0
- data/spec/base_spec.rb +291 -0
- data/spec/command_spec.rb +80 -0
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +48 -0
- data/spec/core_ext/ordered_hash_spec.rb +115 -0
- data/spec/exit_condition_spec.rb +19 -0
- data/spec/fixtures/application.rb +2 -0
- data/spec/fixtures/app{1}/README +3 -0
- data/spec/fixtures/bundle/execute.rb +6 -0
- data/spec/fixtures/bundle/main.thor +1 -0
- data/spec/fixtures/command.thor +10 -0
- data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
- data/spec/fixtures/doc/COMMENTER +11 -0
- data/spec/fixtures/doc/README +3 -0
- data/spec/fixtures/doc/block_helper.rb +3 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/fixtures/doc/config.yaml.tt +1 -0
- data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
- data/spec/fixtures/enum.thor +10 -0
- data/spec/fixtures/group.thor +128 -0
- data/spec/fixtures/invoke.thor +118 -0
- data/spec/fixtures/path with spaces b/data/spec/fixtures/path with → spaces +0 -0
- data/spec/fixtures/preserve/script.sh +3 -0
- data/spec/fixtures/script.thor +220 -0
- data/spec/fixtures/subcommand.thor +17 -0
- data/spec/group_spec.rb +222 -0
- data/spec/helper.rb +67 -0
- data/spec/invocation_spec.rb +108 -0
- data/spec/parser/argument_spec.rb +53 -0
- data/spec/parser/arguments_spec.rb +66 -0
- data/spec/parser/option_spec.rb +202 -0
- data/spec/parser/options_spec.rb +400 -0
- data/spec/rake_compat_spec.rb +72 -0
- data/spec/register_spec.rb +197 -0
- data/spec/runner_spec.rb +241 -0
- data/spec/shell/basic_spec.rb +330 -0
- data/spec/shell/color_spec.rb +95 -0
- data/spec/shell/html_spec.rb +31 -0
- data/spec/shell_spec.rb +47 -0
- data/spec/subcommand_spec.rb +30 -0
- data/spec/thor_spec.rb +499 -0
- data/spec/util_spec.rb +196 -0
- data/thor.gemspec +24 -0
- metadata +191 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Thor::Shell::Color do
|
4
|
+
def shell
|
5
|
+
@shell ||= Thor::Shell::Color.new
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow_any_instance_of(StringIO).to receive(:tty?).and_return(true)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#say" do
|
13
|
+
it "set the color if specified and tty?" do
|
14
|
+
out = capture(:stdout) do
|
15
|
+
shell.say "Wow! Now we have colors!", :green
|
16
|
+
end
|
17
|
+
|
18
|
+
expect(out.chomp).to eq("\e[32mWow! Now we have colors!\e[0m")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "does not set the color if output is not a tty" do
|
22
|
+
out = capture(:stdout) do
|
23
|
+
expect($stdout).to 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
|
+
|
30
|
+
it "does not use a new line even with colors" do
|
31
|
+
out = capture(:stdout) do
|
32
|
+
shell.say "Wow! Now we have colors! ", :green
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(out.chomp).to eq("\e[32mWow! Now we have colors! \e[0m")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "handles an Array of colors" do
|
39
|
+
out = capture(:stdout) do
|
40
|
+
shell.say "Wow! Now we have colors *and* background colors", [:green, :on_red, :bold]
|
41
|
+
end
|
42
|
+
|
43
|
+
expect(out.chomp).to eq("\e[32m\e[41m\e[1mWow! Now we have colors *and* background colors\e[0m")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#say_status" do
|
48
|
+
it "uses color to say status" do
|
49
|
+
out = capture(:stdout) do
|
50
|
+
shell.say_status :conflict, "README", :red
|
51
|
+
end
|
52
|
+
|
53
|
+
expect(out.chomp).to eq("\e[1m\e[31m conflict\e[0m README")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#set_color" do
|
58
|
+
it "colors a string with a foreground color" do
|
59
|
+
red = shell.set_color "hi!", :red
|
60
|
+
expect(red).to eq("\e[31mhi!\e[0m")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "colors a string with a background color" do
|
64
|
+
on_red = shell.set_color "hi!", :white, :on_red
|
65
|
+
expect(on_red).to eq("\e[37m\e[41mhi!\e[0m")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "colors a string with a bold color" do
|
69
|
+
bold = shell.set_color "hi!", :white, true
|
70
|
+
expect(bold).to eq("\e[1m\e[37mhi!\e[0m")
|
71
|
+
|
72
|
+
bold = shell.set_color "hi!", :white, :bold
|
73
|
+
expect(bold).to eq("\e[37m\e[1mhi!\e[0m")
|
74
|
+
|
75
|
+
bold = shell.set_color "hi!", :white, :on_red, :bold
|
76
|
+
expect(bold).to eq("\e[37m\e[41m\e[1mhi!\e[0m")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#file_collision" do
|
81
|
+
describe "when a block is given" do
|
82
|
+
it "invokes the diff command" do
|
83
|
+
allow($stdout).to receive(:print)
|
84
|
+
allow($stdout).to receive(:tty?).and_return(true)
|
85
|
+
expect($stdin).to receive(:gets).and_return('d')
|
86
|
+
expect($stdin).to receive(:gets).and_return('n')
|
87
|
+
|
88
|
+
output = capture(:stdout) { shell.file_collision('spec/fixtures/doc/README'){ "README\nEND\n" } }
|
89
|
+
expect(output).to match(/\e\[31m\- __start__\e\[0m/)
|
90
|
+
expect(output).to match(/^ README/)
|
91
|
+
expect(output).to match(/\e\[32m\+ END\e\[0m/)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Thor::Shell::HTML do
|
4
|
+
def shell
|
5
|
+
@shell ||= Thor::Shell::HTML.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#say" do
|
9
|
+
it "sets the color if specified" do
|
10
|
+
out = capture(:stdout) { shell.say "Wow! Now we have colors!", :green }
|
11
|
+
expect(out.chomp).to eq('<span style="color: green;">Wow! Now we have colors!</span>')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "sets bold if specified" do
|
15
|
+
out = capture(:stdout) { shell.say "Wow! Now we have colors *and* bold!", [:green, :bold] }
|
16
|
+
expect(out.chomp).to eq('<span style="color: green; font-weight: bold;">Wow! Now we have colors *and* bold!</span>')
|
17
|
+
end
|
18
|
+
|
19
|
+
it "does not use a new line even with colors" do
|
20
|
+
out = capture(:stdout) { shell.say "Wow! Now we have colors! ", :green }
|
21
|
+
expect(out.chomp).to eq('<span style="color: green;">Wow! Now we have colors! </span>')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#say_status" do
|
26
|
+
it "uses color to say status" do
|
27
|
+
expect($stdout).to receive(:print).with("<span style=\"color: red; font-weight: bold;\"> conflict</span> README\n")
|
28
|
+
shell.say_status :conflict, "README", :red
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/shell_spec.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Thor::Shell do
|
4
|
+
def shell
|
5
|
+
@shell ||= Thor::Base.shell.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
it "sets shell value" do
|
10
|
+
base = MyCounter.new [1, 2], { }, :shell => shell
|
11
|
+
expect(base.shell).to eq(shell)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "sets the base value on the shell if an accessor is available" do
|
15
|
+
base = MyCounter.new [1, 2], { }, :shell => shell
|
16
|
+
expect(shell.base).to eq(base)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#shell" do
|
21
|
+
it "returns the shell in use" do
|
22
|
+
expect(MyCounter.new([1,2]).shell).to be_kind_of(Thor::Base.shell)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "uses $THOR_SHELL" do
|
26
|
+
class Thor::Shell::TestShell < Thor::Shell::Basic; end
|
27
|
+
|
28
|
+
expect(Thor::Base.shell).to eq(shell.class)
|
29
|
+
ENV['THOR_SHELL'] = 'TestShell'
|
30
|
+
Thor::Base.shell = nil
|
31
|
+
expect(Thor::Base.shell).to eq(Thor::Shell::TestShell)
|
32
|
+
ENV['THOR_SHELL'] = ''
|
33
|
+
Thor::Base.shell = shell.class
|
34
|
+
expect(Thor::Base.shell).to eq(shell.class)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "with_padding" do
|
39
|
+
it "uses padding for inside block outputs" do
|
40
|
+
base = MyCounter.new([1,2])
|
41
|
+
base.with_padding do
|
42
|
+
expect(capture(:stdout) { base.say_status :padding, "cool" }.strip).to eq("padding cool")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -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
|
data/spec/thor_spec.rb
ADDED
@@ -0,0 +1,499 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Thor do
|
4
|
+
describe "#method_option" do
|
5
|
+
it "sets options to the next method to be invoked" do
|
6
|
+
args = ["foo", "bar", "--force"]
|
7
|
+
arg, options = MyScript.start(args)
|
8
|
+
expect(options).to eq({ "force" => true })
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ":lazy_default" do
|
12
|
+
it "is absent when option is not specified" do
|
13
|
+
arg, options = MyScript.start(["with_optional"])
|
14
|
+
expect(options).to eq({})
|
15
|
+
end
|
16
|
+
|
17
|
+
it "sets a default that can be overridden for strings" do
|
18
|
+
arg, options = MyScript.start(["with_optional", "--lazy"])
|
19
|
+
expect(options).to eq({ "lazy" => "yes" })
|
20
|
+
|
21
|
+
arg, options = MyScript.start(["with_optional", "--lazy", "yesyes!"])
|
22
|
+
expect(options).to eq({ "lazy" => "yesyes!" })
|
23
|
+
end
|
24
|
+
|
25
|
+
it "sets a default that can be overridden for numerics" do
|
26
|
+
arg, options = MyScript.start(["with_optional", "--lazy-numeric"])
|
27
|
+
expect(options).to eq({ "lazy_numeric" => 42 })
|
28
|
+
|
29
|
+
arg, options = MyScript.start(["with_optional", "--lazy-numeric", 20000])
|
30
|
+
expect(options).to eq({ "lazy_numeric" => 20000 })
|
31
|
+
end
|
32
|
+
|
33
|
+
it "sets a default that can be overridden for arrays" do
|
34
|
+
arg, options = MyScript.start(["with_optional", "--lazy-array"])
|
35
|
+
expect(options).to eq({ "lazy_array" => %w[eat at joes] })
|
36
|
+
|
37
|
+
arg, options = MyScript.start(["with_optional", "--lazy-array", "hello", "there"])
|
38
|
+
expect(options).to eq({ "lazy_array" => %w[hello there] })
|
39
|
+
end
|
40
|
+
|
41
|
+
it "sets a default that can be overridden for hashes" do
|
42
|
+
arg, options = MyScript.start(["with_optional", "--lazy-hash"])
|
43
|
+
expect(options).to eq({ "lazy_hash" => {'swedish' => 'meatballs'} })
|
44
|
+
|
45
|
+
arg, options = MyScript.start(["with_optional", "--lazy-hash", "polish:sausage"])
|
46
|
+
expect(options).to eq({ "lazy_hash" => {'polish' => 'sausage'} })
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "when :for is supplied" do
|
51
|
+
it "updates an already defined command" do
|
52
|
+
args, options = MyChildScript.start(["animal", "horse", "--other=fish"])
|
53
|
+
expect(options[:other]).to eq("fish")
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "and the target is on the parent class" do
|
57
|
+
it "updates an already defined command" do
|
58
|
+
args = ["example_default_command", "my_param", "--new-option=verified"]
|
59
|
+
options = Scripts::MyScript.start(args)
|
60
|
+
expect(options[:new_option]).to eq("verified")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "adds a command to the command list if the updated command is on the parent class" do
|
64
|
+
expect(Scripts::MyScript.commands["example_default_command"]).to be
|
65
|
+
end
|
66
|
+
|
67
|
+
it "clones the parent command" do
|
68
|
+
expect(Scripts::MyScript.commands["example_default_command"]).not_to eq(MyChildScript.commands["example_default_command"])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#default_command" do
|
75
|
+
it "sets a default command" do
|
76
|
+
expect(MyScript.default_command).to eq("example_default_command")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "invokes the default command if no command is specified" do
|
80
|
+
expect(MyScript.start([])).to eq("default command")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "invokes the default command if no command is specified even if switches are given" do
|
84
|
+
expect(MyScript.start(["--with", "option"])).to eq({"with"=>"option"})
|
85
|
+
end
|
86
|
+
|
87
|
+
it "inherits the default command from parent" do
|
88
|
+
expect(MyChildScript.default_command).to eq("example_default_command")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#stop_on_unknown_option!" do
|
93
|
+
my_script = Class.new(Thor) do
|
94
|
+
class_option "verbose", :type => :boolean
|
95
|
+
class_option "mode", :type => :string
|
96
|
+
|
97
|
+
stop_on_unknown_option! :exec
|
98
|
+
|
99
|
+
desc "exec", "Run a command"
|
100
|
+
def exec(*args)
|
101
|
+
return options, args
|
102
|
+
end
|
103
|
+
|
104
|
+
desc "boring", "An ordinary command"
|
105
|
+
def boring(*args)
|
106
|
+
return options, args
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "passes remaining args to command when it encounters a non-option" do
|
111
|
+
expect(my_script.start(%w[exec command --verbose])).to eq [{}, ["command", "--verbose"]]
|
112
|
+
end
|
113
|
+
|
114
|
+
it "passes remaining args to command when it encounters an unknown option" do
|
115
|
+
expect(my_script.start(%w[exec --foo command --bar])).to eq [{}, ["--foo", "command", "--bar"]]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "still accepts options that are given before non-options" do
|
119
|
+
expect(my_script.start(%w[exec --verbose command --foo])).to eq [{"verbose" => true}, ["command", "--foo"]]
|
120
|
+
end
|
121
|
+
|
122
|
+
it "still accepts options that require a value" do
|
123
|
+
expect(my_script.start(%w[exec --mode rashly command])).to eq [{"mode" => "rashly"}, ["command"]]
|
124
|
+
end
|
125
|
+
|
126
|
+
it "still passes everything after -- to command" do
|
127
|
+
expect(my_script.start(%w[exec -- --verbose])).to eq [{}, ["--verbose"]]
|
128
|
+
end
|
129
|
+
|
130
|
+
it "does not affect ordinary commands" do
|
131
|
+
expect(my_script.start(%w[boring command --verbose])).to eq [{"verbose" => true}, ["command"]]
|
132
|
+
end
|
133
|
+
|
134
|
+
context "when provided with multiple command names" do
|
135
|
+
klass = Class.new(Thor) do
|
136
|
+
stop_on_unknown_option! :foo, :bar
|
137
|
+
end
|
138
|
+
it "affects all specified commands" do
|
139
|
+
expect(klass.stop_on_unknown_option?(double(:name => "foo"))).to be_true
|
140
|
+
expect(klass.stop_on_unknown_option?(double(:name => "bar"))).to be_true
|
141
|
+
expect(klass.stop_on_unknown_option?(double(:name => "baz"))).to be_false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when invoked several times" do
|
146
|
+
klass = Class.new(Thor) do
|
147
|
+
stop_on_unknown_option! :foo
|
148
|
+
stop_on_unknown_option! :bar
|
149
|
+
end
|
150
|
+
it "affects all specified commands" do
|
151
|
+
expect(klass.stop_on_unknown_option?(double(:name => "foo"))).to be_true
|
152
|
+
expect(klass.stop_on_unknown_option?(double(:name => "bar"))).to be_true
|
153
|
+
expect(klass.stop_on_unknown_option?(double(:name => "baz"))).to be_false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it "doesn't break new" do
|
158
|
+
expect(my_script.new).to be_a(Thor)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "#map" do
|
163
|
+
it "calls the alias of a method if one is provided" do
|
164
|
+
expect(MyScript.start(["-T", "fish"])).to eq(["fish"])
|
165
|
+
end
|
166
|
+
|
167
|
+
it "calls the alias of a method if several are provided via #map" do
|
168
|
+
expect(MyScript.start(["-f", "fish"])).to eq(["fish", {}])
|
169
|
+
expect(MyScript.start(["--foo", "fish"])).to eq(["fish", {}])
|
170
|
+
end
|
171
|
+
|
172
|
+
it "inherits all mappings from parent" do
|
173
|
+
expect(MyChildScript.default_command).to eq("example_default_command")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#package_name" do
|
178
|
+
it "provides a proper description for a command when the package_name is assigned" do
|
179
|
+
content = capture(:stdout) { PackageNameScript.start(["help"]) }
|
180
|
+
expect(content).to match(/Baboon commands:/m)
|
181
|
+
end
|
182
|
+
|
183
|
+
# TODO: remove this, might be redundant, just wanted to prove full coverage
|
184
|
+
it "provides a proper description for a command when the package_name is NOT assigned" do
|
185
|
+
content = capture(:stdout) { MyScript.start(["help"]) }
|
186
|
+
expect(content).to match(/Commands:/m)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "#desc" do
|
191
|
+
it "provides description for a command" do
|
192
|
+
content = capture(:stdout) { MyScript.start(["help"]) }
|
193
|
+
expect(content).to match(/thor my_script:zoo\s+# zoo around/m)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "provides no namespace if $thor_runner is false" do
|
197
|
+
begin
|
198
|
+
$thor_runner = false
|
199
|
+
content = capture(:stdout) { MyScript.start(["help"]) }
|
200
|
+
expect(content).to match(/thor zoo\s+# zoo around/m)
|
201
|
+
ensure
|
202
|
+
$thor_runner = true
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "when :for is supplied" do
|
207
|
+
it "overwrites a previous defined command" do
|
208
|
+
expect(capture(:stdout) { MyChildScript.start(["help"]) }).to match(/animal KIND \s+# fish around/m)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "when :hide is supplied" do
|
213
|
+
it "does not show the command in help" do
|
214
|
+
expect(capture(:stdout) { MyScript.start(["help"]) }).not_to match(/this is hidden/m)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "but the command is still invokable, does not show the command in help" do
|
218
|
+
expect(MyScript.start(["hidden", "yesyes"])).to eq(["yesyes"])
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "#method_options" do
|
224
|
+
it "sets default options if called before an initializer" do
|
225
|
+
options = MyChildScript.class_options
|
226
|
+
expect(options[:force].type).to eq(:boolean)
|
227
|
+
expect(options[:param].type).to eq(:numeric)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "overwrites default options if called on the method scope" do
|
231
|
+
args = ["zoo", "--force", "--param", "feathers"]
|
232
|
+
options = MyChildScript.start(args)
|
233
|
+
expect(options).to eq({ "force" => true, "param" => "feathers" })
|
234
|
+
end
|
235
|
+
|
236
|
+
it "allows default options to be merged with method options" do
|
237
|
+
args = ["animal", "bird", "--force", "--param", "1.0", "--other", "tweets"]
|
238
|
+
arg, options = MyChildScript.start(args)
|
239
|
+
expect(arg).to eq('bird')
|
240
|
+
expect(options).to eq({ "force"=>true, "param"=>1.0, "other"=>"tweets" })
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "#start" do
|
245
|
+
it "calls a no-param method when no params are passed" do
|
246
|
+
expect(MyScript.start(["zoo"])).to eq(true)
|
247
|
+
end
|
248
|
+
|
249
|
+
it "calls a single-param method when a single param is passed" do
|
250
|
+
expect(MyScript.start(["animal", "fish"])).to eq(["fish"])
|
251
|
+
end
|
252
|
+
|
253
|
+
it "does not set options in attributes" do
|
254
|
+
expect(MyScript.start(["with_optional", "--all"])).to eq([nil, { "all" => true }, []])
|
255
|
+
end
|
256
|
+
|
257
|
+
it "raises an error if the wrong number of params are provided" do
|
258
|
+
arity_asserter = lambda do |args, msg|
|
259
|
+
stderr = capture(:stderr) { Scripts::Arities.start(args) }
|
260
|
+
expect(stderr.strip).to eq(msg)
|
261
|
+
end
|
262
|
+
arity_asserter.call ["zero_args", "one" ], %Q'ERROR: "thor zero_args" was called with arguments ["one"]\nUsage: "thor scripts:arities:zero_args"'
|
263
|
+
arity_asserter.call ["one_arg" ], %Q'ERROR: "thor one_arg" was called with no arguments\nUsage: "thor scripts:arities:one_arg ARG"'
|
264
|
+
arity_asserter.call ["one_arg", "one", "two" ], %Q'ERROR: "thor one_arg" was called with arguments ["one", "two"]\nUsage: "thor scripts:arities:one_arg ARG"'
|
265
|
+
arity_asserter.call ["one_arg", "one", "two" ], %Q'ERROR: "thor one_arg" was called with arguments ["one", "two"]\nUsage: "thor scripts:arities:one_arg ARG"'
|
266
|
+
arity_asserter.call ["two_args", "one" ], %Q'ERROR: "thor two_args" was called with arguments ["one"]\nUsage: "thor scripts:arities:two_args ARG1 ARG2"'
|
267
|
+
arity_asserter.call ["optional_arg", "one", "two" ], %Q'ERROR: "thor optional_arg" was called with arguments ["one", "two"]\nUsage: "thor scripts:arities:optional_arg [ARG]"'
|
268
|
+
end
|
269
|
+
|
270
|
+
it "raises an error if the invoked command does not exist" do
|
271
|
+
expect(capture(:stderr) { Amazing.start(["animal"]) }.strip).to eq('Could not find command "animal" in "amazing" namespace.')
|
272
|
+
end
|
273
|
+
|
274
|
+
it "calls method_missing if an unknown method is passed in" do
|
275
|
+
expect(MyScript.start(["unk", "hello"])).to eq([:unk, ["hello"]])
|
276
|
+
end
|
277
|
+
|
278
|
+
it "does not call a private method no matter what" do
|
279
|
+
expect(capture(:stderr) { MyScript.start(["what"]) }.strip).to eq('Could not find command "what" in "my_script" namespace.')
|
280
|
+
end
|
281
|
+
|
282
|
+
it "uses command default options" do
|
283
|
+
options = MyChildScript.start(["animal", "fish"]).last
|
284
|
+
expect(options).to eq({ "other" => "method default" })
|
285
|
+
end
|
286
|
+
|
287
|
+
it "raises when an exception happens within the command call" do
|
288
|
+
expect{ MyScript.start(["call_myself_with_wrong_arity"]) }.to raise_error(ArgumentError)
|
289
|
+
end
|
290
|
+
|
291
|
+
context "when the user enters an unambiguous substring of a command" do
|
292
|
+
it "invokes a command" do
|
293
|
+
expect(MyScript.start(["z"])).to eq(MyScript.start(["zoo"]))
|
294
|
+
end
|
295
|
+
|
296
|
+
it "invokes a command, even when there's an alias it resolves to the same command" do
|
297
|
+
expect(MyScript.start(["hi", "arg"])).to eq(MyScript.start(["hidden", "arg"]))
|
298
|
+
end
|
299
|
+
|
300
|
+
it "invokes an alias" do
|
301
|
+
expect(MyScript.start(["animal_pri"])).to eq(MyScript.start(["zoo"]))
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context "when the user enters an ambiguous substring of a command" do
|
306
|
+
it "raises an exception and displays a message that explains the ambiguity" do
|
307
|
+
shell = Thor::Base.shell.new
|
308
|
+
expect(shell).to receive(:error).with('Ambiguous command call matches [call_myself_with_wrong_arity, call_unexistent_method]')
|
309
|
+
MyScript.start(["call"], :shell => shell)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "raises an exception when there is an alias" do
|
313
|
+
shell = Thor::Base.shell.new
|
314
|
+
expect(shell).to receive(:error).with('Ambiguous command f matches [foo, fu]')
|
315
|
+
MyScript.start(["f"], :shell => shell)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "#help" do
|
322
|
+
def shell
|
323
|
+
@shell ||= Thor::Base.shell.new
|
324
|
+
end
|
325
|
+
|
326
|
+
describe "on general" do
|
327
|
+
before do
|
328
|
+
@content = capture(:stdout) { MyScript.help(shell) }
|
329
|
+
end
|
330
|
+
|
331
|
+
it "provides useful help info for the help method itself" do
|
332
|
+
expect(@content).to match(/help \[COMMAND\]\s+# Describe available commands/)
|
333
|
+
end
|
334
|
+
|
335
|
+
it "provides useful help info for a method with params" do
|
336
|
+
expect(@content).to match(/animal TYPE\s+# horse around/)
|
337
|
+
end
|
338
|
+
|
339
|
+
it "uses the maximum terminal size to show commands" do
|
340
|
+
expect(@shell).to receive(:terminal_width).and_return(80)
|
341
|
+
content = capture(:stdout) { MyScript.help(shell) }
|
342
|
+
expect(content).to match(/aaa\.\.\.$/)
|
343
|
+
end
|
344
|
+
|
345
|
+
it "provides description for commands from classes in the same namespace" do
|
346
|
+
expect(@content).to match(/baz\s+# do some bazing/)
|
347
|
+
end
|
348
|
+
|
349
|
+
it "shows superclass commands" do
|
350
|
+
content = capture(:stdout) { MyChildScript.help(shell) }
|
351
|
+
expect(content).to match(/foo BAR \s+# do some fooing/)
|
352
|
+
end
|
353
|
+
|
354
|
+
it "shows class options information" do
|
355
|
+
content = capture(:stdout) { MyChildScript.help(shell) }
|
356
|
+
expect(content).to match(/Options\:/)
|
357
|
+
expect(content).to match(/\[\-\-param=N\]/)
|
358
|
+
end
|
359
|
+
|
360
|
+
it "injects class arguments into default usage" do
|
361
|
+
content = capture(:stdout) { Scripts::MyScript.help(shell) }
|
362
|
+
expect(content).to match(/zoo ACCESSOR \-\-param\=PARAM/)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
describe "for a specific command" do
|
367
|
+
it "provides full help info when talking about a specific command" do
|
368
|
+
expect(capture(:stdout) { MyScript.command_help(shell, "foo") }).to eq(<<-END)
|
369
|
+
Usage:
|
370
|
+
thor my_script:foo BAR
|
371
|
+
|
372
|
+
Options:
|
373
|
+
[--force] # Force to do some fooing
|
374
|
+
|
375
|
+
do some fooing
|
376
|
+
This is more info!
|
377
|
+
Everyone likes more info!
|
378
|
+
END
|
379
|
+
end
|
380
|
+
|
381
|
+
it "raises an error if the command can't be found" do
|
382
|
+
expect {
|
383
|
+
MyScript.command_help(shell, "unknown")
|
384
|
+
}.to raise_error(Thor::UndefinedCommandError, 'Could not find command "unknown" in "my_script" namespace.')
|
385
|
+
end
|
386
|
+
|
387
|
+
it "normalizes names before claiming they don't exist" do
|
388
|
+
expect(capture(:stdout) { MyScript.command_help(shell, "name-with-dashes") }).to match(/thor my_script:name-with-dashes/)
|
389
|
+
end
|
390
|
+
|
391
|
+
it "uses the long description if it exists" do
|
392
|
+
expect(capture(:stdout) { MyScript.command_help(shell, "long_description") }).to eq(<<-HELP)
|
393
|
+
Usage:
|
394
|
+
thor my_script:long_description
|
395
|
+
|
396
|
+
Description:
|
397
|
+
This is a really really really long description. Here you go. So very long.
|
398
|
+
|
399
|
+
It even has two paragraphs.
|
400
|
+
HELP
|
401
|
+
end
|
402
|
+
|
403
|
+
it "doesn't assign the long description to the next command without one" do
|
404
|
+
expect(capture(:stdout) {
|
405
|
+
MyScript.command_help(shell, "name_with_dashes")
|
406
|
+
}).not_to match(/so very long/i)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
describe "instance method" do
|
411
|
+
it "calls the class method" do
|
412
|
+
expect(capture(:stdout) { MyScript.start(["help"]) }).to match(/Commands:/)
|
413
|
+
end
|
414
|
+
|
415
|
+
it "calls the class method" do
|
416
|
+
expect(capture(:stdout) { MyScript.start(["help", "foo"]) }).to match(/Usage:/)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
describe "when creating commands" do
|
422
|
+
it "prints a warning if a public method is created without description or usage" do
|
423
|
+
expect(capture(:stdout) {
|
424
|
+
klass = Class.new(Thor)
|
425
|
+
klass.class_eval "def hello_from_thor; end"
|
426
|
+
}).to match(/\[WARNING\] Attempted to create command "hello_from_thor" without usage or description/)
|
427
|
+
end
|
428
|
+
|
429
|
+
it "does not print if overwriting a previous command" do
|
430
|
+
expect(capture(:stdout) {
|
431
|
+
klass = Class.new(Thor)
|
432
|
+
klass.class_eval "def help; end"
|
433
|
+
}).to be_empty
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe "edge-cases" do
|
438
|
+
it "can handle boolean options followed by arguments" do
|
439
|
+
klass = Class.new(Thor) do
|
440
|
+
method_option :loud, :type => :boolean
|
441
|
+
desc "hi NAME", "say hi to name"
|
442
|
+
def hi(name)
|
443
|
+
name.upcase! if options[:loud]
|
444
|
+
"Hi #{name}"
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
expect(klass.start(["hi", "jose"])).to eq("Hi jose")
|
449
|
+
expect(klass.start(["hi", "jose", "--loud"])).to eq("Hi JOSE")
|
450
|
+
expect(klass.start(["hi", "--loud", "jose"])).to eq("Hi JOSE")
|
451
|
+
end
|
452
|
+
|
453
|
+
it "passes through unknown options" do
|
454
|
+
klass = Class.new(Thor) do
|
455
|
+
desc "unknown", "passing unknown options"
|
456
|
+
def unknown(*args)
|
457
|
+
args
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
expect(klass.start(["unknown", "foo", "--bar", "baz", "bat", "--bam"])).to eq(["foo", "--bar", "baz", "bat", "--bam"])
|
462
|
+
expect(klass.start(["unknown", "--bar", "baz"])).to eq(["--bar", "baz"])
|
463
|
+
end
|
464
|
+
|
465
|
+
it "does not pass through unknown options with strict args" do
|
466
|
+
klass = Class.new(Thor) do
|
467
|
+
strict_args_position!
|
468
|
+
|
469
|
+
desc "unknown", "passing unknown options"
|
470
|
+
def unknown(*args)
|
471
|
+
args
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
expect(klass.start(["unknown", "--bar", "baz"])).to eq([])
|
476
|
+
expect(klass.start(["unknown", "foo", "--bar", "baz"])).to eq(["foo"])
|
477
|
+
end
|
478
|
+
|
479
|
+
it "strict args works in the inheritance chain" do
|
480
|
+
parent = Class.new(Thor) do
|
481
|
+
strict_args_position!
|
482
|
+
end
|
483
|
+
|
484
|
+
klass = Class.new(parent) do
|
485
|
+
desc "unknown", "passing unknown options"
|
486
|
+
def unknown(*args)
|
487
|
+
args
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
expect(klass.start(["unknown", "--bar", "baz"])).to eq([])
|
492
|
+
expect(klass.start(["unknown", "foo", "--bar", "baz"])).to eq(["foo"])
|
493
|
+
end
|
494
|
+
|
495
|
+
it "send as a command name" do
|
496
|
+
expect(MyScript.start(["send"])).to eq(true)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|