thor 0.9.9 → 0.11.5

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 (65) hide show
  1. data/CHANGELOG.rdoc +29 -4
  2. data/README.rdoc +234 -0
  3. data/Thorfile +57 -0
  4. data/VERSION +1 -0
  5. data/bin/rake2thor +4 -0
  6. data/bin/thor +1 -1
  7. data/lib/thor.rb +216 -119
  8. data/lib/thor/actions.rb +272 -0
  9. data/lib/thor/actions/create_file.rb +102 -0
  10. data/lib/thor/actions/directory.rb +87 -0
  11. data/lib/thor/actions/empty_directory.rb +133 -0
  12. data/lib/thor/actions/file_manipulation.rb +195 -0
  13. data/lib/thor/actions/inject_into_file.rb +78 -0
  14. data/lib/thor/base.rb +510 -0
  15. data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  16. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  17. data/lib/thor/error.rb +25 -1
  18. data/lib/thor/group.rb +263 -0
  19. data/lib/thor/invocation.rb +178 -0
  20. data/lib/thor/parser.rb +4 -0
  21. data/lib/thor/parser/argument.rb +67 -0
  22. data/lib/thor/parser/arguments.rb +145 -0
  23. data/lib/thor/parser/option.rb +132 -0
  24. data/lib/thor/parser/options.rb +142 -0
  25. data/lib/thor/rake_compat.rb +67 -0
  26. data/lib/thor/runner.rb +232 -242
  27. data/lib/thor/shell.rb +72 -0
  28. data/lib/thor/shell/basic.rb +220 -0
  29. data/lib/thor/shell/color.rb +108 -0
  30. data/lib/thor/task.rb +97 -60
  31. data/lib/thor/util.rb +230 -55
  32. data/spec/actions/create_file_spec.rb +170 -0
  33. data/spec/actions/directory_spec.rb +118 -0
  34. data/spec/actions/empty_directory_spec.rb +91 -0
  35. data/spec/actions/file_manipulation_spec.rb +242 -0
  36. data/spec/actions/inject_into_file_spec.rb +80 -0
  37. data/spec/actions_spec.rb +291 -0
  38. data/spec/base_spec.rb +236 -0
  39. data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  40. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  41. data/spec/fixtures/bundle/execute.rb +6 -0
  42. data/spec/fixtures/doc/config.rb +1 -0
  43. data/spec/group_spec.rb +177 -0
  44. data/spec/invocation_spec.rb +107 -0
  45. data/spec/parser/argument_spec.rb +47 -0
  46. data/spec/parser/arguments_spec.rb +64 -0
  47. data/spec/parser/option_spec.rb +212 -0
  48. data/spec/parser/options_spec.rb +255 -0
  49. data/spec/rake_compat_spec.rb +64 -0
  50. data/spec/runner_spec.rb +204 -0
  51. data/spec/shell/basic_spec.rb +206 -0
  52. data/spec/shell/color_spec.rb +41 -0
  53. data/spec/shell_spec.rb +25 -0
  54. data/spec/spec_helper.rb +52 -0
  55. data/spec/task_spec.rb +82 -0
  56. data/spec/thor_spec.rb +234 -0
  57. data/spec/util_spec.rb +196 -0
  58. metadata +69 -25
  59. data/README.markdown +0 -76
  60. data/Rakefile +0 -6
  61. data/lib/thor/options.rb +0 -242
  62. data/lib/thor/ordered_hash.rb +0 -64
  63. data/lib/thor/task_hash.rb +0 -22
  64. data/lib/thor/tasks.rb +0 -77
  65. data/lib/thor/tasks/package.rb +0 -18
