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 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
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
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
- pid_filename = File.join(Astrotrain.root, 'log', 'astrotrain_job.pid')
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.2.1
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.2.1"
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-23}
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 xmpp4r-simple tmail rest_client).each do |lib|
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
@@ -1,4 +1,3 @@
1
- require 'rubygems'
2
1
  require 'sinatra'
3
2
 
4
3
  before do
@@ -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, String, :size => 255, :length => 1..255
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 = Message.parse_email_addresses(message.sender).first
29
- logged.subject = message.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
@@ -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 = "#{$!.class}: #{$!}"
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
- module Astrotrain
2
- class Mapping
3
- # This is experimental. No attachments are supported.
4
- class Jabber < Transport
5
- class << self
6
- attr_accessor :login, :password
7
- end
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
- def process
10
- return unless Transport.processing
11
- connection.deliver(@mapping.destination, content)
12
- end
11
+ def process
12
+ return unless Transport.processing
13
+ connection.deliver(@mapping.destination, content)
14
+ end
13
15
 
14
- def connection
15
- @connection ||= ::Jabber::Simple.new(self.class.login, self.class.password)
16
- end
16
+ def connection
17
+ @connection ||= ::Jabber::Simple.new(self.class.login, self.class.password)
18
+ end
17
19
 
18
- def content
19
- @content ||= "From: %s\nTo: %s\nSubject: %s\nEmails: %s\n%s" % [fields[:from], fields[:to], fields[:subject], fields[:emails] * ", ", fields[:body]]
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
@@ -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, raw = nil)
62
- message = receive IO.read(path)
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
- FileUtils.mv path, daily_archive_path / File.basename(path)
67
- else
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
- message
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
@@ -5,6 +5,8 @@
5
5
  # Taken from:
6
6
  # http://www.missiondata.com/blog/ruby/29/streaming-data-to-s3-with-ruby/
7
7
 
8
+ Net::HTTP.ssl_context_accessor(:tmp_dh_callback)
9
+
8
10
  module Net
9
11
  class HTTP
10
12
  alias __request__ request
@@ -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====--
@@ -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
data/test/test_helper.rb CHANGED
@@ -18,6 +18,9 @@ module Astrotrain
18
18
  })
19
19
  end
20
20
 
21
+ LoggedMail.auto_migrate!
22
+ Mapping.auto_migrate!
23
+
21
24
  LoggedMail.log_path = Astrotrain.root / 'messages'
22
25
  FileUtils.rm_rf LoggedMail.log_path
23
26
  FileUtils.mkdir_p LoggedMail.log_path