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.
- data/CHANGELOG.rdoc +29 -4
- data/README.rdoc +234 -0
- data/Thorfile +57 -0
- data/VERSION +1 -0
- data/bin/rake2thor +4 -0
- data/bin/thor +1 -1
- data/lib/thor.rb +216 -119
- data/lib/thor/actions.rb +272 -0
- data/lib/thor/actions/create_file.rb +102 -0
- data/lib/thor/actions/directory.rb +87 -0
- data/lib/thor/actions/empty_directory.rb +133 -0
- data/lib/thor/actions/file_manipulation.rb +195 -0
- data/lib/thor/actions/inject_into_file.rb +78 -0
- data/lib/thor/base.rb +510 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
- data/lib/thor/core_ext/ordered_hash.rb +100 -0
- data/lib/thor/error.rb +25 -1
- data/lib/thor/group.rb +263 -0
- data/lib/thor/invocation.rb +178 -0
- data/lib/thor/parser.rb +4 -0
- data/lib/thor/parser/argument.rb +67 -0
- data/lib/thor/parser/arguments.rb +145 -0
- data/lib/thor/parser/option.rb +132 -0
- data/lib/thor/parser/options.rb +142 -0
- data/lib/thor/rake_compat.rb +67 -0
- data/lib/thor/runner.rb +232 -242
- data/lib/thor/shell.rb +72 -0
- data/lib/thor/shell/basic.rb +220 -0
- data/lib/thor/shell/color.rb +108 -0
- data/lib/thor/task.rb +97 -60
- data/lib/thor/util.rb +230 -55
- data/spec/actions/create_file_spec.rb +170 -0
- data/spec/actions/directory_spec.rb +118 -0
- data/spec/actions/empty_directory_spec.rb +91 -0
- data/spec/actions/file_manipulation_spec.rb +242 -0
- data/spec/actions/inject_into_file_spec.rb +80 -0
- data/spec/actions_spec.rb +291 -0
- data/spec/base_spec.rb +236 -0
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
- data/spec/core_ext/ordered_hash_spec.rb +115 -0
- data/spec/fixtures/bundle/execute.rb +6 -0
- data/spec/fixtures/doc/config.rb +1 -0
- data/spec/group_spec.rb +177 -0
- data/spec/invocation_spec.rb +107 -0
- data/spec/parser/argument_spec.rb +47 -0
- data/spec/parser/arguments_spec.rb +64 -0
- data/spec/parser/option_spec.rb +212 -0
- data/spec/parser/options_spec.rb +255 -0
- data/spec/rake_compat_spec.rb +64 -0
- data/spec/runner_spec.rb +204 -0
- data/spec/shell/basic_spec.rb +206 -0
- data/spec/shell/color_spec.rb +41 -0
- data/spec/shell_spec.rb +25 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/task_spec.rb +82 -0
- data/spec/thor_spec.rb +234 -0
- data/spec/util_spec.rb +196 -0
- metadata +69 -25
- data/README.markdown +0 -76
- data/Rakefile +0 -6
- data/lib/thor/options.rb +0 -242
- data/lib/thor/ordered_hash.rb +0 -64
- data/lib/thor/task_hash.rb +0 -22
- data/lib/thor/tasks.rb +0 -77
- 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
|