Skip to content
Browse files

Change the API of connection setup.

This paves the way for making the connection itself a useful deferrable.
  • Loading branch information...
1 parent f8182e1 commit 434fb6fb9a1e9775be4e250a4095651bf7dba917 @ConradIrwin ConradIrwin committed Jul 2, 2011
Showing with 49 additions and 29 deletions.
  1. +13 −12 README.md
  2. +4 −0 lib/em-imap.rb
  3. +10 −2 lib/em-imap/client.rb
  4. +13 −9 lib/em-imap/connection.rb
  5. +9 −6 spec/client_spec.rb
View
25 README.md
@@ -10,16 +10,16 @@ This document tries to introduce concepts of IMAP alongside the facilities of th
### Connecting
-Before you can communicate with an IMAP server, you must first connect to it. There are three connection parameters, the hostname, the port number, and whether to use SSL/TLS. As with every method in EM::IMAP, `EM::IMAP.connect` returns a [deferrable](http://eventmachine.rubyforge.org/docs/DEFERRABLES.html) enhanced by the [deferrable\_gratification](https://github.com/samstokes/deferrable_gratification) library.
+Before you can communicate with an IMAP server, you must first connect to it. There are three connection parameters, the hostname, the port number, and whether to use SSL/TLS. As with every method in EM::IMAP, `EM::IMAP::Client#connect` returns a [deferrable](http://eventmachine.rubyforge.org/docs/DEFERRABLES.html) enhanced by the [deferrable\_gratification](https://github.com/samstokes/deferrable_gratification) library.
For example, to connect to Gmail's IMAP server, you can use the following snippet:
require 'rubygems'
require 'em-imap'
EM::run do
- client = EM::IMAP.connect('imap.gmail.com', 993, true)
- client.errback do |error|
+ client = EM::IMAP.new('imap.gmail.com', 993, true)
+ client.connect.errback do |error|
puts "Connecting failed: #{error}"
end.callback do |hello_response|
puts "Connecting succeeded!"
@@ -34,8 +34,8 @@ There are two authentication mechanisms in IMAP, `LOGIN` and `AUTHENTICATE`, exp
Extending our previous example to also log in to Gmail:
- client = EM::IMAP.connect('imap.gmail.com', 993, true)
- client.bind! do
+ client = EM::IMAP.new('imap.gmail.com', 993, true)
+ client.connect.bind! do
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
end.callback do
puts "Connected and logged in!"
@@ -49,8 +49,8 @@ The `.authenticate` method is more advanced and uses the same extensible mechani
Once the authentication has completed successfully, you can perform IMAP commands that don't require a currently selected mailbox. For example to get a list of the names of all Gmail mailboxes (including labels):
- client = EM::IMAP.connect('imap.gmail.com', 993, true)
- client.bind! do
+ client = EM::IMAP.new('imap.gmail.com', 993, true)
+ client.connect.bind! do
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
end.bind! do
client.list
@@ -68,8 +68,8 @@ In order to do useful things which actual messages, you need to first select a m
For example to search for all emails relevant to em-imap in Gmail:
- client = EM::IMAP.connect('imap.gmail.com', 993, true)
- client.bind! do
+ client = EM::IMAP.new('imap.gmail.com', 993, true)
+ client.connect.bind! do
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
end.bind! do
client.select('[Google Mail]/All Mail')
@@ -83,8 +83,8 @@ For example to search for all emails relevant to em-imap in Gmail:
Once you have a list of message sequence numbers, as returned by search, you can actually read the emails with `.fetch`:
- client = EM::IMAP.connect('imap.gmail.com', 993, true)
- client.bind! do
+ client = EM::IMAP.new('imap.gmail.com', 993, true)
+ client.connect.bind! do
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
end.bind! do
client.select('[Google Mail]/All Mail')
@@ -148,7 +148,8 @@ If you want to receive server responses at any time, you can call `.add_response
If you want to send commands without waiting for previous replies, you can also do so. em-imap handles the few cases where this is not permitted (for example, during an IDLE command) by queueing the command until the connection becomes available again. If you do this, bear in mind that any blocks that are listening on the connection may receive responses from multiple commands interleaved.
- client = EM::Imap.connect('imap.gmail.com', 993, true).callback do
+ client = EM::Imap.new('imap.gmail.com', 993, true)
+ client.connect.callback do
logger_in = client.login('conrad.irwin@gmail.com', ENV["GMAIL_PASSWORD"])
selecter = client.select('[Google Mail]/All Mail')
searcher = client.search('from:conrad@rapportive.com').callback do |results|
View
4 lib/em-imap.rb
@@ -28,6 +28,10 @@ def self.connect(host, port, ssl=false)
Client.new(EventMachine::IMAP::Connection.connect(host, port, ssl))
end
+ def self.new(host, port, ssl=false)
+ Client.new(host, port, ssl)
+ end
+
class Command < Listener
attr_accessor :tag, :cmd, :args
def initialize(tag, cmd, args=[], &block)
View
12 lib/em-imap/client.rb
@@ -7,8 +7,16 @@ class Client
include IMAP::Authenticators
- def initialize(connection)
- @connection = connection.errback{ |e| fail e }.callback{ |response| succeed response }
+ def initialize(host, port, usessl=false)
+ @connect_args=[host, port, usessl]
+ end
+
+ def connect
+ @connection = EM::IMAP::Connection.connect(*@connect_args)
+ @connection.errback{ |e| fail e }.
+ callback{ |*args| succeed *args }
+
+ @connection.hello_listener
end
def disconnect
View
22 lib/em-imap/connection.rb
@@ -80,22 +80,26 @@ def post_init
# Listen for the first response from the server and succeed or fail
# the connection deferrable.
def listen_for_greeting
- hello_listener = add_response_handler do |response|
- hello_listener.stop
+ add_to_listener_pool(hello_listener)
+ listen_for_bye_response(hello_listener)
+ hello_listener.listen do |response|
+ # TODO: Is this the right condition? I think it can be one of several
+ # possible answers depending on how trusted the connection is, but probably
+ # not *anything* except BYE.
if response.is_a?(Net::IMAP::UntaggedResponse)
- if response.name == "BYE"
- fail Net::IMAP::ByeResponseError.new(response.raw_data)
- else
- succeed response
- end
+ hello_listener.succeed response
else
- fail Net::IMAP::ResponseParseError.new(response.raw_data)
+ hello_listener.fail Net::IMAP::ResponseParseError.new(response.raw_data)
end
end.errback do |e|
- fail e
+ hello_listener.fail e
end
end
+ def hello_listener
+ @hello_listener ||= Listener.new.errback{ |e| fail_all e }.bothback{ hello_listener.stop }
+ end
+
# Called when the connection is closed.
# TODO: Figure out how to send a useful error...
def unbind
View
15 spec/client_spec.rb
@@ -6,12 +6,13 @@
@connection = Class.new(EMStub) do
include EM::IMAP::Connection
end.new
+ EM::IMAP::Connection.stub!(:connect).and_return(@connection)
end
describe "connection" do
it "should succeed if the connection receives a successful greeting" do
a = false
- EM::IMAP::Client.new(@connection).callback do |response|
+ EM::IMAP::Client.new("mail.example.com", 993).connect.callback do |response|
a = true
end
@connection.receive_data "* OK Welcome, test IMAP!\r\n"
@@ -20,7 +21,7 @@
it "should fail if the connection receives a BYE" do
a = false
- EM::IMAP::Client.new(@connection).errback do |e|
+ EM::IMAP::Client.new("mail.example.com", 993).connect.errback do |e|
a = true
end
@connection.receive_data "* BYE Test IMAP\r\n"
@@ -29,7 +30,7 @@
it "should fail if the connection receives gibberish" do
a = false
- EM::IMAP::Client.new(@connection).errback do |e|
+ EM::IMAP::Client.new("mail.example.com", 993).connect.errback do |e|
a = true
end
@connection.receive_data "HTTP 1.1 GET /\r\n"
@@ -38,7 +39,7 @@
it "should fail if the connection does not complete" do
a = false
- EM::IMAP::Client.new(@connection).errback do |e|
+ EM::IMAP::Client.new("mail.example.com", 993).connect.errback do |e|
a = true
end
@connection.unbind
@@ -48,7 +49,8 @@
describe "commands" do
before :each do
- @client = EM::IMAP::Client.new(@connection)
+ @client = EM::IMAP::Client.new("mail.example.com", 993)
+ @client.connect
@connection.receive_data "* OK Ready to test!\r\n"
end
@@ -163,7 +165,8 @@
describe "multi-command concurrency" do
before :each do
- @client = EM::IMAP::Client.new(@connection)
+ @client = EM::IMAP::Client.new("mail.example.com", 993)
+ @client.connect
@connection.receive_data "* OK Ready to test!\r\n"
end

0 comments on commit 434fb6f

Please sign in to comment.
Something went wrong with that request. Please try again.