thor_dleavitt 0.18.1

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 (86) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/CHANGELOG.md +139 -0
  4. data/LICENSE.md +20 -0
  5. data/README.md +35 -0
  6. data/Thorfile +30 -0
  7. data/bin/thor_dleavitt +6 -0
  8. data/lib/thor/actions.rb +318 -0
  9. data/lib/thor/actions/create_file.rb +105 -0
  10. data/lib/thor/actions/create_link.rb +60 -0
  11. data/lib/thor/actions/directory.rb +119 -0
  12. data/lib/thor/actions/empty_directory.rb +137 -0
  13. data/lib/thor/actions/file_manipulation.rb +317 -0
  14. data/lib/thor/actions/inject_into_file.rb +109 -0
  15. data/lib/thor/base.rb +654 -0
  16. data/lib/thor/command.rb +136 -0
  17. data/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  18. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  19. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  20. data/lib/thor/error.rb +32 -0
  21. data/lib/thor/group.rb +282 -0
  22. data/lib/thor/invocation.rb +172 -0
  23. data/lib/thor/parser.rb +4 -0
  24. data/lib/thor/parser/argument.rb +74 -0
  25. data/lib/thor/parser/arguments.rb +171 -0
  26. data/lib/thor/parser/option.rb +121 -0
  27. data/lib/thor/parser/options.rb +218 -0
  28. data/lib/thor/rake_compat.rb +72 -0
  29. data/lib/thor/runner.rb +322 -0
  30. data/lib/thor/shell.rb +88 -0
  31. data/lib/thor/shell/basic.rb +422 -0
  32. data/lib/thor/shell/color.rb +148 -0
  33. data/lib/thor/shell/html.rb +127 -0
  34. data/lib/thor/util.rb +270 -0
  35. data/lib/thor/version.rb +3 -0
  36. data/lib/thor_dleavitt.rb +473 -0
  37. data/spec/actions/create_file_spec.rb +170 -0
  38. data/spec/actions/create_link_spec.rb +95 -0
  39. data/spec/actions/directory_spec.rb +169 -0
  40. data/spec/actions/empty_directory_spec.rb +129 -0
  41. data/spec/actions/file_manipulation_spec.rb +382 -0
  42. data/spec/actions/inject_into_file_spec.rb +135 -0
  43. data/spec/actions_spec.rb +331 -0
  44. data/spec/base_spec.rb +291 -0
  45. data/spec/command_spec.rb +80 -0
  46. data/spec/core_ext/hash_with_indifferent_access_spec.rb +48 -0
  47. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  48. data/spec/exit_condition_spec.rb +19 -0
  49. data/spec/fixtures/application.rb +2 -0
  50. data/spec/fixtures/app{1}/README +3 -0
  51. data/spec/fixtures/bundle/execute.rb +6 -0
  52. data/spec/fixtures/bundle/main.thor +1 -0
  53. data/spec/fixtures/command.thor +10 -0
  54. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  55. data/spec/fixtures/doc/COMMENTER +11 -0
  56. data/spec/fixtures/doc/README +3 -0
  57. data/spec/fixtures/doc/block_helper.rb +3 -0
  58. data/spec/fixtures/doc/config.rb +1 -0
  59. data/spec/fixtures/doc/config.yaml.tt +1 -0
  60. data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
  61. data/spec/fixtures/enum.thor +10 -0
  62. data/spec/fixtures/group.thor +128 -0
  63. data/spec/fixtures/invoke.thor +118 -0
  64. data/spec/fixtures/path with spaces b/data/spec/fixtures/path with → spaces +0 -0
  65. data/spec/fixtures/preserve/script.sh +3 -0
  66. data/spec/fixtures/script.thor +220 -0
  67. data/spec/fixtures/subcommand.thor +17 -0
  68. data/spec/group_spec.rb +222 -0
  69. data/spec/helper.rb +67 -0
  70. data/spec/invocation_spec.rb +108 -0
  71. data/spec/parser/argument_spec.rb +53 -0
  72. data/spec/parser/arguments_spec.rb +66 -0
  73. data/spec/parser/option_spec.rb +202 -0
  74. data/spec/parser/options_spec.rb +400 -0
  75. data/spec/rake_compat_spec.rb +72 -0
  76. data/spec/register_spec.rb +197 -0
  77. data/spec/runner_spec.rb +241 -0
  78. data/spec/shell/basic_spec.rb +330 -0
  79. data/spec/shell/color_spec.rb +95 -0
  80. data/spec/shell/html_spec.rb +31 -0
  81. data/spec/shell_spec.rb +47 -0
  82. data/spec/subcommand_spec.rb +30 -0
  83. data/spec/thor_spec.rb +499 -0
  84. data/spec/util_spec.rb +196 -0
  85. data/thor.gemspec +24 -0
  86. metadata +191 -0
