thor 0.9.9 → 0.11.5

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 (65) hide show
  1. data/CHANGELOG.rdoc +29 -4
  2. data/README.rdoc +234 -0
  3. data/Thorfile +57 -0
  4. data/VERSION +1 -0
  5. data/bin/rake2thor +4 -0
  6. data/bin/thor +1 -1
  7. data/lib/thor.rb +216 -119
  8. data/lib/thor/actions.rb +272 -0
  9. data/lib/thor/actions/create_file.rb +102 -0
  10. data/lib/thor/actions/directory.rb +87 -0
  11. data/lib/thor/actions/empty_directory.rb +133 -0
  12. data/lib/thor/actions/file_manipulation.rb +195 -0
  13. data/lib/thor/actions/inject_into_file.rb +78 -0
  14. data/lib/thor/base.rb +510 -0
  15. data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  16. data/lib/thor/core_ext/ordered_hash.rb +100 -0
  17. data/lib/thor/error.rb +25 -1
  18. data/lib/thor/group.rb +263 -0
  19. data/lib/thor/invocation.rb +178 -0
  20. data/lib/thor/parser.rb +4 -0
  21. data/lib/thor/parser/argument.rb +67 -0
  22. data/lib/thor/parser/arguments.rb +145 -0
  23. data/lib/thor/parser/option.rb +132 -0
  24. data/lib/thor/parser/options.rb +142 -0
  25. data/lib/thor/rake_compat.rb +67 -0
  26. data/lib/thor/runner.rb +232 -242
  27. data/lib/thor/shell.rb +72 -0
  28. data/lib/thor/shell/basic.rb +220 -0
  29. data/lib/thor/shell/color.rb +108 -0
  30. data/lib/thor/task.rb +97 -60
  31. data/lib/thor/util.rb +230 -55
  32. data/spec/actions/create_file_spec.rb +170 -0
  33. data/spec/actions/directory_spec.rb +118 -0
  34. data/spec/actions/empty_directory_spec.rb +91 -0
  35. data/spec/actions/file_manipulation_spec.rb +242 -0
  36. data/spec/actions/inject_into_file_spec.rb +80 -0
  37. data/spec/actions_spec.rb +291 -0
  38. data/spec/base_spec.rb +236 -0
  39. data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  40. data/spec/core_ext/ordered_hash_spec.rb +115 -0
  41. data/spec/fixtures/bundle/execute.rb +6 -0
  42. data/spec/fixtures/doc/config.rb +1 -0
  43. data/spec/group_spec.rb +177 -0
  44. data/spec/invocation_spec.rb +107 -0
  45. data/spec/parser/argument_spec.rb +47 -0
  46. data/spec/parser/arguments_spec.rb +64 -0
  47. data/spec/parser/option_spec.rb +212 -0
  48. data/spec/parser/options_spec.rb +255 -0
  49. data/spec/rake_compat_spec.rb +64 -0
  50. data/spec/runner_spec.rb +204 -0
  51. data/spec/shell/basic_spec.rb +206 -0
  52. data/spec/shell/color_spec.rb +41 -0
  53. data/spec/shell_spec.rb +25 -0
  54. data/spec/spec_helper.rb +52 -0
  55. data/spec/task_spec.rb +82 -0
  56. data/spec/thor_spec.rb +234 -0
  57. data/spec/util_spec.rb +196 -0
  58. metadata +69 -25
  59. data/README.markdown +0 -76
  60. data/Rakefile +0 -6
  61. data/lib/thor/options.rb +0 -242
  62. data/lib/thor/ordered_hash.rb +0 -64
  63. data/lib/thor/task_hash.rb +0 -22
  64. data/lib/thor/tasks.rb +0 -77
  65. data/lib/thor/tasks/package.rb +0 -18
