angry_mob 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +123 -0
  3. data/bin/mob +139 -0
  4. data/lib/angry_mob.rb +28 -0
  5. data/lib/angry_mob/act.rb +111 -0
  6. data/lib/angry_mob/act/scheduler.rb +143 -0
  7. data/lib/angry_mob/action.rb +11 -0
  8. data/lib/angry_mob/builder.rb +115 -0
  9. data/lib/angry_mob/extend.rb +10 -0
  10. data/lib/angry_mob/extend/array.rb +30 -0
  11. data/lib/angry_mob/extend/blank.rb +108 -0
  12. data/lib/angry_mob/extend/blankslate.rb +109 -0
  13. data/lib/angry_mob/extend/dictionary.rb +140 -0
  14. data/lib/angry_mob/extend/hash.rb +67 -0
  15. data/lib/angry_mob/extend/object.rb +21 -0
  16. data/lib/angry_mob/extend/pathname.rb +23 -0
  17. data/lib/angry_mob/extend/string.rb +8 -0
  18. data/lib/angry_mob/log.rb +28 -0
  19. data/lib/angry_mob/mob.rb +77 -0
  20. data/lib/angry_mob/mob_loader.rb +115 -0
  21. data/lib/angry_mob/node.rb +44 -0
  22. data/lib/angry_mob/notifier.rb +76 -0
  23. data/lib/angry_mob/target.rb +257 -0
  24. data/lib/angry_mob/target/arguments.rb +71 -0
  25. data/lib/angry_mob/target/call.rb +57 -0
  26. data/lib/angry_mob/target/default_resource_locator.rb +11 -0
  27. data/lib/angry_mob/target/defaults.rb +23 -0
  28. data/lib/angry_mob/target/mother.rb +66 -0
  29. data/lib/angry_mob/target/notify.rb +57 -0
  30. data/lib/angry_mob/target/tracking.rb +96 -0
  31. data/lib/angry_mob/ui.rb +247 -0
  32. data/lib/angry_mob/util.rb +11 -0
  33. data/lib/angry_mob/vendored.rb +8 -0
  34. data/lib/angry_mob/version.rb +3 -0
  35. data/vendor/angry_hash/Rakefile +17 -0
  36. data/vendor/angry_hash/VERSION +1 -0
  37. data/vendor/angry_hash/angry_hash.gemspec +47 -0
  38. data/vendor/angry_hash/examples/accessors_eg.rb +46 -0
  39. data/vendor/angry_hash/examples/creation_eg.rb +43 -0
  40. data/vendor/angry_hash/examples/dsl.eg.rb +18 -0
  41. data/vendor/angry_hash/examples/dup_eg.rb +86 -0
  42. data/vendor/angry_hash/examples/eg_helper.rb +24 -0
  43. data/vendor/angry_hash/examples/merge_eg.rb +135 -0
  44. data/vendor/angry_hash/lib/angry_hash.rb +215 -0
  45. data/vendor/angry_hash/lib/angry_hash/dsl.rb +44 -0
  46. data/vendor/angry_hash/lib/angry_hash/extension_tracking.rb +12 -0
  47. data/vendor/angry_hash/lib/angry_hash/merge_string.rb +58 -0
  48. data/vendor/json/COPYING +58 -0
  49. data/vendor/json/GPL +340 -0
  50. data/vendor/json/README +360 -0
  51. data/vendor/json/lib/json/common.rb +371 -0
  52. data/vendor/json/lib/json/pure.rb +77 -0
  53. data/vendor/json/lib/json/pure/generator.rb +443 -0
  54. data/vendor/json/lib/json/pure/parser.rb +303 -0
  55. data/vendor/json/lib/json/version.rb +8 -0
  56. data/vendor/thor/CHANGELOG.rdoc +89 -0
  57. data/vendor/thor/LICENSE +20 -0
  58. data/vendor/thor/README.rdoc +297 -0
  59. data/vendor/thor/Thorfile +69 -0
  60. data/vendor/thor/bin/rake2thor +86 -0
  61. data/vendor/thor/bin/thor +6 -0
  62. data/vendor/thor/lib/thor.rb +244 -0
  63. data/vendor/thor/lib/thor/actions.rb +275 -0
  64. data/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  65. data/vendor/thor/lib/thor/actions/directory.rb +91 -0
  66. data/vendor/thor/lib/thor/actions/empty_directory.rb +134 -0
  67. data/vendor/thor/lib/thor/actions/file_manipulation.rb +223 -0
  68. data/vendor/thor/lib/thor/actions/inject_into_file.rb +104 -0
  69. data/vendor/thor/lib/thor/base.rb +540 -0
  70. data/vendor/thor/lib/thor/core_ext/file_binary_read.rb +9 -0
  71. data/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  72. data/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
  73. data/vendor/thor/lib/thor/error.rb +30 -0
  74. data/vendor/thor/lib/thor/group.rb +271 -0
  75. data/vendor/thor/lib/thor/invocation.rb +180 -0
  76. data/vendor/thor/lib/thor/parser.rb +4 -0
  77. data/vendor/thor/lib/thor/parser/argument.rb +67 -0
  78. data/vendor/thor/lib/thor/parser/arguments.rb +150 -0
  79. data/vendor/thor/lib/thor/parser/option.rb +128 -0
  80. data/vendor/thor/lib/thor/parser/options.rb +169 -0
  81. data/vendor/thor/lib/thor/rake_compat.rb +66 -0
  82. data/vendor/thor/lib/thor/runner.rb +314 -0
  83. data/vendor/thor/lib/thor/shell.rb +83 -0
  84. data/vendor/thor/lib/thor/shell/basic.rb +239 -0
  85. data/vendor/thor/lib/thor/shell/color.rb +108 -0
  86. data/vendor/thor/lib/thor/task.rb +102 -0
  87. data/vendor/thor/lib/thor/util.rb +224 -0
  88. data/vendor/thor/lib/thor/version.rb +3 -0
  89. data/vendor/thor/spec/actions/create_file_spec.rb +170 -0
  90. data/vendor/thor/spec/actions/directory_spec.rb +131 -0
  91. data/vendor/thor/spec/actions/empty_directory_spec.rb +91 -0
  92. data/vendor/thor/spec/actions/file_manipulation_spec.rb +271 -0
  93. data/vendor/thor/spec/actions/inject_into_file_spec.rb +135 -0
  94. data/vendor/thor/spec/actions_spec.rb +292 -0
  95. data/vendor/thor/spec/base_spec.rb +263 -0
  96. data/vendor/thor/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  97. data/vendor/thor/spec/core_ext/ordered_hash_spec.rb +115 -0
  98. data/vendor/thor/spec/fixtures/application.rb +2 -0
  99. data/vendor/thor/spec/fixtures/bundle/execute.rb +6 -0
  100. data/vendor/thor/spec/fixtures/bundle/main.thor +1 -0
  101. data/vendor/thor/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  102. data/vendor/thor/spec/fixtures/doc/README +3 -0
  103. data/vendor/thor/spec/fixtures/doc/config.rb +1 -0
  104. data/vendor/thor/spec/fixtures/group.thor +90 -0
  105. data/vendor/thor/spec/fixtures/invoke.thor +112 -0
  106. data/vendor/thor/spec/fixtures/script.thor +145 -0
  107. data/vendor/thor/spec/fixtures/task.thor +10 -0
  108. data/vendor/thor/spec/group_spec.rb +171 -0
  109. data/vendor/thor/spec/invocation_spec.rb +107 -0
  110. data/vendor/thor/spec/parser/argument_spec.rb +47 -0
  111. data/vendor/thor/spec/parser/arguments_spec.rb +64 -0
  112. data/vendor/thor/spec/parser/option_spec.rb +202 -0
  113. data/vendor/thor/spec/parser/options_spec.rb +292 -0
  114. data/vendor/thor/spec/rake_compat_spec.rb +68 -0
  115. data/vendor/thor/spec/runner_spec.rb +210 -0
  116. data/vendor/thor/spec/shell/basic_spec.rb +205 -0
  117. data/vendor/thor/spec/shell/color_spec.rb +41 -0
  118. data/vendor/thor/spec/shell_spec.rb +34 -0
  119. data/vendor/thor/spec/spec.opts +1 -0
  120. data/vendor/thor/spec/spec_helper.rb +54 -0
  121. data/vendor/thor/spec/task_spec.rb +69 -0
  122. data/vendor/thor/spec/thor_spec.rb +237 -0
  123. data/vendor/thor/spec/util_spec.rb +163 -0
  124. data/vendor/thor/thor.gemspec +120 -0
  125. metadata +199 -0
