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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/CHANGELOG.md +139 -0
  4. data/LICENSE.md +20 -0
  5. data/README.md +35 -0
  6. data/Thorfile +30 -0
  7. data/bin/thor_dleavitt +6 -0
  8. data/lib/thor/actions.rb +318 -0
  9. data/lib/thor/actions/create_file.rb +105 -0
  10. data/lib/thor/actions/create_link.rb +60 -0
  11. data/lib/thor/actions/directory.rb +119 -0
  12. data/lib/thor/actions/empty_directory.rb +137 -0
  13. data/lib/thor/actions/file_manipulation.rb +317 -0
  14. data/lib/thor/actions/inject_into_file.rb +109 -0
  15. data/lib/thor/base.rb +654 -0
  16. data/lib/thor/command.rb +136 -0
  17. data/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  18. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  19. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  20. data/lib/thor/error.rb +32 -0
  21. data/lib/thor/group.rb +282 -0
  22. data/lib/thor/invocation.rb +172 -0
  23. data/lib/thor/parser.rb +4 -0
  24. data/lib/thor/parser/argument.rb +74 -0
  25. data/lib/thor/parser/arguments.rb +171 -0
  26. data/lib/thor/parser/option.rb +121 -0
  27. data/lib/thor/parser/options.rb +218 -0
  28. data/lib/thor/rake_compat.rb +72 -0
  29. data/lib/thor/runner.rb +322 -0
  30. data/lib/thor/shell.rb +88 -0
  31. data/lib/thor/shell/basic.rb +422 -0
  32. data/lib/thor/shell/color.rb +148 -0
  33. data/lib/thor/shell/html.rb +127 -0
  34. data/lib/thor/util.rb +270 -0
  35. data/lib/thor/version.rb +3 -0
  36. data/lib/thor_dleavitt.rb +473 -0
  37. data/spec/actions/create_file_spec.rb +170 -0
  38. data/spec/actions/create_link_spec.rb +95 -0
  39. data/spec/actions/directory_spec.rb +169 -0
  40. data/spec/actions/empty_directory_spec.rb +129 -0
  41. data/spec/actions/file_manipulation_spec.rb +382 -0
  42. data/spec/actions/inject_into_file_spec.rb +135 -0
  43. data/spec/actions_spec.rb +331 -0
  44. data/spec/base_spec.rb +291 -0
  45. data/spec/command_spec.rb +80 -0
  46. data/spec/core_ext/hash_with_indifferent_access_spec.rb +48 -0
  47. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  48. data/spec/exit_condition_spec.rb +19 -0
  49. data/spec/fixtures/application.rb +2 -0
  50. data/spec/fixtures/app{1}/README +3 -0
  51. data/spec/fixtures/bundle/execute.rb +6 -0
  52. data/spec/fixtures/bundle/main.thor +1 -0
  53. data/spec/fixtures/command.thor +10 -0
  54. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  55. data/spec/fixtures/doc/COMMENTER +11 -0
  56. data/spec/fixtures/doc/README +3 -0
  57. data/spec/fixtures/doc/block_helper.rb +3 -0
  58. data/spec/fixtures/doc/config.rb +1 -0
  59. data/spec/fixtures/doc/config.yaml.tt +1 -0
  60. data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
  61. data/spec/fixtures/enum.thor +10 -0
  62. data/spec/fixtures/group.thor +128 -0
  63. data/spec/fixtures/invoke.thor +118 -0
  64. data/spec/fixtures/path with spaces b/data/spec/fixtures/path with → spaces +0 -0
  65. data/spec/fixtures/preserve/script.sh +3 -0
  66. data/spec/fixtures/script.thor +220 -0
  67. data/spec/fixtures/subcommand.thor +17 -0
  68. data/spec/group_spec.rb +222 -0
  69. data/spec/helper.rb +67 -0
  70. data/spec/invocation_spec.rb +108 -0
  71. data/spec/parser/argument_spec.rb +53 -0
  72. data/spec/parser/arguments_spec.rb +66 -0
  73. data/spec/parser/option_spec.rb +202 -0
  74. data/spec/parser/options_spec.rb +400 -0
  75. data/spec/rake_compat_spec.rb +72 -0
  76. data/spec/register_spec.rb +197 -0
  77. data/spec/runner_spec.rb +241 -0
  78. data/spec/shell/basic_spec.rb +330 -0
  79. data/spec/shell/color_spec.rb +95 -0
  80. data/spec/shell/html_spec.rb +31 -0
  81. data/spec/shell_spec.rb +47 -0
  82. data/spec/subcommand_spec.rb +30 -0
  83. data/spec/thor_spec.rb +499 -0
  84. data/spec/util_spec.rb +196 -0
  85. data/thor.gemspec +24 -0
  86. 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
@@ -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