luggage 1.0.0
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.
- 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
|