@@ -0,0 +1,292 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_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
+ MyCounter.class_options.keys.must include(:pretend)
19
+ MyCounter.class_options.keys.must include(:force)
20
+ MyCounter.class_options.keys.must include(:quiet)
21
+ MyCounter.class_options.keys.must include(:skip)
22
+ end
23
+ end
24
+
25
+ describe "#initialize" do
26
+ it "has default behavior invoke" do
27
+ runner.behavior.must == :invoke
28
+ end
29
+
30
+ it "can have behavior revoke" do
31
+ MyCounter.new([1], {}, :behavior => :revoke).behavior.must == :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
+ runner.behavior.must == :invoke
37
+ runner.options.force.must be_true
38
+ runner.options.skip.must_not 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
+ runner.behavior.must == :invoke
44
+ runner.options.force.must_not be_true
45
+ runner.options.skip.must 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
+ base.destination_root.must == 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
+ base.destination_root.must == root
62
+ end
63
+
64
+ it "uses the current directory if none is given" do
65
+ base = MyCounter.new([1])
66
+ base.destination_root.must == 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
+ runner.relative_to_original_destination_root(file).must == "foo"
73
+ end
74
+
75
+ it "does not remove dot if required" do
76
+ runner.relative_to_original_destination_root(file, false).must == "./foo"
77
+ end
78
+
79
+ it "always use the absolute root" do
80
+ runner.inside("foo") do
81
+ runner.relative_to_original_destination_root(file).must == "foo"
82
+ end
83
+ end
84
+
85
+ describe "#source_paths_for_search" do
86
+ it "add source_root to source_paths_for_search" do
87
+ MyCounter.source_paths_for_search.must include(File.expand_path("fixtures", File.dirname(__FILE__)))
88
+ end
89
+
90
+ it "keeps only current source root in source paths" do
91
+ ClearCounter.source_paths_for_search.must include(File.expand_path("fixtures/bundle", File.dirname(__FILE__)))
92
+ ClearCounter.source_paths_for_search.must_not include(File.expand_path("fixtures", File.dirname(__FILE__)))
93
+ end
94
+
95
+ it "customized source paths should be before source roots" do
96
+ ClearCounter.source_paths_for_search[0].must == File.expand_path("fixtures/doc", File.dirname(__FILE__))
97
+ ClearCounter.source_paths_for_search[1].must == File.expand_path("fixtures/bundle", File.dirname(__FILE__))
98
+ end
99
+
100
+ it "keeps inherited source paths at the end" do
101
+ ClearCounter.source_paths_for_search.last.must == File.expand_path("fixtures/broken", File.dirname(__FILE__))
102
+ end
103
+ end
104
+ end
105
+
106
+ describe "#find_in_source_paths" do
107
+ it "raises an error if source path is empty" do
108
+ lambda {
109
+ A.new.find_in_source_paths("foo")
110
+ }.must raise_error(Thor::Error, /You don't have any source path defined for class A/)
111
+ end
112
+
113
+ it "finds a template inside the source path" do
114
+ runner.find_in_source_paths("doc").must == File.expand_path("doc", source_root)
115
+ lambda { runner.find_in_source_paths("README") }.must raise_error
116
+
117
+ new_path = File.join(source_root, "doc")
118
+ runner.instance_variable_set(:@source_paths, nil)
119
+ runner.source_paths.unshift(new_path)
120
+ runner.find_in_source_paths("README").must == File.expand_path("README", new_path)
121
+ end
122
+ end
123
+ end
124
+
125
+ describe "#inside" do
126
+ it "executes the block inside the given folder" do
127
+ runner.inside("foo") do
128
+ Dir.pwd.must == file
129
+ end
130
+ end
131
+
132
+ it "changes the base root" do
133
+ runner.inside("foo") do
134
+ runner.destination_root.must == file
135
+ end
136
+ end
137
+
138
+ it "creates the directory if it does not exist" do
139
+ runner.inside("foo") do
140
+ File.exists?(file).must be_true
141
+ end
142
+ end
143
+
144
+ describe "when verbose" do
145
+ it "logs status" do
146
+ capture(:stdout) do
147
+ runner.inside("foo", :verbose => true) {}
148
+ end.must =~ /inside foo/
149
+ end
150
+
151
+ it "uses padding in next status" do
152
+ capture(:stdout) do
153
+ runner.inside("foo", :verbose => true) do
154
+ runner.say_status :cool, :padding
155
+ end
156
+ end.must =~ /cool padding/
157
+ end
158
+
159
+ it "removes padding after block" do
160
+ capture(:stdout) do
161
+ runner.inside("foo", :verbose => true) {}
162
+ runner.say_status :no, :padding
163
+ end.must =~ /no padding/
164
+ end
165
+ end
166
+ end
167
+
168
+ describe "#in_root" do
169
+ it "executes the block in the root folder" do
170
+ runner.inside("foo") do
171
+ runner.in_root { Dir.pwd.must == destination_root }
172
+ end
173
+ end
174
+
175
+ it "changes the base root" do
176
+ runner.inside("foo") do
177
+ runner.in_root { runner.destination_root.must == destination_root }
178
+ end
179
+ end
180
+
181
+ it "returns to the previous state" do
182
+ runner.inside("foo") do
183
+ runner.in_root { }
184
+ runner.destination_root.must == file
185
+ end
186
+ end
187
+ end
188
+
189
+ describe "#apply" do
190
+ before(:each) do
191
+ @template = <<-TEMPLATE
192
+ @foo = "FOO"
193
+ say_status :cool, :padding
194
+ TEMPLATE
195
+ @template.instance_eval "def read; self; end" # Make the string respond to read
196
+
197
+ @file = "http://gist.github.com/103208.txt"
198
+ runner.should_receive(:open).and_return(@template)
199
+ end
200
+
201
+ it "opens a file and executes its content in the instance binding" do
202
+ action :apply, @file
203
+ runner.instance_variable_get("@foo").must == "FOO"
204
+ end
205
+
206
+ it "applies padding to the content inside the file" do
207
+ action(:apply, @file).must =~ /cool padding/
208
+ end
209
+
210
+ it "logs its status" do
211
+ action(:apply, @file).must =~ / apply #{@file}\n/
212
+ end
213
+
214
+ it "does not log status" do
215
+ content = action(:apply, @file, :verbose => false)
216
+ content.must =~ /cool padding/
217
+ content.must_not =~ /apply http/
218
+ end
219
+ end
220
+
221
+ describe "#run" do
222
+ before(:each) do
223
+ runner.should_receive(:`).with("ls")
224
+ end
225
+
226
+ it "executes the command given" do
227
+ action :run, "ls"
228
+ end
229
+
230
+ it "logs status" do
231
+ action(:run, "ls").must == " run ls from \".\"\n"
232
+ end
233
+
234
+ it "does not log status if required" do
235
+ action(:run, "ls", :verbose => false).must be_empty
236
+ end
237
+
238
+ it "accepts a color as status" do
239
+ runner.shell.should_receive(:say_status).with(:run, 'ls from "."', :yellow)
240
+ action :run, "ls", :verbose => :yellow
241
+ end
242
+ end
243
+
244
+ describe "#run_ruby_script" do
245
+ before(:each) do
246
+ Thor::Util.stub!(:ruby_command).and_return("/opt/jruby")
247
+ runner.should_receive(:`).with("/opt/jruby script.rb")
248
+ end
249
+
250
+ it "executes the ruby script" do
251
+ action :run_ruby_script, "script.rb"
252
+ end
253
+
254
+ it "logs status" do
255
+ action(:run_ruby_script, "script.rb").must == " run jruby script.rb from \".\"\n"
256
+ end
257
+
258
+ it "does not log status if required" do
259
+ action(:run_ruby_script, "script.rb", :verbose => false).must be_empty
260
+ end
261
+ end
262
+
263
+ describe "#thor" do
264
+ it "executes the thor command" do
265
+ runner.should_receive(:`).with("thor list")
266
+ action :thor, :list, :verbose => true
267
+ end
268
+
269
+ it "converts extra arguments to command arguments" do
270
+ runner.should_receive(:`).with("thor list foo bar")
271
+ action :thor, :list, "foo", "bar"
272
+ end
273
+
274
+ it "converts options hash to switches" do
275
+ runner.should_receive(:`).with("thor list foo bar --foo")
276
+ action :thor, :list, "foo", "bar", :foo => true
277
+
278
+ runner.should_receive(:`).with("thor list --foo 1 2 3")
279
+ action :thor, :list, :foo => [1,2,3]
280
+ end
281
+
282
+ it "logs status" do
283
+ runner.should_receive(:`).with("thor list")
284
+ action(:thor, :list).must == " run thor list from \".\"\n"
285
+ end
286
+
287
+ it "does not log status if required" do
288
+ runner.should_receive(:`).with("thor list --foo 1 2 3")
289
+ action(:thor, :list, :foo => [1,2,3], :verbose => false).must be_empty
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,263 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_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
+ base.first.must == 1
16
+ base.second.must == 2
17
+ end
18
+
19
+ it "sets arguments default values" do
20
+ base = MyCounter.new [1]
21
+ base.second.must == 2
22
+ end
23
+
24
+ it "sets options default values" do
25
+ base = MyCounter.new [1, 2]
26
+ base.options[:third].must == 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
+ base.options[:third].must == 4
32
+
33
+ base = MyCounter.new [1, 2], "third" => 4
34
+ base.options[:third].must == 4
35
+ end
36
+
37
+ it "creates options with indifferent access" do
38
+ base = MyCounter.new [1, 2], :third => 3
39
+ base.options['third'].must == 3
40
+ end
41
+
42
+ it "creates options with magic predicates" do
43
+ base = MyCounter.new [1, 2], :third => 3
44
+ base.options.third.must == 3
45
+ end
46
+ end
47
+
48
+ describe "#no_tasks" do
49
+ it "avoids methods being added as tasks" do
50
+ MyScript.tasks.keys.must include("animal")
51
+ MyScript.tasks.keys.must_not include("this_is_not_a_task")
52
+ end
53
+ end
54
+
55
+ describe "#argument" do
56
+ it "sets a value as required and creates an accessor for it" do
57
+ MyCounter.start(["1", "2", "--third", "3"])[0].must == 1
58
+ Scripts::MyScript.start(["zoo", "my_special_param", "--param=normal_param"]).must == "my_special_param"
59
+ end
60
+
61
+ it "does not set a value in the options hash" do
62
+ BrokenCounter.start(["1", "2", "--third", "3"])[0].must be_nil
63
+ end
64
+ end
65
+
66
+ describe "#arguments" do
67
+ it "returns the arguments for the class" do
68
+ MyCounter.arguments.must have(2).items
69
+ end
70
+ end
71
+
72
+ describe "#class_option" do
73
+ it "sets options class wise" do
74
+ MyCounter.start(["1", "2", "--third", "3"])[2].must == 3
75
+ end
76
+
77
+ it "does not create an acessor for it" do
78
+ BrokenCounter.start(["1", "2", "--third", "3"])[3].must be_false
79
+ end
80
+ end
81
+
82
+ describe "#class_options" do
83
+ it "sets default options overwriting superclass definitions" do
84
+ options = Scripts::MyScript.class_options
85
+ options[:force].must_not be_required
86
+ end
87
+ end
88
+
89
+ describe "#remove_argument" do
90
+ it "removes previous defined arguments from class" do
91
+ ClearCounter.arguments.must be_empty
92
+ end
93
+
94
+ it "undefine accessors if required" do
95
+ ClearCounter.new.must_not respond_to(:first)
96
+ ClearCounter.new.must_not respond_to(:second)
97
+ end
98
+ end
99
+
100
+ describe "#remove_class_option" do
101
+ it "removes previous defined class option" do
102
+ ClearCounter.class_options[:third].must be_nil
103
+ end
104
+ end
105
+
106
+ describe "#class_options_help" do
107
+ before(:each) do
108
+ @content = capture(:stdout) { MyCounter.help(Thor::Base.shell.new) }
109
+ end
110
+
111
+ it "shows options description" do
112
+ @content.must =~ /# The third argument/
113
+ end
114
+
115
+ it "shows usage with banner content" do
116
+ @content.must =~ /\[\-\-third=THREE\]/
117
+ end
118
+
119
+ it "shows default values below description" do
120
+ @content.must =~ /# Default: 3/
121
+ end
122
+
123
+ it "shows options in different groups" do
124
+ @content.must =~ /Options\:/
125
+ @content.must =~ /Runtime options\:/
126
+ @content.must =~ /\-p, \[\-\-pretend\]/
127
+ end
128
+
129
+ it "use padding in options that does not have aliases" do
130
+ @content.must =~ /^ -t, \[--third/
131
+ @content.must =~ /^ \[--fourth/
132
+ end
133
+
134
+ it "allows extra options to be given" do
135
+ hash = { "Foo" => B.class_options.values }
136
+
137
+ content = capture(:stdout) { MyCounter.send(:class_options_help, Thor::Base.shell.new, hash) }
138
+ content.must =~ /Foo options\:/
139
+ content.must =~ /--last-name=LAST_NAME/
140
+ end
141
+ end
142
+
143
+ describe "#namespace" do
144
+ it "returns the default class namespace" do
145
+ Scripts::MyScript.namespace.must == "scripts:my_script"
146
+ end
147
+
148
+ it "sets a namespace to the class" do
149
+ Scripts::MyDefaults.namespace.must == "default"
150
+ end
151
+ end
152
+
153
+ describe "#group" do
154
+ it "sets a group" do
155
+ MyScript.group.must == "script"
156
+ end
157
+
158
+ it "inherits the group from parent" do
159
+ MyChildScript.group.must == "script"
160
+ end
161
+
162
+ it "defaults to standard if no group is given" do
163
+ Amazing.group.must == "standard"
164
+ end
165
+ end
166
+
167
+ describe "#subclasses" do
168
+ it "tracks its subclasses in an Array" do
169
+ Thor::Base.subclasses.must include(MyScript)
170
+ Thor::Base.subclasses.must include(MyChildScript)
171
+ Thor::Base.subclasses.must include(Scripts::MyScript)
172
+ end
173
+ end
174
+
175
+ describe "#subclass_files" do
176
+ it "returns tracked subclasses, grouped by the files they come from" do
177
+ thorfile = File.join(File.dirname(__FILE__), "fixtures", "script.thor")
178
+ Thor::Base.subclass_files[File.expand_path(thorfile)].must == [
179
+ MyScript, MyScript::AnotherScript, MyChildScript, Scripts::MyScript,
180
+ Scripts::MyDefaults, Scripts::ChildDefault
181
+ ]
182
+ end
183
+
184
+ it "tracks a single subclass across multiple files" do
185
+ thorfile = File.join(File.dirname(__FILE__), "fixtures", "task.thor")
186
+ Thor::Base.subclass_files[File.expand_path(thorfile)].must include(Amazing)
187
+ Thor::Base.subclass_files[File.expand_path(__FILE__)].must include(Amazing)
188
+ end
189
+ end
190
+
191
+ describe "#tasks" do
192
+ it "returns a list with all tasks defined in this class" do
193
+ MyChildScript.new.must respond_to("animal")
194
+ MyChildScript.tasks.keys.must include("animal")
195
+ end
196
+
197
+ it "raises an error if a task with reserved word is defined" do
198
+ lambda {
199
+ klass = Class.new(Thor::Group)
200
+ klass.class_eval "def shell; end"
201
+ }.must raise_error(RuntimeError, /"shell" is a Thor reserved word and cannot be defined as task/)
202
+ end
203
+ end
204
+
205
+ describe "#all_tasks" do
206
+ it "returns a list with all tasks defined in this class plus superclasses" do
207
+ MyChildScript.new.must respond_to("foo")
208
+ MyChildScript.all_tasks.keys.must include("foo")
209
+ end
210
+ end
211
+
212
+ describe "#remove_task" do
213
+ it "removes the task from its tasks hash" do
214
+ MyChildScript.tasks.keys.must_not include("bar")
215
+ MyChildScript.tasks.keys.must_not include("boom")
216
+ end
217
+
218
+ it "undefines the method if desired" do
219
+ MyChildScript.new.must_not respond_to("boom")
220
+ end
221
+ end
222
+
223
+ describe "#from_superclass" do
224
+ it "does not send a method to the superclass if the superclass does not respond to it" do
225
+ MyCounter.get_from_super.must == 13
226
+ end
227
+ end
228
+
229
+ describe "#start" do
230
+ it "raises an error instead of rescueing if --debug is given" do
231
+ lambda {
232
+ MyScript.start ["what", "--debug"]
233
+ }.must raise_error(Thor::UndefinedTaskError, 'Could not find task "what" in "my_script" namespace.')
234
+ end
235
+
236
+ it "does not steal args" do
237
+ args = ["foo", "bar", "--force", "true"]
238
+ MyScript.start(args)
239
+ args.must == ["foo", "bar", "--force", "true"]
240
+ end
241
+
242
+ it "checks unknown options" do
243
+ capture(:stderr) {
244
+ MyScript.start(["foo", "bar", "--force", "true", "--unknown", "baz"])
245
+ }.strip.must == "Unknown switches '--unknown'"
246
+ end
247
+ end
248
+
249
+ describe "attr_*" do
250
+ it "should not add attr_reader as a task" do
251
+ capture(:stderr){ MyScript.start(["another_attribute"]) }.must =~ /Could not find/
252
+ end
253
+
254
+ it "should not add attr_writer as a task" do
255
+ capture(:stderr){ MyScript.start(["another_attribute=", "foo"]) }.must =~ /Could not find/
256
+ end
257
+
258
+ it "should not add attr_accessor as a task" do
259
+ capture(:stderr){ MyScript.start(["some_attribute"]) }.must =~ /Could not find/
260
+ capture(:stderr){ MyScript.start(["some_attribute=", "foo"]) }.must =~ /Could not find/
261
+ end
262
+ end
263
+ end