mailboxer 0.11.0 → 0.12.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +4 -3
  4. data/Appraisals +6 -14
  5. data/Gemfile +1 -1
  6. data/README.md +2 -2
  7. data/app/mailers/mailboxer/base_mailer.rb +14 -0
  8. data/app/mailers/{message_mailer.rb → mailboxer/message_mailer.rb} +9 -14
  9. data/app/mailers/mailboxer/notification_mailer.rb +17 -0
  10. data/app/models/mailboxer/conversation.rb +196 -0
  11. data/app/models/mailboxer/conversation/opt_out.rb +15 -0
  12. data/app/models/{mailbox.rb → mailboxer/mailbox.rb} +10 -10
  13. data/app/models/mailboxer/message.rb +49 -0
  14. data/app/models/{notification.rb → mailboxer/notification.rb} +44 -40
  15. data/app/models/{receipt.rb → mailboxer/receipt.rb} +12 -11
  16. data/app/views/{message_mailer → mailboxer/message_mailer}/new_message_email.html.erb +3 -3
  17. data/app/views/{message_mailer → mailboxer/message_mailer}/new_message_email.text.erb +2 -2
  18. data/app/views/{message_mailer → mailboxer/message_mailer}/reply_message_email.html.erb +3 -3
  19. data/app/views/{message_mailer → mailboxer/message_mailer}/reply_message_email.text.erb +2 -2
  20. data/app/views/{notification_mailer → mailboxer/notification_mailer}/new_notification_email.html.erb +0 -0
  21. data/app/views/{notification_mailer → mailboxer/notification_mailer}/new_notification_email.text.erb +0 -0
  22. data/db/migrate/20110511145103_create_mailboxer.rb +18 -14
  23. data/db/migrate/20131206080416_add_conversation_optout.rb +14 -0
  24. data/gemfiles/rails3.2.gemfile +2 -3
  25. data/gemfiles/rails4.0.gemfile +3 -4
  26. data/gemfiles/rails4.1.gemfile +7 -0
  27. data/lib/generators/mailboxer/install_generator.rb +1 -4
  28. data/lib/generators/mailboxer/namespacing_compatibility_generator.rb +22 -0
  29. data/lib/generators/mailboxer/templates/initializer.rb +8 -4
  30. data/lib/generators/mailboxer/templates/mailboxer_namespacing_compatibility.rb +24 -0
  31. data/lib/mailboxer.rb +7 -1
  32. data/lib/mailboxer/cleaner.rb +9 -0
  33. data/lib/mailboxer/engine.rb +1 -1
  34. data/lib/mailboxer/mail_dispatcher.rb +48 -0
  35. data/lib/mailboxer/models/messageable.rb +21 -20
  36. data/lib/mailboxer/version.rb +3 -0
  37. data/mailboxer.gemspec +16 -8
  38. data/spec/dummy/Gemfile +1 -1
  39. data/spec/dummy/app/models/duck.rb +2 -2
  40. data/spec/dummy/config/initializers/mailboxer.rb +4 -0
  41. data/spec/dummy/db/migrate/20120305103200_create_mailboxer.rb +18 -14
  42. data/spec/dummy/db/migrate/20131206080416_add_conversation_optout.rb +14 -0
  43. data/spec/dummy/db/schema.rb +30 -21
  44. data/spec/integration/message_and_receipt_spec.rb +212 -212
  45. data/spec/mailboxer/mail_dispatcher_spec.rb +114 -0
  46. data/spec/mailers/message_mailer_spec.rb +4 -4
  47. data/spec/mailers/notification_mailer_spec.rb +2 -2
  48. data/spec/models/conversation_spec.rb +148 -47
  49. data/spec/models/mailbox_spec.rb +50 -50
  50. data/spec/models/mailboxer_models_messageable_spec.rb +52 -52
  51. data/spec/models/message_spec.rb +1 -1
  52. data/spec/models/notification_spec.rb +50 -18
  53. data/spec/models/receipt_spec.rb +1 -1
  54. data/spec/spec_helper.rb +3 -0
  55. metadata +68 -57
  56. data/app/mailers/notification_mailer.rb +0 -21
  57. data/app/models/conversation.rb +0 -166
  58. data/app/models/message.rb +0 -68
  59. data/db/migrate/20110719110700_add_notified_object.rb +0 -17
  60. data/db/migrate/20110912163911_add_notification_code.rb +0 -13
  61. data/db/migrate/20111204163911_add_attachments.rb +0 -9
  62. data/db/migrate/20120813110712_rename_receipts_read.rb +0 -9
  63. data/db/migrate/20130305144212_add_global_notification_support.rb +0 -9
  64. data/gemfiles/rails3.0.gemfile +0 -8
  65. data/gemfiles/rails3.1.gemfile +0 -8
  66. data/lib/mailboxer/concerns/configurable_mailer.rb +0 -13
  67. data/spec/dummy/db/migrate/20120305103201_add_notified_object.rb +0 -17
  68. data/spec/dummy/db/migrate/20120305103202_add_notification_code.rb +0 -13
  69. data/spec/dummy/db/migrate/20120305103203_add_attachments.rb +0 -5
  70. data/spec/dummy/db/migrate/20120813110712_rename_receipts_read.rb +0 -9
  71. data/spec/dummy/db/migrate/20130305144212_add_global_notification_support.rb +0 -11
  72. data/spec/mailboxer/concerns/configurable_mailer_spec.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b22e110378d5da7e6e327c807f46a5f0475b2fa1
