thor 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/{CHANGELOG.rdoc → CHANGELOG.md} +30 -36
  2. data/README.md +10 -3
  3. data/lib/thor.rb +205 -180
  4. data/lib/thor/actions.rb +5 -5
  5. data/lib/thor/actions/create_link.rb +3 -0
  6. data/lib/thor/actions/directory.rb +2 -0
  7. data/lib/thor/actions/file_manipulation.rb +1 -1
  8. data/lib/thor/base.rb +84 -95
  9. data/lib/thor/{task.rb → command.rb} +17 -13
  10. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  11. data/lib/thor/error.rb +4 -7
  12. data/lib/thor/group.rb +30 -33
  13. data/lib/thor/invocation.rb +28 -26
  14. data/lib/thor/parser/options.rb +3 -1
  15. data/lib/thor/runner.rb +21 -20
  16. data/lib/thor/shell/basic.rb +5 -1
  17. data/lib/thor/shell/color.rb +4 -0
  18. data/lib/thor/shell/html.rb +4 -0
  19. data/lib/thor/util.rb +214 -210
  20. data/lib/thor/version.rb +1 -1
  21. data/spec/actions/create_file_spec.rb +1 -1
  22. data/spec/actions/create_link_spec.rb +15 -1
  23. data/spec/actions/directory_spec.rb +18 -5
  24. data/spec/actions/empty_directory_spec.rb +1 -1
  25. data/spec/actions/file_manipulation_spec.rb +13 -13
  26. data/spec/actions/inject_into_file_spec.rb +1 -1
  27. data/spec/actions_spec.rb +1 -1
  28. data/spec/base_spec.rb +40 -24
  29. data/spec/command_spec.rb +80 -0
  30. data/spec/core_ext/hash_with_indifferent_access_spec.rb +1 -1
  31. data/spec/core_ext/ordered_hash_spec.rb +1 -1
  32. data/spec/exit_condition_spec.rb +3 -3
  33. data/spec/fixtures/{task.thor → command.thor} +0 -0
  34. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  35. data/spec/fixtures/doc/COMMENTER +11 -0
  36. data/spec/fixtures/doc/README +3 -0
  37. data/spec/fixtures/doc/block_helper.rb +3 -0
  38. data/spec/fixtures/doc/config.rb +1 -0
  39. data/spec/fixtures/doc/config.yaml.tt +1 -0
  40. data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
  41. data/spec/fixtures/group.thor +24 -10
  42. data/spec/fixtures/invoke.thor +3 -3
  43. data/spec/fixtures/script.thor +40 -15
  44. data/spec/fixtures/subcommand.thor +17 -0
  45. data/spec/group_spec.rb +13 -13
  46. data/spec/{spec_helper.rb → helper.rb} +11 -7
  47. data/spec/invocation_spec.rb +16 -16
  48. data/spec/parser/argument_spec.rb +4 -4
  49. data/spec/parser/arguments_spec.rb +1 -1
  50. data/spec/parser/option_spec.rb +1 -1
  51. data/spec/parser/options_spec.rb +6 -1
  52. data/spec/rake_compat_spec.rb +1 -1
  53. data/spec/register_spec.rb +12 -12
  54. data/spec/runner_spec.rb +36 -36
  55. data/spec/shell/basic_spec.rb +9 -10
  56. data/spec/shell/color_spec.rb +16 -2
  57. data/spec/shell/html_spec.rb +1 -1
  58. data/spec/shell_spec.rb +1 -1
  59. data/spec/subcommand_spec.rb +30 -0
  60. data/spec/thor_spec.rb +81 -78
  61. data/spec/util_spec.rb +10 -10
  62. data/thor.gemspec +22 -18
  63. metadata +49 -38
  64. data/.gitignore +0 -44
  65. data/.rspec +0 -3
  66. data/.travis.yml +0 -8
  67. data/Gemfile +0 -19
  68. data/bin/rake2thor +0 -86
  69. data/lib/thor/core_ext/file_binary_read.rb +0 -9
  70. data/spec/task_spec.rb +0 -80
