millisami-thor 0.14.6
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/.autotest +8 -0
- data/.gemtest +0 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/CHANGELOG.rdoc +103 -0
- data/Gemfile +11 -0
- data/LICENSE.md +20 -0
- data/README.md +26 -0
- data/Thorfile +13 -0
- data/bin/rake2thor +86 -0
- data/bin/thor +6 -0
- data/lib/thor/actions/create_file.rb +105 -0
- data/lib/thor/actions/create_link.rb +57 -0
- data/lib/thor/actions/directory.rb +93 -0
- data/lib/thor/actions/empty_directory.rb +134 -0
- data/lib/thor/actions/file_manipulation.rb +270 -0
- data/lib/thor/actions/inject_into_file.rb +109 -0
- data/lib/thor/actions.rb +314 -0
- data/lib/thor/base.rb +598 -0
- data/lib/thor/core_ext/file_binary_read.rb +9 -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 +30 -0
- data/lib/thor/group.rb +276 -0
- data/lib/thor/invocation.rb +168 -0
- data/lib/thor/parser/argument.rb +67 -0
- data/lib/thor/parser/arguments.rb +165 -0
- data/lib/thor/parser/option.rb +120 -0
- data/lib/thor/parser/options.rb +181 -0
- data/lib/thor/parser.rb +4 -0
- data/lib/thor/rake_compat.rb +70 -0
- data/lib/thor/runner.rb +309 -0
- data/lib/thor/shell/basic.rb +302 -0
- data/lib/thor/shell/color.rb +108 -0
- data/lib/thor/shell/html.rb +121 -0
- data/lib/thor/shell.rb +88 -0
- data/lib/thor/task.rb +129 -0
- data/lib/thor/util.rb +229 -0
- data/lib/thor/version.rb +3 -0
- data/lib/thor.rb +336 -0
- data/spec/actions/create_file_spec.rb +170 -0
- data/spec/actions/directory_spec.rb +136 -0
- data/spec/actions/empty_directory_spec.rb +98 -0
- data/spec/actions/file_manipulation_spec.rb +317 -0
- data/spec/actions/inject_into_file_spec.rb +135 -0
- data/spec/actions_spec.rb +322 -0
- data/spec/base_spec.rb +274 -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/application.rb +2 -0
- data/spec/fixtures/bundle/execute.rb +6 -0
- data/spec/fixtures/bundle/main.thor +1 -0
- data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
- data/spec/fixtures/doc/README +3 -0
- data/spec/fixtures/doc/block_helper.rb +3 -0
- data/spec/fixtures/doc/components/.empty_directory +0 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/fixtures/doc/config.yaml.tt +1 -0
- data/spec/fixtures/group.thor +114 -0
- data/spec/fixtures/invoke.thor +112 -0
- data/spec/fixtures/path with spaces +0 -0
- data/spec/fixtures/script.thor +184 -0
- data/spec/fixtures/task.thor +10 -0
- data/spec/group_spec.rb +216 -0
- data/spec/invocation_spec.rb +100 -0
- data/spec/parser/argument_spec.rb +47 -0
- data/spec/parser/arguments_spec.rb +65 -0
- data/spec/parser/option_spec.rb +202 -0
- data/spec/parser/options_spec.rb +329 -0
- data/spec/rake_compat_spec.rb +72 -0
- data/spec/register_spec.rb +92 -0
- data/spec/runner_spec.rb +210 -0
- data/spec/shell/basic_spec.rb +223 -0
- data/spec/shell/color_spec.rb +41 -0
- data/spec/shell/html_spec.rb +27 -0
- data/spec/shell_spec.rb +47 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/task_spec.rb +74 -0
- data/spec/thor_spec.rb +362 -0
- data/spec/util_spec.rb +163 -0
- data/thor.gemspec +25 -0
- metadata +241 -0
@@ -0,0 +1,322 @@
|
|
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.should include(:pretend)
|
19
|
+
MyCounter.class_options.keys.should include(:force)
|
20
|
+
MyCounter.class_options.keys.should include(:quiet)
|
21
|
+
MyCounter.class_options.keys.should include(:skip)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#initialize" do
|
26
|
+
it "has default behavior invoke" do
|
27
|
+
runner.behavior.should == :invoke
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can have behavior revoke" do
|
31
|
+
MyCounter.new([1], {}, :behavior => :revoke).behavior.should == :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.should == :invoke
|
37
|
+
runner.options.force.should be_true
|
38
|
+
runner.options.skip.should_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.should == :invoke
|
44
|
+
runner.options.force.should_not be_true
|
45
|
+
runner.options.skip.should 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.should == 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
|
+
root = File.expand_path("/")
|
59
|
+
base = MyCounter.new([1])
|
60
|
+
base.destination_root = root
|
61
|
+
base.destination_root.should == root
|
62
|
+
end
|
63
|
+
|
64
|
+
it "uses the current directory if none is given" do
|
65
|
+
base = MyCounter.new([1])
|
66
|
+
base.destination_root.should == File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#relative_to_original_destination_root" do
|
71
|
+
it "returns the path relative to the absolute root" do
|
72
|
+
runner.relative_to_original_destination_root(file).should == "foo"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "does not remove dot if required" do
|
76
|
+
runner.relative_to_original_destination_root(file, false).should == "./foo"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "always use the absolute root" do
|
80
|
+
runner.inside("foo") do
|
81
|
+
runner.relative_to_original_destination_root(file).should == "foo"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#source_paths_for_search" do
|
86
|
+
it "add source_root to source_paths_for_search" do
|
87
|
+
MyCounter.source_paths_for_search.should include(File.expand_path("fixtures", File.dirname(__FILE__)))
|
88
|
+
end
|
89
|
+
|
90
|
+
it "keeps only current source root in source paths" do
|
91
|
+
ClearCounter.source_paths_for_search.should include(File.expand_path("fixtures/bundle", File.dirname(__FILE__)))
|
92
|
+
ClearCounter.source_paths_for_search.should_not include(File.expand_path("fixtures", File.dirname(__FILE__)))
|
93
|
+
end
|
94
|
+
|
95
|
+
it "customized source paths should be before source roots" do
|
96
|
+
ClearCounter.source_paths_for_search[0].should == File.expand_path("fixtures/doc", File.dirname(__FILE__))
|
97
|
+
ClearCounter.source_paths_for_search[1].should == File.expand_path("fixtures/bundle", File.dirname(__FILE__))
|
98
|
+
end
|
99
|
+
|
100
|
+
it "keeps inherited source paths at the end" do
|
101
|
+
ClearCounter.source_paths_for_search.last.should == File.expand_path("fixtures/broken", File.dirname(__FILE__))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#find_in_source_paths" do
|
107
|
+
it "raises an error if source path is empty" do
|
108
|
+
lambda {
|
109
|
+
A.new.find_in_source_paths("foo")
|
110
|
+
}.should raise_error(Thor::Error, /Currently you have no source paths/)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "finds a template inside the source path" do
|
114
|
+
runner.find_in_source_paths("doc").should == File.expand_path("doc", source_root)
|
115
|
+
lambda { runner.find_in_source_paths("README") }.should raise_error
|
116
|
+
|
117
|
+
new_path = File.join(source_root, "doc")
|
118
|
+
runner.instance_variable_set(:@source_paths, nil)
|
119
|
+
runner.source_paths.unshift(new_path)
|
120
|
+
runner.find_in_source_paths("README").should == File.expand_path("README", new_path)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "#inside" do
|
126
|
+
it "executes the block inside the given folder" do
|
127
|
+
runner.inside("foo") do
|
128
|
+
Dir.pwd.should == file
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "changes the base root" do
|
133
|
+
runner.inside("foo") do
|
134
|
+
runner.destination_root.should == file
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it "creates the directory if it does not exist" do
|
139
|
+
runner.inside("foo") do
|
140
|
+
File.exists?(file).should be_true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "when pretending" do
|
145
|
+
it "no directories should be created" do
|
146
|
+
runner.inside("bar", :pretend => true) {}
|
147
|
+
File.exists?("bar").should be_false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "when verbose" do
|
152
|
+
it "logs status" do
|
153
|
+
capture(:stdout) do
|
154
|
+
runner.inside("foo", :verbose => true) {}
|
155
|
+
end.should =~ /inside foo/
|
156
|
+
end
|
157
|
+
|
158
|
+
it "uses padding in next status" do
|
159
|
+
capture(:stdout) do
|
160
|
+
runner.inside("foo", :verbose => true) do
|
161
|
+
runner.say_status :cool, :padding
|
162
|
+
end
|
163
|
+
end.should =~ /cool padding/
|
164
|
+
end
|
165
|
+
|
166
|
+
it "removes padding after block" do
|
167
|
+
capture(:stdout) do
|
168
|
+
runner.inside("foo", :verbose => true) {}
|
169
|
+
runner.say_status :no, :padding
|
170
|
+
end.should =~ /no padding/
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#in_root" do
|
176
|
+
it "executes the block in the root folder" do
|
177
|
+
runner.inside("foo") do
|
178
|
+
runner.in_root { Dir.pwd.should == destination_root }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
it "changes the base root" do
|
183
|
+
runner.inside("foo") do
|
184
|
+
runner.in_root { runner.destination_root.should == destination_root }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it "returns to the previous state" do
|
189
|
+
runner.inside("foo") do
|
190
|
+
runner.in_root { }
|
191
|
+
runner.destination_root.should == file
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "#apply" do
|
197
|
+
before(:each) do
|
198
|
+
@template = <<-TEMPLATE
|
199
|
+
@foo = "FOO"
|
200
|
+
say_status :cool, :padding
|
201
|
+
TEMPLATE
|
202
|
+
@template.stub(:read).and_return(@template)
|
203
|
+
|
204
|
+
@file = '/'
|
205
|
+
runner.stub(:open).and_return(@template)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "accepts a URL as the path" do
|
209
|
+
@file = "http://gist.github.com/103208.txt"
|
210
|
+
runner.should_receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template)
|
211
|
+
action(:apply, @file)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "accepts a secure URL as the path" do
|
215
|
+
@file = "https://gist.github.com/103208.txt"
|
216
|
+
runner.should_receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template)
|
217
|
+
action(:apply, @file)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "accepts a local file path with spaces" do
|
221
|
+
@file = File.expand_path("fixtures/path with spaces", File.dirname(__FILE__))
|
222
|
+
runner.should_receive(:open).with(@file).and_return(@template)
|
223
|
+
action(:apply, @file)
|
224
|
+
end
|
225
|
+
|
226
|
+
it "opens a file and executes its content in the instance binding" do
|
227
|
+
action :apply, @file
|
228
|
+
runner.instance_variable_get("@foo").should == "FOO"
|
229
|
+
end
|
230
|
+
|
231
|
+
it "applies padding to the content inside the file" do
|
232
|
+
action(:apply, @file).should =~ /cool padding/
|
233
|
+
end
|
234
|
+
|
235
|
+
it "logs its status" do
|
236
|
+
action(:apply, @file).should =~ / apply #{@file}\n/
|
237
|
+
end
|
238
|
+
|
239
|
+
it "does not log status" do
|
240
|
+
content = action(:apply, @file, :verbose => false)
|
241
|
+
content.should =~ /cool padding/
|
242
|
+
content.should_not =~ /apply http/
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
describe "#run_cmd" do
|
247
|
+
before(:each) do
|
248
|
+
runner.should_receive(:system).with("ls")
|
249
|
+
end
|
250
|
+
|
251
|
+
it "executes the command given" do
|
252
|
+
action :run_cmd, "ls"
|
253
|
+
end
|
254
|
+
|
255
|
+
it "logs status" do
|
256
|
+
action(:run_cmd, "ls").should == " run_cmd ls from \".\"\n"
|
257
|
+
end
|
258
|
+
|
259
|
+
it "does not log status if required" do
|
260
|
+
action(:run_cmd, "ls", :verbose => false).should be_empty
|
261
|
+
end
|
262
|
+
|
263
|
+
it "accepts a color as status" do
|
264
|
+
runner.shell.should_receive(:say_status).with(:run_cmd, 'ls from "."', :yellow)
|
265
|
+
action :run_cmd, "ls", :verbose => :yellow
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe "#run_ruby_script" do
|
270
|
+
before(:each) do
|
271
|
+
Thor::Util.stub!(:ruby_command).and_return("/opt/jruby")
|
272
|
+
runner.should_receive(:system).with("/opt/jruby script.rb")
|
273
|
+
end
|
274
|
+
|
275
|
+
it "executes the ruby script" do
|
276
|
+
action :run_ruby_script, "script.rb"
|
277
|
+
end
|
278
|
+
|
279
|
+
it "logs status" do
|
280
|
+
action(:run_ruby_script, "script.rb").should == " run_cmd jruby script.rb from \".\"\n"
|
281
|
+
end
|
282
|
+
|
283
|
+
it "does not log status if required" do
|
284
|
+
action(:run_ruby_script, "script.rb", :verbose => false).should be_empty
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe "#thor" do
|
289
|
+
it "executes the thor command" do
|
290
|
+
runner.should_receive(:system).with("thor list")
|
291
|
+
action :thor, :list, :verbose => true
|
292
|
+
end
|
293
|
+
|
294
|
+
it "converts extra arguments to command arguments" do
|
295
|
+
runner.should_receive(:system).with("thor list foo bar")
|
296
|
+
action :thor, :list, "foo", "bar"
|
297
|
+
end
|
298
|
+
|
299
|
+
it "converts options hash to switches" do
|
300
|
+
runner.should_receive(:system).with("thor list foo bar --foo")
|
301
|
+
action :thor, :list, "foo", "bar", :foo => true
|
302
|
+
|
303
|
+
runner.should_receive(:system).with("thor list --foo 1 2 3")
|
304
|
+
action :thor, :list, :foo => [1,2,3]
|
305
|
+
end
|
306
|
+
|
307
|
+
it "logs status" do
|
308
|
+
runner.should_receive(:system).with("thor list")
|
309
|
+
action(:thor, :list).should == " run_cmd thor list from \".\"\n"
|
310
|
+
end
|
311
|
+
|
312
|
+
it "does not log status if required" do
|
313
|
+
runner.should_receive(:system).with("thor list --foo 1 2 3")
|
314
|
+
action(:thor, :list, :foo => [1,2,3], :verbose => false).should be_empty
|
315
|
+
end
|
316
|
+
|
317
|
+
it "captures the output when :capture is given" do
|
318
|
+
runner.should_receive(:`).with("thor foo bar")
|
319
|
+
action(:thor, "foo", "bar", :capture => true)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,274 @@
|
|
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.should == 1
|
16
|
+
base.second.should == 2
|
17
|
+
end
|
18
|
+
|
19
|
+
it "sets arguments default values" do
|
20
|
+
base = MyCounter.new [1]
|
21
|
+
base.second.should == 2
|
22
|
+
end
|
23
|
+
|
24
|
+
it "sets options default values" do
|
25
|
+
base = MyCounter.new [1, 2]
|
26
|
+
base.options[:third].should == 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].should == 4
|
32
|
+
|
33
|
+
base = MyCounter.new [1, 2], "third" => 4
|
34
|
+
base.options[:third].should == 4
|
35
|
+
end
|
36
|
+
|
37
|
+
it "creates options with indifferent access" do
|
38
|
+
base = MyCounter.new [1, 2], :third => 3
|
39
|
+
base.options['third'].should == 3
|
40
|
+
end
|
41
|
+
|
42
|
+
it "creates options with magic predicates" do
|
43
|
+
base = MyCounter.new [1, 2], :third => 3
|
44
|
+
base.options.third.should == 3
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#no_tasks" do
|
49
|
+
it "avoids methods being added as tasks" do
|
50
|
+
MyScript.tasks.keys.should include("animal")
|
51
|
+
MyScript.tasks.keys.should_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].should == 1
|
58
|
+
Scripts::MyScript.start(["zoo", "my_special_param", "--param=normal_param"]).should == "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].should be_nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#arguments" do
|
67
|
+
it "returns the arguments for the class" do
|
68
|
+
MyCounter.arguments.should 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].should == 3
|
75
|
+
end
|
76
|
+
|
77
|
+
it "does not create an accessor for it" do
|
78
|
+
BrokenCounter.start(["1", "2", "--third", "3"])[3].should 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].should_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.should be_empty
|
92
|
+
end
|
93
|
+
|
94
|
+
it "undefine accessors if required" do
|
95
|
+
ClearCounter.new.should_not respond_to(:first)
|
96
|
+
ClearCounter.new.should_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].should 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.should =~ /# The third argument/
|
113
|
+
end
|
114
|
+
|
115
|
+
it "shows usage with banner content" do
|
116
|
+
@content.should =~ /\[\-\-third=THREE\]/
|
117
|
+
end
|
118
|
+
|
119
|
+
it "shows default values below description" do
|
120
|
+
@content.should =~ /# Default: 3/
|
121
|
+
end
|
122
|
+
|
123
|
+
it "shows options in different groups" do
|
124
|
+
@content.should =~ /Options\:/
|
125
|
+
@content.should =~ /Runtime options\:/
|
126
|
+
@content.should =~ /\-p, \[\-\-pretend\]/
|
127
|
+
end
|
128
|
+
|
129
|
+
it "use padding in options that does not have aliases" do
|
130
|
+
@content.should =~ /^ -t, \[--third/
|
131
|
+
@content.should =~ /^ \[--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, hash) }
|
138
|
+
content.should =~ /Foo options\:/
|
139
|
+
content.should =~ /--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.should == "scripts:my_script"
|
146
|
+
end
|
147
|
+
|
148
|
+
it "sets a namespace to the class" do
|
149
|
+
Scripts::MyDefaults.namespace.should == "default"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#group" do
|
154
|
+
it "sets a group" do
|
155
|
+
MyScript.group.should == "script"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "inherits the group from parent" do
|
159
|
+
MyChildScript.group.should == "script"
|
160
|
+
end
|
161
|
+
|
162
|
+
it "defaults to standard if no group is given" do
|
163
|
+
Amazing.group.should == "standard"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "#subclasses" do
|
168
|
+
it "tracks its subclasses in an Array" do
|
169
|
+
Thor::Base.subclasses.should include(MyScript)
|
170
|
+
Thor::Base.subclasses.should include(MyChildScript)
|
171
|
+
Thor::Base.subclasses.should 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)].should == [
|
179
|
+
MyScript, MyScript::AnotherScript, MyChildScript, Barn,
|
180
|
+
Scripts::MyScript, 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)].should include(Amazing)
|
187
|
+
Thor::Base.subclass_files[File.expand_path(__FILE__)].should 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.should respond_to("animal")
|
194
|
+
MyChildScript.tasks.keys.should 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
|
+
}.should 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.should respond_to("foo")
|
208
|
+
MyChildScript.all_tasks.keys.should 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.should_not include("bar")
|
215
|
+
MyChildScript.tasks.keys.should_not include("boom")
|
216
|
+
end
|
217
|
+
|
218
|
+
it "undefines the method if desired" do
|
219
|
+
MyChildScript.new.should_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.should == 13
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#start" do
|
230
|
+
it "raises an error instead of rescueing if THOR_DEBUG=1 is given" do
|
231
|
+
begin
|
232
|
+
ENV["THOR_DEBUG"] = 1
|
233
|
+
lambda {
|
234
|
+
MyScript.start ["what", "--debug"]
|
235
|
+
}.should raise_error(Thor::UndefinedTaskError, 'Could not find task "what" in "my_script" namespace.')
|
236
|
+
rescue
|
237
|
+
ENV["THOR_DEBUG"] = nil
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it "does not steal args" do
|
242
|
+
args = ["foo", "bar", "--force", "true"]
|
243
|
+
MyScript.start(args)
|
244
|
+
args.should == ["foo", "bar", "--force", "true"]
|
245
|
+
end
|
246
|
+
|
247
|
+
it "checks unknown options" do
|
248
|
+
capture(:stderr) {
|
249
|
+
MyScript.start(["foo", "bar", "--force", "true", "--unknown", "baz"])
|
250
|
+
}.strip.should == "Unknown switches '--unknown'"
|
251
|
+
end
|
252
|
+
|
253
|
+
it "checks unknown options except specified" do
|
254
|
+
capture(:stderr) {
|
255
|
+
MyScript.start(["with_optional", "NAME", "--omg", "--invalid"]).should == ["NAME", {}, ["--omg", "--invalid"]]
|
256
|
+
}.strip.should be_empty
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "attr_*" do
|
261
|
+
it "should not add attr_reader as a task" do
|
262
|
+
capture(:stderr){ MyScript.start(["another_attribute"]) }.should =~ /Could not find/
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should not add attr_writer as a task" do
|
266
|
+
capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }.should =~ /Could not find/
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should not add attr_accessor as a task" do
|
270
|
+
capture(:stderr){ MyScript.start(["some_attribute"]) }.should =~ /Could not find/
|
271
|
+
capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }.should =~ /Could not find/
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'thor/core_ext/hash_with_indifferent_access'
|
3
|
+
|
4
|
+
describe Thor::CoreExt::HashWithIndifferentAccess do
|
5
|
+
before(:each) do
|
6
|
+
@hash = Thor::CoreExt::HashWithIndifferentAccess.new :foo => 'bar', 'baz' => 'bee', :force => true
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has values accessible by either strings or symbols" do
|
10
|
+
@hash['foo'].should == 'bar'
|
11
|
+
@hash[:foo].should == 'bar'
|
12
|
+
|
13
|
+
@hash.values_at(:foo, :baz).should == ['bar', 'bee']
|
14
|
+
@hash.delete(:foo).should == 'bar'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "handles magic boolean predicates" do
|
18
|
+
@hash.force?.should be_true
|
19
|
+
@hash.foo?.should be_true
|
20
|
+
@hash.nothing?.should be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it "handles magic comparisions" do
|
24
|
+
@hash.foo?('bar').should be_true
|
25
|
+
@hash.foo?('bee').should be_false
|
26
|
+
end
|
27
|
+
|
28
|
+
it "maps methods to keys" do
|
29
|
+
@hash.foo.should == @hash['foo']
|
30
|
+
end
|
31
|
+
|
32
|
+
it "merges keys independent if they are symbols or strings" do
|
33
|
+
@hash.merge!('force' => false, :baz => "boom")
|
34
|
+
@hash[:force].should == false
|
35
|
+
@hash[:baz].should == "boom"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "creates a new hash by merging keys independent if they are symbols or strings" do
|
39
|
+
other = @hash.merge('force' => false, :baz => "boom")
|
40
|
+
other[:force].should == false
|
41
|
+
other[:baz].should == "boom"
|
42
|
+
end
|
43
|
+
end
|