inbox-sync 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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