inbox-sync 0.1.0 → 0.2.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.
- data/lib/inbox-sync/mail_item.rb +50 -26
- data/lib/inbox-sync/runner.rb +56 -29
- data/lib/inbox-sync/sync.rb +12 -6
- data/lib/inbox-sync/version.rb +1 -1
- data/test/helper.rb +6 -3
- data/test/mail_item_test.rb +9 -9
- data/test/runner_test.rb +1 -0
- metadata +4 -4
data/lib/inbox-sync/mail_item.rb
CHANGED
@@ -5,32 +5,51 @@ module InboxSync
|
|
5
5
|
class MailItem
|
6
6
|
|
7
7
|
def self.find(imap)
|
8
|
-
imap.uid_search(['ALL']).
|
9
|
-
|
10
|
-
|
11
|
-
end.
|
12
|
-
map do |uid_meta|
|
13
|
-
self.new(
|
14
|
-
uid_meta.first,
|
15
|
-
uid_meta.last.attr['RFC822'],
|
16
|
-
uid_meta.last.attr["INTERNALDATE"]
|
17
|
-
)
|
18
|
-
end
|
8
|
+
imap.uid_search(['ALL']).map do |uid|
|
9
|
+
self.new(imap, uid)
|
10
|
+
end
|
19
11
|
end
|
20
12
|
|
21
|
-
attr_reader :
|
13
|
+
attr_reader :imap, :uid
|
22
14
|
|
23
|
-
def initialize(
|
15
|
+
def initialize(imap, uid, attrs={})
|
16
|
+
@imap = imap
|
24
17
|
@uid = uid
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
}
|
29
|
-
@message = ::Mail.new(rfc822)
|
18
|
+
@rfc822 = attrs[:rfc822]
|
19
|
+
@internal_date = attrs[:internal_date]
|
20
|
+
@message = attrs[:message]
|
30
21
|
end
|
31
22
|
|
32
23
|
def name
|
33
|
-
"[#{
|
24
|
+
@name ||= "[#{self.uid}] #{self.message.from}: #{self.message.subject.inspect} (#{time_s(self.message.date)})"
|
25
|
+
end
|
26
|
+
|
27
|
+
def meta
|
28
|
+
@meta ||= @imap.uid_fetch(self.uid, ['RFC822', 'INTERNALDATE']).first
|
29
|
+
end
|
30
|
+
|
31
|
+
def rfc822
|
32
|
+
@rfc822 ||= self.meta.attr['RFC822']
|
33
|
+
end
|
34
|
+
|
35
|
+
def rfc822=(value)
|
36
|
+
@rfc822 = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def internal_date
|
40
|
+
@internal_date ||= self.meta.attr['INTERNALDATE']
|
41
|
+
end
|
42
|
+
|
43
|
+
def internal_date=(value)
|
44
|
+
@internal_date = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def message
|
48
|
+
@message ||= ::Mail.new(self.rfc822)
|
49
|
+
end
|
50
|
+
|
51
|
+
def message=(value)
|
52
|
+
@message = value
|
34
53
|
end
|
35
54
|
|
36
55
|
# Returns a stripped down version of the mail item
|
@@ -40,15 +59,11 @@ module InboxSync
|
|
40
59
|
# This implies that stripped down mail items have no attachments.
|
41
60
|
|
42
61
|
def stripped
|
43
|
-
@stripped ||= strip_down(
|
44
|
-
self.uid,
|
45
|
-
self.meta['RFC822'],
|
46
|
-
self.meta["INTERNALDATE"]
|
47
|
-
))
|
62
|
+
@stripped ||= strip_down(copy_mail_item(self))
|
48
63
|
end
|
49
64
|
|
50
65
|
def inspect
|
51
|
-
"#<#{self.class}:#{'0x%x' % (self.object_id << 1)}: @uid=#{
|
66
|
+
"#<#{self.class}:#{'0x%x' % (self.object_id << 1)}: @uid=#{self.uid.inspect}, from=#{self.message.from.inspect}, subject=#{self.message.subject.inspect}, 'INTERNALDATE'=#{self.internal_date.inspect}>"
|
52
67
|
end
|
53
68
|
|
54
69
|
private
|
@@ -57,6 +72,14 @@ module InboxSync
|
|
57
72
|
datetime.strftime("%a %b %-d %Y, %I:%M %p")
|
58
73
|
end
|
59
74
|
|
75
|
+
def copy_mail_item(item)
|
76
|
+
MailItem.new(item.imap, item.uid, {
|
77
|
+
:rfc822 => item.rfc822,
|
78
|
+
:internal_date => item.internal_date,
|
79
|
+
:message => item.message
|
80
|
+
})
|
81
|
+
end
|
82
|
+
|
60
83
|
def strip_down(mail_item)
|
61
84
|
message = mail_item.message
|
62
85
|
if message.multipart?
|
@@ -64,7 +87,8 @@ module InboxSync
|
|
64
87
|
!part.content_type.match(/text\/plain/)
|
65
88
|
end
|
66
89
|
message.parts.first.body = strip_down_body_s(message.parts.first.body)
|
67
|
-
mail_item.
|
90
|
+
mail_item.message = message
|
91
|
+
mail_item.rfc822 = message.to_s
|
68
92
|
end
|
69
93
|
mail_item
|
70
94
|
end
|
data/lib/inbox-sync/runner.rb
CHANGED
@@ -18,67 +18,94 @@ module InboxSync
|
|
18
18
|
@shutdown = false
|
19
19
|
@running_syncs_thread = nil
|
20
20
|
|
21
|
-
Signal.trap('SIGINT',
|
22
|
-
Signal.trap('SIGQUIT', lambda{
|
21
|
+
Signal.trap('SIGINT', lambda{ raise Interrupt, 'SIGINT' })
|
22
|
+
Signal.trap('SIGQUIT', lambda{ raise Interrupt, 'SIGQUIT' })
|
23
|
+
Signal.trap('TERM', lambda{ raise Interrupt, 'TERM' })
|
23
24
|
end
|
24
25
|
|
25
26
|
def start
|
26
|
-
startup
|
27
|
+
startup!
|
27
28
|
loop do
|
28
|
-
break if @shutdown
|
29
29
|
loop_run
|
30
|
+
break if shutdown?
|
30
31
|
end
|
31
|
-
shutdown
|
32
|
+
shutdown!
|
32
33
|
end
|
33
34
|
|
34
35
|
def stop
|
35
|
-
|
36
|
-
|
36
|
+
if @shutdown != true
|
37
|
+
main_log "Stop signal - waiting for any running syncs to finish."
|
38
|
+
@shutdown = true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def shutdown?
|
43
|
+
!!@shutdown
|
37
44
|
end
|
38
45
|
|
39
46
|
protected
|
40
47
|
|
41
|
-
def startup
|
48
|
+
def startup!
|
42
49
|
main_log "Starting up the runner."
|
50
|
+
if @interval < 0
|
51
|
+
main_log "run-once interval - signaling stop"
|
52
|
+
stop
|
53
|
+
@interval = 0
|
54
|
+
end
|
43
55
|
end
|
44
56
|
|
45
|
-
def shutdown
|
57
|
+
def shutdown!
|
46
58
|
main_log "Shutting down the runner"
|
47
59
|
end
|
48
60
|
|
49
61
|
def loop_run
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
thread_log_error(err, :error)
|
62
|
+
begin
|
63
|
+
main_log "Starting syncs in fresh thread."
|
64
|
+
@running_syncs_thread = Thread.new { run_syncs_thread }
|
65
|
+
|
66
|
+
if @interval > 0
|
67
|
+
main_log "Sleeping for #{@interval} second interval."
|
68
|
+
sleep(@interval)
|
69
|
+
main_log "Woke from sleep"
|
59
70
|
end
|
60
|
-
|
61
|
-
|
71
|
+
rescue Interrupt => err
|
72
|
+
stop
|
73
|
+
ensure
|
74
|
+
join_syncs_thread
|
62
75
|
end
|
76
|
+
end
|
63
77
|
|
64
|
-
|
65
|
-
|
78
|
+
def join_syncs_thread
|
79
|
+
begin
|
80
|
+
if @running_syncs_thread
|
81
|
+
main_log "Waiting for running syncs thread to join..."
|
82
|
+
@running_syncs_thread.join
|
83
|
+
main_log "... running syncs thread has joined."
|
84
|
+
end
|
85
|
+
@running_syncs_thread = nil
|
86
|
+
rescue Interrupt => err
|
66
87
|
stop
|
67
|
-
|
88
|
+
join_syncs_thread
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def run_syncs_thread
|
93
|
+
thread_log "starting syncs..."
|
94
|
+
|
95
|
+
begin
|
96
|
+
run_syncs
|
97
|
+
rescue Exception => err
|
98
|
+
thread_log_error(err, :error)
|
68
99
|
end
|
69
100
|
|
70
|
-
|
71
|
-
sleep(@interval)
|
72
|
-
main_log "Woke from sleep - waiting for running syncs thread to join..."
|
73
|
-
@running_syncs_thread.join
|
74
|
-
main_log "... running sycs thread joined."
|
101
|
+
thread_log "...syncs finished"
|
75
102
|
end
|
76
103
|
|
77
104
|
def run_syncs
|
78
105
|
@syncs.each do |sync|
|
79
106
|
begin
|
80
107
|
sync.setup
|
81
|
-
sync.run
|
108
|
+
sync.run(self)
|
82
109
|
rescue Exception => err
|
83
110
|
run_sync_handle_error(sync, err)
|
84
111
|
end
|
data/lib/inbox-sync/sync.rb
CHANGED
@@ -52,8 +52,9 @@ module InboxSync
|
|
52
52
|
logger.info "=== #{config_log_detail(@config.source)} sync finished. ==="
|
53
53
|
end
|
54
54
|
|
55
|
-
def run
|
56
|
-
|
55
|
+
def run(runner=nil)
|
56
|
+
return if runner && runner.shutdown?
|
57
|
+
each_source_mail_item(runner) do |mail_item|
|
57
58
|
begin
|
58
59
|
response = send_to_dest(mail_item)
|
59
60
|
dest_uid = parse_append_response_uid(response)
|
@@ -95,11 +96,16 @@ module InboxSync
|
|
95
96
|
true
|
96
97
|
end
|
97
98
|
|
98
|
-
def each_source_mail_item
|
99
|
+
def each_source_mail_item(runner=nil)
|
100
|
+
logger.info "* find: #{config_log_detail(@config.source)}, #{@config.source.inbox.inspect}..."
|
99
101
|
items = MailItem.find(@source_imap)
|
100
|
-
logger.info "* found #{items.size}
|
102
|
+
logger.info "* ...found #{items.size} mail items"
|
101
103
|
|
102
104
|
items.each do |mail_item|
|
105
|
+
if runner && runner.shutdown?
|
106
|
+
logger.info "* the runner has been shutdown - aborting the sync"
|
107
|
+
break
|
108
|
+
end
|
103
109
|
logger.debug "** #{mail_item.inspect}"
|
104
110
|
yield mail_item
|
105
111
|
end
|
@@ -128,9 +134,9 @@ module InboxSync
|
|
128
134
|
logger.info "** Appending #{desc}#{mail_item.uid} to dest #{@config.dest.inbox}..."
|
129
135
|
|
130
136
|
inbox = @config.dest.inbox
|
131
|
-
mail_s = mail_item.
|
137
|
+
mail_s = mail_item.rfc822
|
132
138
|
flags = []
|
133
|
-
date = mail_item.
|
139
|
+
date = mail_item.internal_date
|
134
140
|
|
135
141
|
using_dest_imap do |imap|
|
136
142
|
imap.append(inbox, mail_s, flags, date)
|
data/lib/inbox-sync/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -41,7 +41,10 @@ class Assert::Context
|
|
41
41
|
}
|
42
42
|
|
43
43
|
def test_mail_item
|
44
|
-
InboxSync::MailItem.new('
|
44
|
+
InboxSync::MailItem.new('dummy_imap', '12345', {
|
45
|
+
:rfc822 => TEST_MAIL_DATA['RFC822'],
|
46
|
+
:internal_date => TEST_MAIL_DATA['INTERNALDATE']
|
47
|
+
})
|
45
48
|
end
|
46
49
|
|
47
50
|
def setup_sync_mail_item
|
@@ -64,9 +67,9 @@ class Assert::Context
|
|
64
67
|
|
65
68
|
# append the test mail on the source imap
|
66
69
|
inbox = sync.config.source.inbox
|
67
|
-
mail_s = test_mail_item.
|
70
|
+
mail_s = test_mail_item.rfc822
|
68
71
|
flags = []
|
69
|
-
date = test_mail_item.
|
72
|
+
date = test_mail_item.internal_date
|
70
73
|
|
71
74
|
sync.source_imap.append(inbox, mail_s, flags, date)
|
72
75
|
end
|
data/test/mail_item_test.rb
CHANGED
@@ -11,20 +11,20 @@ module InboxSync
|
|
11
11
|
end
|
12
12
|
subject { @item }
|
13
13
|
|
14
|
-
should have_readers :
|
14
|
+
should have_readers :imap, :uid
|
15
15
|
should have_class_method :find
|
16
|
-
should
|
16
|
+
should have_instance_methods :name, :meta, :stripped
|
17
|
+
should have_instance_methods :rfc822, :rfc822=
|
18
|
+
should have_instance_methods :internal_date, :internal_date=
|
19
|
+
should have_instance_methods :message, :message=
|
17
20
|
|
18
21
|
should "build a Mail Message from the raw IMAP attr data" do
|
19
22
|
assert_kind_of ::Mail::Message, subject.message
|
20
23
|
end
|
21
24
|
|
22
|
-
should "provide the
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
assert_equal TEST_MAIL_DATA['RFC822'], subject.meta['RFC822']
|
27
|
-
assert_equal TEST_MAIL_DATA['INTERNALDATE'], subject.meta['INTERNALDATE']
|
25
|
+
should "provide the IMAP rfc822 and internal_date" do
|
26
|
+
assert_equal TEST_MAIL_DATA['RFC822'], subject.rfc822
|
27
|
+
assert_equal TEST_MAIL_DATA['INTERNALDATE'], subject.internal_date
|
28
28
|
end
|
29
29
|
|
30
30
|
should "be named by its uid, from, subject, and date" do
|
@@ -55,7 +55,7 @@ module InboxSync
|
|
55
55
|
end
|
56
56
|
|
57
57
|
should "set its RFC822 to the stripped message string" do
|
58
|
-
assert_equal subject.
|
58
|
+
assert_equal subject.rfc822, subject.message.to_s
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
data/test/runner_test.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inbox-sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kelly Redding
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-06-
|
18
|
+
date: 2012-06-06 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :development
|