4
- data.tar.gz: bc9242e076e3a10d2e3d9ca1106a42200ca7bc12
3
+ metadata.gz: e699bff86b951b515780d88e719ae2b02eca59e7
4
+ data.tar.gz: 7024930841d1c7b675ecca3eab46767896fcc67c
5
5
  SHA512:
6
- metadata.gz: f67c3839eb84ce3153e7e9b1b50b012f80a1d17bdb56589e18279ba1043e3e4fc4f443c17b2ed9cff7a0e4bcbc9ebef10139bd63de67b520ac12f3fcd40bc05e
7
- data.tar.gz: 277d0c3921688613e407b095665d7d2b2afe783294f1500d9ba2a423c83460b5854711520a9cfaddcec876b864924c65136edcd78dbdad4fbbd8286eb5d5405f
6
+ metadata.gz: 81b0a14bd89091e70042fc95e215482bb3c64c2d352821f40c0da348b51eb0fa1d07c2d802bc73f933758f767e1b65c3f314e7741da3b8035c91607f10321848
7
+ data.tar.gz: d9e30049e35052c5253f3587239ce9dfb6568c9350056d82fb261ed9a9d20096aef578c1252c3f48353aa4f88688782e019ba187c37e448a73c8f5092224634e
data/.gitignore CHANGED
@@ -9,6 +9,7 @@ spec/dummy/.project
9
9
  spec/dummy/solr
10
10
  **.tmp_*
11
11
  Gemfile.lock
