winton-fetcher 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Dan Weinand and Slantwise Design, LLC
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
@@ -0,0 +1,94 @@
1
+ = Fetcher
2
+
3
+ Fetcher is a simple message fetcher perfect for using in a daemon or via cron.
4
+
5
+ It implements the following common pattern:
6
+
7
+ 1. Connect to a server
8
+ 2. Download available messages
9
+ 3. Send each message to another object for further processing
10
+ 4. Remove downloaded messages from the remote server
11
+
12
+ Install using:
13
+
14
+ sudo gem install winton-fetcher
15
+
16
+ == Notice
17
+
18
+ This is a fork of {fetcher}[http://github.com/look/fetcher] by {Luke Francl}[http://github.com/look].
19
+
20
+ Differences:
21
+
22
+ * Gemified
23
+ * Doesn't have generators
24
+ * Fixes a connection exception issue
25
+
26
+ == Usage
27
+
28
+ Create a new fetcher object like the following:
29
+
30
+ @fetcher = Fetcher.create({:type => :pop,
31
+ :receiver => IncomingMailHandler,
32
+ :server => 'mail.example.com',
33
+ :username => 'jim',
34
+ :password => 'test'})
35
+
36
+ The receiver object is expected to have a receive method that takes a message as its only argument
37
+ (e.g., the way <tt>ActionMailer::Base.recieve</tt> works; but you don't <em>have</em> to use ActionMailer.).
38
+
39
+ Call <tt>fetch</tt> to download messages and process them.
40
+
41
+ @fetcher.fetch
42
+
43
+ == Configuration
44
+
45
+ The following options can be passed to the <tt>Fetcher.create</tt> factory method:
46
+
47
+ [<tt>type</tt>] POP or IMAP
48
+ [<tt>server</tt>] The IP address or domain name of the server
49
+ [<tt>port</tt>] The port to connect to (defaults to the standard port for the type of server)
50
+ [<tt>ssl</tt>] Set to any value to use SSL encryption
51
+ [<tt>username</tt>] The username used to connect to the server
52
+ [<tt>password</tt>] The password used to connect to the server
53
+ [<tt>authentication</tt>] The authentication scheme to use (IMAP only). Supports LOGIN, CRAM-MD5, and PASSWORD (defaults to PLAIN)
54
+ [<tt>use_login</tt>] Set to any value to use the LOGIN command instead of AUTHENTICATE. Some servers, like GMail, do not support AUTHENTICATE (IMAP only).
55
+ [<tt>sleep_time</tt>] The number of seconds to sleep between fetches (defaults to 60 seconds; valid only for the generated daemon)
56
+ [<tt>processed_folder</tt>] The name of a folder to move mail to after it has been processed (IMAP only). <b>NOTE:</b> If not specified, mail is deleted.
57
+ [<tt>error_folder</tt>] The name a folder to move mail that causes an error during processing (IMAP only). Defaults to +bogus+.
58
+
59
+ == Running via cron
60
+
61
+ You can also run the Fetcher periodically via cron. It is important to ensure that only one
62
+ instance is running at one time, and for that the {Lockfile gem}[http://codeforpeople.com/lib/ruby/lockfile/] is recommended.
63
+
64
+ Here is an example script to be with <tt>script/runner</tt> via cron:
65
+
66
+ begin
67
+ Lockfile.new('cron_mail_fetcher.lock', :retries => 0) do
68
+ config = YAML.load_file("#{RAILS_ROOT}/config/mail.yml")
69
+ config = config[RAILS_ENV].to_options
70
+
71
+ fetcher = Fetcher.create({:receiver => MailReceiver}.merge(config))
72
+ fetcher.fetch
73
+ end
74
+ rescue Lockfile::MaxTriesLockError => e
75
+ puts "Another fetcher is already running. Exiting."
76
+ end
77
+
78
+ == Extending
79
+
80
+ You can subclass <tt>Fetcher::Base</tt> or one of the protocol-specific classed to override the standard behavior.
81
+
82
+ == Further documentation
83
+
84
+ <shameless-plug>
85
+
86
+ You can read more about how to use the Fetcher in the PeepCode book {Receiving Email with Ruby}[https://peepcode.com/products/mms2r-pdf].
87
+
88
+ </shameless-plug>
89
+
90
+ == Credit & Copyright
91
+
92
+ Created by Dan Weinand and Luke Francl. Development supported by {Slantwise Design}[http://slantwisedesign.com].
93
+
94
+ Licensed under the terms of the MIT License. Be excellent to each other.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'gemspec'
5
+
6
+ desc 'Default: run unit tests.'
7
+ task :default => :test
8
+
9
+ desc "Generate gemspec"
10
+ task :gemspec do
11
+ File.open("#{Dir.pwd}/#{GEM_NAME}.gemspec", 'w') do |f|
12
+ f.write(GEM_SPEC.to_ruby)
13
+ end
14
+ end
15
+
16
+ desc 'Test the fetcher plugin.'
17
+ Rake::TestTask.new(:test) do |t|
18
+ t.libs << 'lib'
19
+ t.pattern = 'test/**/*_test.rb'
20
+ t.verbose = true
21
+ end
22
+
23
+ desc 'Generate documentation for the fetcher plugin.'
24
+ Rake::RDocTask.new(:rdoc) do |rdoc|
25
+ rdoc.rdoc_dir = 'rdoc'
26
+ rdoc.title = 'Fetcher'
27
+ rdoc.options << '--line-numbers' << '--inline-source'
28
+ rdoc.rdoc_files.include('README')
29
+ rdoc.rdoc_files.include('lib/**/*.rb')
30
+ end
data/fetcher.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{fetcher}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Luke Francl"]
9
+ s.date = %q{2009-06-27}
10
+ s.email = %q{look@recursion.org}
11
+ s.extra_rdoc_files = ["README.rdoc"]
12
+ s.files = ["fetcher.gemspec", "gemspec.rb", "init.rb", "lib", "lib/fetcher", "lib/fetcher/base.rb", "lib/fetcher/imap.rb", "lib/fetcher/pop.rb", "lib/fetcher.rb", "lib/vendor", "lib/vendor/plain_imap.rb", "lib/vendor/secure_pop.rb", "MIT-LICENSE", "Rakefile", "README.rdoc", "test", "test/fetcher_test.rb"]
13
+ s.homepage = %q{http://github.com/winton/fetcher}
14
+ s.require_paths = ["lib"]
15
+ s.rubygems_version = %q{1.3.1}
16
+ s.summary = %q{download email from POP3 or IMAP and do stuff with it. gemified fork}
17
+
18
+ if s.respond_to? :specification_version then
19
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
20
+ s.specification_version = 2
21
+
22
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
23
+ else
24
+ end
25
+ else
26
+ end
27
+ end
data/gemspec.rb ADDED
@@ -0,0 +1,17 @@
1
+ GEM_NAME = 'fetcher'
2
+ GEM_FILES = FileList['**/*'] - FileList['coverage', 'coverage/**/*', 'pkg', 'pkg/**/*']
3
+ GEM_SPEC = Gem::Specification.new do |s|
4
+ # == CONFIGURE ==
5
+ s.author = "Luke Francl"
6
+ s.email = "look@recursion.org"
7
+ s.homepage = "http://github.com/winton/#{GEM_NAME}"
8
+ s.summary = "download email from POP3 or IMAP and do stuff with it. gemified fork"
9
+ # == CONFIGURE ==
10
+ s.extra_rdoc_files = [ "README.rdoc" ]
11
+ s.files = GEM_FILES.to_a
12
+ s.has_rdoc = false
13
+ s.name = GEM_NAME
14
+ s.platform = Gem::Platform::RUBY
15
+ s.require_path = "lib"
16
+ s.version = "0.1.1"
17
+ end
data/init.rb ADDED
File without changes
data/lib/fetcher.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Fetcher
2
+ # Use factory-style initialization or insantiate directly from a subclass
3
+ #
4
+ # Options:
5
+ # * <tt>:type</tt> - Name of class as a symbol to instantiate
6
+ #
7
+ # Other options are the same as Fetcher::Base.new
8
+ #
9
+ # Example:
10
+ #
11
+ # Fetcher.create(:type => :pop) is equivalent to
12
+ # Fetcher::Pop.new()
13
+ def self.create(options={})
14
+ klass = options.delete(:type)
15
+ raise ArgumentError, 'Must supply a type' unless klass
16
+ module_eval "#{klass.to_s.capitalize}.new(options)"
17
+ end
18
+ end
19
+
20
+ require 'fetcher/base'
21
+ require 'fetcher/pop'
22
+ require 'fetcher/imap'
@@ -0,0 +1,63 @@
1
+ module Fetcher
2
+ class Base
3
+ # Options:
4
+ # * <tt>:server</tt> - Server to connect to.
5
+ # * <tt>:username</tt> - Username to use when connecting to server.
6
+ # * <tt>:password</tt> - Password to use when connecting to server.
7
+ # * <tt>:receiver</tt> - Receiver object to pass messages to. Assumes the
8
+ # receiver object has a receive method that takes a message as it's argument
9
+ #
10
+ # Additional protocol-specific options implimented by sub-classes
11
+ #
12
+ # Example:
13
+ # Fetcher::Base.new(:server => 'mail.example.com',
14
+ # :username => 'pam',
15
+ # :password => 'test',
16
+ # :receiver => IncomingMailHandler)
17
+ def initialize(options={})
18
+ %w(server username password receiver).each do |opt|
19
+ raise ArgumentError, "#{opt} is required" unless options[opt.to_sym]
20
+ # convert receiver to a Class if it isn't already.
21
+ if opt == "receiver" && options[:receiver].is_a?(String)
22
+ options[:receiver] = Kernel.const_get(options[:receiver])
23
+ end
24
+
25
+ instance_eval("@#{opt} = options[:#{opt}]")
26
+ end
27
+ end
28
+
29
+ # Run the fetching process
30
+ def fetch
31
+ establish_connection
32
+ get_messages
33
+ close_connection
34
+ end
35
+
36
+ protected
37
+
38
+ # Stub. Should be overridden by subclass.
39
+ def establish_connection #:nodoc:
40
+ raise NotImplementedError, "This method should be overridden by subclass"
41
+ end
42
+
43
+ # Stub. Should be overridden by subclass.
44
+ def get_messages #:nodoc:
45
+ raise NotImplementedError, "This method should be overridden by subclass"
46
+ end
47
+
48
+ # Stub. Should be overridden by subclass.
49
+ def close_connection #:nodoc:
50
+ raise NotImplementedError, "This method should be overridden by subclass"
51
+ end
52
+
53
+ # Send message to receiver object
54
+ def process_message(message)
55
+ @receiver.receive(message)
56
+ end
57
+
58
+ # Stub. Should be overridden by subclass.
59
+ def handle_bogus_message(message) #:nodoc:
60
+ raise NotImplementedError, "This method should be overridden by subclass"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + '/../vendor/plain_imap'
2
+
3
+ module Fetcher
4
+ class Imap < Base
5
+
6
+ PORT = 143
7
+
8
+ protected
9
+
10
+ # Additional Options:
11
+ # * <tt>:authentication</tt> - authentication type to use, defaults to PLAIN
12
+ # * <tt>:port</tt> - port to use (defaults to 143)
13
+ # * <tt>:ssl</tt> - use SSL to connect
14
+ # * <tt>:use_login</tt> - use LOGIN instead of AUTHENTICATE to connect (some IMAP servers, like GMail, do not support AUTHENTICATE)
15
+ # * <tt>:processed_folder</tt> - if set to the name of a mailbox, messages will be moved to that mailbox instead of deleted after processing. The mailbox will be created if it does not exist.
16
+ # * <tt>:error_folder:</tt> - the name of a mailbox where messages that cannot be processed (i.e., your receiver throws an exception) will be moved. Defaults to "bogus". The mailbox will be created if it does not exist.
17
+ def initialize(options={})
18
+ @authentication = options.delete(:authentication) || 'PLAIN'
19
+ @port = options.delete(:port) || PORT
20
+ @ssl = options.delete(:ssl)
21
+ @use_login = options.delete(:use_login)
22
+ @processed_folder = options.delete(:processed_folder)
23
+ @error_folder = options.delete(:error_folder) || 'bogus'
24
+ super(options)
25
+ end
26
+
27
+ # Open connection and login to server
28
+ def establish_connection
29
+ @connection = Net::IMAP.new(@server, @port, @ssl)
30
+ if @use_login
31
+ @connection.login(@username, @password)
32
+ else
33
+ @connection.authenticate(@authentication, @username, @password)
34
+ end
35
+ end
36
+
37
+ # Retrieve messages from server
38
+ def get_messages
39
+ @connection.select('INBOX')
40
+ @connection.uid_search(['ALL']).each do |uid|
41
+ msg = @connection.uid_fetch(uid,'RFC822').first.attr['RFC822']
42
+ begin
43
+ process_message(msg)
44
+ add_to_processed_folder(uid) if @processed_folder
45
+ rescue
46
+ handle_bogus_message(msg)
47
+ end
48
+ # Mark message as deleted
49
+ @connection.uid_store(uid, "+FLAGS", [:Seen, :Deleted])
50
+ end
51
+ end
52
+
53
+ # Store the message for inspection if the receiver errors
54
+ def handle_bogus_message(message)
55
+ create_mailbox(@error_folder)
56
+ @connection.append(@error_folder, message)
57
+ end
58
+
59
+ # Delete messages and log out
60
+ def close_connection
61
+ @connection.expunge
62
+ @connection.logout
63
+ @connection.disconnect
64
+ rescue
65
+ end
66
+
67
+ def add_to_processed_folder(uid)
68
+ create_mailbox(@processed_folder)
69
+ @connection.uid_copy(uid, @processed_folder)
70
+ end
71
+
72
+ def create_mailbox(mailbox)
73
+ unless @connection.list("", mailbox)
74
+ @connection.create(mailbox)
75
+ end
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../vendor/secure_pop'
2
+
3
+ module Fetcher
4
+ class Pop < Base
5
+
6
+ protected
7
+
8
+ # Additional Options:
9
+ # * <tt>:ssl</tt> - whether or not to use ssl encryption
10
+ # * <tt>:port</tt> - port to use (defaults to 110)
11
+ def initialize(options={})
12
+ @ssl = options.delete(:ssl)
13
+ @port = options.delete(:port) || Net::POP3.default_port
14
+ super(options)
15
+ end
16
+
17
+ # Open connection and login to server
18
+ def establish_connection
19
+ @connection = Net::POP3.new(@server, @port)
20
+ @connection.enable_ssl(OpenSSL::SSL::VERIFY_NONE) if @ssl
21
+ @connection.start(@username, @password)
22
+ end
23
+
24
+ # Retrieve messages from server
25
+ def get_messages
26
+ unless @connection.mails.empty?
27
+ @connection.each_mail do |msg|
28
+ begin
29
+ process_message(msg.pop)
30
+ rescue
31
+ handle_bogus_message(msg.pop)
32
+ end
33
+ # Delete message from server
34
+ msg.delete
35
+ end
36
+ end
37
+ end
38
+
39
+ # Store the message for inspection if the receiver errors
40
+ def handle_bogus_message(message)
41
+ # This needs a good solution
42
+ end
43
+
44
+ # Close connection to server
45
+ def close_connection
46
+ @connection.finish
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ require 'net/imap'
2
+ # add plain as an authentication type...
3
+ # This is taken from:
4
+ # http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/net/imap.rb?revision=7657&view=markup&pathrev=10966
5
+
6
+ # Authenticator for the "PLAIN" authentication type. See
7
+ # #authenticate().
8
+ class PlainAuthenticator
9
+ def process(data)
10
+ return "\0#{@user}\0#{@password}"
11
+ end
12
+
13
+ private
14
+
15
+ def initialize(user, password)
16
+ @user = user
17
+ @password = password
18
+ end
19
+ end
20
+
21
+ Net::IMAP.add_authenticator "PLAIN", PlainAuthenticator
@@ -0,0 +1,125 @@
1
+ require 'socket'
2
+ require 'net/pop'
3
+ require 'net/protocol'
4
+ require 'openssl/ssl'
5
+
6
+ # Backport of ruby 1.9's POP3 SSL support
7
+ class Net::POP3
8
+ @@usessl = nil
9
+ @@verify = nil
10
+ @@certs = nil
11
+ PORT = 110
12
+ SSL_PORT = 995
13
+
14
+ def self.default_port
15
+ PORT
16
+ end
17
+
18
+ def self.default_ssl_port
19
+ SSL_PORT
20
+ end
21
+
22
+ # Enable SSL for all new instances.
23
+ # +verify+ is the type of verification to do on the Server Cert; Defaults
24
+ # to OpenSSL::SSL::VERIFY_PEER.
25
+ # +certs+ is a file or directory holding CA certs to use to verify the
26
+ # server cert; Defaults to nil.
27
+ def self.enable_ssl( verify = OpenSSL::SSL::VERIFY_PEER, certs = nil )
28
+ @@usessl = true
29
+ @@verify = verify
30
+ @@certs = certs
31
+ end
32
+
33
+ # Disable SSL for all new instances.
34
+ def self.disable_ssl
35
+ @@usessl = nil
36
+ @@verify = nil
37
+ @@certs = nil
38
+ end
39
+
40
+ # Creates a new POP3 object.
41
+ # +addr+ is the hostname or ip address of your POP3 server.
42
+ # The optional +port+ is the port to connect to.
43
+ # The optional +isapop+ specifies whether this connection is going
44
+ # to use APOP authentication; it defaults to +false+.
45
+ # This method does *not* open the TCP connection.
46
+ def initialize(addr, port = nil, isapop = false)
47
+ @address = addr
48
+ @usessl = @@usessl
49
+ if @usessl
50
+ @port = port || SSL_PORT
51
+ else
52
+ @port = port || PORT
53
+ end
54
+ @apop = isapop
55
+
56
+ @certs = @@certs
57
+ @verify = @@verify
58
+
59
+ @command = nil
60
+ @socket = nil
61
+ @started = false
62
+ @open_timeout = 30
63
+ @read_timeout = 60
64
+ @debug_output = nil
65
+
66
+ @mails = nil
67
+ @n_mails = nil
68
+ @n_bytes = nil
69
+ end
70
+
71
+ # does this instance use SSL?
72
+ def usessl?
73
+ @usessl
74
+ end
75
+
76
+ # Enables SSL for this instance. Must be called before the connection is
77
+ # established to have any effect.
78
+ # +verify+ is the type of verification to do on the Server Cert; Defaults
79
+ # to OpenSSL::SSL::VERIFY_PEER.
80
+ # +certs+ is a file or directory holding CA certs to use to verify the
81
+ # server cert; Defaults to nil.
82
+ # +port+ is port to establish the SSL conection on; Defaults to 995.
83
+ def enable_ssl(verify = OpenSSL::SSL::VERIFY_PEER, certs = nil,
84
+ port = SSL_PORT)
85
+ @usessl = true
86
+ @verify = verify
87
+ @certs = certs
88
+ @port = port
89
+ end
90
+
91
+ def disable_ssl
92
+ @usessl = nil
93
+ @verify = nil
94
+ @certs = nil
95
+ end
96
+
97
+ def do_start( account, password )
98
+ s = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
99
+ if @usessl
100
+ unless defined?(OpenSSL)
101
+ raise "SSL extension not installed"
102
+ end
103
+ sslctx = OpenSSL::SSL::SSLContext.new
104
+ sslctx.verify_mode = @verify
105
+ sslctx.ca_file = @certs if @certs && FileTest::file?(@certs)
106
+ sslctx.ca_path = @certs if @certs && FileTest::directory?(@certs)
107
+ s = OpenSSL::SSL::SSLSocket.new(s, sslctx)
108
+ s.sync_close = true
109
+ s.connect
110
+ end
111
+
112
+ @socket = Net::InternetMessageIO.new(s)
113
+ on_connect
114
+ @command = Net::POP3Command.new(@socket)
115
+ if apop?
116
+ @command.apop account, password
117
+ else
118
+ @command.auth account, password
119
+ end
120
+ @started = true
121
+ ensure
122
+ do_finish if not @started
123
+ end
124
+ private :do_start
125
+ end
@@ -0,0 +1,74 @@
1
+ # require File.dirname(__FILE__) + '/../../../../config/boot'
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ require 'mocha'
5
+ require 'fetcher'
6
+
7
+ class FetcherTest < Test::Unit::TestCase
8
+
9
+ def setup
10
+ @receiver = mock()
11
+ end
12
+
13
+ def test_should_set_configuration_instance_variables
14
+ create_fetcher
15
+ assert_equal 'test.host', @fetcher.instance_variable_get(:@server)
16
+ assert_equal 'name', @fetcher.instance_variable_get(:@username)
17
+ assert_equal 'password', @fetcher.instance_variable_get(:@password)
18
+ assert_equal @receiver, @fetcher.instance_variable_get(:@receiver)
19
+ end
20
+
21
+ def test_should_require_subclass
22
+ create_fetcher
23
+ assert_raise(NotImplementedError) { @fetcher.fetch }
24
+ end
25
+
26
+ def test_should_require_server
27
+ assert_raise(ArgumentError) { create_fetcher(:server => nil) }
28
+ end
29
+
30
+ def test_should_require_username
31
+ assert_raise(ArgumentError) { create_fetcher(:username => nil) }
32
+ end
33
+
34
+ def test_should_require_password
35
+ assert_raise(ArgumentError) { create_fetcher(:password => nil) }
36
+ end
37
+
38
+ def test_should_require_receiver
39
+ assert_raise(ArgumentError) { create_fetcher(:receiver => nil) }
40
+ end
41
+
42
+ def create_fetcher(options={})
43
+ @fetcher = Fetcher::Base.new({:server => 'test.host', :username => 'name', :password => 'password', :receiver => @receiver}.merge(options))
44
+ end
45
+
46
+ end
47
+
48
+ class FactoryFetcherTest < Test::Unit::TestCase
49
+
50
+ def setup
51
+ @receiver = mock()
52
+ @pop_fetcher = Fetcher.create(:type => :pop, :server => 'test.host',
53
+ :username => 'name',
54
+ :password => 'password',
55
+ :receiver => @receiver)
56
+
57
+ @imap_fetcher = Fetcher.create(:type => :imap, :server => 'test.host',
58
+ :username => 'name',
59
+ :password => 'password',
60
+ :receiver => @receiver)
61
+ end
62
+
63
+ def test_should_be_sublcass
64
+ assert_equal Fetcher::Pop, @pop_fetcher.class
65
+ assert_equal Fetcher::Imap, @imap_fetcher.class
66
+ end
67
+
68
+ def test_should_require_type
69
+ assert_raise(ArgumentError) { Fetcher.create({}) }
70
+ end
71
+
72
+ end
73
+
74
+ # Write tests for sub-classes
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: winton-fetcher
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Luke Francl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-27 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: look@recursion.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - fetcher.gemspec
26
+ - gemspec.rb
27
+ - init.rb
28
+ - lib
29
+ - lib/fetcher
30
+ - lib/fetcher/base.rb
31
+ - lib/fetcher/imap.rb
32
+ - lib/fetcher/pop.rb
33
+ - lib/fetcher.rb
34
+ - lib/vendor
35
+ - lib/vendor/plain_imap.rb
36
+ - lib/vendor/secure_pop.rb
37
+ - MIT-LICENSE
38
+ - Rakefile
39
+ - README.rdoc
40
+ - test
41
+ - test/fetcher_test.rb
42
+ has_rdoc: false
43
+ homepage: http://github.com/winton/fetcher
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.2.0
65
+ signing_key:
66
+ specification_version: 2
67
+ summary: download email from POP3 or IMAP and do stuff with it. gemified fork
68
+ test_files: []
69
+