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,197 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class BoringVendorProvidedCLI < Thor
|
4
|
+
desc "boring", "do boring stuff"
|
5
|
+
def boring
|
6
|
+
puts "bored. <yawn>"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ExcitingPluginCLI < Thor
|
11
|
+
desc "hooray", "say hooray!"
|
12
|
+
def hooray
|
13
|
+
puts "hooray!"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "fireworks", "exciting fireworks!"
|
17
|
+
def fireworks
|
18
|
+
puts "kaboom!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class SuperSecretPlugin < Thor
|
23
|
+
default_command :squirrel
|
24
|
+
|
25
|
+
desc "squirrel", "All of secret squirrel's secrets"
|
26
|
+
def squirrel
|
27
|
+
puts "I love nuts"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class GroupPlugin < Thor::Group
|
32
|
+
desc "part one"
|
33
|
+
def part_one
|
34
|
+
puts "part one"
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "part two"
|
38
|
+
def part_two
|
39
|
+
puts "part two"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ClassOptionGroupPlugin < Thor::Group
|
44
|
+
class_option :who,
|
45
|
+
:type => :string,
|
46
|
+
:aliases => "-w",
|
47
|
+
:default => "zebra"
|
48
|
+
end
|
49
|
+
|
50
|
+
class CompatibleWith19Plugin < ClassOptionGroupPlugin
|
51
|
+
desc "animal"
|
52
|
+
def animal
|
53
|
+
p options[:who]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class PluginWithDefault < Thor
|
58
|
+
desc "say MSG", "print MSG"
|
59
|
+
def say(msg)
|
60
|
+
puts msg
|
61
|
+
end
|
62
|
+
|
63
|
+
default_command :say
|
64
|
+
end
|
65
|
+
|
66
|
+
class PluginWithDefaultMultipleArguments < Thor
|
67
|
+
desc "say MSG [MSG]", "print multiple messages"
|
68
|
+
def say(*args)
|
69
|
+
puts args
|
70
|
+
end
|
71
|
+
|
72
|
+
default_command :say
|
73
|
+
end
|
74
|
+
|
75
|
+
class PluginWithDefaultcommandAndDeclaredArgument < Thor
|
76
|
+
desc "say MSG [MSG]", "print multiple messages"
|
77
|
+
argument :msg
|
78
|
+
def say
|
79
|
+
puts msg
|
80
|
+
end
|
81
|
+
|
82
|
+
default_command :say
|
83
|
+
end
|
84
|
+
|
85
|
+
BoringVendorProvidedCLI.register(
|
86
|
+
ExcitingPluginCLI,
|
87
|
+
"exciting",
|
88
|
+
"do exciting things",
|
89
|
+
"Various non-boring actions")
|
90
|
+
|
91
|
+
BoringVendorProvidedCLI.register(
|
92
|
+
SuperSecretPlugin,
|
93
|
+
"secret",
|
94
|
+
"secret stuff",
|
95
|
+
"Nothing to see here. Move along.",
|
96
|
+
:hide => true)
|
97
|
+
|
98
|
+
BoringVendorProvidedCLI.register(
|
99
|
+
GroupPlugin,
|
100
|
+
'groupwork',
|
101
|
+
"Do a bunch of things in a row",
|
102
|
+
"purple monkey dishwasher")
|
103
|
+
|
104
|
+
BoringVendorProvidedCLI.register(
|
105
|
+
CompatibleWith19Plugin,
|
106
|
+
'zoo',
|
107
|
+
"zoo [-w animal]",
|
108
|
+
"Shows a provided animal or just zebra")
|
109
|
+
|
110
|
+
BoringVendorProvidedCLI.register(
|
111
|
+
PluginWithDefault,
|
112
|
+
'say',
|
113
|
+
'say message',
|
114
|
+
'subcommands ftw')
|
115
|
+
|
116
|
+
BoringVendorProvidedCLI.register(
|
117
|
+
PluginWithDefaultMultipleArguments,
|
118
|
+
'say_multiple',
|
119
|
+
'say message',
|
120
|
+
'subcommands ftw')
|
121
|
+
|
122
|
+
BoringVendorProvidedCLI.register(
|
123
|
+
PluginWithDefaultcommandAndDeclaredArgument,
|
124
|
+
'say_argument',
|
125
|
+
'say message',
|
126
|
+
'subcommands ftw')
|
127
|
+
|
128
|
+
describe ".register-ing a Thor subclass" do
|
129
|
+
it "registers the plugin as a subcommand" do
|
130
|
+
fireworks_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[exciting fireworks]) }
|
131
|
+
expect(fireworks_output).to eq("kaboom!\n")
|
132
|
+
end
|
133
|
+
|
134
|
+
it "includes the plugin's usage in the help" do
|
135
|
+
help_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[help]) }
|
136
|
+
expect(help_output).to include('do exciting things')
|
137
|
+
end
|
138
|
+
|
139
|
+
it "invokes the default command correctly" do
|
140
|
+
output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say hello]) }
|
141
|
+
expect(output).to include("hello")
|
142
|
+
end
|
143
|
+
|
144
|
+
it "invokes the default command correctly with multiple args" do
|
145
|
+
output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say_multiple hello adam]) }
|
146
|
+
expect(output).to include("hello")
|
147
|
+
expect(output).to include("adam")
|
148
|
+
end
|
149
|
+
|
150
|
+
it "invokes the default command correctly with a declared argument" do
|
151
|
+
output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[say_argument hello]) }
|
152
|
+
expect(output).to include("hello")
|
153
|
+
end
|
154
|
+
|
155
|
+
context "when $thor_runner is false" do
|
156
|
+
it "includes the plugin's subcommand name in subcommand's help" do
|
157
|
+
begin
|
158
|
+
$thor_runner = false
|
159
|
+
help_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[exciting]) }
|
160
|
+
expect(help_output).to include('thor exciting_plugin_c_l_i fireworks')
|
161
|
+
ensure
|
162
|
+
$thor_runner = true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "when hidden" do
|
168
|
+
it "omits the hidden plugin's usage from the help" do
|
169
|
+
help_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[help]) }
|
170
|
+
expect(help_output).not_to include('secret stuff')
|
171
|
+
end
|
172
|
+
|
173
|
+
it "registers the plugin as a subcommand" do
|
174
|
+
secret_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[secret squirrel]) }
|
175
|
+
expect(secret_output).to eq("I love nuts\n")
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe ".register-ing a Thor::Group subclass" do
|
181
|
+
it "registers the group as a single command" do
|
182
|
+
group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[groupwork]) }
|
183
|
+
expect(group_output).to eq("part one\npart two\n")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "1.8 and 1.9 syntax compatibility" do
|
188
|
+
it "is compatible with both 1.8 and 1.9 syntax w/o command options" do
|
189
|
+
group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[zoo]) }
|
190
|
+
expect(group_output).to match(/zebra/)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "is compatible with both 1.8 and 1.9 syntax w/command options" do
|
194
|
+
group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w[zoo -w lion]) }
|
195
|
+
expect(group_output).to match(/lion/)
|
196
|
+
end
|
197
|
+
end
|
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'thor/runner'
|
3
|
+
|
4
|
+
describe Thor::Runner do
|
5
|
+
def when_no_thorfiles_exist
|
6
|
+
old_dir = Dir.pwd
|
7
|
+
Dir.chdir '..'
|
8
|
+
delete = Thor::Base.subclasses.select {|e| e.namespace == 'default' }
|
9
|
+
delete.each {|e| Thor::Base.subclasses.delete e }
|
10
|
+
yield
|
11
|
+
Thor::Base.subclasses.concat delete
|
12
|
+
Dir.chdir old_dir
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#help" do
|
16
|
+
it "shows information about Thor::Runner itself" do
|
17
|
+
expect(capture(:stdout) { Thor::Runner.start(["help"]) }).to match(/List the available thor commands/)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "shows information about a specific Thor::Runner command" do
|
21
|
+
content = capture(:stdout) { Thor::Runner.start(["help", "list"]) }
|
22
|
+
expect(content).to match(/List the available thor commands/)
|
23
|
+
expect(content).not_to match(/help \[COMMAND\]/)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "shows information about a specific Thor class" do
|
27
|
+
content = capture(:stdout) { Thor::Runner.start(["help", "my_script"]) }
|
28
|
+
expect(content).to match(/zoo\s+# zoo around/m)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "shows information about a specific command from a specific Thor class" do
|
32
|
+
content = capture(:stdout) { Thor::Runner.start(["help", "my_script:zoo"]) }
|
33
|
+
expect(content).to match(/zoo around/)
|
34
|
+
expect(content).not_to match(/help \[COMMAND\]/)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "shows information about a specific Thor group class" do
|
38
|
+
content = capture(:stdout) { Thor::Runner.start(["help", "my_counter"]) }
|
39
|
+
expect(content).to match(/my_counter N/)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "raises error if a class/command cannot be found" do
|
43
|
+
content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) }
|
44
|
+
expect(content.strip).to eq('Could not find command "unknown" in "default" namespace.')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises error if a class/command cannot be found for a setup without thorfiles" do
|
48
|
+
when_no_thorfiles_exist do
|
49
|
+
expect(Thor::Runner).to receive :exit
|
50
|
+
content = capture(:stderr){ Thor::Runner.start(["help", "unknown"]) }
|
51
|
+
expect(content.strip).to eq('Could not find command "unknown".')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#start" do
|
57
|
+
it "invokes a command from Thor::Runner" do
|
58
|
+
ARGV.replace ["list"]
|
59
|
+
expect(capture(:stdout) { Thor::Runner.start }).to match(/my_counter N/)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "invokes a command from a specific Thor class" do
|
63
|
+
ARGV.replace ["my_script:zoo"]
|
64
|
+
expect(Thor::Runner.start).to be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "invokes the default command from a specific Thor class if none is specified" do
|
68
|
+
ARGV.replace ["my_script"]
|
69
|
+
expect(Thor::Runner.start).to eq("default command")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "forwards arguments to the invoked command" do
|
73
|
+
ARGV.replace ["my_script:animal", "horse"]
|
74
|
+
expect(Thor::Runner.start).to eq(["horse"])
|
75
|
+
end
|
76
|
+
|
77
|
+
it "invokes commands through shortcuts" do
|
78
|
+
ARGV.replace ["my_script", "-T", "horse"]
|
79
|
+
expect(Thor::Runner.start).to eq(["horse"])
|
80
|
+
end
|
81
|
+
|
82
|
+
it "invokes a Thor::Group" do
|
83
|
+
ARGV.replace ["my_counter", "1", "2", "--third", "3"]
|
84
|
+
expect(Thor::Runner.start).to eq([1, 2, 3, nil, nil, nil])
|
85
|
+
end
|
86
|
+
|
87
|
+
it "raises an error if class/command can't be found" do
|
88
|
+
ARGV.replace ["unknown"]
|
89
|
+
content = capture(:stderr){ Thor::Runner.start }
|
90
|
+
expect(content.strip).to eq('Could not find command "unknown" in "default" namespace.')
|
91
|
+
end
|
92
|
+
|
93
|
+
it "raises an error if class/command can't be found in a setup without thorfiles" do
|
94
|
+
when_no_thorfiles_exist do
|
95
|
+
ARGV.replace ["unknown"]
|
96
|
+
expect(Thor::Runner).to receive :exit
|
97
|
+
content = capture(:stderr){ Thor::Runner.start }
|
98
|
+
expect(content.strip).to eq('Could not find command "unknown".')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it "does not swallow NoMethodErrors that occur inside the called method" do
|
103
|
+
ARGV.replace ["my_script:call_unexistent_method"]
|
104
|
+
expect{ Thor::Runner.start }.to raise_error(NoMethodError)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "does not swallow Thor::Group InvocationError" do
|
108
|
+
ARGV.replace ["whiny_generator"]
|
109
|
+
expect{ Thor::Runner.start }.to raise_error(ArgumentError, /thor wrong_arity takes 1 argument, but it should not/)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "does not swallow Thor InvocationError" do
|
113
|
+
ARGV.replace ["my_script:animal"]
|
114
|
+
content = capture(:stderr) { Thor::Runner.start }
|
115
|
+
expect(content.strip).to eq(%Q'ERROR: "thor animal" was called with no arguments\nUsage: "thor my_script:animal TYPE"')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "commands" do
|
120
|
+
before do
|
121
|
+
@location = "#{File.dirname(__FILE__)}/fixtures/command.thor"
|
122
|
+
@original_yaml = {
|
123
|
+
"random" => {
|
124
|
+
:location => @location,
|
125
|
+
:filename => "4a33b894ffce85d7b412fc1b36f88fe0",
|
126
|
+
:namespaces => ["amazing"]
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
root_file = File.join(Thor::Util.thor_root, "thor.yml")
|
131
|
+
|
132
|
+
# Stub load and save to avoid thor.yaml from being overwritten
|
133
|
+
allow(YAML).to receive(:load_file).and_return(@original_yaml)
|
134
|
+
allow(File).to receive(:exists?).with(root_file).and_return(true)
|
135
|
+
allow(File).to receive(:open).with(root_file, "w")
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "list" do
|
139
|
+
it "gives a list of the available commands" do
|
140
|
+
ARGV.replace ["list"]
|
141
|
+
content = capture(:stdout) { Thor::Runner.start }
|
142
|
+
expect(content).to match(/amazing:describe NAME\s+# say that someone is amazing/m)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "gives a list of the available Thor::Group classes" do
|
146
|
+
ARGV.replace ["list"]
|
147
|
+
expect(capture(:stdout) { Thor::Runner.start }).to match(/my_counter N/)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "can filter a list of the available commands by --group" do
|
151
|
+
ARGV.replace ["list", "--group", "standard"]
|
152
|
+
expect(capture(:stdout) { Thor::Runner.start }).to match(/amazing:describe NAME/)
|
153
|
+
ARGV.replace []
|
154
|
+
expect(capture(:stdout) { Thor::Runner.start }).not_to match(/my_script:animal TYPE/)
|
155
|
+
ARGV.replace ["list", "--group", "script"]
|
156
|
+
expect(capture(:stdout) { Thor::Runner.start }).to match(/my_script:animal TYPE/)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "can skip all filters to show all commands using --all" do
|
160
|
+
ARGV.replace ["list", "--all"]
|
161
|
+
content = capture(:stdout) { Thor::Runner.start }
|
162
|
+
expect(content).to match(/amazing:describe NAME/)
|
163
|
+
expect(content).to match(/my_script:animal TYPE/)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "doesn't list superclass commands in the subclass" do
|
167
|
+
ARGV.replace ["list"]
|
168
|
+
expect(capture(:stdout) { Thor::Runner.start }).not_to match(/amazing:help/)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "presents commands in the default namespace with an empty namespace" do
|
172
|
+
ARGV.replace ["list"]
|
173
|
+
expect(capture(:stdout) { Thor::Runner.start }).to match(/^thor :cow\s+# prints 'moo'/m)
|
174
|
+
end
|
175
|
+
|
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
|
+
end
|
180
|
+
|
181
|
+
it "runs groups even when there is a command with the same name" do
|
182
|
+
ARGV.replace ["command_conflict"]
|
183
|
+
expect(capture(:stdout) { Thor::Runner.start }).to eq("group\n")
|
184
|
+
end
|
185
|
+
|
186
|
+
it "runs commands with no colon in the default namespace" do
|
187
|
+
ARGV.replace ["cow"]
|
188
|
+
expect(capture(:stdout) { Thor::Runner.start }).to eq("moo\n")
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "uninstall" do
|
193
|
+
before do
|
194
|
+
path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename])
|
195
|
+
expect(FileUtils).to receive(:rm_rf).with(path)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "uninstalls existing thor modules" do
|
199
|
+
silence(:stdout) { Thor::Runner.start(["uninstall", "random"]) }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "installed" do
|
204
|
+
before do
|
205
|
+
expect(Dir).to receive(:[]).and_return([])
|
206
|
+
end
|
207
|
+
|
208
|
+
it "displays the modules installed in a pretty way" do
|
209
|
+
stdout = capture(:stdout) { Thor::Runner.start(["installed"]) }
|
210
|
+
expect(stdout).to match(/random\s*amazing/)
|
211
|
+
expect(stdout).to match(/amazing:describe NAME\s+# say that someone is amazing/m)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "install/update" do
|
216
|
+
before do
|
217
|
+
allow(FileUtils).to receive(:mkdir_p)
|
218
|
+
allow(FileUtils).to receive(:touch)
|
219
|
+
allow($stdin).to receive(:gets).and_return("Y")
|
220
|
+
|
221
|
+
path = File.join(Thor::Util.thor_root, Digest::MD5.hexdigest(@location + "random"))
|
222
|
+
expect(File).to receive(:open).with(path, "w")
|
223
|
+
end
|
224
|
+
|
225
|
+
it "updates existing thor files" do
|
226
|
+
path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename])
|
227
|
+
if File.directory? path
|
228
|
+
expect(FileUtils).to receive(:rm_rf).with(path)
|
229
|
+
else
|
230
|
+
expect(File).to receive(:delete).with(path)
|
231
|
+
end
|
232
|
+
silence(:stdout) { Thor::Runner.start(["update", "random"]) }
|
233
|
+
end
|
234
|
+
|
235
|
+
it "installs thor files" do
|
236
|
+
ARGV.replace ["install", @location]
|
237
|
+
silence(:stdout) { Thor::Runner.start }
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,330 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
describe Thor::Shell::Basic do
|
5
|
+
def shell
|
6
|
+
@shell ||= Thor::Shell::Basic.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#padding" do
|
10
|
+
it "cannot be set to below zero" do
|
11
|
+
shell.padding = 10
|
12
|
+
expect(shell.padding).to eq(10)
|
13
|
+
|
14
|
+
shell.padding = -1
|
15
|
+
expect(shell.padding).to eq(0)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#ask" do
|
20
|
+
it "prints a message to the user and gets the response" do
|
21
|
+
expect($stdout).to receive(:print).with("Should I overwrite it? ")
|
22
|
+
expect($stdin).to receive(:gets).and_return('Sure')
|
23
|
+
expect($stdin).to_not receive(:noecho)
|
24
|
+
expect(shell.ask("Should I overwrite it?")).to eq("Sure")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "prints a message and returns nil if EOF is sent to stdin" do
|
28
|
+
expect($stdout).to receive(:print).with(" ")
|
29
|
+
expect($stdin).to receive(:gets).and_return(nil)
|
30
|
+
expect(shell.ask("")).to eq(nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "prints a message to the user and does not echo stdin if the echo option is set to false" do
|
34
|
+
expect($stdout).to receive(:print).with('What\'s your password? ')
|
35
|
+
expect($stdin).to receive(:noecho).and_return('mysecretpass')
|
36
|
+
expect(shell.ask("What's your password?", :echo => false)).to eq("mysecretpass")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "prints a message to the user with the available options and determines the correctness of the answer" do
|
40
|
+
expect($stdout).to receive(:print).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] ')
|
41
|
+
expect($stdin).to receive(:gets).and_return('chocolate')
|
42
|
+
expect(shell.ask("What's your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])).to eq("chocolate")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "prints a message to the user with the available options and reasks the question after an incorrect repsonse" do
|
46
|
+
expect($stdout).to receive(:print).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] ').twice
|
47
|
+
expect($stdout).to receive(:print).with("Your response must be one of: [strawberry, chocolate, vanilla]. Please try again.\n")
|
48
|
+
expect($stdin).to receive(:gets).and_return('moose tracks', 'chocolate')
|
49
|
+
expect(shell.ask("What's your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])).to eq("chocolate")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "prints a message to the user containing a default and sets the default if only enter is pressed" do
|
53
|
+
expect($stdout).to receive(:print).with('What\'s your favorite Neopolitan flavor? (vanilla) ')
|
54
|
+
expect($stdin).to receive(:gets).and_return('')
|
55
|
+
expect(shell.ask("What's your favorite Neopolitan flavor?", :default => "vanilla")).to eq("vanilla")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "prints a message to the user with the available options and reasks the question after an incorrect repsonse and then returns the default" do
|
59
|
+
expect($stdout).to receive(:print).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] (vanilla) ').twice
|
60
|
+
expect($stdout).to receive(:print).with("Your response must be one of: [strawberry, chocolate, vanilla]. Please try again.\n")
|
61
|
+
expect($stdin).to receive(:gets).and_return('moose tracks', '')
|
62
|
+
expect(shell.ask("What's your favorite Neopolitan flavor?", :default => "vanilla", :limited_to => ["strawberry", "chocolate", "vanilla"])).to eq("vanilla")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#yes?" do
|
67
|
+
it "asks the user and returns true if the user replies yes" do
|
68
|
+
expect($stdout).to receive(:print).with("Should I overwrite it? ")
|
69
|
+
expect($stdin).to receive(:gets).and_return('y')
|
70
|
+
expect(shell.yes?("Should I overwrite it?")).to be_true
|
71
|
+
|
72
|
+
expect($stdout).to receive(:print).with("Should I overwrite it? ")
|
73
|
+
expect($stdin).to receive(:gets).and_return('n')
|
74
|
+
expect(shell.yes?("Should I overwrite it?")).not_to be_true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#no?" do
|
79
|
+
it "asks the user and returns true if the user replies no" do
|
80
|
+
expect($stdout).to receive(:print).with("Should I overwrite it? ")
|
81
|
+
expect($stdin).to receive(:gets).and_return('n')
|
82
|
+
expect(shell.no?("Should I overwrite it?")).to be_true
|
83
|
+
|
84
|
+
expect($stdout).to receive(:print).with("Should I overwrite it? ")
|
85
|
+
expect($stdin).to receive(:gets).and_return('Yes')
|
86
|
+
expect(shell.no?("Should I overwrite it?")).to be_false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#say" do
|
91
|
+
it "prints a message to the user" do
|
92
|
+
expect($stdout).to receive(:print).with("Running...\n")
|
93
|
+
shell.say("Running...")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "prints a message to the user without new line if it ends with a whitespace" do
|
97
|
+
expect($stdout).to receive(:print).with("Running... ")
|
98
|
+
shell.say("Running... ")
|
99
|
+
end
|
100
|
+
|
101
|
+
it "does not use a new line with whitespace+newline embedded" do
|
102
|
+
expect($stdout).to receive(:print).with("It's \nRunning...\n")
|
103
|
+
shell.say("It's \nRunning...")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "prints a message to the user without new line" do
|
107
|
+
expect($stdout).to receive(:print).with("Running...")
|
108
|
+
shell.say("Running...", nil, false)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#say_status" do
|
113
|
+
it "prints a message to the user with status" do
|
114
|
+
expect($stdout).to receive(:print).with(" create ~/.thor/command.thor\n")
|
115
|
+
shell.say_status(:create, "~/.thor/command.thor")
|
116
|
+
end
|
117
|
+
|
118
|
+
it "always uses new line" do
|
119
|
+
expect($stdout).to receive(:print).with(" create \n")
|
120
|
+
shell.say_status(:create, "")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "does not print a message if base is muted" do
|
124
|
+
expect(shell).to receive(:mute?).and_return(true)
|
125
|
+
expect($stdout).not_to receive(:print)
|
126
|
+
|
127
|
+
shell.mute do
|
128
|
+
shell.say_status(:created, "~/.thor/command.thor")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "does not print a message if base is set to quiet" do
|
133
|
+
base = MyCounter.new [1,2]
|
134
|
+
expect(base).to receive(:options).and_return(:quiet => true)
|
135
|
+
|
136
|
+
expect($stdout).not_to receive(:print)
|
137
|
+
shell.base = base
|
138
|
+
shell.say_status(:created, "~/.thor/command.thor")
|
139
|
+
end
|
140
|
+
|
141
|
+
it "does not print a message if log status is set to false" do
|
142
|
+
expect($stdout).not_to receive(:print)
|
143
|
+
shell.say_status(:created, "~/.thor/command.thor", false)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "uses padding to set message's left margin" do
|
147
|
+
shell.padding = 2
|
148
|
+
expect($stdout).to receive(:print).with(" create ~/.thor/command.thor\n")
|
149
|
+
shell.say_status(:create, "~/.thor/command.thor")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#print_in_columns" do
|
154
|
+
before do
|
155
|
+
@array = [1234567890]
|
156
|
+
@array += ('a'..'e').to_a
|
157
|
+
end
|
158
|
+
|
159
|
+
it "prints in columns" do
|
160
|
+
content = capture(:stdout) { shell.print_in_columns(@array) }
|
161
|
+
expect(content.rstrip).to eq("1234567890 a b c d e")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#print_table" do
|
166
|
+
before do
|
167
|
+
@table = []
|
168
|
+
@table << ["abc", "#123", "first three"]
|
169
|
+
@table << ["", "#0", "empty"]
|
170
|
+
@table << ["xyz", "#786", "last three"]
|
171
|
+
end
|
172
|
+
|
173
|
+
it "prints a table" do
|
174
|
+
content = capture(:stdout) { shell.print_table(@table) }
|
175
|
+
expect(content).to eq(<<-TABLE)
|
176
|
+
abc #123 first three
|
177
|
+
#0 empty
|
178
|
+
xyz #786 last three
|
179
|
+
TABLE
|
180
|
+
end
|
181
|
+
|
182
|
+
it "prints a table with indentation" do
|
183
|
+
content = capture(:stdout) { shell.print_table(@table, :indent => 2) }
|
184
|
+
expect(content).to eq(<<-TABLE)
|
185
|
+
abc #123 first three
|
186
|
+
#0 empty
|
187
|
+
xyz #786 last three
|
188
|
+
TABLE
|
189
|
+
end
|
190
|
+
|
191
|
+
it "uses maximum terminal width" do
|
192
|
+
@table << ["def", "#456", "Lançam foo bar"]
|
193
|
+
@table << ["ghi", "#789", "بالله عليكم"]
|
194
|
+
expect(shell).to receive(:terminal_width).and_return(20)
|
195
|
+
content = capture(:stdout) { shell.print_table(@table, :indent => 2, :truncate => true) }
|
196
|
+
expect(content).to eq(<<-TABLE)
|
197
|
+
abc #123 firs...
|
198
|
+
#0 empty
|
199
|
+
xyz #786 last...
|
200
|
+
def #456 Lanç...
|
201
|
+
ghi #789 بالل...
|
202
|
+
TABLE
|
203
|
+
end
|
204
|
+
|
205
|
+
it "honors the colwidth option" do
|
206
|
+
content = capture(:stdout) { shell.print_table(@table, :colwidth => 10)}
|
207
|
+
expect(content).to eq(<<-TABLE)
|
208
|
+
abc #123 first three
|
209
|
+
#0 empty
|
210
|
+
xyz #786 last three
|
211
|
+
TABLE
|
212
|
+
end
|
213
|
+
|
214
|
+
it "prints tables with implicit columns" do
|
215
|
+
2.times { @table.first.pop }
|
216
|
+
content = capture(:stdout) { shell.print_table(@table) }
|
217
|
+
expect(content).to eq(<<-TABLE)
|
218
|
+
abc
|
219
|
+
#0 empty
|
220
|
+
xyz #786 last three
|
221
|
+
TABLE
|
222
|
+
end
|
223
|
+
|
224
|
+
it "prints a table with small numbers and right-aligns them" do
|
225
|
+
table = [
|
226
|
+
["Name", "Number", "Color"],
|
227
|
+
["Erik", 1, "green"]
|
228
|
+
]
|
229
|
+
content = capture(:stdout) { shell.print_table(table) }
|
230
|
+
expect(content).to eq(<<-TABLE)
|
231
|
+
Name Number Color
|
232
|
+
Erik 1 green
|
233
|
+
TABLE
|
234
|
+
end
|
235
|
+
|
236
|
+
it "doesn't output extra spaces for right-aligned columns in the last column" do
|
237
|
+
table = [
|
238
|
+
["Name", "Number"],
|
239
|
+
["Erik", 1]
|
240
|
+
]
|
241
|
+
content = capture(:stdout) { shell.print_table(table) }
|
242
|
+
expect(content).to eq(<<-TABLE)
|
243
|
+
Name Number
|
244
|
+
Erik 1
|
245
|
+
TABLE
|
246
|
+
end
|
247
|
+
|
248
|
+
it "prints a table with big numbers" do
|
249
|
+
table = [
|
250
|
+
["Name", "Number", "Color"],
|
251
|
+
["Erik", 1234567890123, "green"]
|
252
|
+
]
|
253
|
+
content = capture(:stdout) { shell.print_table(table) }
|
254
|
+
expect(content).to eq(<<-TABLE)
|
255
|
+
Name Number Color
|
256
|
+
Erik 1234567890123 green
|
257
|
+
TABLE
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "#file_collision" do
|
262
|
+
it "shows a menu with options" do
|
263
|
+
expect($stdout).to receive(:print).with('Overwrite foo? (enter "h" for help) [Ynaqh] ')
|
264
|
+
expect($stdin).to receive(:gets).and_return('n')
|
265
|
+
shell.file_collision('foo')
|
266
|
+
end
|
267
|
+
|
268
|
+
it "returns true if the user chooses default option" do
|
269
|
+
allow($stdout).to receive(:print)
|
270
|
+
expect($stdin).to receive(:gets).and_return('')
|
271
|
+
expect(shell.file_collision('foo')).to be_true
|
272
|
+
end
|
273
|
+
|
274
|
+
it "returns false if the user chooses no" do
|
275
|
+
allow($stdout).to receive(:print)
|
276
|
+
expect($stdin).to receive(:gets).and_return('n')
|
277
|
+
expect(shell.file_collision('foo')).to be_false
|
278
|
+
end
|
279
|
+
|
280
|
+
it "returns true if the user chooses yes" do
|
281
|
+
allow($stdout).to receive(:print)
|
282
|
+
expect($stdin).to receive(:gets).and_return('y')
|
283
|
+
expect(shell.file_collision('foo')).to be_true
|
284
|
+
end
|
285
|
+
|
286
|
+
it "shows help usage if the user chooses help" do
|
287
|
+
allow($stdout).to receive(:print)
|
288
|
+
expect($stdin).to receive(:gets).and_return('h')
|
289
|
+
expect($stdin).to receive(:gets).and_return('n')
|
290
|
+
help = capture(:stdout) { shell.file_collision('foo') }
|
291
|
+
expect(help).to match(/h \- help, show this help/)
|
292
|
+
end
|
293
|
+
|
294
|
+
it "quits if the user chooses quit" do
|
295
|
+
allow($stdout).to receive(:print)
|
296
|
+
expect($stdout).to receive(:print).with("Aborting...\n")
|
297
|
+
expect($stdin).to receive(:gets).and_return('q')
|
298
|
+
|
299
|
+
expect {
|
300
|
+
shell.file_collision('foo')
|
301
|
+
}.to raise_error(SystemExit)
|
302
|
+
end
|
303
|
+
|
304
|
+
it "always returns true if the user chooses always" do
|
305
|
+
expect($stdout).to receive(:print).with('Overwrite foo? (enter "h" for help) [Ynaqh] ')
|
306
|
+
expect($stdin).to receive(:gets).and_return('a')
|
307
|
+
|
308
|
+
expect(shell.file_collision('foo')).to be_true
|
309
|
+
|
310
|
+
expect($stdout).not_to receive(:print)
|
311
|
+
expect(shell.file_collision('foo')).to be_true
|
312
|
+
end
|
313
|
+
|
314
|
+
describe "when a block is given" do
|
315
|
+
it "displays diff options to the user" do
|
316
|
+
expect($stdout).to receive(:print).with('Overwrite foo? (enter "h" for help) [Ynaqdh] ')
|
317
|
+
expect($stdin).to receive(:gets).and_return('s')
|
318
|
+
shell.file_collision('foo'){ }
|
319
|
+
end
|
320
|
+
|
321
|
+
it "invokes the diff command" do
|
322
|
+
allow($stdout).to receive(:print)
|
323
|
+
expect($stdin).to receive(:gets).and_return('d')
|
324
|
+
expect($stdin).to receive(:gets).and_return('n')
|
325
|
+
expect(shell).to receive(:system).with(/diff -u/)
|
326
|
+
capture(:stdout) { shell.file_collision('foo'){ } }
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|