@@ -0,0 +1,331 @@
1
+ require '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
+ expect(MyCounter.class_options.keys).to include(:pretend)
19
+ expect(MyCounter.class_options.keys).to include(:force)
20
+ expect(MyCounter.class_options.keys).to include(:quiet)
21
+ expect(MyCounter.class_options.keys).to include(:skip)
22
+ end
23
+ end
24
+
25
+ describe "#initialize" do
26
+ it "has default behavior invoke" do
27
+ expect(runner.behavior).to eq(:invoke)
28
+ end
29
+
30
+ it "can have behavior revoke" do
31
+ expect(MyCounter.new([1], {}, :behavior => :revoke).behavior).to eq(: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
+ expect(runner.behavior).to eq(:invoke)
37
+ expect(runner.options.force).to be_true
38
+ expect(runner.options.skip).not_to 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
+ expect(runner.behavior).to eq(:invoke)
44
+ expect(runner.options.force).not_to be_true
45
+ expect(runner.options.skip).to 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
+ expect(base.destination_root).to eq(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
+ expect(base.destination_root).to eq(root)
62
+ end
63
+
64
+ it "uses the current directory if none is given" do
65
+ base = MyCounter.new([1])
66
+ expect(base.destination_root).to eq(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
+ expect(runner.relative_to_original_destination_root(file)).to eq("foo")
73
+ end
74
+
75
+ it "does not remove dot if required" do
76
+ expect(runner.relative_to_original_destination_root(file, false)).to eq("./foo")
77
+ end
78
+
79
+ it "always use the absolute root" do
80
+ runner.inside("foo") do
81
+ expect(runner.relative_to_original_destination_root(file)).to eq("foo")
82
+ end
83
+ end
84
+
85
+ it "creates proper relative paths for absolute file location" do
86
+ expect(runner.relative_to_original_destination_root('/test/file')).to eq("/test/file")
87
+ end
88
+
89
+ it "does not fail with files containing regexp characters" do
90
+ runner = MyCounter.new([1], {}, { :destination_root => File.join(destination_root, "fo[o-b]ar") })
91
+ expect(runner.relative_to_original_destination_root("bar")).to eq("bar")
92
+ end
93
+
94
+ describe "#source_paths_for_search" do
95
+ it "add source_root to source_paths_for_search" do
96
+ expect(MyCounter.source_paths_for_search).to include(File.expand_path("fixtures", File.dirname(__FILE__)))
97
+ end
98
+
99
+ it "keeps only current source root in source paths" do
100
+ expect(ClearCounter.source_paths_for_search).to include(File.expand_path("fixtures/bundle", File.dirname(__FILE__)))
101
+ expect(ClearCounter.source_paths_for_search).not_to include(File.expand_path("fixtures", File.dirname(__FILE__)))
102
+ end
103
+
104
+ it "customized source paths should be before source roots" do
105
+ expect(ClearCounter.source_paths_for_search[0]).to eq(File.expand_path("fixtures/doc", File.dirname(__FILE__)))
106
+ expect(ClearCounter.source_paths_for_search[1]).to eq(File.expand_path("fixtures/bundle", File.dirname(__FILE__)))
107
+ end
108
+
109
+ it "keeps inherited source paths at the end" do
110
+ expect(ClearCounter.source_paths_for_search.last).to eq(File.expand_path("fixtures/broken", File.dirname(__FILE__)))
111
+ end
112
+ end
113
+ end
114
+
115
+ describe "#find_in_source_paths" do
116
+ it "raises an error if source path is empty" do
117
+ expect {
118
+ A.new.find_in_source_paths("foo")
119
+ }.to raise_error(Thor::Error, /Currently you have no source paths/)
120
+ end
121
+
122
+ it "finds a template inside the source path" do
123
+ expect(runner.find_in_source_paths("doc")).to eq(File.expand_path("doc", source_root))
124
+ expect{ runner.find_in_source_paths("README") }.to raise_error
125
+
126
+ new_path = File.join(source_root, "doc")
127
+ runner.instance_variable_set(:@source_paths, nil)
128
+ runner.source_paths.unshift(new_path)
129
+ expect(runner.find_in_source_paths("README")).to eq(File.expand_path("README", new_path))
130
+ end
131
+ end
132
+ end
133
+
134
+ describe "#inside" do
135
+ it "executes the block inside the given folder" do
136
+ runner.inside("foo") do
137
+ expect(Dir.pwd).to eq(file)
138
+ end
139
+ end
140
+
141
+ it "changes the base root" do
142
+ runner.inside("foo") do
143
+ expect(runner.destination_root).to eq(file)
144
+ end
145
+ end
146
+
147
+ it "creates the directory if it does not exist" do
148
+ runner.inside("foo") do
149
+ expect(File.exists?(file)).to be_true
150
+ end
151
+ end
152
+
153
+ describe "when pretending" do
154
+ it "no directories should be created" do
155
+ runner.inside("bar", :pretend => true) {}
156
+ expect(File.exists?("bar")).to be_false
157
+ end
158
+ end
159
+
160
+ describe "when verbose" do
161
+ it "logs status" do
162
+ expect(capture(:stdout) {
163
+ runner.inside("foo", :verbose => true) {}
164
+ }).to match(/inside foo/)
165
+ end
166
+
167
+ it "uses padding in next status" do
168
+ expect(capture(:stdout) {
169
+ runner.inside("foo", :verbose => true) do
170
+ runner.say_status :cool, :padding
171
+ end
172
+ }).to match(/cool padding/)
173
+ end
174
+
175
+ it "removes padding after block" do
176
+ expect(capture(:stdout) {
177
+ runner.inside("foo", :verbose => true) {}
178
+ runner.say_status :no, :padding
179
+ }).to match(/no padding/)
180
+ end
181
+ end
182
+ end
183
+
184
+ describe "#in_root" do
185
+ it "executes the block in the root folder" do
186
+ runner.inside("foo") do
187
+ runner.in_root { expect(Dir.pwd).to eq(destination_root) }
188
+ end
189
+ end
190
+
191
+ it "changes the base root" do
192
+ runner.inside("foo") do
193
+ runner.in_root { expect(runner.destination_root).to eq(destination_root) }
194
+ end
195
+ end
196
+
197
+ it "returns to the previous state" do
198
+ runner.inside("foo") do
199
+ runner.in_root { }
200
+ expect(runner.destination_root).to eq(file)
201
+ end
202
+ end
203
+ end
204
+
205
+ describe "#apply" do
206
+ before do
207
+ @template = <<-TEMPLATE
208
+ @foo = "FOO"
209
+ say_status :cool, :padding
210
+ TEMPLATE
211
+ allow(@template).to receive(:read).and_return(@template)
212
+
213
+ @file = '/'
214
+ allow(runner).to receive(:open).and_return(@template)
215
+ end
216
+
217
+ it "accepts a URL as the path" do
218
+ @file = "http://gist.github.com/103208.txt"
219
+ expect(runner).to receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template)
220
+ action(:apply, @file)
221
+ end
222
+
223
+ it "accepts a secure URL as the path" do
224
+ @file = "https://gist.github.com/103208.txt"
225
+ expect(runner).to receive(:open).with(@file, "Accept" => "application/x-thor-template").and_return(@template)
226
+ action(:apply, @file)
227
+ end
228
+
229
+ it "accepts a local file path with spaces" do
230
+ @file = File.expand_path("fixtures/path with spaces", File.dirname(__FILE__))
231
+ expect(runner).to receive(:open).with(@file).and_return(@template)
232
+ action(:apply, @file)
233
+ end
234
+
235
+ it "opens a file and executes its content in the instance binding" do
236
+ action :apply, @file
237
+ expect(runner.instance_variable_get("@foo")).to eq("FOO")
238
+ end
239
+
240
+ it "applies padding to the content inside the file" do
241
+ expect(action(:apply, @file)).to match(/cool padding/)
242
+ end
243
+
244
+ it "logs its status" do
245
+ expect(action(:apply, @file)).to match(/ apply #{@file}\n/)
246
+ end
247
+
248
+ it "does not log status" do
249
+ content = action(:apply, @file, :verbose => false)
250
+ expect(content).to match(/cool padding/)
251
+ expect(content).not_to match(/apply http/)
252
+ end
253
+ end
254
+
255
+ describe "#run" do
256
+ before do
257
+ expect(runner).to receive(:system).with("ls")
258
+ end
259
+
260
+ it "executes the command given" do
261
+ action :run, "ls"
262
+ end
263
+
264
+ it "logs status" do
265
+ expect(action(:run, "ls")).to eq(" run ls from \".\"\n")
266
+ end
267
+
268
+ it "does not log status if required" do
269
+ expect(action(:run, "ls", :verbose => false)).to be_empty
270
+ end
271
+
272
+ it "accepts a color as status" do
273
+ expect(runner.shell).to receive(:say_status).with(:run, 'ls from "."', :yellow)
274
+ action :run, "ls", :verbose => :yellow
275
+ end
276
+ end
277
+
278
+ describe "#run_ruby_script" do
279
+ before do
280
+ allow(Thor::Util).to receive(:ruby_command).and_return("/opt/jruby")
281
+ expect(runner).to receive(:system).with("/opt/jruby script.rb")
282
+ end
283
+
284
+ it "executes the ruby script" do
285
+ action :run_ruby_script, "script.rb"
286
+ end
287
+
288
+ it "logs status" do
289
+ expect(action(:run_ruby_script, "script.rb")).to eq(" run jruby script.rb from \".\"\n")
290
+ end
291
+
292
+ it "does not log status if required" do
293
+ expect(action(:run_ruby_script, "script.rb", :verbose => false)).to be_empty
294
+ end
295
+ end
296
+
297
+ describe "#thor" do
298
+ it "executes the thor command" do
299
+ expect(runner).to receive(:system).with("thor list")
300
+ action :thor, :list, :verbose => true
301
+ end
302
+
303
+ it "converts extra arguments to command arguments" do
304
+ expect(runner).to receive(:system).with("thor list foo bar")
305
+ action :thor, :list, "foo", "bar"
306
+ end
307
+
308
+ it "converts options hash to switches" do
309
+ expect(runner).to receive(:system).with("thor list foo bar --foo")
310
+ action :thor, :list, "foo", "bar", :foo => true
311
+
312
+ expect(runner).to receive(:system).with("thor list --foo 1 2 3")
313
+ action :thor, :list, :foo => [1,2,3]
314
+ end
315
+
316
+ it "logs status" do
317
+ expect(runner).to receive(:system).with("thor list")
318
+ expect(action(:thor, :list)).to eq(" run thor list from \".\"\n")
319
+ end
320
+
321
+ it "does not log status if required" do
322
+ expect(runner).to receive(:system).with("thor list --foo 1 2 3")
323
+ expect(action(:thor, :list, :foo => [1,2,3], :verbose => false)).to be_empty
324
+ end
325
+
326
+ it "captures the output when :capture is given" do
327
+ expect(runner).to receive(:`).with("thor foo bar")
328
+ action(:thor, "foo", "bar", :capture => true)
329
+ end
330
+ end
331
+ end
data/spec/base_spec.rb ADDED
@@ -0,0 +1,291 @@
1
+ require '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
+ expect(base.first).to eq(1)
16
+ expect(base.second).to eq(2)
17
+ end
18
+
19
+ it "sets arguments default values" do
20
+ base = MyCounter.new [1]
21
+ expect(base.second).to eq(2)
22
+ end
23
+
24
+ it "sets options default values" do
25
+ base = MyCounter.new [1, 2]
26
+ expect(base.options[:third]).to eq(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
+ expect(base.options[:third]).to eq(4)
32
+
33
+ base = MyCounter.new [1, 2], "third" => 4
34
+ expect(base.options[:third]).to eq(4)
35
+ end
36
+
37
+ it "creates options with indifferent access" do
38
+ base = MyCounter.new [1, 2], :third => 3
39
+ expect(base.options['third']).to eq(3)
40
+ end
41
+
42
+ it "creates options with magic predicates" do
43
+ base = MyCounter.new [1, 2], :third => 3
44
+ expect(base.options.third).to eq(3)
45
+ end
46
+ end
47
+
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
+ end
53
+ end
54
+
55
+ describe "#argument" do
56
+ it "sets a value as required and creates an accessor for it" do
57
+ expect(MyCounter.start(["1", "2", "--third", "3"])[0]).to eq(1)
58
+ expect(Scripts::MyScript.start(["zoo", "my_special_param", "--param=normal_param"])).to eq("my_special_param")
59
+ end
60
+
61
+ it "does not set a value in the options hash" do
62
+ expect(BrokenCounter.start(["1", "2", "--third", "3"])[0]).to be_nil
63
+ end
64
+ end
65
+
66
+ describe "#arguments" do
67
+ it "returns the arguments for the class" do
68
+ expect(MyCounter.arguments).to have(2).items
69
+ end
70
+ end
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
+
83
+ describe "#class_option" do
84
+ it "sets options class wise" do
85
+ expect(MyCounter.start(["1", "2", "--third", "3"])[2]).to eq(3)
86
+ end
87
+
88
+ it "does not create an accessor for it" do
89
+ expect(BrokenCounter.start(["1", "2", "--third", "3"])[3]).to be_false
90
+ end
91
+ end
92
+
93
+ describe "#class_options" do
94
+ it "sets default options overwriting superclass definitions" do
95
+ options = Scripts::MyScript.class_options
96
+ expect(options[:force]).not_to be_required
97
+ end
98
+ end
99
+
100
+ describe "#remove_argument" do
101
+ it "removes previously defined arguments from class" do
102
+ expect(ClearCounter.arguments).to be_empty
103
+ end
104
+
105
+ it "undefine accessors if required" do
106
+ expect(ClearCounter.new).not_to respond_to(:first)
107
+ expect(ClearCounter.new).not_to respond_to(:second)
108
+ end
109
+ end
110
+
111
+ describe "#remove_class_option" do
112
+ it "removes previous defined class option" do
113
+ expect(ClearCounter.class_options[:third]).to be_nil
114
+ end
115
+ end
116
+
117
+ describe "#class_options_help" do
118
+ before do
119
+ @content = capture(:stdout) { MyCounter.help(Thor::Base.shell.new) }
120
+ end
121
+
122
+ it "shows option's description" do
123
+ expect(@content).to match(/# The third argument/)
124
+ end
125
+
126
+ it "shows usage with banner content" do
127
+ expect(@content).to match(/\[\-\-third=THREE\]/)
128
+ end
129
+
130
+ it "shows default values below descriptions" do
131
+ expect(@content).to match(/# Default: 3/)
132
+ end
133
+
134
+ it "shows options in different groups" do
135
+ expect(@content).to match(/Options\:/)
136
+ expect(@content).to match(/Runtime options\:/)
137
+ expect(@content).to match(/\-p, \[\-\-pretend\]/)
138
+ end
139
+
140
+ it "use padding in options that do not have aliases" do
141
+ expect(@content).to match(/^ -t, \[--third/)
142
+ expect(@content).to match(/^ \[--fourth/)
143
+ end
144
+
145
+ it "allows extra options to be given" do
146
+ hash = { "Foo" => B.class_options.values }
147
+
148
+ content = capture(:stdout) { MyCounter.send(:class_options_help, Thor::Base.shell.new, hash) }
149
+ expect(content).to match(/Foo options\:/)
150
+ expect(content).to match(/--last-name=LAST_NAME/)
151
+ end
152
+
153
+ it "displays choices for enums" do
154
+ content = capture(:stdout) { Enum.help(Thor::Base.shell.new) }
155
+ expect(content).to match(/Possible values\: apple, banana/)
156
+ end
157
+ end
158
+
159
+ describe "#namespace" do
160
+ it "returns the default class namespace" do
161
+ expect(Scripts::MyScript.namespace).to eq("scripts:my_script")
162
+ end
163
+
164
+ it "sets a namespace to the class" do
165
+ expect(Scripts::MyDefaults.namespace).to eq("default")
166
+ end
167
+ end
168
+
169
+ describe "#group" do
170
+ it "sets a group" do
171
+ expect(MyScript.group).to eq("script")
172
+ end
173
+
174
+ it "inherits the group from parent" do
175
+ expect(MyChildScript.group).to eq("script")
176
+ end
177
+
178
+ it "defaults to standard if no group is given" do
179
+ expect(Amazing.group).to eq("standard")
180
+ end
181
+ end
182
+
183
+ describe "#subclasses" do
184
+ it "tracks its subclasses in an Array" do
185
+ expect(Thor::Base.subclasses).to include(MyScript)
186
+ expect(Thor::Base.subclasses).to include(MyChildScript)
187
+ expect(Thor::Base.subclasses).to include(Scripts::MyScript)
188
+ end
189
+ end
190
+
191
+ describe "#subclass_files" do
192
+ it "returns tracked subclasses, grouped by the files they come from" do
193
+ thorfile = File.join(File.dirname(__FILE__), "fixtures", "script.thor")
194
+ expect(Thor::Base.subclass_files[File.expand_path(thorfile)]).to eq([
195
+ MyScript, MyScript::AnotherScript, MyChildScript, Barn,
196
+ PackageNameScript, Scripts::MyScript, Scripts::MyDefaults,
197
+ Scripts::ChildDefault, Scripts::Arities
198
+ ])
199
+ end
200
+
201
+ it "tracks a single subclass across multiple files" do
202
+ thorfile = File.join(File.dirname(__FILE__), "fixtures", "command.thor")
203
+ expect(Thor::Base.subclass_files[File.expand_path(thorfile)]).to include(Amazing)
204
+ expect(Thor::Base.subclass_files[File.expand_path(__FILE__)]).to include(Amazing)
205
+ end
206
+ end
207
+
208
+ describe "#commands" do
209
+ it "returns a list with all commands defined in this class" do
210
+ expect(MyChildScript.new).to respond_to("animal")
211
+ expect(MyChildScript.commands.keys).to include("animal")
212
+ end
213
+
214
+ it "raises an error if a command with reserved word is defined" do
215
+ expect {
216
+ klass = Class.new(Thor::Group)
217
+ klass.class_eval "def shell; end"
218
+ }.to raise_error(RuntimeError, /"shell" is a Thor reserved word and cannot be defined as command/)
219
+ end
220
+ end
221
+
222
+ describe "#all_commands" do
223
+ it "returns a list with all commands defined in this class plus superclasses" do
224
+ expect(MyChildScript.new).to respond_to("foo")
225
+ expect(MyChildScript.all_commands.keys).to include("foo")
226
+ end
227
+ end
228
+
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")
233
+ end
234
+
235
+ it "undefines the method if desired" do
236
+ expect(MyChildScript.new).not_to respond_to("boom")
237
+ end
238
+ end
239
+
240
+ describe "#from_superclass" do
241
+ it "does not send a method to the superclass if the superclass does not respond to it" do
242
+ expect(MyCounter.get_from_super).to eq(13)
243
+ end
244
+ end
245
+
246
+ describe "#start" do
247
+ it "raises an error instead of rescuing if THOR_DEBUG=1 is given" do
248
+ begin
249
+ ENV["THOR_DEBUG"] = 1
250
+ expect {
251
+ MyScript.start ["what", "--debug"]
252
+ }.to raise_error(Thor::UndefinedcommandError, 'Could not find command "what" in "my_script" namespace.')
253
+ rescue
254
+ ENV["THOR_DEBUG"] = nil
255
+ end
256
+ end
257
+
258
+ it "does not steal args" do
259
+ args = ["foo", "bar", "--force", "true"]
260
+ MyScript.start(args)
261
+ expect(args).to eq(["foo", "bar", "--force", "true"])
262
+ end
263
+
264
+ it "checks unknown options" do
265
+ expect(capture(:stderr) {
266
+ MyScript.start(["foo", "bar", "--force", "true", "--unknown", "baz"])
267
+ }.strip).to eq("Unknown switches '--unknown'")
268
+ end
269
+
270
+ it "checks unknown options except specified" do
271
+ expect(capture(:stderr) {
272
+ expect(MyScript.start(["with_optional", "NAME", "--omg", "--invalid"])).to eq(["NAME", {}, ["--omg", "--invalid"]])
273
+ }.strip).to be_empty
274
+ end
275
+ end
276
+
277
+ describe "attr_*" do
278
+ it "does not add attr_reader as a command" do
279
+ expect(capture(:stderr){ MyScript.start(["another_attribute"]) }).to match(/Could not find/)
280
+ end
281
+
282
+ it "does not add attr_writer as a command" do
283
+ expect(capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }).to match(/Could not find/)
284
+ end
285
+
286
+ it "does not add attr_accessor as a command" do
287
+ expect(capture(:stderr){ MyScript.start(["some_attribute"]) }).to match(/Could not find/)
288
+ expect(capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }).to match(/Could not find/)
289
+ end
290
+ end
291
+ end