@@ -0,0 +1,80 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'thor/actions'
3
+
4
+ describe Thor::Actions::InjectIntoFile do
5
+ before(:each) do
6
+ ::FileUtils.rm_rf(destination_root)
7
+ ::FileUtils.cp_r(source_root, destination_root)
8
+ end
9
+
10
+ def invoker(options={})
11
+ @invoker ||= MyCounter.new([1,2], options, { :destination_root => destination_root })
12
+ end
13
+
14
+ def revoker
15
+ @revoker ||= MyCounter.new([1,2], {}, { :destination_root => destination_root, :behavior => :revoke })
16
+ end
17
+
18
+ def invoke!(*args, &block)
19
+ capture(:stdout){ invoker.inject_into_file(*args, &block) }
20
+ end
21
+
22
+ def revoke!(*args, &block)
23
+ capture(:stdout){ revoker.inject_into_file(*args, &block) }
24
+ end
25
+
26
+ def file
27
+ File.join(destination_root, "doc/README")
28
+ end
29
+
30
+ describe "#invoke!" do
31
+ it "changes the file adding content after the flag" do
32
+ invoke! "doc/README", "\nmore content", :after => "__start__"
33
+ File.read(file).must == "__start__\nmore content\nREADME\n__end__\n"
34
+ end
35
+
36
+ it "changes the file adding content before the flag" do
37
+ invoke! "doc/README", "more content\n", :before => "__end__"
38
+ File.read(file).must == "__start__\nREADME\nmore content\n__end__\n"
39
+ end
40
+
41
+ it "accepts data as a block" do
42
+ invoke! "doc/README", :before => "__end__" do
43
+ "more content\n"
44
+ end
45
+
46
+ File.read(file).must == "__start__\nREADME\nmore content\n__end__\n"
47
+ end
48
+
49
+ it "logs status" do
50
+ invoke!("doc/README", "\nmore content", :after => "__start__").must == " inject doc/README\n"
51
+ end
52
+
53
+ it "does not change the file if pretending" do
54
+ invoker :pretend => true
55
+ invoke! "doc/README", "\nmore content", :after => "__start__"
56
+ File.read(file).must == "__start__\nREADME\n__end__\n"
57
+ end
58
+ end
59
+
60
+ describe "#revoke!" do
61
+ it "deinjects the destination file after injection" do
62
+ invoke! "doc/README", "\nmore content", :after => "__start__"
63
+ revoke! "doc/README", "\nmore content", :after => "__start__"
64
+
65
+ File.read(file).must == "__start__\nREADME\n__end__\n"
66
+ end
67
+
68
+ it "deinjects the destination file before injection" do
69
+ invoke! "doc/README", "\nmore content", :before => "__start__"
70
+ revoke! "doc/README", "\nmore content", :before => "__start__"
71
+
72
+ File.read(file).must == "__start__\nREADME\n__end__\n"
73
+ end
74
+
75
+ it "shows progress information to the user" do
76
+ invoke!("doc/README", "\nmore content", :after => "__start__")
77
+ revoke!("doc/README", "\nmore content", :after => "__start__").must == " deinject doc/README\n"
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,291 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Thor::Actions do
4
+ def runner(options={})
5
+ @runner ||= MyCounter.new([1], options, { :destination_root => destination_root })
6
+ end
7
+
8
+ def action(*args, &block)
9
+ capture(:stdout){ runner.send(*args, &block) }
10
+ end
11
+
12
+ def file
13
+ File.join(destination_root, "foo")
14
+ end
15
+
16
+ describe "on include" do
17
+ it "adds runtime options to the base class" do
18
+ MyCounter.class_options.keys.must include(:pretend)
19
+ MyCounter.class_options.keys.must include(:force)
20
+ MyCounter.class_options.keys.must include(:quiet)
21
+ MyCounter.class_options.keys.must include(:skip)
22
+ end
23
+ end
24
+
25
+ describe "#initialize" do
26
+ it "has default behavior invoke" do
27
+ runner.behavior.must == :invoke
28
+ end
29
+
30
+ it "can have behavior revoke" do
31
+ MyCounter.new([1], {}, :behavior => :revoke).behavior.must == :revoke
32
+ end
33
+
34
+ it "when behavior is set to force, overwrite options" do
35
+ runner = MyCounter.new([1], { :force => false, :skip => true }, :behavior => :force)
36
+ runner.behavior.must == :invoke
37
+ runner.options.force.must be_true
38
+ runner.options.skip.must_not be_true
39
+ end
40
+
41
+ it "when behavior is set to skip, overwrite options" do
42
+ runner = MyCounter.new([1], ["--force"], :behavior => :skip)
43
+ runner.behavior.must == :invoke
44
+ runner.options.force.must_not be_true
45
+ runner.options.skip.must be_true
46
+ end
47
+ end
48
+
49
+ describe "accessors" do
50
+ describe "#destination_root=" do
51
+ it "gets the current directory and expands the path to set the root" do
52
+ base = MyCounter.new([1])
53
+ base.destination_root = "here"
54
+ base.destination_root.must == File.expand_path(File.join(File.dirname(__FILE__), "..", "here"))
55
+ end
56
+
57
+ it "does not use the current directory if one is given" do
58
+ base = MyCounter.new([1])
59
+ base.destination_root = "/"
60
+ base.destination_root.must == "/"
61
+ end
62
+
63
+ it "uses the current directory if none is given" do
64
+ base = MyCounter.new([1])
65
+ base.destination_root.must == File.expand_path(File.join(File.dirname(__FILE__), ".."))
66
+ end
67
+ end
68
+
69
+ describe "#relative_to_original_destination_root" do
70
+ it "returns the path relative to the absolute root" do
71
+ runner.relative_to_original_destination_root(file).must == "foo"
72
+ end
73
+
74
+ it "does not remove dot if required" do
75
+ runner.relative_to_original_destination_root(file, false).must == "./foo"
76
+ end
77
+
78
+ it "always use the absolute root" do
79
+ runner.inside("foo") do
80
+ runner.relative_to_original_destination_root(file).must == "foo"
81
+ end
82
+ end
83
+
84
+ describe "#source_paths_for_search" do
85
+ it "add source_root to source_paths_for_search" do
86
+ MyCounter.source_paths_for_search.must include(File.expand_path("fixtures", File.dirname(__FILE__)))
87
+ end
88
+
89
+ it "keeps only current source root in source paths" do
90
+ ClearCounter.source_paths_for_search.must include(File.expand_path("fixtures/bundle", File.dirname(__FILE__)))
91
+ ClearCounter.source_paths_for_search.must_not include(File.expand_path("fixtures", File.dirname(__FILE__)))
92
+ end
93
+
94
+ it "customized source paths should be before source roots" do
95
+ ClearCounter.source_paths_for_search[0].must == File.expand_path("fixtures/doc", File.dirname(__FILE__))
96
+ ClearCounter.source_paths_for_search[1].must == File.expand_path("fixtures/bundle", File.dirname(__FILE__))
97
+ end
98
+
99
+ it "keeps inherited source paths at the end" do
100
+ ClearCounter.source_paths_for_search.last.must == File.expand_path("fixtures/broken", File.dirname(__FILE__))
101
+ end
102
+ end
103
+ end
104
+
105
+ describe "#find_in_source_paths" do
106
+ it "raises an error if source path is empty" do
107
+ lambda {
108
+ A.new.find_in_source_paths("foo")
109
+ }.must raise_error(Thor::Error, /You don't have any source path defined for class A/)
110
+ end
111
+
112
+ it "finds a template inside the source path" do
113
+ runner.find_in_source_paths("doc").must == File.expand_path("doc", source_root)
114
+ lambda { runner.find_in_source_paths("README") }.must raise_error
115
+
116
+ new_path = File.join(source_root, "doc")
117
+ runner.instance_variable_set(:@source_paths, nil)
118
+ runner.source_paths.unshift(new_path)
119
+ runner.find_in_source_paths("README").must == File.expand_path("README", new_path)
120
+ end
121
+ end
122
+ end
123
+
124
+ describe "#inside" do
125
+ it "executes the block inside the given folder" do
126
+ runner.inside("foo") do
127
+ Dir.pwd.must == file
128
+ end
129
+ end
130
+
131
+ it "changes the base root" do
132
+ runner.inside("foo") do
133
+ runner.destination_root.must == file
134
+ end
135
+ end
136
+
137
+ it "creates the directory if it does not exist" do
138
+ runner.inside("foo") do
139
+ File.exists?(file).must be_true
140
+ end
141
+ end
142
+
143
+ describe "when verbose" do
144
+ it "logs status" do
145
+ capture(:stdout) do
146
+ runner.inside("foo", :verbose => true) {}
147
+ end.must =~ /inside foo/
148
+ end
149
+
150
+ it "uses padding in next status" do
151
+ capture(:stdout) do
152
+ runner.inside("foo", :verbose => true) do
153
+ runner.say_status :cool, :padding
154
+ end
155
+ end.must =~ /cool padding/
156
+ end
157
+
158
+ it "removes padding after block" do
159
+ capture(:stdout) do
160
+ runner.inside("foo", :verbose => true) {}
161
+ runner.say_status :no, :padding
162
+ end.must =~ /no padding/
163
+ end
164
+ end
165
+ end
166
+
167
+ describe "#in_root" do
168
+ it "executes the block in the root folder" do
169
+ runner.inside("foo") do
170
+ runner.in_root { Dir.pwd.must == destination_root }
171
+ end
172
+ end
173
+
174
+ it "changes the base root" do
175
+ runner.inside("foo") do
176
+ runner.in_root { runner.destination_root.must == destination_root }
177
+ end
178
+ end
179
+
180
+ it "returns to the previous state" do
181
+ runner.inside("foo") do
182
+ runner.in_root { }
183
+ runner.destination_root.must == file
184
+ end
185
+ end
186
+ end
187
+
188
+ describe "#apply" do
189
+ before(:each) do
190
+ @template = <<-TEMPLATE
191
+ @foo = "FOO"
192
+ say_status :cool, :padding
193
+ TEMPLATE
194
+ @template.instance_eval "def read; self; end" # Make the string respond to read
195
+
196
+ @file = "http://gist.github.com/103208.txt"
197
+ mock(runner).open(@file){ @template }
198
+ end
199
+
200
+ it "opens a file and executes its content in the instance binding" do
201
+ action :apply, @file
202
+ runner.instance_variable_get("@foo").must == "FOO"
203
+ end
204
+
205
+ it "applies padding to the content inside the file" do
206
+ action(:apply, @file).must =~ /cool padding/
207
+ end
208
+
209
+ it "logs its status" do
210
+ action(:apply, @file).must =~ / apply #{@file}\n/
211
+ end
212
+
213
+ it "does not log status" do
214
+ content = action(:apply, @file, :verbose => false)
215
+ content.must =~ /cool padding/
216
+ content.must_not =~ /apply http/
217
+ end
218
+ end
219
+
220
+ describe "#run" do
221
+ before(:each) do
222
+ mock(runner).system("ls")
223
+ end
224
+
225
+ it "executes the command given" do
226
+ action :run, "ls"
227
+ end
228
+
229
+ it "logs status" do
230
+ action(:run, "ls").must == " run ls from \".\"\n"
231
+ end
232
+
233
+ it "does not log status if required" do
234
+ action(:run, "ls", :verbose => false).must be_empty
235
+ end
236
+
237
+ it "accepts a color as status" do
238
+ mock(runner.shell).say_status(:run, 'ls from "."', :yellow)
239
+ action :run, "ls", :verbose => :yellow
240
+ end
241
+ end
242
+
243
+ describe "#run_ruby_script" do
244
+ before(:each) do
245
+ stub(Thor::Util).ruby_command{ "/opt/jruby" }
246
+ mock(runner).system("/opt/jruby script.rb")
247
+ end
248
+
249
+ it "executes the ruby script" do
250
+ action :run_ruby_script, "script.rb"
251
+ end
252
+
253
+ it "logs status" do
254
+ action(:run_ruby_script, "script.rb").must == " run jruby script.rb from \".\"\n"
255
+ end
256
+
257
+ it "does not log status if required" do
258
+ action(:run_ruby_script, "script.rb", :verbose => false).must be_empty
259
+ end
260
+ end
261
+
262
+ describe "#thor" do
263
+ it "executes the thor command" do
264
+ mock(runner).system("thor list")
265
+ action :thor, :list, :verbose => true
266
+ end
267
+
268
+ it "converts extra arguments to command arguments" do
269
+ mock(runner).system("thor list foo bar")
270
+ action :thor, :list, "foo", "bar"
271
+ end
272
+
273
+ it "converts options hash to switches" do
274
+ mock(runner).system("thor list foo bar --foo")
275
+ action :thor, :list, "foo", "bar", :foo => true
276
+
277
+ mock(runner).system("thor list --foo 1 2 3")
278
+ action :thor, :list, :foo => [1,2,3]
279
+ end
280
+
281
+ it "logs status" do
282
+ mock(runner).system("thor list")
283
+ action(:thor, :list).must == " run thor list from \".\"\n"
284
+ end
285
+
286
+ it "does not log status if required" do
287
+ mock(runner).system("thor list --foo 1 2 3")
288
+ action(:thor, :list, :foo => [1,2,3], :verbose => false).must be_empty
289
+ end
290
+ end
291
+ end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,236 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'thor/base'
3
+
4
+ class Amazing
5
+ desc "hello", "say hello"
6
+ def hello
7
+ puts "Hello"
8
+ end
9
+ end
10
+
11
+ describe Thor::Base do
12
+ describe "#initialize" do
13
+ it "sets arguments array" do
14
+ base = MyCounter.new [1, 2]
15
+ base.first.must == 1
16
+ base.second.must == 2
17
+ end
18
+
19
+ it "sets arguments default values" do
20
+ base = MyCounter.new [1]
21
+ base.second.must == 2
22
+ end
23
+
24
+ it "sets options default values" do
25
+ base = MyCounter.new [1, 2]
26
+ base.options[:third].must == 3
27
+ end
28
+
29
+ it "allows options to be given as symbols or strings" do
30
+ base = MyCounter.new [1, 2], :third => 4
31
+ base.options[:third].must == 4
32
+
33
+ base = MyCounter.new [1, 2], "third" => 4
34
+ base.options[:third].must == 4
35
+ end
36
+
37
+ it "creates options with indifferent access" do
38
+ base = MyCounter.new [1, 2], :third => 3
39
+ base.options['third'].must == 3
40
+ end
41
+
42
+ it "creates options with magic predicates" do
43
+ base = MyCounter.new [1, 2], :third => 3
44
+ base.options.third.must == 3
45
+ end
46
+ end
47
+
48
+ describe "#no_tasks" do
49
+ it "avoids methods being added as tasks" do
50
+ MyScript.tasks.keys.must include("animal")
51
+ MyScript.tasks.keys.must_not include("this_is_not_a_task")
52
+ end
53
+ end
54
+
55
+ describe "#argument" do
56
+ it "sets a value as required and creates an accessor for it" do
57
+ MyCounter.start(["1", "2", "--third", "3"])[0].must == 1
58
+ Scripts::MyScript.start(["zoo", "my_special_param", "--param=normal_param"]).must == "my_special_param"
59
+ end
60
+
61
+ it "does not set a value in the options hash" do
62
+ BrokenCounter.start(["1", "2", "--third", "3"])[0].must be_nil
63
+ end
64
+ end
65
+
66
+ describe "#arguments" do
67
+ it "returns the arguments for the class" do
68
+ MyCounter.arguments.must have(2).items
69
+ end
70
+ end
71
+
72
+ describe "#class_option" do
73
+ it "sets options class wise" do
74
+ MyCounter.start(["1", "2", "--third", "3"])[2].must == 3
75
+ end
76
+
77
+ it "does not create an acessor for it" do
78
+ BrokenCounter.start(["1", "2", "--third", "3"])[3].must be_false
79
+ end
80
+ end
81
+
82
+ describe "#class_options" do
83
+ it "sets default options overwriting superclass definitions" do
84
+ options = Scripts::MyScript.class_options
85
+ options[:force].must_not be_required
86
+ end
87
+ end
88
+
89
+ describe "#remove_argument" do
90
+ it "removes previous defined arguments from class" do
91
+ ClearCounter.arguments.must be_empty
92
+ end
93
+
94
+ it "undefine accessors if required" do
95
+ ClearCounter.new.must_not respond_to(:first)
96
+ ClearCounter.new.must_not respond_to(:second)
97
+ end
98
+ end
99
+
100
+ describe "#remove_class_option" do
101
+ it "removes previous defined class option" do
102
+ ClearCounter.class_options[:third].must be_nil
103
+ end
104
+ end
105
+
106
+ describe "#class_options_help" do
107
+ before(:each) do
108
+ @content = capture(:stdout) { MyCounter.help(Thor::Base.shell.new) }
109
+ end
110
+
111
+ it "shows options description" do
112
+ @content.must =~ /# The third argument/
113
+ end
114
+
115
+ it "shows usage with banner content" do
116
+ @content.must =~ /\[\-\-third=THREE\]/
117
+ end
118
+
119
+ it "shows default values below description" do
120
+ @content.must =~ /# Default: 3/
121
+ end
122
+
123
+ it "shows options in different groups" do
124
+ @content.must =~ /Options\:/
125
+ @content.must =~ /Runtime options\:/
126
+ @content.must =~ /\-p, \[\-\-pretend\]/
127
+ end
128
+
129
+ it "use padding in options that does not have aliases" do
130
+ @content.must =~ /^ -t, \[--third/
131
+ @content.must =~ /^ \[--fourth/
132
+ end
133
+
134
+ it "allows extra options to be given" do
135
+ hash = { "Foo" => B.class_options.values }
136
+
137
+ content = capture(:stdout) { MyCounter.send(:class_options_help, Thor::Base.shell.new, nil, hash) }
138
+ content.must =~ /Foo options\:/
139
+ content.must =~ /--last-name=LAST_NAME/
140
+ end
141
+ end
142
+
143
+ describe "#namespace" do
144
+ it "returns the default class namespace" do
145
+ Scripts::MyScript.namespace.must == "scripts:my_script"
146
+ end
147
+
148
+ it "sets a namespace to the class" do
149
+ Scripts::MyDefaults.namespace.must == "default"
150
+ end
151
+ end
152
+
153
+ describe "#group" do
154
+ it "sets a group" do
155
+ MyScript.group.must == "script"
156
+ end
157
+
158
+ it "inherits the group from parent" do
159
+ MyChildScript.group.must == "script"
160
+ end
161
+
162
+ it "defaults to standard if no group is given" do
163
+ Amazing.group.must == "standard"
164
+ end
165
+ end
166
+
167
+ describe "#subclasses" do
168
+ it "tracks its subclasses in an Array" do
169
+ Thor::Base.subclasses.must include(MyScript)
170
+ Thor::Base.subclasses.must include(MyChildScript)
171
+ Thor::Base.subclasses.must include(Scripts::MyScript)
172
+ end
173
+ end
174
+
175
+ describe "#subclass_files" do
176
+ it "returns tracked subclasses, grouped by the files they come from" do
177
+ thorfile = File.join(File.dirname(__FILE__), "fixtures", "script.thor")
178
+ Thor::Base.subclass_files[File.expand_path(thorfile)].must == [
179
+ MyScript, MyScript::AnotherScript, MyChildScript, Scripts::MyScript,
180
+ Scripts::MyDefaults, Scripts::ChildDefault
181
+ ]
182
+ end
183
+
184
+ it "tracks a single subclass across multiple files" do
185
+ thorfile = File.join(File.dirname(__FILE__), "fixtures", "task.thor")
186
+ Thor::Base.subclass_files[File.expand_path(thorfile)].must include(Amazing)
187
+ Thor::Base.subclass_files[File.expand_path(__FILE__)].must include(Amazing)
188
+ end
189
+ end
190
+
191
+ describe "#tasks" do
192
+ it "returns a list with all tasks defined in this class" do
193
+ MyChildScript.new.must respond_to("animal")
194
+ MyChildScript.tasks.keys.must include("animal")
195
+ end
196
+
197
+ it "raises an error if a task with reserved word is defined" do
198
+ lambda {
199
+ klass = Class.new(Thor::Group)
200
+ klass.class_eval "def shell; end"
201
+ }.must raise_error(RuntimeError, /"shell" is a Thor reserved word and cannot be defined as task/)
202
+ end
203
+ end
204
+
205
+ describe "#all_tasks" do
206
+ it "returns a list with all tasks defined in this class plus superclasses" do
207
+ MyChildScript.new.must respond_to("foo")
208
+ MyChildScript.all_tasks.keys.must include("foo")
209
+ end
210
+ end
211
+
212
+ describe "#remove_task" do
213
+ it "removes the task from its tasks hash" do
214
+ MyChildScript.tasks.keys.must_not include("bar")
215
+ MyChildScript.tasks.keys.must_not include("boom")
216
+ end
217
+
218
+ it "undefines the method if desired" do
219
+ MyChildScript.new.must_not respond_to("boom")
220
+ end
221
+ end
222
+
223
+ describe "#from_superclass" do
224
+ it "does not send a method to the superclass if the superclass does not respond to it" do
225
+ MyCounter.get_from_super.must == 13
226
+ end
227
+ end
228
+
229
+ describe "#start" do
230
+ it "raises an error instead of rescueing if --debug is given" do
231
+ lambda {
232
+ MyScript.start ["what", "--debug"]
233
+ }.must raise_error(Thor::UndefinedTaskError, /the 'what' task of MyScript is private/)
234
+ end
235
+ end
236
+ end