entp-astrotrain 0.2.1 → 0.3.0
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/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
|