thor-exclude_pattern 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 (107) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/CHANGELOG.md +130 -0
  4. data/LICENSE.md +20 -0
  5. data/README.md +34 -0
  6. data/Thorfile +30 -0
  7. data/bin/rake2thor +86 -0
  8. data/bin/thor +6 -0
  9. data/lib/thor.rb +458 -0
  10. data/lib/thor/actions.rb +318 -0
  11. data/lib/thor/actions/create_file.rb +105 -0
  12. data/lib/thor/actions/create_link.rb +60 -0
  13. data/lib/thor/actions/directory.rb +119 -0
  14. data/lib/thor/actions/empty_directory.rb +153 -0
  15. data/lib/thor/actions/file_manipulation.rb +314 -0
  16. data/lib/thor/actions/inject_into_file.rb +109 -0
  17. data/lib/thor/base.rb +649 -0
  18. data/lib/thor/command.rb +136 -0
  19. data/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  20. data/lib/thor/core_ext/io_binary_read.rb +12 -0
  21. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  22. data/lib/thor/error.rb +32 -0
  23. data/lib/thor/exclude_pattern/version.rb +5 -0
  24. data/lib/thor/group.rb +287 -0
  25. data/lib/thor/invocation.rb +172 -0
  26. data/lib/thor/parser.rb +4 -0
  27. data/lib/thor/parser/argument.rb +74 -0
  28. data/lib/thor/parser/arguments.rb +171 -0
  29. data/lib/thor/parser/option.rb +121 -0
  30. data/lib/thor/parser/options.rb +218 -0
  31. data/lib/thor/rake_compat.rb +72 -0
  32. data/lib/thor/runner.rb +322 -0
  33. data/lib/thor/shell.rb +88 -0
  34. data/lib/thor/shell/basic.rb +393 -0
  35. data/lib/thor/shell/color.rb +148 -0
  36. data/lib/thor/shell/html.rb +127 -0
  37. data/lib/thor/util.rb +270 -0
  38. data/lib/thor/version.rb +3 -0
  39. data/spec/actions/create_file_spec.rb +170 -0
  40. data/spec/actions/create_link_spec.rb +95 -0
  41. data/spec/actions/directory_spec.rb +169 -0
  42. data/spec/actions/empty_directory_spec.rb +130 -0
  43. data/spec/actions/file_manipulation_spec.rb +382 -0
  44. data/spec/actions/inject_into_file_spec.rb +135 -0
  45. data/spec/actions_spec.rb +331 -0
  46. data/spec/base_spec.rb +294 -0
  47. data/spec/command_spec.rb +80 -0
  48. data/spec/core_ext/hash_with_indifferent_access_spec.rb +48 -0
  49. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  50. data/spec/exit_condition_spec.rb +19 -0
  51. data/spec/fixtures/application.rb +2 -0
  52. data/spec/fixtures/app{1}/README +3 -0
  53. data/spec/fixtures/bundle/execute.rb +6 -0
  54. data/spec/fixtures/bundle/main.thor +1 -0
  55. data/spec/fixtures/command.thor +10 -0
  56. data/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  57. data/spec/fixtures/doc/COMMENTER +11 -0
  58. data/spec/fixtures/doc/README +3 -0
  59. data/spec/fixtures/doc/block_helper.rb +3 -0
  60. data/spec/fixtures/doc/config.rb +1 -0
  61. data/spec/fixtures/doc/config.yaml.tt +1 -0
  62. data/spec/fixtures/doc/excluding/%file_name%.rb.tt +1 -0
  63. data/spec/fixtures/enum.thor +10 -0
  64. data/spec/fixtures/group.thor +128 -0
  65. data/spec/fixtures/invoke.thor +112 -0
  66. data/spec/fixtures/path with spaces b/data/spec/fixtures/path with → spaces +0 -0
  67. data/spec/fixtures/preserve/script.sh +3 -0
  68. data/spec/fixtures/script.thor +199 -0
  69. data/spec/fixtures/subcommand.thor +17 -0
  70. data/spec/group_spec.rb +216 -0
  71. data/spec/helper.rb +67 -0
  72. data/spec/invocation_spec.rb +100 -0
  73. data/spec/parser/argument_spec.rb +53 -0
  74. data/spec/parser/arguments_spec.rb +66 -0
  75. data/spec/parser/option_spec.rb +202 -0
  76. data/spec/parser/options_spec.rb +400 -0
  77. data/spec/rake_compat_spec.rb +72 -0
  78. data/spec/register_spec.rb +197 -0
  79. data/spec/runner_spec.rb +241 -0
  80. data/spec/sandbox/application.rb +2 -0
  81. data/spec/sandbox/app{1}/README +3 -0
  82. data/spec/sandbox/bundle/execute.rb +6 -0
  83. data/spec/sandbox/bundle/main.thor +1 -0
  84. data/spec/sandbox/command.thor +10 -0
  85. data/spec/sandbox/doc/%file_name%.rb.tt +1 -0
  86. data/spec/sandbox/doc/COMMENTER +11 -0
  87. data/spec/sandbox/doc/README +4 -0
  88. data/spec/sandbox/doc/block_helper.rb +3 -0
  89. data/spec/sandbox/doc/config.rb +1 -0
  90. data/spec/sandbox/doc/config.yaml.tt +1 -0
  91. data/spec/sandbox/doc/excluding/%file_name%.rb.tt +1 -0
  92. data/spec/sandbox/enum.thor +10 -0
  93. data/spec/sandbox/group.thor +128 -0
  94. data/spec/sandbox/invoke.thor +112 -0
  95. data/spec/sandbox/path with spaces b/data/spec/sandbox/path with → spaces +0 -0
  96. data/spec/sandbox/preserve/script.sh +3 -0
  97. data/spec/sandbox/script.thor +199 -0
  98. data/spec/sandbox/subcommand.thor +17 -0
  99. data/spec/shell/basic_spec.rb +311 -0
  100. data/spec/shell/color_spec.rb +95 -0
  101. data/spec/shell/html_spec.rb +32 -0
  102. data/spec/shell_spec.rb +47 -0
  103. data/spec/subcommand_spec.rb +30 -0
  104. data/spec/thor_spec.rb +469 -0
  105. data/spec/util_spec.rb +196 -0
  106. data/thor.gemspec +24 -0
  107. metadata +232 -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 constaining 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
