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.
- data/CHANGELOG.rdoc +29 -4
- data/README.rdoc +234 -0
- data/Thorfile +57 -0
- data/VERSION +1 -0
- data/bin/rake2thor +4 -0
- data/bin/thor +1 -1
- data/lib/thor.rb +216 -119
- data/lib/thor/actions.rb +272 -0
- data/lib/thor/actions/create_file.rb +102 -0
- data/lib/thor/actions/directory.rb +87 -0
- data/lib/thor/actions/empty_directory.rb +133 -0
- data/lib/thor/actions/file_manipulation.rb +195 -0
- data/lib/thor/actions/inject_into_file.rb +78 -0
- data/lib/thor/base.rb +510 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
- data/lib/thor/core_ext/ordered_hash.rb +100 -0
- data/lib/thor/error.rb +25 -1
- data/lib/thor/group.rb +263 -0
- data/lib/thor/invocation.rb +178 -0
- data/lib/thor/parser.rb +4 -0
- data/lib/thor/parser/argument.rb +67 -0
- data/lib/thor/parser/arguments.rb +145 -0
- data/lib/thor/parser/option.rb +132 -0
- data/lib/thor/parser/options.rb +142 -0
- data/lib/thor/rake_compat.rb +67 -0
- data/lib/thor/runner.rb +232 -242
- data/lib/thor/shell.rb +72 -0
- data/lib/thor/shell/basic.rb +220 -0
- data/lib/thor/shell/color.rb +108 -0
- data/lib/thor/task.rb +97 -60
- data/lib/thor/util.rb +230 -55
- data/spec/actions/create_file_spec.rb +170 -0
- data/spec/actions/directory_spec.rb +118 -0
- data/spec/actions/empty_directory_spec.rb +91 -0
- data/spec/actions/file_manipulation_spec.rb +242 -0
- data/spec/actions/inject_into_file_spec.rb +80 -0
- data/spec/actions_spec.rb +291 -0
- data/spec/base_spec.rb +236 -0
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
- data/spec/core_ext/ordered_hash_spec.rb +115 -0
- data/spec/fixtures/bundle/execute.rb +6 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/group_spec.rb +177 -0
- data/spec/invocation_spec.rb +107 -0
- data/spec/parser/argument_spec.rb +47 -0
- data/spec/parser/arguments_spec.rb +64 -0
- data/spec/parser/option_spec.rb +212 -0
- data/spec/parser/options_spec.rb +255 -0
- data/spec/rake_compat_spec.rb +64 -0
- data/spec/runner_spec.rb +204 -0
- data/spec/shell/basic_spec.rb +206 -0
- data/spec/shell/color_spec.rb +41 -0
- data/spec/shell_spec.rb +25 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/task_spec.rb +82 -0
- data/spec/thor_spec.rb +234 -0
- data/spec/util_spec.rb +196 -0
- metadata +69 -25
- data/README.markdown +0 -76
- data/Rakefile +0 -6
- data/lib/thor/options.rb +0 -242
- data/lib/thor/ordered_hash.rb +0 -64
- data/lib/thor/task_hash.rb +0 -22
- data/lib/thor/tasks.rb +0 -77
- 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
|