winton-fetcher 0.1.1

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/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
+