entp-astrotrain 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +15 -38
- data/VERSION +1 -1
- data/astrotrain.gemspec +37 -4
- data/lib/astrotrain.rb +1 -3
- data/lib/astrotrain/api.rb +0 -1
- data/lib/astrotrain/logged_mail.rb +9 -4
- data/lib/astrotrain/mapping.rb +3 -3
- data/lib/astrotrain/mapping/jabber.rb +21 -16
- data/lib/astrotrain/message.rb +8 -8
- data/lib/astrotrain/worker.rb +61 -0
- data/lib/vendor/rest-client/lib/rest_client.rb +0 -1
- data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +2 -0
- data/test/fixtures/bad_content_type.txt +27 -0
- data/test/logged_mail_test.rb +5 -1
- data/test/test_helper.rb +3 -0
- metadata +115 -6
- data/tasks/doc.thor +0 -149
- data/tasks/merb.thor +0 -2020
data/Rakefile
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'rake'
|
3
2
|
|
4
3
|
begin
|
@@ -9,7 +8,19 @@ begin
|
|
9
8
|
gem.email = "technoweenie@gmail.com"
|
10
9
|
gem.homepage = "http://github.com/entp/astrotrain"
|
11
10
|
gem.authors = ["technoweenie"]
|
12
|
-
|
11
|
+
|
12
|
+
dm_ver = "0.9.11"
|
13
|
+
gem.add_runtime_dependency 'addressable', '2.0.2'
|
14
|
+
gem.add_runtime_dependency "tmail", "1.2.3.1"
|
15
|
+
gem.add_runtime_dependency "dm-core", dm_ver # The datamapper ORM
|
16
|
+
gem.add_runtime_dependency "dm-aggregates", dm_ver # Provides your DM models with count, sum, avg, min, max, etc.
|
17
|
+
gem.add_runtime_dependency "dm-timestamps", dm_ver # Automatically populate created_at, created_on, etc. when those properties are present.
|
18
|
+
gem.add_runtime_dependency "dm-types", dm_ver # Provides additional types, including csv, json, yaml.
|
19
|
+
gem.add_runtime_dependency "dm-validations", dm_ver # Validation framework
|
20
|
+
gem.add_development_dependency "context"
|
21
|
+
gem.add_development_dependency "rr"
|
22
|
+
gem.add_development_dependency "sinatra"
|
23
|
+
gem.add_development_dependency "xmppr4-simple"
|
13
24
|
end
|
14
25
|
|
15
26
|
rescue LoadError
|
@@ -67,42 +78,8 @@ namespace :at do
|
|
67
78
|
|
68
79
|
desc "Start astrotrain DRb server."
|
69
80
|
task :process => :init do
|
70
|
-
|
71
|
-
|
72
|
-
FileUtils.mkdir_p File.dirname(pid_filename)
|
73
|
-
require 'benchmark'
|
74
|
-
|
75
|
-
begin
|
76
|
-
File.open(pid_filename, 'w') { |f| f << Process.pid.to_s }
|
77
|
-
SLEEP = 5
|
78
|
-
|
79
|
-
trap('TERM') { puts 'Exiting...'; $exit = true }
|
80
|
-
trap('INT') { puts 'Exiting...'; $exit = true }
|
81
|
-
|
82
|
-
loop do
|
83
|
-
count = nil
|
84
|
-
|
85
|
-
realtime = Benchmark.realtime do
|
86
|
-
files = Dir["#{Astrotrain::Message.queue_path}/*"]
|
87
|
-
files.each do |mail|
|
88
|
-
Astrotrain::Message.receive_file(mail)
|
89
|
-
end
|
90
|
-
count = files.size
|
91
|
-
end
|
92
|
-
|
93
|
-
break if $exit
|
94
|
-
|
95
|
-
if count.zero?
|
96
|
-
sleep(SLEEP)
|
97
|
-
else
|
98
|
-
puts "#{count} mails processed at %.4f m/s ..." % [count / realtime]
|
99
|
-
end
|
100
|
-
|
101
|
-
break if $exit
|
102
|
-
end
|
103
|
-
ensure
|
104
|
-
FileUtils.rm(pid_filename) rescue nil
|
105
|
-
end
|
81
|
+
require 'astrotrain/worker'
|
82
|
+
Astrotrain::Worker.start
|
106
83
|
end
|
107
84
|
end
|
108
85
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/astrotrain.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{astrotrain}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.3.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["technoweenie"]
|
9
|
-
s.date = %q{2009-09-
|
9
|
+
s.date = %q{2009-09-25}
|
10
10
|
s.email = %q{technoweenie@gmail.com}
|
11
11
|
s.extra_rdoc_files = [
|
12
12
|
"LICENSE",
|
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
"lib/astrotrain/mapping/transport.rb",
|
30
30
|
"lib/astrotrain/message.rb",
|
31
31
|
"lib/astrotrain/tmail.rb",
|
32
|
+
"lib/astrotrain/worker.rb",
|
32
33
|
"lib/vendor/rest-client/README.rdoc",
|
33
34
|
"lib/vendor/rest-client/Rakefile",
|
34
35
|
"lib/vendor/rest-client/bin/restclient",
|
@@ -45,10 +46,9 @@ Gem::Specification.new do |s|
|
|
45
46
|
"lib/vendor/rest-client/spec/request_errors_spec.rb",
|
46
47
|
"lib/vendor/rest-client/spec/resource_spec.rb",
|
47
48
|
"lib/vendor/rest-client/spec/rest_client_spec.rb",
|
48
|
-
"tasks/doc.thor",
|
49
|
-
"tasks/merb.thor",
|
50
49
|
"test/api_test.rb",
|
51
50
|
"test/fixtures/apple_multipart.txt",
|
51
|
+
"test/fixtures/bad_content_type.txt",
|
52
52
|
"test/fixtures/basic.txt",
|
53
53
|
"test/fixtures/custom.txt",
|
54
54
|
"test/fixtures/fwd.txt",
|
@@ -89,8 +89,41 @@ Gem::Specification.new do |s|
|
|
89
89
|
s.specification_version = 3
|
90
90
|
|
91
91
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
92
|
+
s.add_runtime_dependency(%q<addressable>, ["= 2.0.2"])
|
93
|
+
s.add_runtime_dependency(%q<tmail>, ["= 1.2.3.1"])
|
94
|
+
s.add_runtime_dependency(%q<dm-core>, ["= 0.9.11"])
|
95
|
+
s.add_runtime_dependency(%q<dm-aggregates>, ["= 0.9.11"])
|
96
|
+
s.add_runtime_dependency(%q<dm-timestamps>, ["= 0.9.11"])
|
97
|
+
s.add_runtime_dependency(%q<dm-types>, ["= 0.9.11"])
|
98
|
+
s.add_runtime_dependency(%q<dm-validations>, ["= 0.9.11"])
|
99
|
+
s.add_development_dependency(%q<context>, [">= 0"])
|
100
|
+
s.add_development_dependency(%q<rr>, [">= 0"])
|
101
|
+
s.add_development_dependency(%q<sinatra>, [">= 0"])
|
102
|
+
s.add_development_dependency(%q<xmppr4-simple>, [">= 0"])
|
92
103
|
else
|
104
|
+
s.add_dependency(%q<addressable>, ["= 2.0.2"])
|
105
|
+
s.add_dependency(%q<tmail>, ["= 1.2.3.1"])
|
106
|
+
s.add_dependency(%q<dm-core>, ["= 0.9.11"])
|
107
|
+
s.add_dependency(%q<dm-aggregates>, ["= 0.9.11"])
|
108
|
+
s.add_dependency(%q<dm-timestamps>, ["= 0.9.11"])
|
109
|
+
s.add_dependency(%q<dm-types>, ["= 0.9.11"])
|
110
|
+
s.add_dependency(%q<dm-validations>, ["= 0.9.11"])
|
111
|
+
s.add_dependency(%q<context>, [">= 0"])
|
112
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
113
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
114
|
+
s.add_dependency(%q<xmppr4-simple>, [">= 0"])
|
93
115
|
end
|
94
116
|
else
|
117
|
+
s.add_dependency(%q<addressable>, ["= 2.0.2"])
|
118
|
+
s.add_dependency(%q<tmail>, ["= 1.2.3.1"])
|
119
|
+
s.add_dependency(%q<dm-core>, ["= 0.9.11"])
|
120
|
+
s.add_dependency(%q<dm-aggregates>, ["= 0.9.11"])
|
121
|
+
s.add_dependency(%q<dm-timestamps>, ["= 0.9.11"])
|
122
|
+
s.add_dependency(%q<dm-types>, ["= 0.9.11"])
|
123
|
+
s.add_dependency(%q<dm-validations>, ["= 0.9.11"])
|
124
|
+
s.add_dependency(%q<context>, [">= 0"])
|
125
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
126
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
127
|
+
s.add_dependency(%q<xmppr4-simple>, [">= 0"])
|
95
128
|
end
|
96
129
|
end
|
data/lib/astrotrain.rb
CHANGED
@@ -36,8 +36,6 @@ private
|
|
36
36
|
def self.load_dependencies
|
37
37
|
require 'rubygems'
|
38
38
|
gem 'addressable', '2.0.2'
|
39
|
-
gem "tmail", "1.2.3.1"
|
40
|
-
gem "xmpp4r-simple", "0.8.8"
|
41
39
|
|
42
40
|
dm_ver = "0.9.11"
|
43
41
|
gem "dm-core", dm_ver # The datamapper ORM
|
@@ -48,7 +46,7 @@ private
|
|
48
46
|
|
49
47
|
$LOAD_PATH.unshift File.join(lib_root, 'vendor', 'rest-client', 'lib')
|
50
48
|
|
51
|
-
%w(dm-core dm-aggregates dm-timestamps dm-types dm-validations
|
49
|
+
%w(dm-core dm-aggregates dm-timestamps dm-types dm-validations tmail rest_client).each do |lib|
|
52
50
|
require lib
|
53
51
|
end
|
54
52
|
end
|
data/lib/astrotrain/api.rb
CHANGED
@@ -16,21 +16,26 @@ module Astrotrain
|
|
16
16
|
property :sender, String, :index => true, :size => 255, :length => 1..255
|
17
17
|
property :recipient, String, :index => true, :size => 255, :length => 1..255
|
18
18
|
property :subject, String, :index => true, :size => 255, :length => 1..255
|
19
|
+
property :mail_file, String, :size => 255, :length => 1..255
|
19
20
|
property :created_at, DateTime
|
20
21
|
property :delivered_at, DateTime
|
21
|
-
property :error_message,
|
22
|
+
property :error_message, Text
|
22
23
|
|
23
24
|
belongs_to :mapping
|
24
25
|
|
25
|
-
def self.from(message)
|
26
|
+
def self.from(message, file = nil)
|
26
27
|
logged = new
|
27
28
|
begin
|
28
|
-
logged.sender
|
29
|
-
logged.subject
|
29
|
+
logged.sender = Message.parse_email_addresses(message.sender).first
|
30
|
+
logged.subject = message.subject
|
31
|
+
logged.mail_file = file if file
|
30
32
|
end
|
31
33
|
if !block_given? || yield(logged)
|
32
34
|
begin
|
33
35
|
logged.save
|
36
|
+
if logged.delivered_at && File.exist?(logged.mail_file.to_s)
|
37
|
+
FileUtils.rm_rf logged.mail_file
|
38
|
+
end
|
34
39
|
rescue
|
35
40
|
puts $!.inspect
|
36
41
|
end
|
data/lib/astrotrain/mapping.rb
CHANGED
@@ -38,8 +38,8 @@ module Astrotrain
|
|
38
38
|
|
39
39
|
# Processes a given message. It finds a mapping, creates a LoggedMail record,
|
40
40
|
# and attempts to process the message.
|
41
|
-
def self.process(message)
|
42
|
-
LoggedMail.from(message) do |logged|
|
41
|
+
def self.process(message, file = nil)
|
42
|
+
LoggedMail.from(message, file) do |logged|
|
43
43
|
save_logged = begin
|
44
44
|
mapping, recipient = match(message.recipients)
|
45
45
|
if mapping
|
@@ -50,7 +50,7 @@ module Astrotrain
|
|
50
50
|
end
|
51
51
|
LoggedMail.log_processed # save successfully processed messages?
|
52
52
|
rescue
|
53
|
-
logged.error_message = "#{$!.
|
53
|
+
logged.error_message = "#{$!.message}\n#{$!.backtrace.join("\n")}"
|
54
54
|
end
|
55
55
|
Astrotrain.callback(:post_processing, message, mapping, logged)
|
56
56
|
save_logged
|
@@ -1,23 +1,28 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
begin
|
2
|
+
require 'xmpp4r-simple'
|
3
|
+
module Astrotrain
|
4
|
+
class Mapping
|
5
|
+
# This is experimental. No attachments are supported.
|
6
|
+
class Jabber < Transport
|
7
|
+
class << self
|
8
|
+
attr_accessor :login, :password
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
def process
|
12
|
+
return unless Transport.processing
|
13
|
+
connection.deliver(@mapping.destination, content)
|
14
|
+
end
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
def connection
|
17
|
+
@connection ||= ::Jabber::Simple.new(self.class.login, self.class.password)
|
18
|
+
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
+
def content
|
21
|
+
@content ||= "From: %s\nTo: %s\nSubject: %s\nEmails: %s\n%s" % [fields[:from], fields[:to], fields[:subject], fields[:emails] * ", ", fields[:body]]
|
22
|
+
end
|
20
23
|
end
|
21
24
|
end
|
22
25
|
end
|
26
|
+
rescue LoadError
|
27
|
+
puts "Install xmpp4r-simple for Jabber support."
|
23
28
|
end
|
data/lib/astrotrain/message.rb
CHANGED
@@ -49,25 +49,25 @@ module Astrotrain
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Parses the given raw email text and processes it with a matching Mapping.
|
52
|
-
def self.receive(raw)
|
52
|
+
def self.receive(raw, file = nil)
|
53
53
|
message = parse(raw)
|
54
54
|
Astrotrain.callback(:pre_mapping, message)
|
55
|
-
Mapping.process(message)
|
55
|
+
Mapping.process(message, file)
|
56
56
|
message
|
57
57
|
end
|
58
58
|
|
59
59
|
# Processes the given file. It parses it by reading the contents, and optionally
|
60
60
|
# archives or removes the original file.
|
61
|
-
def self.receive_file(path
|
62
|
-
|
61
|
+
def self.receive_file(path)
|
62
|
+
raw = IO.read(path)
|
63
|
+
logged_path = path
|
63
64
|
if archive_path
|
64
65
|
daily_archive_path = archive_path / Time.now.year.to_s / Time.now.month.to_s / Time.now.day.to_s
|
65
66
|
FileUtils.mkdir_p(daily_archive_path)
|
66
|
-
|
67
|
-
|
68
|
-
FileUtils.rm_rf path
|
67
|
+
logged_path = daily_archive_path / File.basename(path)
|
68
|
+
FileUtils.mv path, logged_path if path != logged_path
|
69
69
|
end
|
70
|
-
|
70
|
+
receive(raw, logged_path)
|
71
71
|
end
|
72
72
|
|
73
73
|
# Parses the raw email headers into a Astrotrain::Message instance.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
module Astrotrain
|
3
|
+
class Worker
|
4
|
+
attr_accessor :logger, :sleep_duration, :name
|
5
|
+
|
6
|
+
def self.start(options = {})
|
7
|
+
new(options).run
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@name = options[:name] || "pid:#{Process.pid}"
|
12
|
+
@pid = options[:pid] || File.join(Astrotrain.root, 'log', 'astrotrain_job.pid')
|
13
|
+
@sleep_duration = options[:sleep] || 5
|
14
|
+
@logger = options.key?(:logger) ? options[:logger] : STDOUT
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
setup do
|
19
|
+
files = 0
|
20
|
+
Dir.foreach(Message.queue_path) do |f|
|
21
|
+
next if f =~ /^\.{1,2}$/
|
22
|
+
files += 1
|
23
|
+
file = File.join(Message.queue_path, f)
|
24
|
+
Message.receive_file(file)
|
25
|
+
end
|
26
|
+
files
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def say(s)
|
31
|
+
@logger << "#{s}\n" if @logger
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup
|
35
|
+
say "*** Starting Astrotrain::Worker #{@name}"
|
36
|
+
FileUtils.mkdir_p File.dirname(@pid)
|
37
|
+
|
38
|
+
File.open(@pid, 'w') { |f| f << Process.pid.to_s }
|
39
|
+
|
40
|
+
trap('TERM') { puts 'Exiting...'; $exit = true }
|
41
|
+
trap('INT') { puts 'Exiting...'; $exit = true }
|
42
|
+
|
43
|
+
loop do
|
44
|
+
count = nil
|
45
|
+
realtime = Benchmark.realtime { count = yield }
|
46
|
+
|
47
|
+
break if $exit
|
48
|
+
|
49
|
+
if count.zero?
|
50
|
+
sleep(@sleep_duration)
|
51
|
+
else
|
52
|
+
puts "#{count} mails processed at %.4f m/s ..." % [count / realtime]
|
53
|
+
end
|
54
|
+
|
55
|
+
break if $exit
|
56
|
+
end
|
57
|
+
ensure
|
58
|
+
FileUtils.rm(@pid) rescue nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -137,7 +137,6 @@ module RestClient
|
|
137
137
|
|
138
138
|
net = Net::HTTP.new(uri.host, uri.port)
|
139
139
|
net.use_ssl = uri.is_a?(URI::HTTPS)
|
140
|
-
|
141
140
|
net.start do |http|
|
142
141
|
## Ok. I know this is weird but it's a hack for now
|
143
142
|
## this lets process_result determine if it should read the body
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Delivered-To: "Processor" <processor@astrotrain.com>
|
2
|
+
Message-ID: <a16be7390810161014n52b603e9k1aa6bb803c6735aa@mail.gmail.com>
|
3
|
+
Date: Thu, 16 Oct 2008 10:14:18 -0700
|
4
|
+
From: Bob <user@example.com>
|
5
|
+
To: Processor <processor@astrotrain.com>
|
6
|
+
Subject: Fwd: blah blah
|
7
|
+
MIME-Version: 1.0
|
8
|
+
X-Custom: reply
|
9
|
+
Content-Type: multipart/mixed; boundary="====boundary===="
|
10
|
+
Content-Transfer-Encoding: 7bit
|
11
|
+
Content-Disposition: inline
|
12
|
+
|
13
|
+
--====boundary====
|
14
|
+
Content-Type: text/plain; charset="us-ascii"
|
15
|
+
|
16
|
+
This message is being generated automatically to notify you
|
17
|
+
that PowerMTA has crashed on mtasv.net.
|
18
|
+
|
19
|
+
As the information below is likely to be essential for debugging
|
20
|
+
the problem, please forward this message to <support@port25.com>.
|
21
|
+
Thank you.
|
22
|
+
|
23
|
+
--====boundary====
|
24
|
+
Content-Type: text/plain; charset="us-ascii"
|
25
|
+
|
26
|
+
Yo
|
27
|
+
--====boundary====--
|
data/test/logged_mail_test.rb
CHANGED
@@ -7,7 +7,7 @@ class Astrotrain::LoggedMailTest < Astrotrain::TestCase
|
|
7
7
|
@mapping = Astrotrain::Mapping.create!(:email_user => '*')
|
8
8
|
@raw = mail(:custom)
|
9
9
|
@message = Astrotrain::Message.parse(@raw)
|
10
|
-
@logged = Astrotrain::LoggedMail.from(@message) do |l|
|
10
|
+
@logged = Astrotrain::LoggedMail.from(@message, 'foo/bar') do |l|
|
11
11
|
l.recipient = @message.recipients(%w(delivered_to)).first
|
12
12
|
l.mapping = @mapping
|
13
13
|
end
|
@@ -17,6 +17,10 @@ class Astrotrain::LoggedMailTest < Astrotrain::TestCase
|
|
17
17
|
assert_equal @message.recipients(%w(delivered_to)).first, @logged.recipient
|
18
18
|
end
|
19
19
|
|
20
|
+
it "sets mail_file" do
|
21
|
+
assert_equal 'foo/bar', @logged.mail_file
|
22
|
+
end
|
23
|
+
|
20
24
|
it "sets sender" do
|
21
25
|
assert_equal 'user@example.com', @logged.sender
|
22
26
|
end
|