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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
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.
@@ -0,0 +1,7 @@
1
+ = bounce_fu
2
+
3
+ Description goes here.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 James Golick. See LICENSE for details.
@@ -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
@@ -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
@@ -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,14 @@
1
+ module BounceFu
2
+ class TestFetcher
3
+ include Enumerable
4
+
5
+ class << self
6
+ attr_accessor :messages
7
+ end
8
+
9
+ def each(&block)
10
+ self.class.messages.each(&block)
11
+ end
12
+ end
13
+ end
14
+
@@ -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
@@ -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
@@ -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