cap-taffy 0.0.2
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/History.txt +9 -0
- data/README.md +83 -0
- data/Rakefile +34 -0
- data/cap-taffy.gemspec +45 -0
- data/lib/cap-taffy/db.rb +239 -0
- data/lib/cap-taffy/parse.rb +53 -0
- data/lib/cap-taffy/ssh.rb +18 -0
- data/lib/cap-taffy.rb +45 -0
- data/spec/cap-taffy/db_spec.rb +287 -0
- data/spec/cap-taffy/parse_spec.rb +57 -0
- data/spec/cap-taffy/ssh_spec.rb +37 -0
- data/spec/cap-taffy_spec.rb +4 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +179 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- metadata +131 -0
@@ -0,0 +1,287 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
2
|
+
|
3
|
+
module CapTaffy
|
4
|
+
describe 'Db' do
|
5
|
+
context "when loaded" do
|
6
|
+
include CapistranoHelpers
|
7
|
+
include TaffyHelpers
|
8
|
+
|
9
|
+
before do
|
10
|
+
CapTaffy.send(:remove_const, "Db") rescue nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should load in capistrano configuration instance" do;
|
14
|
+
Capistrano::Configuration.instance.expects(:load)
|
15
|
+
|
16
|
+
load 'lib/cap-taffy/db.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should define :db namespace" do
|
20
|
+
Capistrano::Configuration.instance.expects(:namespace).with(:db)
|
21
|
+
|
22
|
+
load 'lib/cap-taffy/db.rb'
|
23
|
+
end
|
24
|
+
|
25
|
+
for_task :detect, :roles => :app, :in => :Db, :it => "should be defined" do
|
26
|
+
@mod.expects(:remote_database_url).returns("remote_db_url")
|
27
|
+
@mod.expects(:local_database_url).returns("local_db_url")
|
28
|
+
|
29
|
+
load 'lib/cap-taffy/db.rb'
|
30
|
+
|
31
|
+
@namespace.instance_variable_get(:@remote_database_url).should == "remote_db_url"
|
32
|
+
@namespace.instance_variable_get(:@local_database_url).should == "local_db_url"
|
33
|
+
end
|
34
|
+
|
35
|
+
def load_taffy_db # :nodoc:
|
36
|
+
with_logger do
|
37
|
+
load 'lib/cap-taffy/db.rb'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
for_task :push, :roles => :app, :in => :Db, :it => "should send taps client cmd_send" do
|
42
|
+
options = {:remote_database_url => "remote", :local_database_url => "local", :port => nil, :login => "a_user", :password => "a_pass"}
|
43
|
+
@namespace.expects(:detect)
|
44
|
+
namespace_with_variables(:taps_port => nil)
|
45
|
+
db_with_expected_options(options)
|
46
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
47
|
+
@mod.expects(:run).with(@namespace, options).yields(taps_client_who(:expects, :cmd_send))
|
48
|
+
|
49
|
+
load_taffy_db
|
50
|
+
end
|
51
|
+
|
52
|
+
for_task :push, :roles => :app, :in => :Db, :it => "should use cli argument for port" do
|
53
|
+
options = {:remote_database_url => "remote", :local_database_url => "local", :port => 1234, :login => "a_user", :password => "a_pass"}
|
54
|
+
@namespace.expects(:detect)
|
55
|
+
namespace_with_variables(:taps_port => 1234)
|
56
|
+
db_with_expected_options(options)
|
57
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
58
|
+
@mod.expects(:run).with(@namespace, options)
|
59
|
+
|
60
|
+
load_taffy_db
|
61
|
+
end
|
62
|
+
|
63
|
+
for_task :push, :roles => :app, :in => :Db, :it => "should force 127.0.0.1 (local) for ssh local forwarding" do
|
64
|
+
options = {:remote_database_url => "remote", :local_database_url => "local", :port => 1234, :login => "a_user", :password => "a_pass"}
|
65
|
+
@namespace.expects(:detect)
|
66
|
+
namespace_with_variables(:taps_port => 1234, :local => true)
|
67
|
+
db_with_expected_options(options)
|
68
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
69
|
+
@mod.expects(:run).with(@namespace, options.merge(:local => true))
|
70
|
+
|
71
|
+
load_taffy_db
|
72
|
+
end
|
73
|
+
|
74
|
+
for_task :pull, :roles => :app, :in => :Db, :it => "should send taps client cmd_receive" do
|
75
|
+
options = {:remote_database_url => "remote", :local_database_url => "local", :port => nil, :login => "a_user", :password => "a_pass"}
|
76
|
+
@namespace.expects(:detect)
|
77
|
+
namespace_with_variables(:taps_port => nil)
|
78
|
+
db_with_expected_options(options)
|
79
|
+
@mod.expects(:tmp_pass).with(@namespace.fetch(:user)).returns(options[:password])
|
80
|
+
@mod.expects(:run).with(@namespace, options).yields(taps_client_who(:expects, :cmd_receive))
|
81
|
+
|
82
|
+
load_taffy_db
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "after capistrano" do
|
87
|
+
include CapistranoHelpers
|
88
|
+
include TaffyHelpers
|
89
|
+
|
90
|
+
before do
|
91
|
+
Capistrano::Configuration.instance.expects(:namespace).with(:db)
|
92
|
+
CapTaffy.send(:remove_const, "Db") rescue nil
|
93
|
+
load 'lib/cap-taffy/db.rb'
|
94
|
+
|
95
|
+
@conf = {"test"=>
|
96
|
+
{"reconnect"=>false,
|
97
|
+
"encoding"=>"utf8",
|
98
|
+
"username"=>"root",
|
99
|
+
"adapter"=>"postgresql",
|
100
|
+
"database"=>"test_test",
|
101
|
+
"pool"=>5,
|
102
|
+
"password"=>"root",
|
103
|
+
"host"=>"localhost"}}
|
104
|
+
|
105
|
+
@options = {:remote_database_url => "remote", :local_database_url => "local", :port => nil, :login => "a_user", :password => "a_pass"}
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should detect local database url" do
|
109
|
+
File.expects(:exists?).returns(true)
|
110
|
+
File.expects(:read).returns(mock())
|
111
|
+
YAML.expects(:load).returns(@conf)
|
112
|
+
env = 'test'
|
113
|
+
Parse.expects(:database_url).with(@conf, env)
|
114
|
+
|
115
|
+
Db.local_database_url(env)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should detect remote database url" do
|
119
|
+
instance = mock()
|
120
|
+
instance.stubs(:current_path).returns("/home/user/public_html/current")
|
121
|
+
instance.expects(:capture).with("cat /home/user/public_html/current/config/database.yml")
|
122
|
+
YAML.expects(:load).returns(@conf)
|
123
|
+
env = 'test'
|
124
|
+
Parse.expects(:database_url).with(@conf, env)
|
125
|
+
|
126
|
+
Db.remote_database_url(instance, env)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should create temporary password from time and user" do
|
130
|
+
Time.stubs(:now).returns("asdfasdfasdf")
|
131
|
+
user = "me"
|
132
|
+
tmp_pass = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{user}--")
|
133
|
+
|
134
|
+
Db.tmp_pass(user).should == tmp_pass
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should expose a basic taps client" do
|
138
|
+
client = mock()
|
139
|
+
local_database_url = mock()
|
140
|
+
remote_url = mock()
|
141
|
+
Taps::Config.expects(:chunksize=).with(1000)
|
142
|
+
Taps::Config.expects(:database_url=).with(local_database_url)
|
143
|
+
Taps::Config.expects(:remote_url=).with(remote_url)
|
144
|
+
Taps::Config.expects(:verify_database_url)
|
145
|
+
client.expects(:do_something)
|
146
|
+
|
147
|
+
Taps::ClientSession.expects(:quickstart).yields(client)
|
148
|
+
|
149
|
+
Db.taps_client(local_database_url, remote_url) do |client|
|
150
|
+
client.do_something
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should define default port" do
|
155
|
+
Db.default_server_port.should == 5000
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should build server command" do
|
159
|
+
remote_database_url, login, password, port = @options[:remote_database_url], @options[:login], @options[:password], @options[:port]
|
160
|
+
|
161
|
+
Db.server_command(@options).should == "taps server #{remote_database_url} #{login} #{password}"
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should build server command with port" do
|
165
|
+
@options[:port] = 1234
|
166
|
+
remote_database_url, login, password, port = @options[:remote_database_url], @options[:login], @options[:password], @options[:port]
|
167
|
+
|
168
|
+
Db.server_command(@options).should == "taps server #{remote_database_url} #{login} #{password} --port=#{port}"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should build server command without port if same as default port" do
|
172
|
+
@options[:port] = 5000
|
173
|
+
remote_database_url, login, password = @options[:remote_database_url], @options[:login], @options[:password]
|
174
|
+
|
175
|
+
Db.server_command(@options).should == "taps server #{remote_database_url} #{login} #{password}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def parser_expects_uri_hash_to_url_with(login, password, host)
|
179
|
+
parser = mock()
|
180
|
+
Parse.expects(:new).at_least_once.returns(parser)
|
181
|
+
parser.expects(:uri_hash_to_url).
|
182
|
+
with('username' => login, 'password' => password, 'host' => host, 'scheme' => 'http', 'path' => '')
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should build remote url (with some help)" do
|
186
|
+
@options[:host] = "127.0.0.1"
|
187
|
+
parser_expects_uri_hash_to_url_with(@options[:login], @options[:password], "#{@options[:host]}:#{Db.default_server_port}").returns("remote_url/")
|
188
|
+
|
189
|
+
Db.remote_url(@options)
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should build remote url with different port" do
|
193
|
+
@options[:host] = "127.0.0.1"
|
194
|
+
@options[:port] = 1234
|
195
|
+
parser_expects_uri_hash_to_url_with(@options[:login], @options[:password], "#{@options[:host]}:#{@options[:port]}").returns("remote_url")
|
196
|
+
|
197
|
+
Db.remote_url(@options)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should remove trailing slash from remote url" do
|
201
|
+
@options[:host] = "127.0.0.1"
|
202
|
+
parser_expects_uri_hash_to_url_with(@options[:login], @options[:password], "#{@options[:host]}:#{Db.default_server_port}").returns("remote_url/")
|
203
|
+
|
204
|
+
Db.remote_url(@options).should == "remote_url"
|
205
|
+
end
|
206
|
+
|
207
|
+
running_db_it "should run with capistrano" do
|
208
|
+
capistrano_run_with(Db.server_command(@options))
|
209
|
+
|
210
|
+
Db.run(@capistrano, @options)
|
211
|
+
end
|
212
|
+
|
213
|
+
running_db_it "should do something to taps client" do
|
214
|
+
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:5000, CTRL+C to stop\r\n" do
|
215
|
+
capistrano_run_with(Db.server_command(@options))
|
216
|
+
end
|
217
|
+
channel.expects(:close)
|
218
|
+
|
219
|
+
Db.expects(:remote_url).with(@options.merge(:host => channel[:host], :port => 5000)).returns("remote_url")
|
220
|
+
Db.expects(:taps_client).with("local", "remote_url").yields(taps_client_who(:expects, :do_something))
|
221
|
+
Db.run(@capistrano, @options) do |client|
|
222
|
+
client.do_something
|
223
|
+
end
|
224
|
+
|
225
|
+
channel[:status].should == 0
|
226
|
+
end
|
227
|
+
|
228
|
+
running_db_it "should run taffy on different port" do
|
229
|
+
@options[:port] = 1234
|
230
|
+
|
231
|
+
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:1234, CTRL+C to stop\r\n" do
|
232
|
+
capistrano_run_with(Db.server_command(@options))
|
233
|
+
end
|
234
|
+
channel.expects(:close)
|
235
|
+
Db.expects(:remote_url).with(@options.merge(:host => channel[:host])).returns("remote_url")
|
236
|
+
Db.expects(:taps_client).with("local", "remote_url").yields(taps_client_who(:expects, :do_something))
|
237
|
+
|
238
|
+
Db.run(@capistrano, @options) do |client|
|
239
|
+
client.do_something
|
240
|
+
end
|
241
|
+
|
242
|
+
channel[:status].should == 0
|
243
|
+
end
|
244
|
+
|
245
|
+
running_db_it "should not do anything until taps sinatra server is running" do
|
246
|
+
simulating_run_loop_with :data => "asdfasdf" do
|
247
|
+
capistrano_run_with(Db.server_command(@options))
|
248
|
+
end
|
249
|
+
|
250
|
+
client = mock()
|
251
|
+
client.expects(:do_something).never
|
252
|
+
|
253
|
+
Db.run(@capistrano, @options) do |client|
|
254
|
+
client.do_something
|
255
|
+
end
|
256
|
+
|
257
|
+
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:5000, CTRL+C to stop\r\n" do
|
258
|
+
capistrano_run_with(Db.server_command(@options))
|
259
|
+
end
|
260
|
+
channel.expects(:close)
|
261
|
+
Db.expects(:remote_url).with(@options.merge(:host => channel[:host], :port => 5000)).returns("remote_url")
|
262
|
+
Db.expects(:taps_client).with("local", "remote_url").yields(taps_client_who(:expects, :do_something))
|
263
|
+
|
264
|
+
Db.run(@capistrano, @options) do |client|
|
265
|
+
client.do_something
|
266
|
+
end
|
267
|
+
|
268
|
+
channel[:status].should == 0
|
269
|
+
end
|
270
|
+
|
271
|
+
running_db_it "should force 127.0.0.1 (local) for remote url" do
|
272
|
+
channel, stream, data = simulating_run_loop_with :data => ">> Listening on 0.0.0.0:5000, CTRL+C to stop\r\n" do
|
273
|
+
capistrano_run_with(Db.server_command(@options))
|
274
|
+
end
|
275
|
+
channel.expects(:close)
|
276
|
+
|
277
|
+
Db.expects(:remote_url).with(@options.merge(:host => '127.0.0.1', :port => 5000)).returns("remote_url")
|
278
|
+
Db.expects(:taps_client).with("local", "remote_url").yields(taps_client_who(:expects, :do_something))
|
279
|
+
Db.run(@capistrano, @options.merge(:local => true)) do |client|
|
280
|
+
client.do_something
|
281
|
+
end
|
282
|
+
|
283
|
+
channel[:status].should == 0
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
2
|
+
require 'lib/cap-taffy/parse'
|
3
|
+
|
4
|
+
module CapTaffy
|
5
|
+
describe Parse do
|
6
|
+
before do
|
7
|
+
@database_config = {"production"=>
|
8
|
+
{"reconnect"=>false,
|
9
|
+
"encoding"=>"utf8",
|
10
|
+
"username"=>"root",
|
11
|
+
"adapter"=>"mysql",
|
12
|
+
"database"=>"test_production",
|
13
|
+
"pool"=>5,
|
14
|
+
"password"=>"root",
|
15
|
+
"host"=>"localhost"},
|
16
|
+
"development"=>
|
17
|
+
{"username"=>"root",
|
18
|
+
"adapter"=>"sqlite3",
|
19
|
+
"database"=>"test_development",
|
20
|
+
"password"=>"root"},
|
21
|
+
"test"=>
|
22
|
+
{"reconnect"=>false,
|
23
|
+
"encoding"=>"utf8",
|
24
|
+
"username"=>"root",
|
25
|
+
"adapter"=>"postgresql",
|
26
|
+
"database"=>"test_test",
|
27
|
+
"pool"=>5,
|
28
|
+
"password"=>"root",
|
29
|
+
"host"=>"localhost"}}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should do pass-through for escape" do
|
33
|
+
Parse.new.escape("something").should == "something"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should parse database config for sqlite" do
|
37
|
+
Parse.expects(:conf_to_uri_hash).never
|
38
|
+
Parse.database_url(@database_config, 'development').should == "sqlite://test_development"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should parse database config for postgresql" do
|
42
|
+
Parse.database_url(@database_config, 'test').should == "postgres://root:root@localhost/test_test?encoding=utf8"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should parse database config for mysql" do
|
46
|
+
Parse.database_url(@database_config, 'production').should == "mysql://root:root@localhost/test_production?encoding=utf8"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should raise invalid conf if so" do
|
50
|
+
lambda { Parse.database_url(nil, nil) }.should raise_error(Parse::Invalid)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should raise invalid conf if so" do
|
54
|
+
lambda { Parse.database_url(@database_config, nil) }.should raise_error(Parse::Invalid)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
2
|
+
|
3
|
+
module CapTaffy
|
4
|
+
describe 'SSH' do
|
5
|
+
include CapistranoHelpers
|
6
|
+
|
7
|
+
before do
|
8
|
+
CapTaffy.send(:remove_const, "SSH") rescue nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should load in capistrano configuration instance" do;
|
12
|
+
Capistrano::Configuration.instance.expects(:load)
|
13
|
+
|
14
|
+
load 'lib/cap-taffy/ssh.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should define :db namespace" do
|
18
|
+
Capistrano::Configuration.instance.expects(:namespace).with(:ssh)
|
19
|
+
|
20
|
+
load 'lib/cap-taffy/ssh.rb'
|
21
|
+
end
|
22
|
+
|
23
|
+
def simulating_line(line, &blk)
|
24
|
+
blk.call.then.yields(line)
|
25
|
+
line
|
26
|
+
end
|
27
|
+
|
28
|
+
for_task :authorize, :in => :SSH, :it => "should authorize on each server" do
|
29
|
+
public_key = "ssh-key2\n"
|
30
|
+
File.expects(:read).with(File.expand_path(File.join(%w[~/ .ssh id_rsa.pub]))).returns(public_key)
|
31
|
+
|
32
|
+
run_with(@namespace, anything) # Don't rly wna test bash scripts
|
33
|
+
|
34
|
+
load 'lib/cap-taffy/ssh.rb'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib cap-taffy]))
|
2
|
+
|
3
|
+
module Capistrano # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
Spec::Runner.configure do |config|
|
7
|
+
# == Mock Framework
|
8
|
+
#
|
9
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
10
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
11
|
+
#
|
12
|
+
config.mock_with :mocha
|
13
|
+
# config.mock_with :flexmock
|
14
|
+
# config.mock_with :rr
|
15
|
+
end
|
16
|
+
|
17
|
+
Capistrano.send(:remove_const, "Configuration") rescue nil
|
18
|
+
Capistrano.const_set("Configuration", Class.new)
|
19
|
+
Capistrano::Configuration.class_eval do
|
20
|
+
def self.instance
|
21
|
+
@instance ||= Capistrano::Configuration.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def load(*args, &block)
|
25
|
+
instance_eval(&block) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
def namespaces
|
29
|
+
@namespaces ||= {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def namespace(name, &block)
|
33
|
+
namespaces[name].instance_eval(&block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class String # :nodoc:
|
38
|
+
def demodulize
|
39
|
+
gsub(/^.*::/, '')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module CapistranoHelpers
|
44
|
+
def self.included(base) # :nodoc:
|
45
|
+
base.extend CapistranoHelpers::ClassMethods
|
46
|
+
end
|
47
|
+
|
48
|
+
# Stubs the Capistrano Logger and yields to the block
|
49
|
+
def with_logger(&blk) # :yields:
|
50
|
+
logger_class = Class.new
|
51
|
+
logger = mock()
|
52
|
+
logger.stub_everything
|
53
|
+
logger_class.stubs(:new).returns(logger)
|
54
|
+
Capistrano.const_set("Logger", logger_class)
|
55
|
+
yield
|
56
|
+
ensure
|
57
|
+
Capistrano.send(:remove_const, "Logger") rescue nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Helper for common operations in db tasks
|
61
|
+
def db_with_expected_options(options) # :nodoc:
|
62
|
+
@namespace.stubs(:fetch).with(:user).returns(options[:login])
|
63
|
+
@namespace.instance_variable_set(:@remote_database_url, options[:remote_database_url])
|
64
|
+
@namespace.instance_variable_set(:@local_database_url, options[:local_database_url])
|
65
|
+
end
|
66
|
+
|
67
|
+
# Stubs the variables hash used by Capistrano to accept command-line parameters
|
68
|
+
def namespace_with_variables(variables) # :nodoc:
|
69
|
+
@namespace.stubs(:variables).returns(variables)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates an expectation for the Capistrano namespace/task <tt>instance</tt> for the <tt>:run</tt> action with <tt>*args</tt>.
|
73
|
+
def run_with(instance, *args)
|
74
|
+
instance.expects(:run).with(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
# The Capistrano <tt>:run</tt> action loops with <tt>channel</tt>, <tt>stream</tt>, and <tt>data</tt> until the channel is closed.
|
78
|
+
#
|
79
|
+
# Passing in a <tt>:run</tt> expectation, modifies the expectation such that each subsequent invocation ("loop") yields <tt>channel</tt>, <tt>stream</tt>, and <tt>data</tt>
|
80
|
+
#
|
81
|
+
# ==== Parameters
|
82
|
+
#
|
83
|
+
# * <tt>:channel</tt> - A hash containing <tt>:host</tt>.
|
84
|
+
# * <tt>:stream</tt> - A stream object.
|
85
|
+
# * <tt>:data</tt> - A data object, usually a String.
|
86
|
+
def simulating_run_loop_with(options={}, &blk) # :yields:
|
87
|
+
channel = options[:channel] || {:host => "192.168.1.20"}
|
88
|
+
stream = options[:stream]
|
89
|
+
data = options[:data]
|
90
|
+
|
91
|
+
blk.call.then.yields(channel, stream, data)
|
92
|
+
[channel, stream, data]
|
93
|
+
end
|
94
|
+
|
95
|
+
module ClassMethods
|
96
|
+
# Used in specs to test Capistrano tasks.
|
97
|
+
#
|
98
|
+
#
|
99
|
+
# Code defined in the task will be executed automatically on load. (Note the <tt>yields</tt>, see Mocha#yields[http://mocha.rubyforge.org/classes/Mocha/Expectation.html#M000043])
|
100
|
+
#
|
101
|
+
# ==== Parameters
|
102
|
+
#
|
103
|
+
# * <tt>:it</tt> - The description for the current example group.
|
104
|
+
# * <tt>:in</tt> - Specifies the module under test (as well as the namespace the task is defined in). The namespace will be deduced with the downcase of <tt>:in</tt>
|
105
|
+
#
|
106
|
+
# ==== Examples
|
107
|
+
#
|
108
|
+
# for_task :detect, :roles => :app, :in => :Somewhere, :it => "should so something" do
|
109
|
+
# @namespace # refers to the current task under test (in Capistrano tasks are executed on Capistrano::Namespaces::Namespace instances)
|
110
|
+
# @mod # refers to module CapTaffy::Somewhere
|
111
|
+
# end
|
112
|
+
def for_task(task_name, options, &block)
|
113
|
+
description = options.delete(:it)
|
114
|
+
namespace = options.delete(:in)
|
115
|
+
|
116
|
+
context ":#{task_name.to_s} task" do
|
117
|
+
before do
|
118
|
+
@namespace = Capistrano::Configuration.instance.namespaces[namespace.to_s.downcase.to_sym] = mock()
|
119
|
+
@namespace.stubs(:desc)
|
120
|
+
@namespace.stubs(:task)
|
121
|
+
args = [task_name, options]
|
122
|
+
unless options.empty?
|
123
|
+
@namespace.expects(:task).with(*args).yields
|
124
|
+
else
|
125
|
+
@namespace.expects(:task).with(task_name).yields
|
126
|
+
end
|
127
|
+
|
128
|
+
@mod = Module.new # The module under test
|
129
|
+
CapTaffy.const_set(namespace, @mod)
|
130
|
+
end
|
131
|
+
|
132
|
+
it description, &block
|
133
|
+
|
134
|
+
after do
|
135
|
+
const_name = @mod.to_s.demodulize
|
136
|
+
CapTaffy.send(:remove_const, const_name)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
module TaffyHelpers
|
144
|
+
def self.included(base) # :nodoc:
|
145
|
+
base.extend TaffyHelpers::ClassMethods
|
146
|
+
end
|
147
|
+
|
148
|
+
# A simple helper for mocking a quick object
|
149
|
+
#
|
150
|
+
# Usage:
|
151
|
+
# taps_client_who(:expects, :do_something)
|
152
|
+
def taps_client_who(method_symbol, *args)
|
153
|
+
client = mock()
|
154
|
+
client.send(method_symbol, *args)
|
155
|
+
client
|
156
|
+
end
|
157
|
+
|
158
|
+
module ClassMethods
|
159
|
+
# A wrapper for running CapTaffy::Db::run
|
160
|
+
def running_db_it(message, &blk)
|
161
|
+
context "when running db" do
|
162
|
+
before do
|
163
|
+
@capistrano = mock()
|
164
|
+
end
|
165
|
+
|
166
|
+
# See CapistranoHelpers
|
167
|
+
def capistrano_run_with(*args)
|
168
|
+
run_with(@capistrano, *args)
|
169
|
+
end
|
170
|
+
|
171
|
+
it message, &blk
|
172
|
+
|
173
|
+
after do
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/tasks/ann.rake
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
begin
|
3
|
+
require 'bones/smtp_tls'
|
4
|
+
rescue LoadError
|
5
|
+
require 'net/smtp'
|
6
|
+
end
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
namespace :ann do
|
10
|
+
|
11
|
+
# A prerequisites task that all other tasks depend upon
|
12
|
+
task :prereqs
|
13
|
+
|
14
|
+
file PROJ.ann.file do
|
15
|
+
ann = PROJ.ann
|
16
|
+
puts "Generating #{ann.file}"
|
17
|
+
File.open(ann.file,'w') do |fd|
|
18
|
+
fd.puts("#{PROJ.name} version #{PROJ.version}")
|
19
|
+
fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
|
20
|
+
fd.puts(" #{PROJ.url}") if PROJ.url.valid?
|
21
|
+
fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
|
22
|
+
fd.puts
|
23
|
+
fd.puts("== DESCRIPTION")
|
24
|
+
fd.puts
|
25
|
+
fd.puts(PROJ.description)
|
26
|
+
fd.puts
|
27
|
+
fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
|
28
|
+
fd.puts
|
29
|
+
ann.paragraphs.each do |p|
|
30
|
+
fd.puts "== #{p.upcase}"
|
31
|
+
fd.puts
|
32
|
+
fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
|
33
|
+
fd.puts
|
34
|
+
end
|
35
|
+
fd.puts ann.text if ann.text
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Create an announcement file"
|
40
|
+
task :announcement => ['ann:prereqs', PROJ.ann.file]
|
41
|
+
|
42
|
+
desc "Send an email announcement"
|
43
|
+
task :email => ['ann:prereqs', PROJ.ann.file] do
|
44
|
+
ann = PROJ.ann
|
45
|
+
from = ann.email[:from] || Array(PROJ.authors).first || PROJ.email
|
46
|
+
to = Array(ann.email[:to])
|
47
|
+
|
48
|
+
### build a mail header for RFC 822
|
49
|
+
rfc822msg = "From: #{from}\n"
|
50
|
+
rfc822msg << "To: #{to.join(',')}\n"
|
51
|
+
rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
|
52
|
+
rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
|
53
|
+
rfc822msg << "\n"
|
54
|
+
rfc822msg << "Date: #{Time.new.rfc822}\n"
|
55
|
+
rfc822msg << "Message-Id: "
|
56
|
+
rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{ann.email[:domain]}>\n\n"
|
57
|
+
rfc822msg << File.read(ann.file)
|
58
|
+
|
59
|
+
params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
|
60
|
+
ann.email[key]
|
61
|
+
end
|
62
|
+
|
63
|
+
params[3] = PROJ.email if params[3].nil?
|
64
|
+
|
65
|
+
if params[4].nil?
|
66
|
+
STDOUT.write "Please enter your e-mail password (#{params[3]}): "
|
67
|
+
params[4] = STDIN.gets.chomp
|
68
|
+
end
|
69
|
+
|
70
|
+
### send email
|
71
|
+
Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
|
72
|
+
end
|
73
|
+
end # namespace :ann
|
74
|
+
|
75
|
+
desc 'Alias to ann:announcement'
|
76
|
+
task :ann => 'ann:announcement'
|
77
|
+
|
78
|
+
CLOBBER << PROJ.ann.file
|
79
|
+
|
80
|
+
# EOF
|
data/tasks/bones.rake
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
if HAVE_BONES
|
3
|
+
|
4
|
+
namespace :bones do
|
5
|
+
|
6
|
+
desc 'Show the PROJ open struct'
|
7
|
+
task :debug do |t|
|
8
|
+
atr = if t.application.top_level_tasks.length == 2
|
9
|
+
t.application.top_level_tasks.pop
|
10
|
+
end
|
11
|
+
|
12
|
+
if atr then Bones::Debug.show_attr(PROJ, atr)
|
13
|
+
else Bones::Debug.show PROJ end
|
14
|
+
end
|
15
|
+
|
16
|
+
end # namespace :bones
|
17
|
+
|
18
|
+
end # HAVE_BONES
|
19
|
+
|
20
|
+
# EOF
|