thor 0.17.0 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGELOG.rdoc → CHANGELOG.md} +30 -36
- data/README.md +10 -3
- data/lib/thor.rb +205 -180
- data/lib/thor/actions.rb +5 -5
- data/lib/thor/actions/create_link.rb +3 -0
- data/lib/thor/actions/directory.rb +2 -0
- data/lib/thor/actions/file_manipulation.rb +1 -1
- data/lib/thor/base.rb +84 -95
- data/lib/thor/{task.rb → command.rb} +17 -13
- data/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/thor/error.rb +4 -7
- data/lib/thor/group.rb +30 -33
- data/lib/thor/invocation.rb +28 -26
- data/lib/thor/parser/options.rb +3 -1
- data/lib/thor/runner.rb +21 -20
- data/lib/thor/shell/basic.rb +5 -1
- data/lib/thor/shell/color.rb +4 -0
- data/lib/thor/shell/html.rb +4 -0
- data/lib/thor/util.rb +214 -210
- data/lib/thor/version.rb +1 -1
- data/spec/actions/create_file_spec.rb +1 -1
- data/spec/actions/create_link_spec.rb +15 -1
- data/spec/actions/directory_spec.rb +18 -5
- data/spec/actions/empty_directory_spec.rb +1 -1
- data/spec/actions/file_manipulation_spec.rb +13 -13
- data/spec/actions/inject_into_file_spec.rb +1 -1
- data/spec/actions_spec.rb +1 -1
- data/spec/base_spec.rb +40 -24
- data/spec/command_spec.rb +80 -0
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +1 -1
- data/spec/core_ext/ordered_hash_spec.rb +1 -1
- data/spec/exit_condition_spec.rb +3 -3
- data/spec/fixtures/{task.thor → command.thor} +0 -0
- data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
- data/spec/fixtures/doc/COMMENTER +11 -0
- data/spec/fixtures/doc/README +3 -0
- data/spec/fixtures/doc/block_helper.rb +3 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/fixtures/doc/config.yaml.tt +1 -0
- data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
- data/spec/fixtures/group.thor +24 -10
- data/spec/fixtures/invoke.thor +3 -3
- data/spec/fixtures/script.thor +40 -15
- data/spec/fixtures/subcommand.thor +17 -0
- data/spec/group_spec.rb +13 -13
- data/spec/{spec_helper.rb → helper.rb} +11 -7
- data/spec/invocation_spec.rb +16 -16
- data/spec/parser/argument_spec.rb +4 -4
- data/spec/parser/arguments_spec.rb +1 -1
- data/spec/parser/option_spec.rb +1 -1
- data/spec/parser/options_spec.rb +6 -1
- data/spec/rake_compat_spec.rb +1 -1
- data/spec/register_spec.rb +12 -12
- data/spec/runner_spec.rb +36 -36
- data/spec/shell/basic_spec.rb +9 -10
- data/spec/shell/color_spec.rb +16 -2
- data/spec/shell/html_spec.rb +1 -1
- data/spec/shell_spec.rb +1 -1
- data/spec/subcommand_spec.rb +30 -0
- data/spec/thor_spec.rb +81 -78
- data/spec/util_spec.rb +10 -10
- data/thor.gemspec +22 -18
- metadata +49 -38
- data/.gitignore +0 -44
- data/.rspec +0 -3
- data/.travis.yml +0 -8
- data/Gemfile +0 -19
- data/bin/rake2thor +0 -86
- data/lib/thor/core_ext/file_binary_read.rb +0 -9
- data/spec/task_spec.rb +0 -80
data/lib/thor/version.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'helper'
|
2
2
|
require 'thor/actions'
|
3
3
|
require 'tempfile'
|
4
4
|
|
@@ -23,6 +23,10 @@ describe Thor::Actions::CreateLink do
|
|
23
23
|
capture(:stdout) { @action.invoke! }
|
24
24
|
end
|
25
25
|
|
26
|
+
def revoke!
|
27
|
+
capture(:stdout) { @action.revoke! }
|
28
|
+
end
|
29
|
+
|
26
30
|
def silence!
|
27
31
|
@silence = true
|
28
32
|
end
|
@@ -78,4 +82,14 @@ describe Thor::Actions::CreateLink do
|
|
78
82
|
expect(@action.identical?).to be_true
|
79
83
|
end
|
80
84
|
end
|
85
|
+
|
86
|
+
describe "#revoke!" do
|
87
|
+
it "removes the symbolic link of non-existent destination" do
|
88
|
+
create_link("doc/config.rb")
|
89
|
+
invoke!
|
90
|
+
File.delete(@tempfile.path)
|
91
|
+
revoke!
|
92
|
+
expect(File.symlink?(@action.destination)).to be_false
|
93
|
+
end
|
94
|
+
end
|
81
95
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'helper'
|
2
2
|
require 'thor/actions'
|
3
3
|
|
4
4
|
describe Thor::Actions::Directory do
|
@@ -56,18 +56,31 @@ describe Thor::Actions::Directory do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it "copies only the first level files if recursive" do
|
59
|
-
invoke! ".", "
|
59
|
+
invoke! ".", "commands", :recursive => false
|
60
60
|
|
61
|
-
file = File.join(destination_root, "
|
61
|
+
file = File.join(destination_root, "commands", "group.thor")
|
62
62
|
expect(File.exists?(file)).to be_true
|
63
63
|
|
64
|
-
file = File.join(destination_root, "
|
64
|
+
file = File.join(destination_root, "commands", "doc")
|
65
65
|
expect(File.exists?(file)).to be_false
|
66
66
|
|
67
|
-
file = File.join(destination_root, "
|
67
|
+
file = File.join(destination_root, "commands", "doc", "README")
|
68
68
|
expect(File.exists?(file)).to be_false
|
69
69
|
end
|
70
70
|
|
71
|
+
it "ignores files within excluding/ directories when exclude_pattern is provided" do
|
72
|
+
invoke! "doc", "docs", :exclude_pattern => /excluding\//
|
73
|
+
file = File.join(destination_root, "docs", "excluding", "rdoc.rb")
|
74
|
+
expect(File.exists?(file)).to be_false
|
75
|
+
end
|
76
|
+
|
77
|
+
it "copies and evalutes files within excluding/ directory when no exclude_pattern is present" do
|
78
|
+
invoke! "doc", "docs"
|
79
|
+
file = File.join(destination_root, "docs", "excluding", "rdoc.rb")
|
80
|
+
expect(File.exists?(file)).to be_true
|
81
|
+
expect(File.read(file)).to eq("BAR = BAR\n")
|
82
|
+
end
|
83
|
+
|
71
84
|
it "copies files from the source relative to the current path" do
|
72
85
|
invoker.inside "doc" do
|
73
86
|
invoke! "."
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'helper'
|
2
2
|
|
3
3
|
class Application; end
|
4
4
|
|
@@ -52,13 +52,13 @@ describe Thor::Actions do
|
|
52
52
|
|
53
53
|
describe "#copy_file" do
|
54
54
|
it "copies file from source to default destination" do
|
55
|
-
action :copy_file, "
|
56
|
-
exists_and_identical?("
|
55
|
+
action :copy_file, "command.thor"
|
56
|
+
exists_and_identical?("command.thor", "command.thor")
|
57
57
|
end
|
58
58
|
|
59
59
|
it "copies file from source to the specified destination" do
|
60
|
-
action :copy_file, "
|
61
|
-
exists_and_identical?("
|
60
|
+
action :copy_file, "command.thor", "foo.thor"
|
61
|
+
exists_and_identical?("command.thor", "foo.thor")
|
62
62
|
end
|
63
63
|
|
64
64
|
it "copies file from the source relative to the current path" do
|
@@ -76,26 +76,26 @@ describe Thor::Actions do
|
|
76
76
|
end
|
77
77
|
|
78
78
|
it "logs status" do
|
79
|
-
expect(action(:copy_file, "
|
79
|
+
expect(action(:copy_file, "command.thor")).to eq(" create command.thor\n")
|
80
80
|
end
|
81
81
|
|
82
82
|
it "accepts a block to change output" do
|
83
|
-
action :copy_file, "
|
83
|
+
action :copy_file, "command.thor" do |content|
|
84
84
|
"OMG" + content
|
85
85
|
end
|
86
|
-
expect(File.read(File.join(destination_root, "
|
86
|
+
expect(File.read(File.join(destination_root, "command.thor"))).to match(/^OMG/)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
90
|
describe "#link_file" do
|
91
91
|
it "links file from source to default destination" do
|
92
|
-
action :link_file, "
|
93
|
-
exists_and_identical?("
|
92
|
+
action :link_file, "command.thor"
|
93
|
+
exists_and_identical?("command.thor", "command.thor")
|
94
94
|
end
|
95
95
|
|
96
96
|
it "links file from source to the specified destination" do
|
97
|
-
action :link_file, "
|
98
|
-
exists_and_identical?("
|
97
|
+
action :link_file, "command.thor", "foo.thor"
|
98
|
+
exists_and_identical?("command.thor", "foo.thor")
|
99
99
|
end
|
100
100
|
|
101
101
|
it "links file from the source relative to the current path" do
|
@@ -106,7 +106,7 @@ describe Thor::Actions do
|
|
106
106
|
end
|
107
107
|
|
108
108
|
it "logs status" do
|
109
|
-
expect(action(:link_file, "
|
109
|
+
expect(action(:link_file, "command.thor")).to eq(" create command.thor\n")
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
data/spec/actions_spec.rb
CHANGED
data/spec/base_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'helper'
|
2
2
|
require 'thor/base'
|
3
3
|
|
4
4
|
class Amazing
|
@@ -45,10 +45,10 @@ describe Thor::Base do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
describe "#
|
49
|
-
it "avoids methods being added as
|
50
|
-
expect(MyScript.
|
51
|
-
expect(MyScript.
|
48
|
+
describe "#no_commands" do
|
49
|
+
it "avoids methods being added as commands" do
|
50
|
+
expect(MyScript.commands.keys).to include("animal")
|
51
|
+
expect(MyScript.commands.keys).not_to include("this_is_not_a_command")
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -69,6 +69,17 @@ describe Thor::Base do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
describe ":aliases" do
|
73
|
+
it "supports string aliases without a dash prefix" do
|
74
|
+
expect(MyCounter.start(["1", "2", "-z", "3"])[4]).to eq(3)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "supports symbol aliases" do
|
78
|
+
expect(MyCounter.start(["1", "2", "-y", "3"])[5]).to eq(3)
|
79
|
+
expect(MyCounter.start(["1", "2", "-r", "3"])[5]).to eq(3)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
72
83
|
describe "#class_option" do
|
73
84
|
it "sets options class wise" do
|
74
85
|
expect(MyCounter.start(["1", "2", "--third", "3"])[2]).to eq(3)
|
@@ -128,7 +139,7 @@ describe Thor::Base do
|
|
128
139
|
|
129
140
|
it "use padding in options that does not have aliases" do
|
130
141
|
expect(@content).to match(/^ -t, \[--third/)
|
131
|
-
expect(@content).to match(/^
|
142
|
+
expect(@content).to match(/^ \[--fourth/)
|
132
143
|
end
|
133
144
|
|
134
145
|
it "allows extra options to be given" do
|
@@ -182,42 +193,43 @@ describe Thor::Base do
|
|
182
193
|
thorfile = File.join(File.dirname(__FILE__), "fixtures", "script.thor")
|
183
194
|
expect(Thor::Base.subclass_files[File.expand_path(thorfile)]).to eq([
|
184
195
|
MyScript, MyScript::AnotherScript, MyChildScript, Barn,
|
185
|
-
Scripts::MyScript, Scripts::MyDefaults,
|
196
|
+
PackageNameScript, Scripts::MyScript, Scripts::MyDefaults,
|
197
|
+
Scripts::ChildDefault, Scripts::Arities
|
186
198
|
])
|
187
199
|
end
|
188
200
|
|
189
201
|
it "tracks a single subclass across multiple files" do
|
190
|
-
thorfile = File.join(File.dirname(__FILE__), "fixtures", "
|
202
|
+
thorfile = File.join(File.dirname(__FILE__), "fixtures", "command.thor")
|
191
203
|
expect(Thor::Base.subclass_files[File.expand_path(thorfile)]).to include(Amazing)
|
192
204
|
expect(Thor::Base.subclass_files[File.expand_path(__FILE__)]).to include(Amazing)
|
193
205
|
end
|
194
206
|
end
|
195
207
|
|
196
|
-
describe "#
|
197
|
-
it "returns a list with all
|
208
|
+
describe "#commands" do
|
209
|
+
it "returns a list with all commands defined in this class" do
|
198
210
|
expect(MyChildScript.new).to respond_to("animal")
|
199
|
-
expect(MyChildScript.
|
211
|
+
expect(MyChildScript.commands.keys).to include("animal")
|
200
212
|
end
|
201
213
|
|
202
|
-
it "raises an error if a
|
214
|
+
it "raises an error if a command with reserved word is defined" do
|
203
215
|
expect {
|
204
216
|
klass = Class.new(Thor::Group)
|
205
217
|
klass.class_eval "def shell; end"
|
206
|
-
}.to raise_error(RuntimeError, /"shell" is a Thor reserved word and cannot be defined as
|
218
|
+
}.to raise_error(RuntimeError, /"shell" is a Thor reserved word and cannot be defined as command/)
|
207
219
|
end
|
208
220
|
end
|
209
221
|
|
210
|
-
describe "#
|
211
|
-
it "returns a list with all
|
222
|
+
describe "#all_commands" do
|
223
|
+
it "returns a list with all commands defined in this class plus superclasses" do
|
212
224
|
expect(MyChildScript.new).to respond_to("foo")
|
213
|
-
expect(MyChildScript.
|
225
|
+
expect(MyChildScript.all_commands.keys).to include("foo")
|
214
226
|
end
|
215
227
|
end
|
216
228
|
|
217
|
-
describe "#
|
218
|
-
it "removes the
|
219
|
-
expect(MyChildScript.
|
220
|
-
expect(MyChildScript.
|
229
|
+
describe "#remove_command" do
|
230
|
+
it "removes the command from its commands hash" do
|
231
|
+
expect(MyChildScript.commands.keys).not_to include("bar")
|
232
|
+
expect(MyChildScript.commands.keys).not_to include("boom")
|
221
233
|
end
|
222
234
|
|
223
235
|
it "undefines the method if desired" do
|
@@ -237,7 +249,7 @@ describe Thor::Base do
|
|
237
249
|
ENV["THOR_DEBUG"] = 1
|
238
250
|
expect {
|
239
251
|
MyScript.start ["what", "--debug"]
|
240
|
-
}.to raise_error(Thor::
|
252
|
+
}.to raise_error(Thor::UndefinedcommandError, 'Could not find command "what" in "my_script" namespace.')
|
241
253
|
rescue
|
242
254
|
ENV["THOR_DEBUG"] = nil
|
243
255
|
end
|
@@ -263,17 +275,21 @@ describe Thor::Base do
|
|
263
275
|
end
|
264
276
|
|
265
277
|
describe "attr_*" do
|
266
|
-
it "does not add attr_reader as a
|
278
|
+
it "does not add attr_reader as a command" do
|
267
279
|
expect(capture(:stderr){ MyScript.start(["another_attribute"]) }).to match(/Could not find/)
|
268
280
|
end
|
269
281
|
|
270
|
-
it "does not add attr_writer as a
|
282
|
+
it "does not add attr_writer as a command" do
|
271
283
|
expect(capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }).to match(/Could not find/)
|
272
284
|
end
|
273
285
|
|
274
|
-
it "does not add attr_accessor as a
|
286
|
+
it "does not add attr_accessor as a command" do
|
275
287
|
expect(capture(:stderr){ MyScript.start(["some_attribute"]) }).to match(/Could not find/)
|
276
288
|
expect(capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }).to match(/Could not find/)
|
277
289
|
end
|
290
|
+
|
291
|
+
it "respects visibility" do
|
292
|
+
expect(MyScript.public_instance_methods).to_not include(:private_attribute)
|
293
|
+
end
|
278
294
|
end
|
279
295
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Thor::Command do
|
4
|
+
def command(options={})
|
5
|
+
options.each do |key, value|
|
6
|
+
options[key] = Thor::Option.parse(key, value)
|
7
|
+
end
|
8
|
+
|
9
|
+
@command ||= Thor::Command.new(:can_has, "I can has cheezburger", "I can has cheezburger\nLots and lots of it", "can_has", options)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#formatted_usage" do
|
13
|
+
it "includes namespace within usage" do
|
14
|
+
object = Struct.new(:namespace, :arguments).new("foo", [])
|
15
|
+
expect(command(:bar => :required).formatted_usage(object)).to eq("foo:can_has --bar=BAR")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "includes subcommand name within subcommand usage" do
|
19
|
+
object = Struct.new(:namespace, :arguments).new("main:foo", [])
|
20
|
+
expect(command(:bar => :required).formatted_usage(object, false, true)).to eq("foo can_has --bar=BAR")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "removes default from namespace" do
|
24
|
+
object = Struct.new(:namespace, :arguments).new("default:foo", [])
|
25
|
+
expect(command(:bar => :required).formatted_usage(object)).to eq(":foo:can_has --bar=BAR")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "injects arguments into usage" do
|
29
|
+
options = {:required => true, :type => :string}
|
30
|
+
object = Struct.new(:namespace, :arguments).new("foo", [Thor::Argument.new(:bar, options)])
|
31
|
+
expect(command(:foo => :required).formatted_usage(object)).to eq("foo:can_has BAR --foo=FOO")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#dynamic" do
|
36
|
+
it "creates a dynamic command with the given name" do
|
37
|
+
expect(Thor::DynamicCommand.new('command').name).to eq('command')
|
38
|
+
expect(Thor::DynamicCommand.new('command').description).to eq('A dynamically-generated command')
|
39
|
+
expect(Thor::DynamicCommand.new('command').usage).to eq('command')
|
40
|
+
expect(Thor::DynamicCommand.new('command').options).to eq({})
|
41
|
+
end
|
42
|
+
|
43
|
+
it "does not invoke an existing method" do
|
44
|
+
mock = mock()
|
45
|
+
mock.class.should_receive(:handle_no_command_error).with("to_s")
|
46
|
+
Thor::DynamicCommand.new('to_s').run(mock)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#dup" do
|
51
|
+
it "dup options hash" do
|
52
|
+
command = Thor::Command.new("can_has", nil, nil, nil, :foo => true, :bar => :required)
|
53
|
+
command.dup.options.delete(:foo)
|
54
|
+
expect(command.options[:foo]).to be
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#run" do
|
59
|
+
it "runs a command by calling a method in the given instance" do
|
60
|
+
mock = mock()
|
61
|
+
mock.should_receive(:can_has).and_return {|*args| args }
|
62
|
+
expect(command.run(mock, [1, 2, 3])).to eq([1, 2, 3])
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises an error if the method to be invoked is private" do
|
66
|
+
klass = Class.new do
|
67
|
+
def self.handle_no_command_error(name)
|
68
|
+
name
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def can_has
|
73
|
+
"fail"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
expect(command.run(klass.new)).to eq("can_has")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/exit_condition_spec.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require 'helper'
|
2
2
|
require 'thor/base'
|
3
3
|
|
4
4
|
describe "Exit conditions" do
|
5
5
|
it "exits 0, not bubble up EPIPE, if EPIPE is raised" do
|
6
6
|
epiped = false
|
7
7
|
|
8
|
-
|
8
|
+
command = Class.new(Thor) do
|
9
9
|
desc "my_action", "testing EPIPE"
|
10
10
|
define_method :my_action do
|
11
11
|
epiped = true
|
@@ -13,7 +13,7 @@ describe "Exit conditions" do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
expect{
|
16
|
+
expect{ command.start(["my_action"]) }.to raise_error(SystemExit)
|
17
17
|
expect(epiped).to eq(true)
|
18
18
|
end
|
19
19
|
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
FOO = <%= "FOO" %>
|