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.
@@ -5,32 +5,51 @@ module InboxSync
5
5
  class MailItem
6
6
 
7
7
  def self.find(imap)
8
- imap.uid_search(['ALL']).
9
- map do |uid|
10
- [uid, imap.uid_fetch(uid, ['RFC822', 'INTERNALDATE']).first]
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 :uid, :meta, :message
13
+ attr_reader :imap, :uid
22
14
 
23
- def initialize(uid, rfc822, internal_date)
15
+ def initialize(imap, uid, attrs={})
16
+ @imap = imap
24
17
  @uid = uid
25
- @meta = {
26
- 'RFC822' => rfc822,
27
- 'INTERNALDATE' => internal_date
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
- "[#{@uid}] #{@message.from}: #{@message.subject.inspect} (#{time_s(@message.date)})"
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(MailItem.new(
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=#{@uid.inspect}, from=#{@message.from.inspect}, subject=#{@message.subject.inspect}, 'INTERNALDATE'=#{@meta['INTERNALDATE'].inspect}>"
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.meta['RFC822'] = message.to_s
90
+ mail_item.message = message
91
+ mail_item.rfc822 = message.to_s
68
92
  end
69
93
  mail_item
70
94
  end
@@ -18,67 +18,94 @@ module InboxSync
18
18
  @shutdown = false
19
19
  @running_syncs_thread = nil
20
20
 
21
- Signal.trap('SIGINT', lambda{ self.stop })
22
- Signal.trap('SIGQUIT', lambda{ self.stop })
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
- main_log "Stop signal - waiting for any running syncs to finish."
36
- @shutdown = true
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
- main_log "Starting syncs in fresh thread."
51
-
52
- @running_syncs_thread = Thread.new do
53
- thread_log "starting syncs..."
54
-
55
- begin
56
- run_syncs
57
- rescue Exception => err
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
- thread_log "...syncs finished"
71
+ rescue Interrupt => err
72
+ stop
73
+ ensure
74
+ join_syncs_thread
62
75
  end
76
+ end
63
77
 
64
- if @interval < 0
65
- main_log "run-once interval - signaling stop"
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
- @interval = 0
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
- main_log "Sleeping for #{@interval} seconds."
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
@@ -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
- each_source_mail_item do |mail_item|
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} mails"
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.meta['RFC822']
137
+ mail_s = mail_item.rfc822
132
138
  flags = []
133
- date = mail_item.meta['INTERNALDATE']
139
+ date = mail_item.internal_date
134
140
 
135
141
  using_dest_imap do |imap|
136
142
  imap.append(inbox, mail_s, flags, date)
@@ -1,3 +1,3 @@
1
1
  module InboxSync
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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('12345', TEST_MAIL_DATA['RFC822'], TEST_MAIL_DATA['INTERNALDATE'])
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.meta['RFC822']
70
+ mail_s = test_mail_item.rfc822
68
71
  flags = []
69
- date = test_mail_item.meta['INTERNALDATE']
72
+ date = test_mail_item.internal_date
70
73
 
71
74
  sync.source_imap.append(inbox, mail_s, flags, date)
72
75
  end
@@ -11,20 +11,20 @@ module InboxSync
11
11
  end
12
12
  subject { @item }
13
13
 
14
- should have_readers :uid, :meta, :message
14
+ should have_readers :imap, :uid
15
15
  should have_class_method :find
16
- should have_instance_method :name, :stripped
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 raw IMAP attrs in the meta hash" do
23
- assert_includes 'RFC822', subject.meta
24
- assert_includes 'INTERNALDATE', subject.meta
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.meta['RFC822'], subject.message.to_s
58
+ assert_equal subject.rfc822, subject.message.to_s
59
59
  end
60
60
  end
61
61
 
data/test/runner_test.rb CHANGED
@@ -14,6 +14,7 @@ module InboxSync
14
14
  subject { @runner }
15
15
 
16
16
  should have_readers :syncs, :interval, :logger
17
+ should have_instance_methods :start, :stop, :shutdown?
17
18
 
18
19
  should "know its syncs" do
19
20
  assert_equal @syncs, subject.syncs
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: 27
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.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-05 00:00:00 Z
18
+ date: 2012-06-06 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  type: :development