bounce_fu 0.0.3
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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bounce_fu.gemspec +62 -0
- data/lib/bounce_fu.rb +24 -0
- data/lib/bounce_fu/abstract_handler.rb +19 -0
- data/lib/bounce_fu/bounce.rb +31 -0
- data/lib/bounce_fu/pop_fetcher.rb +26 -0
- data/lib/bounce_fu/test_fetcher.rb +14 -0
- data/test/abstract_handler_test.rb +17 -0
- data/test/bounce_fu_test.rb +66 -0
- data/test/bounce_test.rb +49 -0
- data/test/fixtures/hotmail-unavailable.email +61 -0
- data/test/fixtures/permanent.email +28 -0
- data/test/fixtures/temporary.email +18 -0
- data/test/pop_fetcher_test.rb +35 -0
- data/test/test_fetcher_test.rb +14 -0
- data/test/test_helper.rb +19 -0
- metadata +79 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 James Golick
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "bounce_fu"
|
8
|
+
gem.summary = %Q{TODO}
|
9
|
+
gem.email = "james@giraffesoft.ca"
|
10
|
+
gem.homepage = "http://github.com/giraffesoft/bounce_fu"
|
11
|
+
gem.authors = ["James Golick"]
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
if File.exist?('VERSION.yml')
|
45
|
+
config = YAML.load(File.read('VERSION.yml'))
|
46
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "bounce_fu #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
56
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.3
|
data/bounce_fu.gemspec
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{bounce_fu}
|
5
|
+
s.version = "0.0.3"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["James Golick"]
|
9
|
+
s.date = %q{2009-06-10}
|
10
|
+
s.email = %q{james@giraffesoft.ca}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".document",
|
17
|
+
".gitignore",
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"bounce_fu.gemspec",
|
23
|
+
"lib/bounce_fu.rb",
|
24
|
+
"lib/bounce_fu/abstract_handler.rb",
|
25
|
+
"lib/bounce_fu/bounce.rb",
|
26
|
+
"lib/bounce_fu/pop_fetcher.rb",
|
27
|
+
"lib/bounce_fu/test_fetcher.rb",
|
28
|
+
"test/abstract_handler_test.rb",
|
29
|
+
"test/bounce_fu_test.rb",
|
30
|
+
"test/bounce_test.rb",
|
31
|
+
"test/fixtures/hotmail-unavailable.email",
|
32
|
+
"test/fixtures/permanent.email",
|
33
|
+
"test/fixtures/temporary.email",
|
34
|
+
"test/pop_fetcher_test.rb",
|
35
|
+
"test/test_fetcher_test.rb",
|
36
|
+
"test/test_helper.rb"
|
37
|
+
]
|
38
|
+
s.has_rdoc = true
|
39
|
+
s.homepage = %q{http://github.com/giraffesoft/bounce_fu}
|
40
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.3.1}
|
43
|
+
s.summary = %q{TODO}
|
44
|
+
s.test_files = [
|
45
|
+
"test/abstract_handler_test.rb",
|
46
|
+
"test/bounce_fu_test.rb",
|
47
|
+
"test/bounce_test.rb",
|
48
|
+
"test/pop_fetcher_test.rb",
|
49
|
+
"test/test_fetcher_test.rb",
|
50
|
+
"test/test_helper.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 2
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
else
|
59
|
+
end
|
60
|
+
else
|
61
|
+
end
|
62
|
+
end
|
data/lib/bounce_fu.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bounce_fu/bounce'
|
2
|
+
require 'bounce_fu/pop_fetcher'
|
3
|
+
require 'bounce_fu/abstract_handler'
|
4
|
+
require 'bounce_fu/test_fetcher'
|
5
|
+
|
6
|
+
module BounceFu
|
7
|
+
class MissingConfigOption < ArgumentError; end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def configuration
|
11
|
+
@configuration ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle_bounces
|
15
|
+
[:host, :username, :password, :handler].each do |key|
|
16
|
+
raise MissingConfigOption, "You must configure BounceFu with a #{key}" if configuration[key].nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
fetcher = (configuration[:fetcher] || PopFetcher).new
|
20
|
+
configuration[:handler].new(fetcher).handle_bounces
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module BounceFu
|
2
|
+
class AbstractHandler
|
3
|
+
attr_accessor :fetcher
|
4
|
+
|
5
|
+
def initialize(fetcher)
|
6
|
+
@fetcher = fetcher
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_bounces
|
10
|
+
fetcher.each do |m|
|
11
|
+
handle_bounce(Bounce.new(m))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle_bounce(bounce)
|
16
|
+
raise UnimplementedError, "Subclass BounceFu::AbstractHandler to add the applicable bounce handling strategy for your application."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module BounceFu
|
2
|
+
class Bounce
|
3
|
+
attr_reader :message_body, :email_address
|
4
|
+
|
5
|
+
def initialize(message_body)
|
6
|
+
@message_body = message_body
|
7
|
+
parse
|
8
|
+
end
|
9
|
+
|
10
|
+
def permanent?
|
11
|
+
@permanent
|
12
|
+
end
|
13
|
+
|
14
|
+
def temporary?
|
15
|
+
!permanent?
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
def parse
|
20
|
+
if message_body.match(/This is a permanent error. The following address\(es\) failed:(.+)/m)
|
21
|
+
@permanent = true
|
22
|
+
$1.split("\n").each do |line|
|
23
|
+
if line.match(/([\w]+@[\w\.]+)/)
|
24
|
+
@email_address = $1.strip
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'net/pop'
|
2
|
+
|
3
|
+
module BounceFu
|
4
|
+
class PopFetcher
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def connection
|
8
|
+
if @connection.nil?
|
9
|
+
@connection = Net::POP3.new(BounceFu.configuration[:host])
|
10
|
+
@connection.start(BounceFu.configuration[:username],
|
11
|
+
BounceFu.configuration[:password])
|
12
|
+
end
|
13
|
+
|
14
|
+
@connection
|
15
|
+
end
|
16
|
+
|
17
|
+
def each
|
18
|
+
connection.each_mail do |e|
|
19
|
+
yield e.pop
|
20
|
+
e.delete
|
21
|
+
end
|
22
|
+
connection.finish
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class AbstractHandlerTest < Test::Unit::TestCase
|
4
|
+
context "Handling bounced mail from a fetcher" do
|
5
|
+
setup do
|
6
|
+
@fetcher = BounceFu::PopFetcher.new
|
7
|
+
@fetcher.stubs(:each).yields("a message")
|
8
|
+
end
|
9
|
+
|
10
|
+
should "call Handler#handle_bounce for each message yielded" do
|
11
|
+
count = 0
|
12
|
+
@handler = BounceFu::AbstractHandler.new(@fetcher)
|
13
|
+
@handler.expects(:handle_bounce).with(instance_of(BounceFu::Bounce)).once
|
14
|
+
@handler.handle_bounces
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class BounceFuTest < Test::Unit::TestCase
|
4
|
+
context "Setting the configuration" do
|
5
|
+
should "behave like a hash" do
|
6
|
+
BounceFu.configuration[:host] = "whatever.com"
|
7
|
+
assert_equal "whatever.com", BounceFu.configuration[:host]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Starting a bounce handling session " do
|
12
|
+
setup { BounceFu.configuration.clear }
|
13
|
+
teardown { BounceFu.configuration.clear }
|
14
|
+
|
15
|
+
should "raise if there's no host" do
|
16
|
+
assert_raise(BounceFu::MissingConfigOption) {
|
17
|
+
BounceFu.handle_bounces
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
should "raise if there's no username" do
|
22
|
+
BounceFu.configuration[:host] = "somewhere.com"
|
23
|
+
assert_raise(BounceFu::MissingConfigOption) {
|
24
|
+
BounceFu.handle_bounces
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
should "raise if there's no password" do
|
29
|
+
BounceFu.configuration[:host] = "somewhere.com"
|
30
|
+
BounceFu.configuration[:username] = "somebody@somewhere.com"
|
31
|
+
assert_raise(BounceFu::MissingConfigOption) {
|
32
|
+
BounceFu.handle_bounces
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
should "raise if there's no handler" do
|
37
|
+
BounceFu.configuration[:host] = "somewhere.com"
|
38
|
+
BounceFu.configuration[:username] = "somebody@somewhere.com"
|
39
|
+
BounceFu.configuration[:password] = "somebody@somewhere.com"
|
40
|
+
assert_raise(BounceFu::MissingConfigOption) {
|
41
|
+
BounceFu.handle_bounces
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
should "start the handler if the configuration is complete" do
|
46
|
+
BounceFu.configuration[:host] = "somewhere.com"
|
47
|
+
BounceFu.configuration[:username] = "somebody@somewhere.com"
|
48
|
+
BounceFu.configuration[:password] = "somebody@somewhere.com"
|
49
|
+
BounceFu.configuration[:handler] = BounceFu::AbstractHandler
|
50
|
+
|
51
|
+
BounceFu::AbstractHandler.expects(:new).with(instance_of(BounceFu::PopFetcher)).returns(mock(:handle_bounces => true))
|
52
|
+
BounceFu.handle_bounces
|
53
|
+
end
|
54
|
+
|
55
|
+
should "use the supplied fetcher if one is supplied" do
|
56
|
+
BounceFu.configuration[:host] = "somewhere.com"
|
57
|
+
BounceFu.configuration[:username] = "somebody@somewhere.com"
|
58
|
+
BounceFu.configuration[:password] = "somebody@somewhere.com"
|
59
|
+
BounceFu.configuration[:handler] = BounceFu::AbstractHandler
|
60
|
+
BounceFu.configuration[:fetcher] = BounceFu::TestFetcher
|
61
|
+
|
62
|
+
BounceFu::AbstractHandler.expects(:new).with(instance_of(BounceFu::TestFetcher)).returns(mock(:handle_bounces => true))
|
63
|
+
BounceFu.handle_bounces
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/test/bounce_test.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class BounceTest < Test::Unit::TestCase
|
4
|
+
context "A permanently bounced email" do
|
5
|
+
setup do
|
6
|
+
@email = BounceFu::Bounce.new(fixture("permanent"))
|
7
|
+
end
|
8
|
+
|
9
|
+
should "be permanent?" do
|
10
|
+
assert @email.permanent?
|
11
|
+
end
|
12
|
+
|
13
|
+
should "not be temporary?" do
|
14
|
+
assert !@email.temporary?
|
15
|
+
end
|
16
|
+
|
17
|
+
should "return the email address that failed" do
|
18
|
+
assert_equal "someuser@yahoo.com", @email.email_address
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "A temporarily bounced email" do
|
23
|
+
setup do
|
24
|
+
@email = BounceFu::Bounce.new(fixture("temporary"))
|
25
|
+
end
|
26
|
+
|
27
|
+
should "be temporary?" do
|
28
|
+
assert @email.temporary?
|
29
|
+
end
|
30
|
+
|
31
|
+
should "not be permanent" do
|
32
|
+
assert !@email.permanent?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "An abuse email from Hotmail" do
|
37
|
+
setup do
|
38
|
+
@email = BounceFu::Bounce.new(fixture("hotmail-unavailable"))
|
39
|
+
end
|
40
|
+
|
41
|
+
should "be permanent" do
|
42
|
+
assert @email.permanent?
|
43
|
+
end
|
44
|
+
|
45
|
+
should "process the email correctly" do
|
46
|
+
assert_equal "someuser@live.com", @email.email_address
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
Return-path: <>
|
2
|
+
Envelope-to: donotreply@app.yourapp.com.com
|
3
|
+
Delivery-date: Wed, 10 Jun 2009 15:03:46 -0700
|
4
|
+
Received: from [10.4.65.128] (helo=smtp.ey04.emailhost.com.com)
|
5
|
+
by smtpi.ey04.emailhost.com.com with esmtp (Exim 4.69)
|
6
|
+
id 1MEVta-0003y0-KE
|
7
|
+
for donotreply@app.yourapp.com.com; Wed, 10 Jun 2009 15:03:46 -0700
|
8
|
+
Received: from mail by smtp.ey04.emailhost.com.com with local (Exim 4.69)
|
9
|
+
id 1MEVta-0006kG-IL
|
10
|
+
for donotreply@app.yourapp.com.com; Wed, 10 Jun 2009 15:03:46 -0700
|
11
|
+
X-Failed-Recipients: someuser@live.com
|
12
|
+
Auto-Submitted: auto-replied
|
13
|
+
From: Mail Delivery System <Mailer-Daemon@smtp.ey04.emailhost.com.com>
|
14
|
+
To: donotreply@app.yourapp.com.com
|
15
|
+
Subject: Mail delivery failed: returning message to sender
|
16
|
+
Message-Id: <E1MEVta-0006kG-IL@smtp.ey04.emailhost.com.com>
|
17
|
+
Date: Wed, 10 Jun 2009 15:03:46 -0700
|
18
|
+
X-Spam_score: -1.4
|
19
|
+
X-Spam_score_int: -13
|
20
|
+
X-Spam_bar: -
|
21
|
+
X-Spam_report: Spam detection software, running on the system "ey04-s00031.ey04.emailhost.com.com", has
|
22
|
+
identified this incoming email as possible spam. The original message
|
23
|
+
has been attached to this so you can view it (if it isn't spam) or label
|
24
|
+
similar future email. If you have any questions, see
|
25
|
+
the administrator of that system for details.
|
26
|
+
Content preview: This message was created automatically by mail delivery software.
|
27
|
+
A message that you sent could not be delivered to one or more of its recipients.
|
28
|
+
This is a permanent error. The following address(es) failed: [...]
|
29
|
+
Content analysis details: (-1.4 points, 5.0 required)
|
30
|
+
pts rule name description
|
31
|
+
---- ---------------------- --------------------------------------------------
|
32
|
+
-1.4 ALL_TRUSTED Passed through trusted hosts only via SMTP
|
33
|
+
|
34
|
+
This message was created automatically by mail delivery software.
|
35
|
+
|
36
|
+
A message that you sent could not be delivered to one or more of its
|
37
|
+
recipients. This is a permanent error. The following address(es) failed:
|
38
|
+
|
39
|
+
someuser@live.com
|
40
|
+
SMTP error from remote mail server after RCPT TO:<someuser@live.com>:
|
41
|
+
host mx3.hotmail.com [65.54.245.72]: 550 Requested action not taken:
|
42
|
+
mailbox unavailable
|
43
|
+
|
44
|
+
------ This is a copy of the message, including all the headers. ------
|
45
|
+
|
46
|
+
Return-path: <donotreply@app.yourapp.com.com>
|
47
|
+
Received: from [10.4.65.147] (helo=yourapp.com.com)
|
48
|
+
by smtp.ey04.emailhost.com.com with esmtp (Exim 4.69)
|
49
|
+
(envelope-from <donotreply@app.yourapp.com.com>)
|
50
|
+
id 1MEVtZ-0006kB-8H
|
51
|
+
for someuser@live.com; Wed, 10 Jun 2009 15:03:45 -0700
|
52
|
+
Date: Wed, 10 Jun 2009 15:03:45 -0700
|
53
|
+
From: Your App <donotreply@app.yourapp.com.com>
|
54
|
+
To: someuser@live.com
|
55
|
+
Message-Id: <4a302dc13cf4d_45c15a0b8943c48436323@ey04-s00402.tmail>
|
56
|
+
Subject: stuff
|
57
|
+
Mime-Version: 1.0
|
58
|
+
Content-Type: text/plain; charset=utf-8
|
59
|
+
X-Custom-Ip-Tag: yourapp.com
|
60
|
+
|
61
|
+
http://yourapp.com.com/users/xyz
|
@@ -0,0 +1,28 @@
|
|
1
|
+
This message was created automatically by mail delivery software.
|
2
|
+
|
3
|
+
A message that you sent could not be delivered to one or more of its
|
4
|
+
recipients. This is a permanent error. The following address(es) failed:
|
5
|
+
|
6
|
+
someuser@yahoo.com
|
7
|
+
|
8
|
+
------ This is a copy of the message, including all the headers. ------
|
9
|
+
|
10
|
+
Return-path: <donotreply@app.myapp.com>
|
11
|
+
Received: from [10.4.64.85] (helo=myapp.com)
|
12
|
+
by smtp.ey04.engineyard.com with esmtp (Exim 4.69)
|
13
|
+
(envelope-from <donotreply@app.myapp.com>)
|
14
|
+
id 1MCMU4-0003mU-Ch
|
15
|
+
for someuser@yahoo.com; Thu, 04 Jun 2009 16:36:32 -0700
|
16
|
+
Date: Thu, 4 Jun 2009 16:36:32 -0700
|
17
|
+
From: MyApp <donotreply@app.myapp.com>
|
18
|
+
To: someuser@yahoo.com
|
19
|
+
Message-Id: <4a285a805de1f_37df159bfda883fc120662c@ey04-s00084.tmail>
|
20
|
+
Subject: A new discussion titled 'Tesla' was started in Some Forum '
|
21
|
+
Mime-Version: 1.0
|
22
|
+
Content-Type: text/plain; charset=utf-8
|
23
|
+
X-Custom-Ip-Tag: myapp
|
24
|
+
|
25
|
+
SomeUser started a new discussion in Some Forum .
|
26
|
+
|
27
|
+
To view the discussion click on the link below:
|
28
|
+
http://myapp.com/groups/1234/group_posts/5678
|
@@ -0,0 +1,18 @@
|
|
1
|
+
This message was created automatically by mail delivery software.
|
2
|
+
A message that you sent has not yet been delivered to one or more of its
|
3
|
+
recipients after more than 24 hours on the queue on smtp.ey04.engineyard.com.
|
4
|
+
|
5
|
+
The message identifier is: 1MBVws-0000Fn-4J
|
6
|
+
The date of the message is: Tue, 2 Jun 2009 08:30:46 -0700
|
7
|
+
The subject of the message is: A new discussion titled 'STUFF IS AWESOME' was started in Some Forum '
|
8
|
+
|
9
|
+
The address to which the message has not yet been delivered is:
|
10
|
+
|
11
|
+
someuser@yahoo.com
|
12
|
+
Delay reason: SMTP error from remote mail server after initial connection:
|
13
|
+
host g.mx.mail.yahoo.com [206.190.53.191]: 421 Message from (74.217.48.48) temporarily deferred - 4.16.50. Please refer to http://help.yahoo.com/help/us/mail/defer/defer-06.html
|
14
|
+
|
15
|
+
No action is required on your part. Delivery attempts will continue for
|
16
|
+
some time, and this warning may be repeated at intervals if the message
|
17
|
+
remains undelivered. Eventually the mail delivery software will give up,
|
18
|
+
and when that happens, the message will be returned to you.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class PopFetcherTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@sample_config = {
|
6
|
+
:host => "smtp.example.com",
|
7
|
+
:username => "example",
|
8
|
+
:password => "swordfish"
|
9
|
+
}
|
10
|
+
BounceFu.stubs(:configuration).returns(@sample_config)
|
11
|
+
end
|
12
|
+
|
13
|
+
should "fetch the POP3 host from the config object" do
|
14
|
+
Net::POP3.any_instance.stubs(:start)
|
15
|
+
BounceFu::PopFetcher.new.connection
|
16
|
+
end
|
17
|
+
|
18
|
+
should "fetch the POP3 credentials from the config object" do
|
19
|
+
Net::POP3.any_instance.expects(:start).with("example", "swordfish")
|
20
|
+
BounceFu::PopFetcher.new.connection
|
21
|
+
end
|
22
|
+
|
23
|
+
should "iterate over each email from the pop collection" do
|
24
|
+
fetcher = BounceFu::PopFetcher.new
|
25
|
+
Net::POP3.any_instance.stubs(:start)
|
26
|
+
connection_mock = mock
|
27
|
+
connection_mock.expects(:pop)
|
28
|
+
connection_mock.expects(:delete)
|
29
|
+
Net::POP3.any_instance.expects(:each_mail).yields(connection_mock)
|
30
|
+
Net::POP3.any_instance.expects(:finish)
|
31
|
+
|
32
|
+
fetcher.each do |e|
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class TestFetcherTest < Test::Unit::TestCase
|
4
|
+
context "The test fetcher" do
|
5
|
+
should "return the mails it's given" do
|
6
|
+
BounceFu::TestFetcher.messages = ["a message", "another message"]
|
7
|
+
fetcher = BounceFu::TestFetcher.new
|
8
|
+
fetcher.each_with_index do |message_body, i|
|
9
|
+
expected = i == 0 ? "a message" : "another message"
|
10
|
+
assert_equal expected, message_body
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'bounce_fu'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
protected
|
12
|
+
def fixture_path(filename)
|
13
|
+
File.join(File.dirname(__FILE__), "fixtures", filename)
|
14
|
+
end
|
15
|
+
|
16
|
+
def fixture(filename)
|
17
|
+
File.read(fixture_path(filename + ".email"))
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bounce_fu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Golick
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-10 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: james@giraffesoft.ca
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- VERSION
|
32
|
+
- bounce_fu.gemspec
|
33
|
+
- lib/bounce_fu.rb
|
34
|
+
- lib/bounce_fu/abstract_handler.rb
|
35
|
+
- lib/bounce_fu/bounce.rb
|
36
|
+
- lib/bounce_fu/pop_fetcher.rb
|
37
|
+
- lib/bounce_fu/test_fetcher.rb
|
38
|
+
- test/abstract_handler_test.rb
|
39
|
+
- test/bounce_fu_test.rb
|
40
|
+
- test/bounce_test.rb
|
41
|
+
- test/fixtures/hotmail-unavailable.email
|
42
|
+
- test/fixtures/permanent.email
|
43
|
+
- test/fixtures/temporary.email
|
44
|
+
- test/pop_fetcher_test.rb
|
45
|
+
- test/test_fetcher_test.rb
|
46
|
+
- test/test_helper.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/giraffesoft/bounce_fu
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --charset=UTF-8
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.3.1
|
70
|
+
signing_key:
|
71
|
+
specification_version: 2
|
72
|
+
summary: TODO
|
73
|
+
test_files:
|
74
|
+
- test/abstract_handler_test.rb
|
75
|
+
- test/bounce_fu_test.rb
|
76
|
+
- test/bounce_test.rb
|
77
|
+
- test/pop_fetcher_test.rb
|
78
|
+
- test/test_fetcher_test.rb
|
79
|
+
- test/test_helper.rb
|