luggage 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/ChangeLog.rdoc +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +221 -0
- data/Rakefile +35 -0
- data/lib/luggage/factory.rb +63 -0
- data/lib/luggage/mailbox.rb +101 -0
- data/lib/luggage/mailbox_array.rb +58 -0
- data/lib/luggage/mailbox_query_builder.rb +77 -0
- data/lib/luggage/message.rb +151 -0
- data/lib/luggage/version.rb +3 -0
- data/lib/luggage.rb +16 -0
- data/luggage.gemspec +30 -0
- data/spec/luggage/factory_spec.rb +83 -0
- data/spec/luggage/mailbox_query_builder_spec.rb +48 -0
- data/spec/luggage/mailbox_spec.rb +135 -0
- data/spec/luggage/message_spec.rb +181 -0
- data/spec/luggage_spec.rb +16 -0
- data/spec/net_imap.rb +15 -0
- data/spec/spec_helper.rb +36 -0
- metadata +201 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
module Luggage
|
2
|
+
class Message
|
3
|
+
class MessageNotFoundError < ArgumentError; end
|
4
|
+
class DuplicateMessageError < StandardError; end
|
5
|
+
|
6
|
+
attr_accessor :flags
|
7
|
+
attr_reader :connection, :mailbox, :template, :date, :message_id
|
8
|
+
|
9
|
+
# Creates a local Message instance
|
10
|
+
#
|
11
|
+
# `connection` should be an authenticated Imap connection
|
12
|
+
# `mailbox` should be either a Mailbox or a string describing a remote mailbox
|
13
|
+
# `args` will be passed to Message::new. Keys used by Message::new will be set, any
|
14
|
+
# other keys will be delegated to Mail::Message.
|
15
|
+
#
|
16
|
+
# Example Usage:
|
17
|
+
# Message.new_local(c, 'INBOX', :template => 'base.eml', :date => 4.days.ago)
|
18
|
+
# Message.new_local(c, m, :subject => "VIAGRA ROCKS!", :body => "<insert phishing here>", :cc => "yourmom@gmail.com")
|
19
|
+
#
|
20
|
+
def self.new_local(connection, mailbox, args = {}, &block)
|
21
|
+
message = new(connection, mailbox, args)
|
22
|
+
message.instance_eval do
|
23
|
+
@mail = @template ? Mail.read(@template) : Mail.new
|
24
|
+
args.each do |key, value|
|
25
|
+
mail[key] = value if mail.respond_to?(key)
|
26
|
+
end
|
27
|
+
mail[:message_id] = message_id
|
28
|
+
end
|
29
|
+
|
30
|
+
message.instance_eval &block if block_given?
|
31
|
+
message
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates a Message instance
|
35
|
+
#
|
36
|
+
# `connection` should be an authenticated Imap connection
|
37
|
+
# `mailbox` should be either a Mailbox or a string describing a remote mailbox
|
38
|
+
# `args[:date]` when this message is appended to the remote server, this is the date which will be used
|
39
|
+
# `args[:template]` use this file as the initial raw email content.
|
40
|
+
# `args[:message_id]` use this as for the Message-ID header. This header is used to identify messages across requests
|
41
|
+
#
|
42
|
+
def initialize(connection, mailbox, args = {}, &block)
|
43
|
+
raise ArgumentError, "Net::IMAP connection required" unless connection.kind_of?(Net::IMAP)
|
44
|
+
|
45
|
+
@connection = connection
|
46
|
+
@mailbox = mailbox.kind_of?(Mailbox) ? mailbox : Mailbox.new(connection, mailbox)
|
47
|
+
@flags = []
|
48
|
+
@date = args[:date] || Time.now
|
49
|
+
@template = args[:template]
|
50
|
+
@message_id = args[:message_id] || "<#{UUIDTools::UUID.random_create}@test.oib.com>"
|
51
|
+
|
52
|
+
raise ArgumentError, "mailbox requried" unless @mailbox.present?
|
53
|
+
|
54
|
+
instance_eval &block if block_given?
|
55
|
+
end
|
56
|
+
|
57
|
+
# Formatted to save to file
|
58
|
+
#
|
59
|
+
# Mail::Message.new( message.to_s ).raw_source = message.to_s
|
60
|
+
#
|
61
|
+
def to_s
|
62
|
+
mail.encoded
|
63
|
+
end
|
64
|
+
|
65
|
+
# Fetch this message from the server and update all its attributes
|
66
|
+
#
|
67
|
+
def reload
|
68
|
+
fields = fetch_fields
|
69
|
+
@mail = Mail.new(fields["BODY[]"])
|
70
|
+
@flags = fields["FLAGS"]
|
71
|
+
@date = Time.parse(fields["INTERNALDATE"])
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Append this message to the remote mailbox
|
76
|
+
#
|
77
|
+
def save!
|
78
|
+
mailbox.select!
|
79
|
+
connection.append(mailbox.name, raw_message, flags.map {|f| f.to_sym.upcase}, date)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add the 'Deleted' flag to this message on the remote server
|
83
|
+
#
|
84
|
+
def delete!
|
85
|
+
mailbox.select!
|
86
|
+
connection.uid_store([uid], "+FLAGS", [:Deleted])
|
87
|
+
@mail = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns true if a message with the same message_id exists in the remote mailbox
|
91
|
+
#
|
92
|
+
def exists?
|
93
|
+
mailbox.select!
|
94
|
+
connection.uid_search("HEADER Message-ID #{message_id}").present?
|
95
|
+
end
|
96
|
+
|
97
|
+
# Proxy all other methods to this instance's Mail::Message
|
98
|
+
#
|
99
|
+
def method_missing(meth, *args, &block)
|
100
|
+
if mail.respond_to?(meth)
|
101
|
+
mail.send(meth, *args, &block)
|
102
|
+
else
|
103
|
+
super
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def inspect
|
108
|
+
"#<Luggage::Message server: \"#{host}\", mailbox: \"#{mailbox.name}\", message_id: \"#{message_id}\">"
|
109
|
+
end
|
110
|
+
|
111
|
+
def host
|
112
|
+
connection.instance_variable_get(:@host)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
# Formatted to upload to IMAP server
|
118
|
+
#
|
119
|
+
def raw_message
|
120
|
+
mail.to_s
|
121
|
+
end
|
122
|
+
|
123
|
+
def mail
|
124
|
+
reload unless @mail.present?
|
125
|
+
@mail
|
126
|
+
end
|
127
|
+
|
128
|
+
def uid
|
129
|
+
unless @uid.present?
|
130
|
+
mailbox.select!
|
131
|
+
@uid = fetch_uid
|
132
|
+
end
|
133
|
+
@uid
|
134
|
+
end
|
135
|
+
|
136
|
+
def fetch_uid
|
137
|
+
response = connection.uid_search("HEADER Message-ID #{message_id}")
|
138
|
+
raise MessageNotFoundError if response.empty?
|
139
|
+
raise DuplicateMessageError if response.length > 1
|
140
|
+
response.first
|
141
|
+
end
|
142
|
+
|
143
|
+
def fetch_fields
|
144
|
+
response = connection.uid_fetch([uid], ["FLAGS", "INTERNALDATE", "BODY.PEEK[]"])
|
145
|
+
raise MessageNotFoundError if response.empty?
|
146
|
+
raise DuplicateMessageError if response.length > 1
|
147
|
+
response.first[:attr]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
data/lib/luggage.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'net/imap'
|
2
|
+
require 'mail'
|
3
|
+
require 'uuidtools'
|
4
|
+
|
5
|
+
require 'luggage/factory'
|
6
|
+
require 'luggage/mailbox'
|
7
|
+
require 'luggage/mailbox_array'
|
8
|
+
require 'luggage/mailbox_query_builder'
|
9
|
+
require 'luggage/message'
|
10
|
+
require 'luggage/version'
|
11
|
+
|
12
|
+
module Luggage
|
13
|
+
def self.new(*args, &block)
|
14
|
+
Factory.new(*args, &block)
|
15
|
+
end
|
16
|
+
end
|
data/luggage.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/luggage/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "luggage"
|
7
|
+
gem.version = Luggage::VERSION
|
8
|
+
gem.summary = %q{Net::IMAP DSL}
|
9
|
+
gem.description = %q{Easily interact with IMAP servers}
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.authors = ["Ryan Michael", "Eric Pinzur"]
|
12
|
+
gem.email = "ryanmichael@otherinbox.com"
|
13
|
+
gem.homepage = "https://github.com/otherinbox/luggage"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'mail', '~> 2.5.0'
|
21
|
+
gem.add_dependency 'uuidtools', '~> 2.1.3'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'bundler', '~> 1.0'
|
24
|
+
gem.add_development_dependency 'rake', '~> 0.8'
|
25
|
+
gem.add_development_dependency 'rdoc', '~> 3.0'
|
26
|
+
gem.add_development_dependency 'rspec', '~> 2.4'
|
27
|
+
gem.add_development_dependency 'activesupport', '~> 3.2.9'
|
28
|
+
gem.add_development_dependency 'fuubar', '~> 1.1.0'
|
29
|
+
gem.add_development_dependency 'pry', '~> 0.9.10'
|
30
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Luggage::Factory do
|
4
|
+
include_context "factories"
|
5
|
+
|
6
|
+
describe "::new" do
|
7
|
+
it "accepts :connection" do
|
8
|
+
expect(Luggage.new(:connection => connection).connection).to eq(connection)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "accepts :server, :authenticate and creates a connection" do
|
12
|
+
Net::IMAP.any_instance.stub(:authenticate).and_return(true)
|
13
|
+
expect(Luggage.new(:server => :foo, :authenticate => "LOGIN username password").connection).to be_a(Net::IMAP)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "accepts :server, :xoauth and creates a connection" do
|
17
|
+
Net::IMAP.any_instance.stub(:send_command).and_return(true)
|
18
|
+
expect(Luggage.new(:server => :foo, :xoauth => "token").connection).to be_a(Net::IMAP)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "requires a way to build a connection" do
|
22
|
+
expect { Luggage.new }.to raise_error(ArgumentError)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#message' do
|
27
|
+
it "returns a Luggage::Message" do
|
28
|
+
expect(factory.message(:mailbox)).to be_a(Luggage::Message)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "passes arguments to initialize" do
|
32
|
+
Luggage::Message.should_receive(:new).with(connection, "mailbox_name", :key => :value).and_return( double('Message').as_null_object )
|
33
|
+
factory.message("mailbox_name", :key => :value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#mailboxes" do
|
38
|
+
before(:each) do
|
39
|
+
connection.stub(:list).
|
40
|
+
and_return([
|
41
|
+
OpenStruct.new(:name => "Mailbox_1"),
|
42
|
+
OpenStruct.new(:name => "Mailbox_2"),
|
43
|
+
OpenStruct.new(:name => "Mailbox_3"),
|
44
|
+
OpenStruct.new(:name => "Mailbox_4") ])
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with no arguments" do
|
48
|
+
it "returns an array of mailboxes defined on the remote server" do
|
49
|
+
expect(factory.mailboxes.map(&:name)).to eq(["Mailbox_1", "Mailbox_2", "Mailbox_3", "Mailbox_4"])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with a string argument (function-call syntax)" do
|
54
|
+
it "returns a mailbox with the passed name" do
|
55
|
+
expect(factory.mailboxes("foo").name).to eq("foo")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "with an iteger (function-call syntax)" do
|
60
|
+
it "returns the nth mailbox on the remote server" do
|
61
|
+
expect(factory.mailboxes(1).name).to eq("Mailbox_2")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "with a string argument (hash-index syntax)" do
|
66
|
+
it "returns a mailbox with the passed name" do
|
67
|
+
expect(factory.mailboxes["foo"].name).to eq("foo")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with an integer (array-index syntax)" do
|
72
|
+
it "returns the nth mailbox on the remote server" do
|
73
|
+
expect(factory.mailboxes[1].name).to eq("Mailbox_2")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "with a range (array-slice syntax)" do
|
78
|
+
it "returns a subset array of mailboxes defined on the remote server" do
|
79
|
+
expect(factory.mailboxes[1..2].map(&:name)).to eq(["Mailbox_2", "Mailbox_3"])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Luggage::MailboxQueryBuilder do
|
4
|
+
include_context "factories"
|
5
|
+
|
6
|
+
describe "::new" do
|
7
|
+
it "sets the mailbox" do
|
8
|
+
expect(Luggage::MailboxQueryBuilder.new(mailbox).mailbox).to eq(mailbox)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "requires a mailbox" do
|
12
|
+
expect { Luggage::MailboxQueryBuilder.new(:not_a_mailbox) }.to raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "sets the connection" do
|
16
|
+
expect(Luggage::MailboxQueryBuilder.new(mailbox).connection).to eq(connection)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#each" do
|
21
|
+
it "yields to each member of #messages" do
|
22
|
+
block = Proc.new {}
|
23
|
+
query_builder.stub(:message_ids).and_return(['<one@example.com>', '<two@example.com>'])
|
24
|
+
query_builder.messages.should_receive(:each) #.with(block)
|
25
|
+
|
26
|
+
query_builder.each(&block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "::where" do
|
31
|
+
it "returns self" do
|
32
|
+
expect(query_builder.where(:foo => :bar)).to be_a(Luggage::MailboxQueryBuilder)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "appends to the query" do
|
36
|
+
expect(query_builder.where(:foo => :bar).where(:baz => :qux).query).to eq([:foo, :bar, :baz, :qux])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "::[]" do
|
41
|
+
it "returns messages[]" do
|
42
|
+
query_builder.stub(:message_ids).and_return(['<one@example.com>', '<two@example.com>'])
|
43
|
+
query_builder.messages.should_receive(:[]).with((1..2))
|
44
|
+
|
45
|
+
query_builder[1..2]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Luggage::Mailbox do
|
4
|
+
include_context "factories"
|
5
|
+
|
6
|
+
describe "::new" do
|
7
|
+
it "executes a passed block" do
|
8
|
+
m = Luggage::Mailbox.new(connection, :mailbox) do
|
9
|
+
@name = "woot"
|
10
|
+
end
|
11
|
+
expect(m.name).to eq("woot")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "sets the connection" do
|
15
|
+
expect(Luggage::Mailbox.new(connection, :mailbox).connection).to eq(connection)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "requires a connection" do
|
19
|
+
expect { Luggage::Mailbox.new(:foo, :mailbox).connection }.to raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets the name" do
|
23
|
+
expect(Luggage::Mailbox.new(connection, :mailbox).name).to eq(:mailbox)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "message" do
|
28
|
+
it "executes a passed block" do
|
29
|
+
m = mailbox.message do
|
30
|
+
subject("new subject")
|
31
|
+
end
|
32
|
+
expect(m.subject).to eq("new subject")
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
it "returns an Luggage::Message" do
|
37
|
+
expect(mailbox.message).to be_a(Luggage::Message)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "instantiates with expected arguments" do
|
41
|
+
message = Luggage::Message.new(connection, mailbox, :foo => :bar)
|
42
|
+
Luggage::Message.should_receive(:new).with(connection, mailbox, :foo => :bar).and_return(message)
|
43
|
+
|
44
|
+
mailbox.message(:foo => :bar)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "all" do
|
49
|
+
it "returns a QueryBuilder" do
|
50
|
+
expect(mailbox.all).to be_a(Luggage::MailboxQueryBuilder)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "each" do
|
55
|
+
it "delegates to QueryBuilder instance" do
|
56
|
+
block = Proc.new {}
|
57
|
+
Luggage::MailboxQueryBuilder.any_instance.should_receive(:each).with(&block)
|
58
|
+
|
59
|
+
mailbox.each(&block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "first" do
|
64
|
+
it "delegates to QueryBuilder instance" do
|
65
|
+
Luggage::MailboxQueryBuilder.any_instance.should_receive(:first)
|
66
|
+
|
67
|
+
mailbox.first
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "where" do
|
72
|
+
it "delegates to QueryBuilder instance" do
|
73
|
+
Luggage::MailboxQueryBuilder.any_instance.should_receive(:where).with(:foo => :bar)
|
74
|
+
|
75
|
+
mailbox.where(:foo => :bar)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "select!" do
|
80
|
+
it "selects the mailbox" do
|
81
|
+
connection.should_receive(:select).with(:mailbox)
|
82
|
+
|
83
|
+
mailbox.select!
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "save!" do
|
88
|
+
it "creates the mailbox if it doesn't exist" do
|
89
|
+
connection.should_receive(:create).with(:mailbox)
|
90
|
+
|
91
|
+
mailbox.save!
|
92
|
+
end
|
93
|
+
|
94
|
+
it "doesn't create the mailbox if the mailbox exists" do
|
95
|
+
mailbox.stub(:exists?).and_return(true)
|
96
|
+
connection.should_not_receive(:create)
|
97
|
+
|
98
|
+
mailbox.save!
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "expunge!" do
|
103
|
+
it "selects the mailbox" do
|
104
|
+
connection.should_receive(:select).with(:mailbox)
|
105
|
+
|
106
|
+
mailbox.expunge!
|
107
|
+
end
|
108
|
+
|
109
|
+
it "expunges the mailbox" do
|
110
|
+
connection.should_receive(:expunge)
|
111
|
+
|
112
|
+
mailbox.expunge!
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "delete!" do
|
117
|
+
it "deletes the mailbox" do
|
118
|
+
connection.should_receive(:delete)
|
119
|
+
|
120
|
+
mailbox.delete!
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "exists?" do
|
125
|
+
it "returns true if list includes the mailbox name" do
|
126
|
+
connection.stub(:list).with("", :mailbox).and_return([:results])
|
127
|
+
|
128
|
+
expect(mailbox.exists?).to be_true
|
129
|
+
end
|
130
|
+
|
131
|
+
it "returns false if the list doesn't include the mailbox name" do
|
132
|
+
expect(mailbox.exists?).to be_false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Luggage::Message do
|
4
|
+
include_context "factories"
|
5
|
+
|
6
|
+
describe "::new_local" do
|
7
|
+
it "executes a passed block" do
|
8
|
+
m = Luggage::Message.new_local(connection, "Inbox") do
|
9
|
+
subject("new subject")
|
10
|
+
end
|
11
|
+
expect(m.subject).to eq("new subject")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "returns an Luggage::Message" do
|
15
|
+
expect(Luggage::Message.new_local(connection, "Inbox")).to be_a(Luggage::Message)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "raises ArgumentError if Luggage::Mailbox isn't passed" do
|
19
|
+
expect{ Luggage::Message.new_local() }.to raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
|
22
|
+
[:subject, :body, :to].each do |method|
|
23
|
+
context "with #{method} passed as argument" do
|
24
|
+
it "sets #{method} on Mail object" do
|
25
|
+
message = Luggage::Message.new_local(connection, :mailbox, method => "string")
|
26
|
+
expect(message.to_s).to match(/string/)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "without template" do
|
32
|
+
it "instatiates a Mail object" do
|
33
|
+
mail = Mail.new
|
34
|
+
Mail.should_receive(:new).and_return(mail)
|
35
|
+
Luggage::Message.new_local(connection, :mailbox)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with a template" do
|
40
|
+
it "instatiates a Mail object" do
|
41
|
+
Mail.should_receive(:read).and_return({})
|
42
|
+
|
43
|
+
Luggage::Message.new_local(connection, :mailbox, :template =>"base")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "::new" do
|
49
|
+
it "executes a passed block" do
|
50
|
+
m = Luggage::Message.new(connection, "Inbox") do
|
51
|
+
subject("new subject")
|
52
|
+
end
|
53
|
+
expect(m.subject).to eq("new subject")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "sets connection" do
|
57
|
+
expect(Luggage::Message.new(connection, :mailbox).connection).to eq(connection)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "requires a connection" do
|
61
|
+
expect { Luggage::Message.new(:not_a_connection, :mailbox).connection }.to raise_error(ArgumentError)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "sets mailbox if Mailbox passed" do
|
65
|
+
expect(Luggage::Message.new(connection, mailbox).mailbox).to eq(mailbox)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "instantiates new mailbox if string passed" do
|
69
|
+
expect(Luggage::Message.new(connection, :mailbox).mailbox).to be_a(Luggage::Mailbox)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "sets date if passed" do
|
73
|
+
date = 2.days.ago
|
74
|
+
expect(Luggage::Message.new(connection, :mailbox, :date => date).date).to eq(date)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "sets date to now if not passed" do
|
78
|
+
expect(Luggage::Message.new(connection, :mailbox).date).to be_a(Time)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "sets message_id if passed" do
|
82
|
+
message_id = "<foo@example.com>"
|
83
|
+
expect(Luggage::Message.new(connection, :mailbox, :message_id => message_id).message_id).to eq(message_id)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "creates message_id if not passed" do
|
87
|
+
expect(Luggage::Message.new(connection, :mailbox).message_id).to match(/<\S*@\S*>/)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#reload" do
|
92
|
+
it "selects mailbox" do
|
93
|
+
connection.should_receive(:select).with("Inbox")
|
94
|
+
|
95
|
+
message.reload
|
96
|
+
end
|
97
|
+
|
98
|
+
it "fetches raw email" do
|
99
|
+
connection.should_receive(:uid_fetch).
|
100
|
+
with([1], ["FLAGS", "INTERNALDATE", "BODY.PEEK[]"]).
|
101
|
+
and_return( [{:attr => {"BODY[]" => "raw_body", "FLAGS" => [], "INTERNALDATE" => 1.day.ago.to_s}}] )
|
102
|
+
|
103
|
+
message.reload
|
104
|
+
end
|
105
|
+
|
106
|
+
it "fetches flags" do
|
107
|
+
connection.should_receive(:uid_fetch).
|
108
|
+
with([1], ["FLAGS", "INTERNALDATE", "BODY.PEEK[]"]).
|
109
|
+
and_return( [{:attr => {"BODY[]" => "raw_body", "FLAGS" => [], "INTERNALDATE" => 1.day.ago.to_s}}] )
|
110
|
+
|
111
|
+
message.reload
|
112
|
+
end
|
113
|
+
|
114
|
+
it "creates new Mail instance" do
|
115
|
+
message # To instantiate the first one...
|
116
|
+
|
117
|
+
Mail.should_receive(:new).with("raw_body")
|
118
|
+
|
119
|
+
message.reload
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "#save!" do
|
124
|
+
it "selects mailbox" do
|
125
|
+
connection.should_receive(:select).with("Inbox")
|
126
|
+
|
127
|
+
message.save!
|
128
|
+
end
|
129
|
+
|
130
|
+
it "appends message to mailbox" do
|
131
|
+
message_date = 2.days.ago
|
132
|
+
message.stub(:raw_message).and_return("Random Content")
|
133
|
+
message.stub(:flags).and_return([:Seen])
|
134
|
+
message.stub(:date).and_return(message_date)
|
135
|
+
|
136
|
+
connection.should_receive(:append).with("Inbox", "Random Content", [:SEEN], message_date)
|
137
|
+
message.save!
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#to_s" do
|
142
|
+
it "returns a string" do
|
143
|
+
expect(message.to_s).to be_a(String)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
describe "#delete!" do
|
149
|
+
it "selects mailbox" do
|
150
|
+
connection.should_receive(:select).with("Inbox")
|
151
|
+
|
152
|
+
message.delete!
|
153
|
+
end
|
154
|
+
|
155
|
+
it "sets Deleted flag" do
|
156
|
+
connection.should_receive(:uid_store).with([1], "+FLAGS", [:Deleted])
|
157
|
+
|
158
|
+
message.delete!
|
159
|
+
end
|
160
|
+
|
161
|
+
it "resets cached mail instance" do
|
162
|
+
message.delete!
|
163
|
+
expect(message.instance_variable_get(:@mail)).to be_nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "#exists?" do
|
168
|
+
it "selects mailbox" do
|
169
|
+
connection.should_receive(:select).with("Inbox")
|
170
|
+
|
171
|
+
message.exists?
|
172
|
+
end
|
173
|
+
|
174
|
+
it "searches for message with message_id" do
|
175
|
+
message.stub(:message_id).and_return("<foo@example.com>")
|
176
|
+
connection.should_receive(:uid_search).with("HEADER Message-ID <foo@example.com>").and_return([1])
|
177
|
+
|
178
|
+
message.exists?
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Luggage do
|
4
|
+
it "should have a VERSION constant" do
|
5
|
+
subject.const_get('VERSION').should_not be_empty
|
6
|
+
end
|
7
|
+
|
8
|
+
include_context "factories"
|
9
|
+
|
10
|
+
describe "::new" do
|
11
|
+
it "proxies Luggage::Factory" do
|
12
|
+
Luggage::Factory.should_receive(:new).with(:server => :foo, :authenticate => :bar)
|
13
|
+
Luggage.new(:server => :foo, :authenticate => :bar)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|