12
+ gemfiles/*.lock
12
13
  .idea
13
14
  .project
14
15
  .document
@@ -16,3 +17,4 @@ Gemfile.lock
16
17
  rdoc/
17
18
  doc/
18
19
  .yardoc/*
20
+ .rvmrc
data/.travis.yml CHANGED
@@ -2,10 +2,11 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
- - rbx-19mode
6
5
  - jruby-19mode
7
6
  gemfile:
8
- #- gemfiles/rails3.0.gemfile
9
- - gemfiles/rails3.1.gemfile
10
7
  - gemfiles/rails3.2.gemfile
11
8
  - gemfiles/rails4.0.gemfile
9
+ - gemfiles/rails4.1.gemfile
10
+ env:
11
+ global:
12
+ - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
data/Appraisals CHANGED
@@ -1,19 +1,11 @@
1
- # appraise "rails3.0" do
2
- # gem "rails", "~> 3.0.15"
3
- # gem "mailboxer", :path => "../"
4
- # end
5
-
6
- appraise "rails3.1" do
7
- gem "rails", "~> 3.1.6"
8
- gem "mailboxer", :path => "../"
9
- end
10
-
11
1
  appraise "rails3.2" do
12
- gem "rails", "~> 3.2.6"
13
- gem "mailboxer", :path => "../"
2
+ gem "rails", "~> 3.2.17"
14
3
  end
15
4
 
16
5
  appraise "rails4.0" do
17
- gem "rails", ">=4.0.0"
18
- gem "mailboxer", :path => "../"
6
+ gem "rails", "~> 4.0.4"
7
+ end
8
+
9
+ appraise "rails4.1" do
10
+ gem "rails", "~> 4.1.0"
19
11
  end
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gemspec
3
+ gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Mailboxer 0.10.x [![](https://secure.travis-ci.org/ging/mailboxer.png)](http://travis-ci.org/ging/mailboxer) [![Gem Version](https://badge.fury.io/rb/mailboxer.png)](http://badge.fury.io/rb/mailboxer) [![](https://gemnasium.com/ging/mailboxer.png)](https://gemnasium.com/ging/mailboxer)
1
+ # Mailboxer [![Build Status](https://travis-ci.org/mailboxer/mailboxer.svg?branch=master)](https://travis-ci.org/mailboxer/mailboxer) [![Gem Version](https://badge.fury.io/rb/mailboxer.png)](http://badge.fury.io/rb/mailboxer) [![](https://gemnasium.com/ging/mailboxer.png)](https://gemnasium.com/ging/mailboxer)
2
2
 
3
3
  This project is based on the need for a private message system for [ging
4
4
  / social\_stream](https://github.com/ging/social_stream). Instead of creating our core message system heavily
@@ -41,7 +41,7 @@ gem 'mailboxer'
41
41
  Then run:
42
42
 
43
43
  ```sh
44
- $ bundle update
44
+ $ bundle install
45
45
  ```
46
46
 
47
47
  Run install script:
@@ -0,0 +1,14 @@
1
+ class Mailboxer::BaseMailer < ActionMailer::Base
2
+ default :from => Mailboxer.default_from
3
+
4
+ private
5
+
6
+ def set_subject(container)
7
+ @subject = container.subject.html_safe? ? container.subject : strip_tags(container.subject)
8
+ end
9
+
10
+ def strip_tags(text)
11
+ ::Mailboxer::Cleaner.instance.strip_tags(text)
12
+ end
13
+
14
+ end
@@ -1,5 +1,4 @@
1
- class MessageMailer < ActionMailer::Base
2
- default :from => Mailboxer.default_from
1
+ class Mailboxer::MessageMailer < Mailboxer::BaseMailer
3
2
  #Sends and email for indicating a new message or a reply to a receiver.
4
3
  #It calls new_message_email if notifing a new message and reply_message_email
5
4
  #when indicating a reply to an already created conversation.
@@ -11,27 +10,23 @@ class MessageMailer < ActionMailer::Base
11
10
  end
12
11
  end
13
12
 
14
- include ActionView::Helpers::SanitizeHelper
15
-
16
13
  #Sends an email for indicating a new message for the receiver
17
14
  def new_message_email(message,receiver)
18
- @message = message
15
+ @message = message
19
16
  @receiver = receiver
20
- subject = message.subject.to_s
21
- subject = strip_tags(subject) unless subject.html_safe?
22
- mail :to => receiver.send(Mailboxer.email_method,message),
23
- :subject => t('mailboxer.message_mailer.subject_new', :subject => subject),
17
+ set_subject(message)
18
+ mail :to => receiver.send(Mailboxer.email_method, message),
19
+ :subject => t('mailboxer.message_mailer.subject_new', :subject => @subject),
24
20
  :template_name => 'new_message_email'
25
21
  end
26
22
 
27
23
  #Sends and email for indicating a reply in an already created conversation
28
24
  def reply_message_email(message,receiver)
29
- @message = message
25
+ @message = message
30
26
  @receiver = receiver
31
- subject = message.subject.to_s
32
- subject = strip_tags(subject) unless subject.html_safe?
33
- mail :to => receiver.send(Mailboxer.email_method,message),
34
- :subject => t('mailboxer.message_mailer.subject_reply', :subject => subject),
27
+ set_subject(message)
28
+ mail :to => receiver.send(Mailboxer.email_method, message),
29
+ :subject => t('mailboxer.message_mailer.subject_reply', :subject => @subject),
35
30
  :template_name => 'reply_message_email'
36
31
  end
37
32
  end
@@ -0,0 +1,17 @@
1
+ class Mailboxer::NotificationMailer < Mailboxer::BaseMailer
2
+ #Sends and email for indicating a new notification to a receiver.
3
+ #It calls new_notification_email.
4
+ def send_email(notification, receiver)
5
+ new_notification_email(notification, receiver)
6
+ end
7
+
8
+ #Sends an email for indicating a new message for the receiver
9
+ def new_notification_email(notification, receiver)
10
+ @notification = notification
11
+ @receiver = receiver
12
+ set_subject(notification)
13
+ mail :to => receiver.send(Mailboxer.email_method, notification),
14
+ :subject => t('mailboxer.notification_mailer.subject', :subject => @subject),
15
+ :template_name => 'new_notification_email'
16
+ end
17
+ end
@@ -0,0 +1,196 @@
1
+ class Mailboxer::Conversation < ActiveRecord::Base
2
+ self.table_name = :mailboxer_conversations
3
+
4
+ attr_accessible :subject if Mailboxer.protected_attributes?
5
+
6
+ has_many :opt_outs, :dependent => :destroy, :class_name => "Mailboxer::Conversation::OptOut"
7
+ has_many :messages, :dependent => :destroy, :class_name => "Mailboxer::Message"
8
+ has_many :receipts, :through => :messages, :class_name => "Mailboxer::Receipt"
9
+
10
+ validates :subject, :presence => true,
11
+ :length => { :maximum => Mailboxer.subject_max_length }
12
+
13
+ before_validation :clean
14
+
15
+ scope :participant, lambda {|participant|
16
+ where('mailboxer_notifications.type'=> Mailboxer::Message.name).
17
+ order("mailboxer_conversations.updated_at DESC").
18
+ joins(:receipts).merge(Mailboxer::Receipt.recipient(participant)).uniq
19
+ }
20
+ scope :inbox, lambda {|participant|
21
+ participant(participant).merge(Mailboxer::Receipt.inbox.not_trash.not_deleted)
22
+ }
23
+ scope :sentbox, lambda {|participant|
24
+ participant(participant).merge(Mailboxer::Receipt.sentbox.not_trash.not_deleted)
25
+ }
26
+ scope :trash, lambda {|participant|
27
+ participant(participant).merge(Mailboxer::Receipt.trash)
28
+ }
29
+ scope :unread, lambda {|participant|
30
+ participant(participant).merge(Mailboxer::Receipt.is_unread)
31
+ }
32
+ scope :not_trash, lambda {|participant|
33
+ participant(participant).merge(Mailboxer::Receipt.not_trash)
34
+ }
35
+
36
+ #Mark the conversation as read for one of the participants
37
+ def mark_as_read(participant)
38
+ return unless participant
39
+ receipts_for(participant).mark_as_read
40
+ end
41
+
42
+ #Mark the conversation as unread for one of the participants
43
+ def mark_as_unread(participant)
44
+ return unless participant
45
+ receipts_for(participant).mark_as_unread
46
+ end
47
+
48
+ #Move the conversation to the trash for one of the participants
49
+ def move_to_trash(participant)
50
+ return unless participant
51
+ receipts_for(participant).move_to_trash
52
+ end
53
+
54
+ #Takes the conversation out of the trash for one of the participants
55
+ def untrash(participant)
56
+ return unless participant
57
+ receipts_for(participant).untrash
58
+ end
59
+
60
+ #Mark the conversation as deleted for one of the participants
61
+ def mark_as_deleted(participant)
62
+ return unless participant
63
+ deleted_receipts = receipts_for(participant).mark_as_deleted
64
+ if is_orphaned?
65
+ destroy
66
+ else
67
+ deleted_receipts
68
+ end
69
+ end
70
+
71
+ #Returns an array of participants
72
+ def recipients
73
+ return [] unless original_message
74
+ Array original_message.recipients
75
+ end
76
+
77
+ #Returns an array of participants
78
+ def participants
79
+ recipients
80
+ end
81
+
82
+ #Originator of the conversation.
83
+ def originator
84
+ @originator ||= self.original_message.sender
85
+ end
86
+
87
+ #First message of the conversation.
88
+ def original_message
89
+ @original_message ||= self.messages.order('created_at').first
90
+ end
91
+
92
+ #Sender of the last message.
93
+ def last_sender
94
+ @last_sender ||= self.last_message.sender
95
+ end
96
+
97
+ #Last message in the conversation.
98
+ def last_message
99
+ @last_message ||= self.messages.order('created_at DESC').first
100
+ end
101
+
102
+ #Returns the receipts of the conversation for one participants
103
+ def receipts_for(participant)
104
+ Mailboxer::Receipt.conversation(self).recipient(participant)
105
+ end
106
+
107
+ #Returns the number of messages of the conversation
108
+ def count_messages
109
+ Mailboxer::Message.conversation(self).count
110
+ end
111
+
112
+ #Returns true if the messageable is a participant of the conversation
113
+ def is_participant?(participant)
114
+ return false unless participant
115
+ receipts_for(participant).any?
116
+ end
117
+
118
+ #Adds a new participant to the conversation
119
+ def add_participant(participant)
120
+ messages = self.messages
121
+ messages.each do |message|
122
+ receipt = Mailboxer::Receipt.new
123
+ receipt.notification = message
124
+ receipt.is_read = false
125
+ receipt.receiver = participant
126
+ receipt.mailbox_type = 'inbox'
127
+ receipt.updated_at = message.updated_at
128
+ receipt.created_at = message.created_at
129
+ receipt.save
130
+ end
131
+ end
132
+
133
+ #Returns true if the participant has at least one trashed message of the conversation
134
+ def is_trashed?(participant)
135
+ return false unless participant
136
+ self.receipts_for(participant).trash.count != 0
137
+ end
138
+
139
+ #Returns true if the participant has deleted the conversation
140
+ def is_deleted?(participant)
141
+ return false unless participant
142
+ return self.receipts_for(participant).deleted.count == self.receipts_for(participant).count
143
+ end
144
+
145
+ #Returns true if both participants have deleted the conversation
146
+ def is_orphaned?
147
+ participants.reduce(true) do |is_orphaned, participant|
148
+ is_orphaned && is_deleted?(participant)
149
+ end
150
+ end
151
+
152
+ #Returns true if the participant has trashed all the messages of the conversation
153
+ def is_completely_trashed?(participant)
154
+ return false unless participant
155
+ receipts_for(participant).trash.count == receipts_for(participant).count
156
+ end
157
+
158
+ def is_read?(participant)
159
+ !is_unread?(participant)
160
+ end
161
+
162
+ #Returns true if the participant has at least one unread message of the conversation
163
+ def is_unread?(participant)
164
+ return false unless participant
165
+ receipts_for(participant).not_trash.is_unread.count != 0
166
+ end
167
+
168
+ # Creates a opt out object
169
+ # because by default all particpants are opt in
170
+ def opt_out(participant)
171
+ return unless has_subscriber?(participant)
172
+ opt_outs.create(:unsubscriber => participant)
173
+ end
174
+
175
+ # Destroys opt out object if any
176
+ # a participant outside of the discussion is, yet, not meant to optin
177
+ def opt_in(participant)
178
+ opt_outs.unsubscriber(participant).destroy_all
179
+ end
180
+
181
+ # tells if participant is opt in
182
+ def has_subscriber?(participant)
183
+ !opt_outs.unsubscriber(participant).any?
184
+ end
185
+
186
+ protected
187
+
188
+ #Use the default sanitize to clean the conversation subject
189
+ def clean
190
+ self.subject = sanitize subject
191
+ end
192
+
193
+ def sanitize(text)
194
+ ::Mailboxer::Cleaner.instance.sanitize(text)
195
+ end
196
+ end
@@ -0,0 +1,15 @@
1
+ module Mailboxer
2
+ class Conversation
3
+ class OptOut < ActiveRecord::Base
4
+ self.table_name = :mailboxer_conversation_opt_outs
5
+
6
+ belongs_to :conversation, :class_name => "Mailboxer::Conversation"
7
+ belongs_to :unsubscriber, :polymorphic => true
8
+
9
+ validates :unsubscriber, :presence => true
10
+
11
+ scope :unsubscriber, lambda { |entity| where(:unsubscriber_type => entity.class.base_class.name, :unsubscriber_id => entity.id) }
12
+
13
+ end
14
+ end
15
+ end
@@ -1,4 +1,4 @@
1
- class Mailbox
1
+ class Mailboxer::Mailbox
2
2
  attr_accessor :type
3
3
  attr_reader :messageable
4
4
 
@@ -10,7 +10,7 @@ class Mailbox
10
10
  #Returns the notifications for the messageable
11
11
  def notifications(options = {})
12
12
  #:type => nil is a hack not to give Messages as Notifications
13
- notifs = Notification.recipient(@messageable).where(:type => nil).order("notifications.created_at DESC")
13
+ notifs = Mailboxer::Notification.recipient(@messageable).where(:type => nil).order("mailboxer_notifications.created_at DESC")
14
14
  if (options[:read].present? and options[:read]==false) or (options[:unread].present? and options[:unread]==true)
15
15
  notifs = notifs.unread
16
16
  end
@@ -31,18 +31,18 @@ class Mailbox
31
31
  #* :unread=true
32
32
  #
33
33
  def conversations(options = {})
34
- conv = Conversation.participant(@messageable)
34
+ conv = Mailboxer::Conversation.participant(@messageable)
35
35
 
36
36
  if options[:mailbox_type].present?
37
37
  case options[:mailbox_type]
38
38
  when 'inbox'
39
- conv = Conversation.inbox(@messageable)
39
+ conv = Mailboxer::Conversation.inbox(@messageable)
40
40
  when 'sentbox'
41
- conv = Conversation.sentbox(@messageable)
41
+ conv = Mailboxer::Conversation.sentbox(@messageable)
42
42
  when 'trash'
43
- conv = Conversation.trash(@messageable)
43
+ conv = Mailboxer::Conversation.trash(@messageable)
44
44
  when 'not_trash'
45
- conv = Conversation.not_trash(@messageable)
45
+ conv = Mailboxer::Conversation.not_trash(@messageable)
46
46
  end
47
47
  end
48
48
 
@@ -79,7 +79,7 @@ class Mailbox
79
79
 
80
80
  #Returns all the receipts of messageable, from Messages and Notifications
81
81
  def receipts(options = {})
82
- Receipt.where(options).recipient(@messageable)
82
+ Mailboxer::Receipt.where(options).recipient(@messageable)
83
83
  end
84
84
 
85
85
  #Deletes all the messages in the trash of messageable. NOT IMPLEMENTED.
@@ -113,9 +113,9 @@ class Mailbox
113
113
  #If object isn't one of the above, a nil will be returned
114
114
  def receipts_for(object)
115
115
  case object
116
- when Message, Notification
116
+ when Mailboxer::Message, Mailboxer::Notification
117
117
  object.receipt_for(@messageable)
118
- when Conversation
118
+ when Mailboxer::Conversation
119
119
  object.receipts_for(@messageable)
120
120
  end
121
121
  end
@@ -0,0 +1,49 @@
1
+ class Mailboxer::Message < Mailboxer::Notification
2
+ attr_accessible :attachment if Mailboxer.protected_attributes?
3
+ self.table_name = :mailboxer_notifications
4
+
5
+ belongs_to :conversation, :class_name => "Mailboxer::Conversation", :validate => true, :autosave => true
6
+ validates_presence_of :sender
7
+
8
+ class_attribute :on_deliver_callback
9
+ protected :on_deliver_callback
10
+ scope :conversation, lambda { |conversation|
11
+ where(:conversation_id => conversation.id)
12
+ }
13
+
14
+ mount_uploader :attachment, AttachmentUploader
15
+
16
+ class << self
17
+ #Sets the on deliver callback method.
18
+ def on_deliver(callback_method)
19
+ self.on_deliver_callback = callback_method
20
+ end
21
+ end
22
+
23
+ #Delivers a Message. USE NOT RECOMENDED.
24
+ #Use Mailboxer::Models::Message.send_message instead.
25
+ def deliver(reply = false, should_clean = true)
26
+ self.clean if should_clean
27
+
28
+ #Receiver receipts
29
+ temp_receipts = recipients.map { |r| build_receipt(r, 'inbox') }
30
+
31
+ #Sender receipt
32
+ sender_receipt = build_receipt(sender, 'sentbox', true)
33
+
34
+ temp_receipts << sender_receipt
35
+
36
+ if temp_receipts.all?(&:valid?)
37
+ temp_receipts.each(&:save!) #Save receipts
38
+
39
+ Mailboxer::MailDispatcher.new(self, recipients).call
40
+
41
+ conversation.touch if reply
42
+
43
+ self.recipients = nil
44
+
45
+ on_deliver_callback.call(self) if on_deliver_callback
46
+ end
47
+ sender_receipt
48
+ end
49
+ end