+ @template.stub(:read).and_return(@template)
212
+
213
+ @file = '/'
214
+ runner.stub(: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
+ runner.should_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
+ runner.should_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
+ runner.should_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
+ runner.should_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
+ runner.shell.should_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
+ Thor::Util.stub!(:ruby_command).and_return("/opt/jruby")
281
+ runner.should_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
+ runner.should_receive(:system).with("thor list")
300
+ action :thor, :list, :verbose => true
301
+ end
302
+
303
+ it "converts extra arguments to command arguments" do
304
+ runner.should_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
+ runner.should_receive(:system).with("thor list foo bar --foo")
310
+ action :thor, :list, "foo", "bar", :foo => true
311
+
312
+ runner.should_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
+ runner.should_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
+ runner.should_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
+ runner.should_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,294 @@
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 previous 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 options 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 description" 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 does 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
+ Scripts::MyScript, Scripts::MyDefaults, Scripts::ChildDefault
197
+ ])
198
+ end
199
+
200
+ it "tracks a single subclass across multiple files" do
201
+ thorfile = File.join(File.dirname(__FILE__), "fixtures", "command.thor")
202
+ expect(Thor::Base.subclass_files[File.expand_path(thorfile)]).to include(Amazing)
203
+ expect(Thor::Base.subclass_files[File.expand_path(__FILE__)]).to include(Amazing)
204
+ end
205
+ end
206
+
207
+ describe "#commands" do
208
+ it "returns a list with all commands defined in this class" do
209
+ expect(MyChildScript.new).to respond_to("animal")
210
+ expect(MyChildScript.commands.keys).to include("animal")
211
+ end
212
+
213
+ it "raises an error if a command with reserved word is defined" do
214
+ expect {
215
+ klass = Class.new(Thor::Group)
216
+ klass.class_eval "def shell; end"
217
+ }.to raise_error(RuntimeError, /"shell" is a Thor reserved word and cannot be defined as command/)
218
+ end
219
+ end
220
+
221
+ describe "#all_commands" do
222
+ it "returns a list with all commands defined in this class plus superclasses" do
223
+ expect(MyChildScript.new).to respond_to("foo")
224
+ expect(MyChildScript.all_commands.keys).to include("foo")
225
+ end
226
+ end
227
+
228
+ describe "#remove_command" do
229
+ it "removes the command from its commands hash" do
230
+ expect(MyChildScript.commands.keys).not_to include("bar")
231
+ expect(MyChildScript.commands.keys).not_to include("boom")
232
+ end
233
+
234
+ it "undefines the method if desired" do
235
+ expect(MyChildScript.new).not_to respond_to("boom")
236
+ end
237
+ end
238
+
239
+ describe "#from_superclass" do
240
+ it "does not send a method to the superclass if the superclass does not respond to it" do
241
+ expect(MyCounter.get_from_super).to eq(13)
242
+ end
243
+ end
244
+
245
+ describe "#start" do
246
+ it "raises an error instead of rescueing if THOR_DEBUG=1 is given" do
247
+ begin
248
+ ENV["THOR_DEBUG"] = 1
249
+ expect {
250
+ MyScript.start ["what", "--debug"]
251
+ }.to raise_error(Thor::UndefinedcommandError, 'Could not find command "what" in "my_script" namespace.')
252
+ rescue
253
+ ENV["THOR_DEBUG"] = nil
254
+ end
255
+ end
256
+
257
+ it "does not steal args" do
258
+ args = ["foo", "bar", "--force", "true"]
259
+ MyScript.start(args)
260
+ expect(args).to eq(["foo", "bar", "--force", "true"])
261
+ end
262
+
263
+ it "checks unknown options" do
264
+ expect(capture(:stderr) {
265
+ MyScript.start(["foo", "bar", "--force", "true", "--unknown", "baz"])
266
+ }.strip).to eq("Unknown switches '--unknown'")
267
+ end
268
+
269
+ it "checks unknown options except specified" do
270
+ expect(capture(:stderr) {
271
+ expect(MyScript.start(["with_optional", "NAME", "--omg", "--invalid"])).to eq(["NAME", {}, ["--omg", "--invalid"]])
272
+ }.strip).to be_empty
273
+ end
274
+ end
275
+
276
+ describe "attr_*" do
277
+ it "does not add attr_reader as a command" do
278
+ expect(capture(:stderr){ MyScript.start(["another_attribute"]) }).to match(/Could not find/)
279
+ end
280
+
281
+ it "does not add attr_writer as a command" do
282
+ expect(capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }).to match(/Could not find/)
283
+ end
284
+
285
+ it "does not add attr_accessor as a command" do
286
+ expect(capture(:stderr){ MyScript.start(["some_attribute"]) }).to match(/Could not find/)
287
+ expect(capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }).to match(/Could not find/)
288
+ end
289
+
290
+ it "respects visibility" do
291
+ expect(MyScript.public_instance_methods).to_not include(:private_attribute)
292
+ end
293
+ end
294
+ end