@@ -0,0 +1,118 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'thor/actions'
3
+
4
+ describe Thor::Actions::Directory do
5
+ before(:each) do
6
+ ::FileUtils.rm_rf(destination_root)
7
+ stub(invoker).file_name{ "rdoc" }
8
+ end
9
+
10
+ def invoker
11
+ @invoker ||= WhinyGenerator.new([1,2], {}, { :destination_root => destination_root })
12
+ end
13
+
14
+ def revoker
15
+ @revoker ||= WhinyGenerator.new([1,2], {}, { :destination_root => destination_root, :behavior => :revoke })
16
+ end
17
+
18
+ def invoke!(*args, &block)
19
+ capture(:stdout){ invoker.directory(*args, &block) }
20
+ end
21
+
22
+ def revoke!(*args, &block)
23
+ capture(:stdout){ revoker.directory(*args, &block) }
24
+ end
25
+
26
+ def exists_and_identical?(source_path, destination_path)
27
+ %w(config.rb README).each do |file|
28
+ source = File.join(source_root, source_path, file)
29
+ destination = File.join(destination_root, destination_path, file)
30
+
31
+ File.exists?(destination).must be_true
32
+ FileUtils.identical?(source, destination).must be_true
33
+ end
34
+ end
35
+
36
+ describe "#invoke!" do
37
+ it "raises an error if the source does not exist" do
38
+ lambda {
39
+ invoke! "unknown"
40
+ }.must raise_error(Thor::Error, /Could not find "unknown" in source paths/)
41
+ end
42
+
43
+ it "copies the whole directory recursively to the default destination" do
44
+ invoke! "doc"
45
+ exists_and_identical?("doc", "doc")
46
+ end
47
+
48
+ it "copies the whole directory recursively to the specified destination" do
49
+ invoke! "doc", "docs"
50
+ exists_and_identical?("doc", "docs")
51
+ end
52
+
53
+ it "copies only the first level files if recursive" do
54
+ invoke! ".", "tasks", :recursive => false
55
+
56
+ file = File.join(destination_root, "tasks", "group.thor")
57
+ File.exists?(file).must be_true
58
+
59
+ file = File.join(destination_root, "tasks", "doc")
60
+ File.exists?(file).must be_false
61
+
62
+ file = File.join(destination_root, "tasks", "doc", "README")
63
+ File.exists?(file).must be_false
64
+ end
65
+
66
+ it "copies files from the source relative to the current path" do
67
+ invoker.inside "doc" do
68
+ invoke! "."
69
+ end
70
+ exists_and_identical?("doc", "doc")
71
+ end
72
+
73
+ it "copies and evaluates templates" do
74
+ invoke! "doc", "docs"
75
+ file = File.join(destination_root, "docs", "rdoc.rb")
76
+ File.exists?(file).must be_true
77
+ File.read(file).must == "FOO = FOO\n"
78
+ end
79
+
80
+ it "copies directories" do
81
+ invoke! "doc", "docs"
82
+ file = File.join(destination_root, "docs", "components")
83
+ File.exists?(file).must be_true
84
+ File.directory?(file).must be_true
85
+ end
86
+
87
+ it "does not copy .empty_diretories files" do
88
+ invoke! "doc", "docs"
89
+ file = File.join(destination_root, "docs", "components", ".empty_directory")
90
+ File.exists?(file).must be_false
91
+ end
92
+
93
+ it "copies directories even if they are empty" do
94
+ invoke! "doc/components", "docs/components"
95
+ file = File.join(destination_root, "docs", "components")
96
+ File.exists?(file).must be_true
97
+ end
98
+
99
+ it "logs status" do
100
+ content = invoke!("doc")
101
+ content.must =~ /create doc\/README/
102
+ content.must =~ /create doc\/config\.rb/
103
+ content.must =~ /create doc\/rdoc\.rb/
104
+ content.must =~ /create doc\/components/
105
+ end
106
+ end
107
+
108
+ describe "#revoke!" do
109
+ it "removes the destination file" do
110
+ invoke! "doc"
111
+ revoke! "doc"
112
+
113
+ File.exists?(File.join(destination_root, "doc", "README")).must be_false
114
+ File.exists?(File.join(destination_root, "doc", "config.rb")).must be_false
115
+ File.exists?(File.join(destination_root, "doc", "components")).must be_false
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,91 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'thor/actions'
3
+
4
+ describe Thor::Actions::EmptyDirectory do
5
+ before(:each) do
6
+ ::FileUtils.rm_rf(destination_root)
7
+ end
8
+
9
+ def empty_directory(destination, options={})
10
+ @action = Thor::Actions::EmptyDirectory.new(base, destination)
11
+ end
12
+
13
+ def invoke!
14
+ capture(:stdout){ @action.invoke! }
15
+ end
16
+
17
+ def revoke!
18
+ capture(:stdout){ @action.revoke! }
19
+ end
20
+
21
+ def base
22
+ @base ||= MyCounter.new([1,2], options, { :destination_root => destination_root })
23
+ end
24
+
25
+ describe "#destination" do
26
+ it "returns the full destination with the destination_root" do
27
+ empty_directory('doc').destination.must == File.join(destination_root, 'doc')
28
+ end
29
+
30
+ it "takes relative root into account" do
31
+ base.inside('doc') do
32
+ empty_directory('contents').destination.must == File.join(destination_root, 'doc', 'contents')
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "#relative_destination" do
38
+ it "returns the relative destination to the original destination root" do
39
+ base.inside('doc') do
40
+ empty_directory('contents').relative_destination.must == 'doc/contents'
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "#given_destination" do
46
+ it "returns the destination supplied by the user" do
47
+ base.inside('doc') do
48
+ empty_directory('contents').given_destination.must == 'contents'
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "#invoke!" do
54
+ it "copies the file to the specified destination" do
55
+ empty_directory("doc")
56
+ invoke!
57
+ File.exists?(File.join(destination_root, "doc")).must be_true
58
+ end
59
+
60
+ it "shows created status to the user" do
61
+ empty_directory("doc")
62
+ invoke!.must == " create doc\n"
63
+ end
64
+
65
+ describe "when directory exists" do
66
+ it "shows exist status" do
67
+ empty_directory("doc")
68
+ invoke!
69
+ invoke!.must == " exist doc\n"
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "#revoke!" do
75
+ it "removes the destination file" do
76
+ empty_directory("doc")
77
+ invoke!
78
+ revoke!
79
+ File.exists?(@action.destination).must be_false
80
+ end
81
+ end
82
+
83
+ describe "#exists?" do
84
+ it "returns true if the destination file exists" do
85
+ empty_directory("doc")
86
+ @action.exists?.must be_false
87
+ invoke!
88
+ @action.exists?.must be_true
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,242 @@
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 exists_and_identical?(source, destination)
13
+ destination = File.join(destination_root, destination)
14
+ File.exists?(destination).must be_true
15
+
16
+ source = File.join(source_root, source)
17
+ FileUtils.must be_identical(source, destination)
18
+ end
19
+
20
+ def file
21
+ File.join(destination_root, "foo")
22
+ end
23
+
24
+ before(:each) do
25
+ ::FileUtils.rm_rf(destination_root)
26
+ end
27
+
28
+ describe "#chmod" do
29
+ it "executes the command given" do
30
+ mock(FileUtils).chmod_R(0755, file)
31
+ action :chmod, "foo", 0755
32
+ end
33
+
34
+ it "does not execute the command if pretending given" do
35
+ dont_allow(FileUtils).chmod_R(0755, file)
36
+ runner(:pretend => true)
37
+ action :chmod, "foo", 0755
38
+ end
39
+
40
+ it "logs status" do
41
+ mock(FileUtils).chmod_R(0755, file)
42
+ action(:chmod, "foo", 0755).must == " chmod foo\n"
43
+ end
44
+
45
+ it "does not log status if required" do
46
+ mock(FileUtils).chmod_R(0755, file)
47
+ action(:chmod, "foo", 0755, :verbose => false).must be_empty
48
+ end
49
+ end
50
+
51
+ describe "#copy_file" do
52
+ it "copies file from source to default destination" do
53
+ action :copy_file, "task.thor"
54
+ exists_and_identical?("task.thor", "task.thor")
55
+ end
56
+
57
+ it "copies file from source to the specified destination" do
58
+ action :copy_file, "task.thor", "foo.thor"
59
+ exists_and_identical?("task.thor", "foo.thor")
60
+ end
61
+
62
+ it "copies file from the source relative to the current path" do
63
+ runner.inside("doc") do
64
+ action :copy_file, "README"
65
+ end
66
+
67
+ exists_and_identical?("doc/README", "doc/README")
68
+ end
69
+
70
+ it "logs status" do
71
+ action(:copy_file, "task.thor").must == " create task.thor\n"
72
+ end
73
+ end
74
+
75
+ describe "#get" do
76
+ it "copies file from source to the specified destination" do
77
+ action :get, "doc/README", "docs/README"
78
+ exists_and_identical?("doc/README", "docs/README")
79
+ end
80
+
81
+ it "uses just the source basename as destination if none is specified" do
82
+ action :get, "doc/README"
83
+ exists_and_identical?("doc/README", "README")
84
+ end
85
+
86
+ it "allows the destination to be set as a block result" do
87
+ action(:get, "doc/README"){ |c| "docs/README" }
88
+ exists_and_identical?("doc/README", "docs/README")
89
+ end
90
+
91
+ it "yields file content to a block" do
92
+ action :get, "doc/README" do |content|
93
+ content.must == "__start__\nREADME\n__end__\n"
94
+ end
95
+ end
96
+
97
+ it "logs status" do
98
+ action(:get, "doc/README", "docs/README").must == " create docs/README\n"
99
+ end
100
+ end
101
+
102
+ describe "#template" do
103
+ it "evaluates the template given as source" do
104
+ runner.instance_variable_set("@klass", "Config")
105
+ action :template, "doc/config.rb"
106
+
107
+ file = File.join(destination_root, "doc/config.rb")
108
+ File.read(file).must == "class Config; end\n"
109
+ end
110
+
111
+ it "copies the template to the specified destination" do
112
+ action :template, "doc/config.rb", "doc/configuration.rb"
113
+ file = File.join(destination_root, "doc/configuration.rb")
114
+ File.exists?(file).must be_true
115
+ end
116
+
117
+ it "converts enconded instructions" do
118
+ mock(runner).file_name{ "rdoc" }
119
+ action :template, "doc/%file_name%.rb.tt"
120
+ file = File.join(destination_root, "doc/rdoc.rb.tt")
121
+ File.exists?(file).must be_true
122
+ end
123
+
124
+ it "logs status" do
125
+ capture(:stdout){ runner.template("doc/config.rb") }.must == " create doc/config.rb\n"
126
+ end
127
+ end
128
+
129
+ describe "when changing existent files" do
130
+ before(:each) do
131
+ ::FileUtils.cp_r(source_root, destination_root)
132
+ end
133
+
134
+ def file
135
+ File.join(destination_root, "doc", "README")
136
+ end
137
+
138
+ describe "#remove_file" do
139
+ it "removes the file given" do
140
+ action :remove_file, "doc/README"
141
+ File.exists?(file).must be_false
142
+ end
143
+
144
+ it "removes directories too" do
145
+ action :remove_dir, "doc"
146
+ File.exists?(File.join(destination_root, "doc")).must be_false
147
+ end
148
+
149
+ it "does not remove if pretending" do
150
+ runner(:pretend => true)
151
+ action :remove_file, "doc/README"
152
+ File.exists?(file).must be_true
153
+ end
154
+
155
+ it "logs status" do
156
+ action(:remove_file, "doc/README").must == " remove doc/README\n"
157
+ end
158
+
159
+ it "does not log status if required" do
160
+ action(:remove_file, "doc/README", :verbose => false).must be_empty
161
+ end
162
+ end
163
+
164
+ describe "#gsub_file" do
165
+ it "replaces the content in the file" do
166
+ action :gsub_file, "doc/README", "__start__", "START"
167
+ File.open(file).read.must == "START\nREADME\n__end__\n"
168
+ end
169
+
170
+ it "does not replace if pretending" do
171
+ runner(:pretend => true)
172
+ action :gsub_file, "doc/README", "__start__", "START"
173
+ File.open(file).read.must == "__start__\nREADME\n__end__\n"
174
+ end
175
+
176
+ it "accepts a block" do
177
+ action(:gsub_file, "doc/README", "__start__"){ |match| match.gsub('__', '').upcase }
178
+ File.open(file).read.must == "START\nREADME\n__end__\n"
179
+ end
180
+
181
+ it "logs status" do
182
+ action(:gsub_file, "doc/README", "__start__", "START").must == " gsub doc/README\n"
183
+ end
184
+
185
+ it "does not log status if required" do
186
+ action(:gsub_file, file, "__", :verbose => false){ |match| match * 2 }.must be_empty
187
+ end
188
+ end
189
+
190
+ describe "#append_file" do
191
+ it "appends content to the file" do
192
+ action :append_file, "doc/README", "END\n"
193
+ File.open(file).read.must == "__start__\nREADME\n__end__\nEND\n"
194
+ end
195
+
196
+ it "does not append if pretending" do
197
+ runner(:pretend => true)
198
+ action :append_file, "doc/README", "END\n"
199
+ File.open(file).read.must == "__start__\nREADME\n__end__\n"
200
+ end
201
+
202
+ it "accepts a block" do
203
+ action(:append_file, "doc/README"){ "END\n" }
204
+ File.open(file).read.must == "__start__\nREADME\n__end__\nEND\n"
205
+ end
206
+
207
+ it "logs status" do
208
+ action(:append_file, "doc/README", "END").must == " append doc/README\n"
209
+ end
210
+
211
+ it "does not log status if required" do
212
+ action(:append_file, "doc/README", nil, :verbose => false){ "END" }.must be_empty
213
+ end
214
+ end
215
+
216
+ describe "#prepend_file" do
217
+ it "prepends content to the file" do
218
+ action :prepend_file, "doc/README", "START\n"
219
+ File.open(file).read.must == "START\n__start__\nREADME\n__end__\n"
220
+ end
221
+
222
+ it "does not prepend if pretending" do
223
+ runner(:pretend => true)
224
+ action :prepend_file, "doc/README", "START\n"
225
+ File.open(file).read.must == "__start__\nREADME\n__end__\n"
226
+ end
227
+
228
+ it "accepts a block" do
229
+ action(:prepend_file, "doc/README"){ "START\n" }
230
+ File.open(file).read.must == "START\n__start__\nREADME\n__end__\n"
231
+ end
232
+
233
+ it "logs status" do
234
+ action(:prepend_file, "doc/README", "START").must == " prepend doc/README\n"
235
+ end
236
+
237
+ it "does not log status if required" do
238
+ action(:prepend_file, "doc/README", "START", :verbose => false).must be_empty
239
+ end
240
+ end
241
+ end
242
+ end