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,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
@@ -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