thor 0.17.0 → 0.18.0

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 (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__