forklift_etl 1.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +10 -0
- data/.rbenv-version +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +74 -0
- data/Rakefile +13 -0
- data/bin/forklift +61 -0
- data/doc/EmailSuffix.html +228 -0
- data/doc/Forklift.html +187 -0
- data/doc/Forklift/Base.html +167 -0
- data/doc/Forklift/Base/Connection.html +590 -0
- data/doc/Forklift/Base/Logger.html +453 -0
- data/doc/Forklift/Base/Mailer.html +399 -0
- data/doc/Forklift/Base/Mailer/ERBBinding.html +256 -0
- data/doc/Forklift/Base/Pid.html +489 -0
- data/doc/Forklift/Base/Utils.html +252 -0
- data/doc/Forklift/Connection.html +164 -0
- data/doc/Forklift/Connection/Elasticsearch.html +419 -0
- data/doc/Forklift/Connection/Mysql.html +939 -0
- data/doc/Forklift/Patterns.html +164 -0
- data/doc/Forklift/Patterns/Elasticsearch.html +169 -0
- data/doc/Forklift/Patterns/Mysql.html +402 -0
- data/doc/Forklift/Plan.html +704 -0
- data/doc/Gemfile.html +132 -0
- data/doc/Object.html +326 -0
- data/doc/Rakefile.html +138 -0
- data/doc/SpecClient.html +291 -0
- data/doc/SpecPlan.html +253 -0
- data/doc/SpecSeeds.html +303 -0
- data/doc/created.rid +35 -0
- data/doc/example/Gemfile.html +129 -0
- data/doc/images/add.png +0 -0
- data/doc/images/brick.png +0 -0
- data/doc/images/brick_link.png +0 -0
- data/doc/images/bug.png +0 -0
- data/doc/images/bullet_black.png +0 -0
- data/doc/images/bullet_toggle_minus.png +0 -0
- data/doc/images/bullet_toggle_plus.png +0 -0
- data/doc/images/date.png +0 -0
- data/doc/images/delete.png +0 -0
- data/doc/images/find.png +0 -0
- data/doc/images/loadingAnimation.gif +0 -0
- data/doc/images/macFFBgHack.png +0 -0
- data/doc/images/package.png +0 -0
- data/doc/images/page_green.png +0 -0
- data/doc/images/page_white_text.png +0 -0
- data/doc/images/page_white_width.png +0 -0
- data/doc/images/plugin.png +0 -0
- data/doc/images/ruby.png +0 -0
- data/doc/images/tag_blue.png +0 -0
- data/doc/images/tag_green.png +0 -0
- data/doc/images/transparent.png +0 -0
- data/doc/images/wrench.png +0 -0
- data/doc/images/wrench_orange.png +0 -0
- data/doc/images/zoom.png +0 -0
- data/doc/index.html +122 -0
- data/doc/js/darkfish.js +155 -0
- data/doc/js/jquery.js +18 -0
- data/doc/js/navigation.js +142 -0
- data/doc/js/search.js +94 -0
- data/doc/js/search_index.js +1 -0
- data/doc/js/searcher.js +228 -0
- data/doc/rdoc.css +543 -0
- data/doc/table_of_contents.html +309 -0
- data/example/Gemfile +3 -0
- data/example/Gemfile.lock +55 -0
- data/example/config/connections/elasticsearch/source.yml +1 -0
- data/example/config/connections/mysql/destination.yml +6 -0
- data/example/config/connections/mysql/source.yml +6 -0
- data/example/config/email.yml +18 -0
- data/example/plan.rb +87 -0
- data/example/template/email.erb +6 -0
- data/example/transformations/cleanup.sql +1 -0
- data/example/transformations/combined_name.sql +7 -0
- data/example/transformations/email_suffix.rb +20 -0
- data/forklift.jpg +0 -0
- data/forklift_etl.gemspec +28 -0
- data/lib/forklift/base/connection.rb +72 -0
- data/lib/forklift/base/logger.rb +49 -0
- data/lib/forklift/base/mailer.rb +83 -0
- data/lib/forklift/base/pid.rb +55 -0
- data/lib/forklift/base/utils.rb +23 -0
- data/lib/forklift/forklift.rb +19 -0
- data/lib/forklift/patterns/elasticsearch_patterns.rb +7 -0
- data/lib/forklift/patterns/mysql_patterns.rb +87 -0
- data/lib/forklift/plan.rb +138 -0
- data/lib/forklift/transports/elasticsearch.rb +75 -0
- data/lib/forklift/transports/mysql.rb +241 -0
- data/lib/forklift/version.rb +3 -0
- data/readme.md +410 -0
- data/spec/config/connections/elasticsearch/forklift_test.yml +1 -0
- data/spec/config/connections/mysql/forklift_test_destination.yml +6 -0
- data/spec/config/connections/mysql/forklift_test_source_a.yml +6 -0
- data/spec/config/connections/mysql/forklift_test_source_b.yml +6 -0
- data/spec/config/connections/mysql/forklift_test_working.yml +6 -0
- data/spec/config/email.yml +4 -0
- data/spec/integration/basic_spec.rb +29 -0
- data/spec/integration/elasticsearch_patterns_spec.rb +5 -0
- data/spec/integration/elasticsearch_spec.rb +95 -0
- data/spec/integration/multi_transport_spec.rb +112 -0
- data/spec/integration/mysql_patterns_spec.rb +76 -0
- data/spec/integration/mysql_spec.rb +138 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/dumps/elasticsearch/forklift_test.json +7 -0
- data/spec/support/dumps/mysql/forklift_test_source_a.sql +79 -0
- data/spec/support/dumps/mysql/forklift_test_source_b.sql +23 -0
- data/spec/support/spec_client.rb +30 -0
- data/spec/support/spec_plan.rb +15 -0
- data/spec/support/spec_seeds.rb +69 -0
- data/spec/template/spec_email_template.erb +4 -0
- data/spec/unit/connection/mysql_spec.rb +102 -0
- data/spec/unit/misc/email_spec.rb +37 -0
- data/spec/unit/misc/pid_spec.rb +25 -0
- data/spec/unit/misc/step_spec.rb +53 -0
- data/template/destination.yml +6 -0
- data/template/email.erb +1 -0
- data/template/email.yml +18 -0
- data/template/plan.rb +10 -0
- data/template/source.yml +6 -0
- metadata +289 -0
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#############
|
2
|
+
## WARNING ##
|
3
|
+
#############
|
4
|
+
|
5
|
+
# THIS TEST SUITE IS VERY MEAN TO MYSQL AND ELASTICSEARCH
|
6
|
+
# IT *WILL* DELETE ANY CONTENT IN THE TEST DBs
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
10
|
+
APP_DIR ||= File.expand_path('../../', __FILE__)
|
11
|
+
|
12
|
+
require 'forklift/forklift'
|
13
|
+
require 'awesome_print'
|
14
|
+
require 'rspec'
|
15
|
+
require 'fileutils'
|
16
|
+
|
17
|
+
ENV["FORKLIFT_RUN_ALL_STEPS"] = 'true'
|
18
|
+
|
19
|
+
Dir["#{APP_DIR}/spec/support/**/*.rb"].each {|f| require f}
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
|
23
|
+
config.before(:all) do
|
24
|
+
piddir = "#{File.dirname(__FILE__)}/pid"
|
25
|
+
FileUtils.rmdir(piddir) if File.exists?(piddir)
|
26
|
+
SpecSeeds.setup_mysql
|
27
|
+
SpecSeeds.setup_elasticsearch
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
[
|
2
|
+
{"id": 1, "user_id": 1, "product_id": 1, "viewed_at": 1396552251},
|
3
|
+
{"id": 2, "user_id": 1, "product_id": 2, "viewed_at": 1396552252},
|
4
|
+
{"id": 3, "user_id": 2, "product_id": 5, "viewed_at": 1396552253},
|
5
|
+
{"id": 4, "user_id": 2, "product_id": 5, "viewed_at": 1396552254},
|
6
|
+
{"id": 5, "user_id": 2, "product_id": 5, "viewed_at": 1396552255}
|
7
|
+
]
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Dump of table products
|
2
|
+
# ------------------------------------------------------------
|
3
|
+
|
4
|
+
DROP TABLE IF EXISTS `products`;
|
5
|
+
|
6
|
+
CREATE TABLE `products` (
|
7
|
+
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
8
|
+
`name` varchar(255) NOT NULL DEFAULT '',
|
9
|
+
`inventory` int(11) DEFAULT NULL,
|
10
|
+
`created_at` datetime NOT NULL,
|
11
|
+
`updated_at` datetime NOT NULL,
|
12
|
+
PRIMARY KEY (`id`)
|
13
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
14
|
+
|
15
|
+
LOCK TABLES `products` WRITE;
|
16
|
+
|
17
|
+
INSERT INTO `products` (`id`, `name`, `inventory`, `created_at`, `updated_at`)
|
18
|
+
VALUES
|
19
|
+
(1,'car',10,'2014-04-03 11:45:51','2014-04-03 11:45:51'),
|
20
|
+
(2,'boat',3,'2014-04-03 11:45:52','2014-04-03 11:45:52'),
|
21
|
+
(3,'bus',5,'2014-04-03 11:45:54','2014-04-03 11:45:54'),
|
22
|
+
(4,'motorcycle',23,'2014-04-03 11:45:56','2014-04-03 11:45:56'),
|
23
|
+
(5,'hang_glider',2,'2014-04-03 11:46:19','2014-04-03 11:46:19');
|
24
|
+
|
25
|
+
UNLOCK TABLES;
|
26
|
+
|
27
|
+
|
28
|
+
# Dump of table sales
|
29
|
+
# ------------------------------------------------------------
|
30
|
+
|
31
|
+
DROP TABLE IF EXISTS `sales`;
|
32
|
+
|
33
|
+
CREATE TABLE `sales` (
|
34
|
+
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
35
|
+
`user_id` int(11) NOT NULL,
|
36
|
+
`product_id` int(11) NOT NULL,
|
37
|
+
`timestamp` datetime NOT NULL,
|
38
|
+
PRIMARY KEY (`id`)
|
39
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
40
|
+
|
41
|
+
LOCK TABLES `sales` WRITE;
|
42
|
+
|
43
|
+
INSERT INTO `sales` (`id`, `user_id`, `product_id`, `timestamp`)
|
44
|
+
VALUES
|
45
|
+
(1,1,1,'2014-04-03 11:47:11'),
|
46
|
+
(2,1,2,'2014-04-03 11:47:11'),
|
47
|
+
(3,4,5,'2014-04-03 11:47:12'),
|
48
|
+
(4,4,4,'2014-04-03 11:47:25'),
|
49
|
+
(5,5,5,'2014-04-03 11:47:26');
|
50
|
+
|
51
|
+
UNLOCK TABLES;
|
52
|
+
|
53
|
+
|
54
|
+
# Dump of table users
|
55
|
+
# ------------------------------------------------------------
|
56
|
+
|
57
|
+
DROP TABLE IF EXISTS `users`;
|
58
|
+
|
59
|
+
CREATE TABLE `users` (
|
60
|
+
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
61
|
+
`email` varchar(255) NOT NULL DEFAULT '',
|
62
|
+
`first_name` varchar(255) NOT NULL DEFAULT '',
|
63
|
+
`last_name` varchar(255) NOT NULL DEFAULT '',
|
64
|
+
`created_at` datetime NOT NULL,
|
65
|
+
`updated_at` datetime NOT NULL,
|
66
|
+
PRIMARY KEY (`id`)
|
67
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
68
|
+
|
69
|
+
LOCK TABLES `users` WRITE;
|
70
|
+
|
71
|
+
INSERT INTO `users` (`id`, `email`, `first_name`, `last_name`, `created_at`, `updated_at`)
|
72
|
+
VALUES
|
73
|
+
(1,'evan@example.com','Evan','T','2014-04-03 11:40:12','2014-04-03 11:39:28'),
|
74
|
+
(2,'pablo@example.com','Pablo ','J','2014-04-03 11:41:08','2014-04-03 11:41:08'),
|
75
|
+
(3,'kevin@example.com','Kevin','B','2014-04-03 11:41:10','2014-04-03 11:41:10'),
|
76
|
+
(4,'brian@example.com','Brian','L','2014-04-03 11:41:12','2014-04-03 11:41:12'),
|
77
|
+
(5,'aaront@example.com','Aaron','B','2014-04-03 11:41:13','2014-04-03 11:41:13');
|
78
|
+
|
79
|
+
UNLOCK TABLES;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Dump of table admin_notes
|
2
|
+
# ------------------------------------------------------------
|
3
|
+
|
4
|
+
DROP TABLE IF EXISTS `admin_notes`;
|
5
|
+
|
6
|
+
CREATE TABLE `admin_notes` (
|
7
|
+
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
8
|
+
`user_id` int(11) NOT NULL,
|
9
|
+
`note` text NOT NULL,
|
10
|
+
`created_at` datetime NOT NULL,
|
11
|
+
`updated_at` datetime NOT NULL,
|
12
|
+
PRIMARY KEY (`id`)
|
13
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
14
|
+
|
15
|
+
LOCK TABLES `admin_notes` WRITE;
|
16
|
+
|
17
|
+
INSERT INTO `admin_notes` (`id`, `user_id`, `note`, `created_at`, `updated_at`)
|
18
|
+
VALUES
|
19
|
+
(1,1,'User 1 called customer support\n','2014-04-03 11:50:25','2014-04-03 11:50:25'),
|
20
|
+
(2,2,'User 2 called customer support','2014-04-03 11:50:26','2014-04-03 11:50:26'),
|
21
|
+
(3,5,'User 5 returned the purchase','2014-04-03 11:50:28','2014-04-03 11:50:28');
|
22
|
+
|
23
|
+
UNLOCK TABLES;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
class SpecClient
|
5
|
+
|
6
|
+
def self.load_config(file)
|
7
|
+
YAML.load(ERB.new(File.read(file)).result)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.mysql(name)
|
11
|
+
file = File.join(File.dirname(__FILE__), '..', 'config', 'connections', 'mysql', "#{name}.yml")
|
12
|
+
config = self.load_config(file)
|
13
|
+
db = config[:database]
|
14
|
+
config.delete(:database)
|
15
|
+
connection = ::Mysql2::Client.new(config)
|
16
|
+
begin
|
17
|
+
connection.query("use `#{db}`")
|
18
|
+
rescue Exception => e
|
19
|
+
puts "#{e} => will create new databse #{db}"
|
20
|
+
end
|
21
|
+
connection
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.elasticsearch(name)
|
25
|
+
file = File.join(File.dirname(__FILE__), '..', 'config', 'connections', 'elasticsearch', "#{name}.yml")
|
26
|
+
config = self.load_config(file)
|
27
|
+
::Elasticsearch::Client.new(config)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class SpecSeeds
|
4
|
+
|
5
|
+
def self.setup_mysql
|
6
|
+
mysql_connections = []
|
7
|
+
mysql_databases = []
|
8
|
+
|
9
|
+
files = Dir["#{File.dirname(__FILE__)}/../config/connections/mysql/*.yml"]
|
10
|
+
files.each do |f|
|
11
|
+
name = f.split('/').last.gsub('.yml','')
|
12
|
+
mysql_connections << ::SpecClient.mysql(name)
|
13
|
+
mysql_databases << name
|
14
|
+
end
|
15
|
+
|
16
|
+
i = 0
|
17
|
+
while i < mysql_connections.count
|
18
|
+
conn = mysql_connections[i]
|
19
|
+
db = mysql_databases[i]
|
20
|
+
seed = File.join(File.dirname(__FILE__), '..', 'support', 'dumps', 'mysql', "#{db}.sql")
|
21
|
+
conn.query("drop database if exists `#{db}`")
|
22
|
+
conn.query("create database `#{db}`")
|
23
|
+
conn.query("use `#{db}`")
|
24
|
+
if File.exists? seed
|
25
|
+
lines = File.read(seed).split(";")
|
26
|
+
lines.each do |line|
|
27
|
+
conn.query(line) if line[0] != "#"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
i = i + 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.setup_elasticsearch
|
36
|
+
elasticsearch_connections = []
|
37
|
+
elasticsearch_databases = []
|
38
|
+
|
39
|
+
files = Dir["#{File.dirname(__FILE__)}/../config/connections/elasticsearch/*.yml"]
|
40
|
+
files.each do |f|
|
41
|
+
name = f.split('/').last.gsub('.yml','')
|
42
|
+
elasticsearch_connections << ::SpecClient.elasticsearch(name)
|
43
|
+
elasticsearch_databases << name
|
44
|
+
end
|
45
|
+
|
46
|
+
i = 0
|
47
|
+
while i < elasticsearch_connections.count
|
48
|
+
conn = elasticsearch_connections[i]
|
49
|
+
index = elasticsearch_databases[i]
|
50
|
+
seed = File.join(File.dirname(__FILE__), '..', 'support', 'dumps', 'elasticsearch', "#{index}.json")
|
51
|
+
conn.indices.delete({ :index => index }) if conn.indices.exists({ :index => index })
|
52
|
+
if File.exists? seed
|
53
|
+
lines = JSON.parse(File.read(seed))
|
54
|
+
lines.each do |line|
|
55
|
+
object = {
|
56
|
+
:index => index,
|
57
|
+
:body => line,
|
58
|
+
:type => 'forklift',
|
59
|
+
:id => line['id']
|
60
|
+
}
|
61
|
+
conn.index object # assumes ES is setup to allow index creation on write
|
62
|
+
end
|
63
|
+
conn.indices.refresh({ :index => index })
|
64
|
+
end
|
65
|
+
i = i + 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
describe Forklift::Connection::Mysql do
|
5
|
+
|
6
|
+
describe "read/write utils" do
|
7
|
+
before(:each) do
|
8
|
+
SpecSeeds.setup_mysql
|
9
|
+
end
|
10
|
+
|
11
|
+
it "can read a list of tables" do
|
12
|
+
plan = SpecPlan.new
|
13
|
+
plan.do! {
|
14
|
+
source = plan.connections[:mysql][:forklift_test_source_a]
|
15
|
+
expect(source.tables).to include 'users'
|
16
|
+
expect(source.tables).to include 'products'
|
17
|
+
expect(source.tables).to include 'sales'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can delte a table" do
|
22
|
+
plan = SpecPlan.new
|
23
|
+
table = "users"
|
24
|
+
plan.do! {
|
25
|
+
source = plan.connections[:mysql][:forklift_test_source_a]
|
26
|
+
expect(source.tables).to include 'users'
|
27
|
+
source.drop! table
|
28
|
+
expect(source.tables).to_not include 'users'
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can count the rows in a table" do
|
33
|
+
plan = SpecPlan.new
|
34
|
+
table = "users"
|
35
|
+
plan.do! {
|
36
|
+
source = plan.connections[:mysql][:forklift_test_source_a]
|
37
|
+
expect(source.count(table)).to eql 5
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can truncate a table (both with and without !)" do
|
42
|
+
plan = SpecPlan.new
|
43
|
+
table = "users"
|
44
|
+
plan.do! {
|
45
|
+
source = plan.connections[:mysql][:forklift_test_source_a]
|
46
|
+
expect(source.count(table)).to eql 5
|
47
|
+
source.truncate! table
|
48
|
+
expect(source.count(table)).to eql 0
|
49
|
+
expect { source.truncate(table) }.to_not raise_error
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'trunacte! will raise if the table does not exist' do
|
54
|
+
plan = SpecPlan.new
|
55
|
+
table = "other_table"
|
56
|
+
plan.do! {
|
57
|
+
source = plan.connections[:mysql][:forklift_test_source_a]
|
58
|
+
expect { source.truncate!(table) }.to raise_error(/Table 'forklift_test_source_a.other_table' doesn't exist/)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
it "can get the columns of a table" do
|
63
|
+
plan = SpecPlan.new
|
64
|
+
table = "sales"
|
65
|
+
plan.do! {
|
66
|
+
source = plan.connections[:mysql][:forklift_test_source_a]
|
67
|
+
expect(source.columns(table)).to include 'id'
|
68
|
+
expect(source.columns(table)).to include 'user_id'
|
69
|
+
expect(source.columns(table)).to include 'product_id'
|
70
|
+
expect(source.columns(table)).to include 'timestamp'
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
it "can create a mysqldump" do
|
75
|
+
dump = "/tmp/destination.sql.gz"
|
76
|
+
plan = SpecPlan.new
|
77
|
+
plan.do! {
|
78
|
+
source = plan.connections[:mysql][:forklift_test_source_a]
|
79
|
+
source.dump(dump)
|
80
|
+
}
|
81
|
+
|
82
|
+
expect(File.exists?(dump)).to eql true
|
83
|
+
contents = Zlib::GzipReader.new(StringIO.new(File.read(dump))).read
|
84
|
+
expect(contents).to include "(1,'evan@example.com','Evan','T','2014-04-03 11:40:12','2014-04-03 11:39:28')"
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#safe_values" do
|
90
|
+
subject { described_class.new({}, {}) }
|
91
|
+
|
92
|
+
it "escapes one trailing backslash" do
|
93
|
+
values = ["foo\\"]
|
94
|
+
subject.send(:safe_values, values).should == "\"foo\\\\\""
|
95
|
+
end
|
96
|
+
|
97
|
+
it "escapes two trailing backslashes" do
|
98
|
+
values = ["foo\\\\"]
|
99
|
+
subject.send(:safe_values, values).should == "\"foo\\\\\\\\\""
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "email_spec"
|
3
|
+
|
4
|
+
describe 'misc forklift core' do
|
5
|
+
describe 'email' do
|
6
|
+
include EmailSpec::Helpers
|
7
|
+
include EmailSpec::Matchers
|
8
|
+
|
9
|
+
it "can send mail with an email template" do
|
10
|
+
plan = SpecPlan.new
|
11
|
+
plan.do! {
|
12
|
+
email_args = {
|
13
|
+
:to => "YOU@FAKE.com",
|
14
|
+
:from => "Forklift",
|
15
|
+
:subject => "Forklift has moved your database",
|
16
|
+
}
|
17
|
+
email_variables = {
|
18
|
+
:total_users_count => 10,
|
19
|
+
:new_users_count => 5,
|
20
|
+
}
|
21
|
+
email_template = "#{File.dirname(__FILE__)}/../../template/spec_email_template.erb"
|
22
|
+
@email = plan.mailer.send_template(email_args, email_template, email_variables).first
|
23
|
+
}
|
24
|
+
|
25
|
+
@email.should deliver_to("YOU@FAKE.com")
|
26
|
+
@email.should have_subject(/Forklift has moved your database/)
|
27
|
+
@email.should have_body_text(/Your forklift email/) # base
|
28
|
+
@email.should have_body_text(/Total Users: 10/) # template
|
29
|
+
@email.should have_body_text(/New Users: 5/) # template
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can send mail with an attachment" do
|
33
|
+
pending("how to test email attachments?")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'misc forklift core' do
|
4
|
+
|
5
|
+
describe 'pidfile' do
|
6
|
+
it "can create a pidfile and will remove it when the plan is over" do
|
7
|
+
plan = SpecPlan.new
|
8
|
+
pid = "#{File.dirname(__FILE__)}/../../pid/pidfile"
|
9
|
+
expect(File.exists?(pid)).to eql false
|
10
|
+
plan.do! {
|
11
|
+
expect(File.exists?(pid)).to eql true
|
12
|
+
expect(File.read(pid).to_i).to eql Process.pid
|
13
|
+
}
|
14
|
+
expect(File.exists?(pid)).to eql false
|
15
|
+
end
|
16
|
+
|
17
|
+
it "will not run with an existing pidfile" do
|
18
|
+
plan = SpecPlan.new
|
19
|
+
plan.pid.store!
|
20
|
+
expect { plan.do! }.to raise_error SystemExit
|
21
|
+
plan.pid.delete!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'misc forklift core' do
|
4
|
+
describe 'steps' do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
ENV['FORKLIFT_RUN_ALL_STEPS'] = 'false'
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
ENV['FORKLIFT_RUN_ALL_STEPS'] = 'true'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "will run all steps with no extra ARGV" do
|
15
|
+
plan = SpecPlan.new
|
16
|
+
plan.stub(:argv){ ['/path/to/plan'] }
|
17
|
+
steps_run = []
|
18
|
+
plan.do! {
|
19
|
+
plan.step("a"){ steps_run << 'a' }
|
20
|
+
plan.step("b"){ steps_run << 'b' }
|
21
|
+
plan.step("c"){ steps_run << 'c' }
|
22
|
+
}
|
23
|
+
expect(steps_run).to include 'a'
|
24
|
+
expect(steps_run).to include 'b'
|
25
|
+
expect(steps_run).to include 'c'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "will only run steps named within ARGV" do
|
29
|
+
plan = SpecPlan.new
|
30
|
+
plan.stub(:argv){ ['/path/to/plan', 'a','c'] }
|
31
|
+
steps_run = []
|
32
|
+
plan.do! {
|
33
|
+
plan.step("a"){ steps_run << 'a' }
|
34
|
+
plan.step("b"){ steps_run << 'b' }
|
35
|
+
plan.step("c"){ steps_run << 'c' }
|
36
|
+
}
|
37
|
+
expect(steps_run).to include 'a'
|
38
|
+
expect(steps_run).to_not include 'b'
|
39
|
+
expect(steps_run).to include 'c'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "won't run on a badly defined step" do
|
43
|
+
plan = SpecPlan.new
|
44
|
+
plan.stub(:argv){ ['/path/to/plan', 'missing_step'] }
|
45
|
+
expect{
|
46
|
+
plan.do! {
|
47
|
+
plan.step("a"){ raise 'never should get here' }
|
48
|
+
}
|
49
|
+
}.to raise_error SystemExit
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|