@@ -1,3 +1,3 @@
1
1
  class Thor
2
- VERSION = "0.17.0"
2
+ VERSION = "0.18.0"
3
3
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/actions'
3
3
 
4
4
  describe Thor::Actions::CreateFile do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
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 File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
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! ".", "tasks", :recursive => false
59
+ invoke! ".", "commands", :recursive => false
60
60
 
61
- file = File.join(destination_root, "tasks", "group.thor")
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, "tasks", "doc")
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, "tasks", "doc", "README")
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 File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/actions'
3
3
 
4
4
  describe Thor::Actions::EmptyDirectory do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
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, "task.thor"
56
- exists_and_identical?("task.thor", "task.thor")
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, "task.thor", "foo.thor"
61
- exists_and_identical?("task.thor", "foo.thor")
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, "task.thor")).to eq(" create task.thor\n")
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, "task.thor" do |content|
83
+ action :copy_file, "command.thor" do |content|
84
84
  "OMG" + content
85
85
  end
86
- expect(File.read(File.join(destination_root, "task.thor"))).to match(/^OMG/)
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, "task.thor"
93
- exists_and_identical?("task.thor", "task.thor")
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, "task.thor", "foo.thor"
98
- exists_and_identical?("task.thor", "foo.thor")
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, "task.thor")).to eq(" create task.thor\n")
109
+ expect(action(:link_file, "command.thor")).to eq(" create command.thor\n")
110
110
  end
111
111
  end
112
112
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/actions'
3
3
 
4
4
  describe Thor::Actions::InjectIntoFile do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'helper'
2
2
 
3
3
  describe Thor::Actions do
4
4
  def runner(options={})
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
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 "#no_tasks" do
49
- it "avoids methods being added as tasks" do
50
- expect(MyScript.tasks.keys).to include("animal")
51
- expect(MyScript.tasks.keys).not_to include("this_is_not_a_task")
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(/^ \[--fourth/)
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, Scripts::ChildDefault
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", "task.thor")
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 "#tasks" do
197
- it "returns a list with all tasks defined in this class" do
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.tasks.keys).to include("animal")
211
+ expect(MyChildScript.commands.keys).to include("animal")
200
212
  end
201
213
 
202
- it "raises an error if a task with reserved word is defined" do
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 task/)
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 "#all_tasks" do
211
- it "returns a list with all tasks defined in this class plus superclasses" do
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.all_tasks.keys).to include("foo")
225
+ expect(MyChildScript.all_commands.keys).to include("foo")
214
226
  end
215
227
  end
216
228
 
217
- describe "#remove_task" do
218
- it "removes the task from its tasks hash" do
219
- expect(MyChildScript.tasks.keys).not_to include("bar")
220
- expect(MyChildScript.tasks.keys).not_to include("boom")
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::UndefinedTaskError, 'Could not find task "what" in "my_script" namespace.')
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 task" do
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 task" do
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 task" do
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
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/core_ext/hash_with_indifferent_access'
3
3
 
4
4
  describe Thor::CoreExt::HashWithIndifferentAccess do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'helper'
2
2
  require 'thor/core_ext/ordered_hash'
3
3
 
4
4
  describe Thor::CoreExt::OrderedHash do
@@ -1,11 +1,11 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
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
- task = Class.new(Thor) do
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{ task.start(["my_action"]) }.to raise_error(SystemExit)
16
+ expect{ command.start(["my_action"]) }.to raise_error(SystemExit)
17
17
  expect(epiped).to eq(true)
18
18
  end
19
19
  end
@@ -0,0 +1 @@
1
+ FOO = <%= "FOO" %>
@@ -0,0 +1,11 @@
1
+ __start__
2
+ # greenblue
3
+ #
4
+ # yellowblue
5
+ #yellowred
6
+ #greenred
7
+ orange
8
+ purple
9
+ ind#igo
10
+ # ind#igo
11
+ __end__
@@ -0,0 +1,3 @@
1
+ __start__
2
+ README
3
+ __end__