mailboxer 0.0.15 → 0.0.16
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/.gitignore +1 -0
- data/Rakefile +13 -39
- data/app/models/conversation.rb +21 -19
- data/app/models/mailbox.rb +100 -55
- data/app/models/message.rb +5 -3
- data/app/models/notification.rb +12 -1
- data/app/models/receipt.rb +23 -7
- data/lib/mailboxer/models/messageable.rb +113 -88
- data/mailboxer.gemspec +1 -1
- data/spec/models/mailboxer_models_messageable_spec.rb +6 -6
- data/spec/models/notification_spec.rb +4 -8
- metadata +20 -20
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -1,53 +1,27 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'bundler'
|
3
2
|
begin
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
exit e.status_code
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'bundler'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
9
7
|
end
|
10
8
|
require 'rake'
|
11
|
-
|
12
|
-
require 'jeweler'
|
13
|
-
Jeweler::Tasks.new do |gem|
|
14
|
-
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
15
|
-
gem.name = "mailboxer"
|
16
|
-
gem.homepage = "http://github.com/roendal/mailboxer"
|
17
|
-
gem.license = "MIT"
|
18
|
-
gem.summary = %Q{Private messaging system for rails apps.}
|
19
|
-
gem.description = %Q{TODO: longer description of your gem}
|
20
|
-
gem.email = "ecasanovac@gmail.com"
|
21
|
-
gem.authors = ["Eduardo Casanova Cuesta"]
|
22
|
-
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
-
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
-
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
25
|
-
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
26
|
-
gem.add_development_dependency('rspec-rails', '~> 2.4.1')
|
27
|
-
gem.add_development_dependency('factory_girl', '~> 1.3.2')
|
28
|
-
gem.add_development_dependency('forgery', '~> 0.3.6')
|
29
|
-
end
|
30
|
-
Jeweler::RubygemsDotOrgTasks.new
|
9
|
+
require 'rake/rdoctask'
|
31
10
|
|
32
11
|
require 'rspec/core'
|
33
12
|
require 'rspec/core/rake_task'
|
34
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
35
|
-
spec.pattern = FileList['spec/**/*_spec.rb']
|
36
|
-
end
|
37
13
|
|
38
|
-
RSpec::Core::RakeTask.new(:
|
39
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
40
|
-
spec.rcov = true
|
41
|
-
end
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
42
15
|
|
43
16
|
task :default => :spec
|
44
17
|
|
45
|
-
require 'rake/rdoctask'
|
46
18
|
Rake::RDocTask.new do |rdoc|
|
47
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
-
|
49
19
|
rdoc.rdoc_dir = 'rdoc'
|
50
|
-
rdoc.title = "
|
51
|
-
rdoc.
|
52
|
-
rdoc.rdoc_files.include('
|
20
|
+
rdoc.title = "Mailboxer"
|
21
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
+
rdoc.rdoc_files.include('README.rdoc')
|
23
|
+
rdoc.rdoc_files.include('lib/**/*.rb', 'app/**/*.rb')
|
53
24
|
end
|
25
|
+
|
26
|
+
|
27
|
+
Bundler::GemHelper.install_tasks
|
data/app/models/conversation.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class Conversation < ActiveRecord::Base
|
2
|
-
attr_reader :originator, :original_message, :last_sender, :last_message
|
3
2
|
has_many :messages
|
4
3
|
has_many :receipts, :through => :messages
|
5
4
|
|
@@ -7,7 +6,6 @@ class Conversation < ActiveRecord::Base
|
|
7
6
|
|
8
7
|
before_validation :clean
|
9
8
|
|
10
|
-
# before_create :clean
|
11
9
|
scope :participant, lambda {|participant|
|
12
10
|
joins(:receipts).select('DISTINCT conversations.*').where('notifications.type'=> Message.to_s,'receipts.receiver_id' => participant.id,'receipts.receiver_type' => participant.class.to_s).order("conversations.updated_at DESC")
|
13
11
|
}
|
@@ -23,32 +21,32 @@ class Conversation < ActiveRecord::Base
|
|
23
21
|
scope :unread, lambda {|participant|
|
24
22
|
joins(:receipts).select('DISTINCT conversations.*').where('notifications.type'=> Message.to_s,'receipts.receiver_id' => participant.id,'receipts.receiver_type' => participant.class.to_s,'receipts.read' => false).order("conversations.updated_at DESC")
|
25
23
|
}
|
26
|
-
class << self
|
27
|
-
def total
|
28
|
-
count('DISTINCT conversations.id')
|
29
|
-
end
|
30
|
-
end
|
31
24
|
|
25
|
+
#Mark the conversation as read for one of the participants
|
32
26
|
def mark_as_read(participant)
|
33
27
|
return if participant.nil?
|
34
28
|
return self.receipts_for(participant).mark_as_read
|
35
29
|
end
|
36
30
|
|
31
|
+
#Mark the conversation as unread for one of the participants
|
37
32
|
def mark_as_unread(participant)
|
38
33
|
return if participant.nil?
|
39
34
|
return self.receipts_for(participant).mark_as_unread
|
40
35
|
end
|
41
36
|
|
37
|
+
#Move the conversation to the trash for one of the participants
|
42
38
|
def move_to_trash(participant)
|
43
39
|
return if participant.nil?
|
44
40
|
return self.receipts_for(participant).move_to_trash
|
45
41
|
end
|
46
42
|
|
43
|
+
#Takes the conversation out of the trash for one of the participants
|
47
44
|
def untrash(participant)
|
48
45
|
return if participant.nil?
|
49
46
|
return self.receipts_for(participant).untrash
|
50
47
|
end
|
51
48
|
|
49
|
+
#Returns an array of participants
|
52
50
|
def recipients
|
53
51
|
if self.last_message
|
54
52
|
recps = self.last_message.recipients
|
@@ -57,71 +55,75 @@ class Conversation < ActiveRecord::Base
|
|
57
55
|
end
|
58
56
|
return []
|
59
57
|
end
|
58
|
+
|
59
|
+
#Returns an array of participants
|
60
|
+
def participants
|
61
|
+
return recipients
|
62
|
+
end
|
60
63
|
|
61
|
-
#
|
64
|
+
#Originator of the conversation.
|
62
65
|
def originator
|
63
66
|
@orignator = self.original_message.sender if @originator.nil?
|
64
67
|
return @orignator
|
65
68
|
end
|
66
69
|
|
67
|
-
#
|
70
|
+
#First message of the conversation.
|
68
71
|
def original_message
|
69
72
|
@original_message = self.messages.find(:first, :order => 'created_at') if @original_message.nil?
|
70
73
|
return @original_message
|
71
74
|
end
|
72
75
|
|
73
|
-
#
|
76
|
+
#Sender of the last message.
|
74
77
|
def last_sender
|
75
78
|
@last_sender = self.last_message.sender if @last_sender.nil?
|
76
79
|
return @last_sender
|
77
80
|
end
|
78
81
|
|
79
|
-
#
|
82
|
+
#Last message in the conversation.
|
80
83
|
def last_message
|
81
84
|
@last_message = self.messages.find(:first, :order => 'created_at DESC') if @last_message.nil?
|
82
85
|
return @last_message
|
83
86
|
end
|
84
87
|
|
88
|
+
#Returns the receipts of the conversation for one participants
|
85
89
|
def receipts_for(participant)
|
86
90
|
return Receipt.conversation(self).receiver(participant)
|
87
91
|
end
|
88
92
|
|
93
|
+
#Returns the number of messages of the conversation
|
89
94
|
def count_messages
|
90
95
|
return Message.conversation(self).count
|
91
96
|
end
|
92
97
|
|
98
|
+
#Returns true if the messageable is a participant of the conversation
|
93
99
|
def is_participant?(participant)
|
94
100
|
return false if participant.nil?
|
95
101
|
return self.receipts_for(participant).count != 0
|
96
102
|
end
|
97
103
|
|
104
|
+
#Returns true if the participant has at least one trashed message of the conversation
|
98
105
|
def is_trashed?(participant)
|
99
106
|
return false if participant.nil?
|
100
107
|
return self.receipts_for(participant).trash.count!=0
|
101
108
|
end
|
102
109
|
|
110
|
+
#Returns true if the participant has trashed all the messages of the conversation
|
103
111
|
def is_completely_trashed?(participant)
|
104
112
|
return false if participant.nil?
|
105
113
|
return self.receipts_for(participant).trash.count==self.receipts(participant).count
|
106
114
|
end
|
107
115
|
|
116
|
+
#Returns true if the participant has at least one unread message of the conversation
|
108
117
|
def is_unread?(participant)
|
109
118
|
return false if participant.nil?
|
110
119
|
return self.receipts_for(participant).unread.count!=0
|
111
120
|
end
|
112
|
-
# protected
|
113
|
-
# #[empty method]
|
114
|
-
# #
|
115
|
-
# #this gets called before_create. Implement this if you wish to clean out illegal content such as scripts or anything that will break layout. This is left empty because what is considered illegal content varies.
|
116
|
-
# def clean
|
117
|
-
# return if subject.nil?
|
118
|
-
# #strip all illegal content here. (scripts, shit that will break layout, etc.)
|
119
|
-
# end
|
120
121
|
|
121
122
|
protected
|
122
123
|
|
123
124
|
include ActionView::Helpers::SanitizeHelper
|
124
125
|
|
126
|
+
#Use the default sanitize to clean the conversation subject
|
125
127
|
def clean
|
126
128
|
self.subject = sanitize self.subject
|
127
129
|
end
|
data/app/models/mailbox.rb
CHANGED
@@ -1,69 +1,114 @@
|
|
1
1
|
class Mailbox
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def notifications(options = {})
|
9
|
-
return Receipt.where(options).receiver(@messageable).notifications
|
10
|
-
end
|
2
|
+
attr_accessor :type
|
3
|
+
attr_reader :messageable
|
4
|
+
#Initializer method
|
5
|
+
def initialize(messageable)
|
6
|
+
@messageable = messageable
|
7
|
+
end
|
11
8
|
|
12
|
-
|
13
|
-
|
9
|
+
#Returns the notifications for the messageable
|
10
|
+
def notifications(options = {})
|
11
|
+
return Notification.receiver(@messageable)
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
#Returns the conversations for the messageable
|
15
|
+
#
|
16
|
+
#Options
|
17
|
+
#
|
18
|
+
#* :mailbox_type
|
19
|
+
# * "inbox"
|
20
|
+
# * "sentbox"
|
21
|
+
# * "trash"
|
22
|
+
#
|
23
|
+
#* :read=false
|
24
|
+
#* :unread=true
|
25
|
+
#
|
26
|
+
def conversations(options = {})
|
27
|
+
conv = Conversation.participant(@messageable)
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
if options[:mailbox_type].present?
|
30
|
+
case options[:mailbox_type]
|
31
|
+
when 'inbox'
|
32
|
+
conv = Conversation.inbox(@messageable)
|
33
|
+
when 'sentbox'
|
34
|
+
conv = Conversation.sentbox(@messageable)
|
35
|
+
when 'trash'
|
36
|
+
conv = Conversation.trash(@messageable)
|
37
|
+
end
|
38
|
+
end
|
32
39
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
40
|
+
if (options[:read].present? and options[:read]==false) or (options[:unread].present? and options[:unread]==true)
|
41
|
+
conv = conv.unread(@messageable)
|
42
|
+
end
|
37
43
|
|
38
|
-
|
39
|
-
|
40
|
-
return self.conversations(options)
|
41
|
-
end
|
44
|
+
return conv.uniq
|
45
|
+
end
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
#Returns the conversations in the inbox of messageable
|
48
|
+
#
|
49
|
+
#Same as conversations({:mailbox_type => 'inbox'})
|
50
|
+
def inbox(options={})
|
51
|
+
options = options.merge(:mailbox_type => 'inbox')
|
52
|
+
return self.conversations(options)
|
53
|
+
end
|
47
54
|
|
48
|
-
|
49
|
-
|
50
|
-
|
55
|
+
#Returns the conversations in the sentbox of messageable
|
56
|
+
#
|
57
|
+
#Same as conversations({:mailbox_type => 'sentbox'})
|
58
|
+
def sentbox(options={})
|
59
|
+
options = options.merge(:mailbox_type => 'sentbox')
|
60
|
+
return self.conversations(options)
|
61
|
+
end
|
51
62
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
63
|
+
#Returns the conversations in the trash of messageable
|
64
|
+
#
|
65
|
+
#Same as conversations({:mailbox_type => 'trash'})
|
66
|
+
def trash(options={})
|
67
|
+
options = options.merge(:mailbox_type => 'trash')
|
68
|
+
return self.conversations(options)
|
69
|
+
end
|
56
70
|
|
57
|
-
|
58
|
-
|
59
|
-
|
71
|
+
#Returns all the receipts of messageable, from Messages and Notifications
|
72
|
+
def receipts(options = {})
|
73
|
+
return Receipt.where(options).receiver(@messageable)
|
74
|
+
end
|
60
75
|
|
61
|
-
|
62
|
-
|
63
|
-
|
76
|
+
#Deletes all the messages in the trash of messageable. NOT IMPLEMENTED.
|
77
|
+
def empty_trash(options = {})
|
78
|
+
#TODO
|
79
|
+
return false
|
80
|
+
end
|
64
81
|
|
65
|
-
|
66
|
-
|
67
|
-
|
82
|
+
#Returns if messageable is a participant of conversation
|
83
|
+
def has_conversation?(conversation)
|
84
|
+
return conversation.is_participant?(@messageable)
|
85
|
+
end
|
86
|
+
|
87
|
+
#Returns true if messageable has at least one trashed message of the conversation
|
88
|
+
def is_trashed?(conversation)
|
89
|
+
return conversation.is_trashed?(@messageable)
|
90
|
+
end
|
91
|
+
|
92
|
+
#Returns true if messageable has trashed all the messages of the conversation
|
93
|
+
def is_completely_trashed?(conversation)
|
94
|
+
return conversation.is_completely_trashed?(@messageable)
|
95
|
+
end
|
96
|
+
|
97
|
+
#Returns the receipts of object for messageable
|
98
|
+
#
|
99
|
+
#Object can be:
|
100
|
+
#* A Message
|
101
|
+
#* A Notification
|
102
|
+
#* A Conversation
|
103
|
+
def receipts_for(object)
|
104
|
+
case object
|
105
|
+
when Message,Notification
|
106
|
+
return [object.receipt_for(@messageable)]
|
107
|
+
when Conversation
|
108
|
+
return object.receipts_for(@messageable)
|
109
|
+
else
|
110
|
+
return Array.new
|
111
|
+
end
|
112
|
+
end
|
68
113
|
|
69
114
|
end
|
data/app/models/message.rb
CHANGED
@@ -8,12 +8,16 @@ class Message < Notification
|
|
8
8
|
scope :conversation, lambda { |conversation|
|
9
9
|
where(:conversation_id => conversation.id)
|
10
10
|
}
|
11
|
+
|
11
12
|
class << self
|
13
|
+
#Sets the on deliver callback method.
|
12
14
|
def on_deliver(callback_method)
|
13
15
|
self.on_deliver_callback = callback_method
|
14
16
|
end
|
15
17
|
end
|
16
|
-
|
18
|
+
|
19
|
+
#Delivers a Message. USE NOT RECOMENDED.
|
20
|
+
#Use Mailboxer::Models::Message.send_message instead.
|
17
21
|
def deliver(reply = false, should_clean = true)
|
18
22
|
self.clean if should_clean
|
19
23
|
temp_receipts = Array.new
|
@@ -45,6 +49,4 @@ class Message < Notification
|
|
45
49
|
end
|
46
50
|
return sender_receipt
|
47
51
|
end
|
48
|
-
|
49
|
-
|
50
52
|
end
|
data/app/models/notification.rb
CHANGED
@@ -5,7 +5,12 @@ class Notification < ActiveRecord::Base
|
|
5
5
|
validates_presence_of :subject, :body
|
6
6
|
has_many :receipts
|
7
7
|
|
8
|
+
scope :receiver, lambda { |receiver|
|
9
|
+
joins(:receipts).where('receipts.receiver_id' => receiver.id,'receipts.receiver_type' => receiver.class.to_s)
|
10
|
+
}
|
11
|
+
|
8
12
|
class << self
|
13
|
+
#Sends a Notification to all the recipients
|
9
14
|
def notify_all(recipients,subject,body)
|
10
15
|
notification = Notification.new({:body => body, :subject => subject})
|
11
16
|
notification.recipients = recipients.is_a?(Array) ? recipients : [recipients]
|
@@ -14,6 +19,8 @@ class Notification < ActiveRecord::Base
|
|
14
19
|
end
|
15
20
|
end
|
16
21
|
|
22
|
+
#Delivers a Notification. USE NOT RECOMENDED.
|
23
|
+
#Use Mailboxer::Models::Message.notify and Notification.notify_all instead.
|
17
24
|
def deliver(should_clean = true)
|
18
25
|
self.clean if should_clean
|
19
26
|
temp_receipts = Array.new
|
@@ -32,7 +39,8 @@ class Notification < ActiveRecord::Base
|
|
32
39
|
end
|
33
40
|
return temp_receipts
|
34
41
|
end
|
35
|
-
|
42
|
+
|
43
|
+
#Returns the recipients of the Notification
|
36
44
|
def recipients
|
37
45
|
if @recipients.blank?
|
38
46
|
recipients_array = Array.new
|
@@ -44,10 +52,12 @@ class Notification < ActiveRecord::Base
|
|
44
52
|
return @recipients
|
45
53
|
end
|
46
54
|
|
55
|
+
#Returns the receipt for the participant
|
47
56
|
def receipt_for(participant)
|
48
57
|
return Receipt.notification(self).receiver(participant)
|
49
58
|
end
|
50
59
|
|
60
|
+
#Returns if the participant have read the Notification
|
51
61
|
def is_unread?(participant)
|
52
62
|
return false if participant.nil?
|
53
63
|
return self.receipt_for(participant).unread.count!=0
|
@@ -55,6 +65,7 @@ class Notification < ActiveRecord::Base
|
|
55
65
|
|
56
66
|
include ActionView::Helpers::SanitizeHelper
|
57
67
|
|
68
|
+
#Sanitizes the body and subject
|
58
69
|
def clean
|
59
70
|
unless self.subject.nil?
|
60
71
|
self.subject = sanitize self.subject
|
data/app/models/receipt.rb
CHANGED
@@ -10,8 +10,8 @@ class Receipt < ActiveRecord::Base
|
|
10
10
|
}
|
11
11
|
#Notifications Scope checks type to be nil, not Notification because of STI behaviour
|
12
12
|
#with the primary class (no type is saved)
|
13
|
-
scope :
|
14
|
-
scope :
|
13
|
+
scope :notifications_receipts, joins(:notification).where('notifications.type' => nil)
|
14
|
+
scope :messages_receipts, joins(:notification).where('notifications.type' => Message.to_s)
|
15
15
|
scope :notification, lambda { |notification|
|
16
16
|
where(:notification_id => notification.id)
|
17
17
|
}
|
@@ -27,66 +27,82 @@ class Receipt < ActiveRecord::Base
|
|
27
27
|
|
28
28
|
after_validation :remove_duplicate_errors
|
29
29
|
class << self
|
30
|
+
|
31
|
+
#Marks all the receipts from the relation as read
|
30
32
|
def mark_as_read(options={})
|
31
33
|
where(options).update_all(:read => true)
|
32
34
|
end
|
33
35
|
|
36
|
+
#Marks all the receipts from the relation as unread
|
34
37
|
def mark_as_unread(options={})
|
35
38
|
where(options).update_all(:read => false)
|
36
39
|
end
|
37
40
|
|
41
|
+
#Marks all the receipts from the relation as trashed
|
38
42
|
def move_to_trash(options={})
|
39
43
|
where(options).update_all(:trashed => true)
|
40
44
|
end
|
41
45
|
|
46
|
+
#Marks all the receipts from the relation as not trashed
|
42
47
|
def untrash(options={})
|
43
48
|
where(options).update_all(:trashed => false)
|
44
49
|
end
|
45
50
|
|
51
|
+
#Moves all the receipts from the relation to inbox
|
46
52
|
def move_to_inbox(options={})
|
47
53
|
where(options).update_all(:mailbox_type => :inbox, :trashed => false)
|
48
54
|
end
|
49
55
|
|
56
|
+
#Moves all the receipts from the relation to sentbox
|
50
57
|
def move_to_sentbox(options={})
|
51
58
|
where(options).update_all(:mailbox_type => :sentbox, :trashed => false)
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
62
|
+
#Marks the receipt as read
|
55
63
|
def mark_as_read
|
56
64
|
update_attributes(:read => true)
|
57
65
|
end
|
58
66
|
|
67
|
+
#Marks the receipt as unread
|
59
68
|
def mark_as_unread
|
60
69
|
update_attributes(:read => false)
|
61
70
|
end
|
62
71
|
|
72
|
+
#Marks the receipt as trashed
|
63
73
|
def move_to_trash
|
64
74
|
update_attributes(:trashed => true)
|
65
75
|
end
|
66
76
|
|
77
|
+
#Marks the receipt as not trashed
|
67
78
|
def untrash
|
68
79
|
update_attributes(:trashed => false)
|
69
80
|
end
|
70
81
|
|
82
|
+
#Moves the receipt to inbox
|
71
83
|
def move_to_inbox
|
72
84
|
update_attributes(:mailbox_type => :inbox, :trashed => false)
|
73
85
|
end
|
74
86
|
|
87
|
+
#Moves the receipt to sentbox
|
75
88
|
def move_to_sentbox
|
76
89
|
update_attributes(:mailbox_type => :sentbox, :trashed => false)
|
77
90
|
end
|
78
91
|
|
92
|
+
#Returns the conversation associated to the receipt if the notification is a Message
|
79
93
|
def conversation
|
80
|
-
return
|
81
|
-
return
|
94
|
+
return message.conversation if message.is_a? Message
|
95
|
+
return nil
|
82
96
|
end
|
83
97
|
|
84
98
|
protected
|
85
99
|
|
100
|
+
#Removes the duplicate error about not present subject from Conversation if it has been already
|
101
|
+
#raised by Message
|
86
102
|
def remove_duplicate_errors
|
87
|
-
if self.errors["
|
88
|
-
self.errors["
|
89
|
-
self.errors["
|
103
|
+
if self.errors["notification.conversation.subject"].present? and self.errors["notification.subject"].present?
|
104
|
+
self.errors["notification.conversation.subject"].each do |msg|
|
105
|
+
self.errors["notification.conversation.subject"].delete(msg)
|
90
106
|
end
|
91
107
|
end
|
92
108
|
end
|
@@ -1,102 +1,127 @@
|
|
1
1
|
module Mailboxer
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
module Models
|
3
|
+
module Messageable
|
4
|
+
def self.included(mod)
|
5
|
+
mod.extend(ClassMethods)
|
6
|
+
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
module ClassMethods
|
9
|
+
#Converts the model into messageable allowing it to interchange messages and
|
10
|
+
#receive notifications
|
11
|
+
def acts_as_messageable
|
12
|
+
has_many :messages
|
13
|
+
has_many :receipts, :order => 'created_at DESC', :dependent => :delete_all
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
include Mailboxer::Models::Messageable::InstanceMethods
|
16
|
+
end
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
################################### NOTIFICATIONS
|
28
|
-
|
29
|
-
def notify(subject,body)
|
19
|
+
module InstanceMethods
|
20
|
+
#Gets the mailbox of the messageable
|
21
|
+
def mailbox
|
22
|
+
@mailbox = Mailbox.new(self) if @mailbox.nil?
|
23
|
+
@mailbox.type = :all
|
24
|
+
return @mailbox
|
25
|
+
end
|
26
|
+
|
27
|
+
#Sends a notification to the messageable
|
28
|
+
def notify(subject,body)
|
30
29
|
notification = Notification.new({:body => body, :subject => subject})
|
31
30
|
notification.recipients = [self]
|
32
|
-
return notification.deliver
|
33
|
-
|
34
|
-
|
35
|
-
################################### MESSAGES
|
31
|
+
return notification.deliver
|
32
|
+
end
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
34
|
+
#Sends a messages, starting a new conversation, with the messageable
|
35
|
+
#as originator
|
36
|
+
def send_message(recipients, msg_body, subject)
|
37
|
+
convo = Conversation.new({:subject => subject})
|
38
|
+
message = Message.new({:sender => self, :conversation => convo, :body => msg_body, :subject => subject})
|
39
|
+
message.recipients = recipients.is_a?(Array) ? recipients : [recipients]
|
40
|
+
message.recipients = message.recipients.uniq
|
41
|
+
return message.deliver
|
42
|
+
end
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
44
|
+
#Basic reply method. USE NOT RECOMENDED.
|
45
|
+
#Use reply_to_sender, reply_to_all and reply_to_conversation instead.
|
46
|
+
def reply(conversation, recipients, reply_body, subject = nil)
|
47
|
+
subject = subject || "RE: #{conversation.subject}"
|
48
|
+
response = Message.new({:sender => self, :conversation => conversation, :body => reply_body, :subject => subject})
|
49
|
+
response.recipients = recipients.is_a?(Array) ? recipients : [recipients]
|
50
|
+
response.recipients = response.recipients.uniq
|
51
|
+
response.recipients.delete(self)
|
52
|
+
return response.deliver(true)
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
-
|
56
|
-
|
55
|
+
#Replies to the sender of the message in the conversation
|
56
|
+
def reply_to_sender(receipt, reply_body, subject = nil)
|
57
|
+
return reply(receipt.conversation, receipt.message.sender, reply_body, subject)
|
58
|
+
end
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
#Replies to all the recipients of the message in the conversation
|
61
|
+
def reply_to_all(receipt, reply_body, subject = nil)
|
62
|
+
return reply(receipt.conversation, receipt.message.recipients, reply_body, subject)
|
63
|
+
end
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
if(!recipients.include?(conversation.originator))
|
72
|
-
recipients << conversation.originator
|
73
|
-
end
|
74
|
-
end
|
75
|
-
return reply(conversation,recipients, reply_body, subject)
|
76
|
-
end
|
65
|
+
#Replies to all the recipients of the last message in the conversation and untrash any trashed message by messageable
|
66
|
+
#if should_untrash is set to true (this is so by default)
|
67
|
+
def reply_to_conversation(conversation, reply_body, subject = nil, should_untrash = true)
|
68
|
+
#move conversation to inbox if it is currently in the trash and should_untrash parameter is true.
|
69
|
+
if should_untrash && mailbox.is_trashed?(conversation)
|
70
|
+
mailbox.receipts_for(conversation).untrash
|
71
|
+
end
|
72
|
+
return reply(conversation, conversation.last_message.recipients, reply_body, subject)
|
73
|
+
end
|
77
74
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
75
|
+
#Mark the object as read for messageable.
|
76
|
+
#
|
77
|
+
#Object can be:
|
78
|
+
#* A Receipt
|
79
|
+
#* A Message
|
80
|
+
#* A Notification
|
81
|
+
#* A Conversation
|
82
|
+
#* An array with any of them
|
83
|
+
def read(obj)
|
84
|
+
case obj
|
85
|
+
when Receipt
|
86
|
+
return obj.mark_as_read if obj.receiver == self
|
87
|
+
when Message, Notification
|
88
|
+
receipt = obj.receipt_for(self)
|
89
|
+
return receipt.mark_as_read
|
90
|
+
when Conversation
|
91
|
+
receipts = obj.receipts_for(self)
|
92
|
+
return receipts.mark_as_read
|
93
|
+
when Array
|
94
|
+
obj.map{ |sub_obj| read(sub_obj) }
|
95
|
+
else
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
end
|
87
99
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
#Mark the object as unread for messageable.
|
101
|
+
#
|
102
|
+
#Object can be:
|
103
|
+
#* A Receipt
|
104
|
+
#* A Message
|
105
|
+
#* A Notification
|
106
|
+
#* A Conversation
|
107
|
+
#* An array with any of them
|
108
|
+
def unread(obj)
|
109
|
+
case obj
|
110
|
+
when Receipt
|
111
|
+
return obj.mark_as_unread if obj.receiver == self
|
112
|
+
when Message, Notification
|
113
|
+
receipt = obj.receipt_for(self)
|
114
|
+
return receipt.mark_as_unread
|
115
|
+
when Conversation
|
116
|
+
receipts = obj.receipts_for(self)
|
117
|
+
return receipts.mark_as_unread
|
118
|
+
when Array
|
119
|
+
obj.map{ |sub_obj| unread(sub_obj) }
|
120
|
+
else
|
121
|
+
return nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
102
127
|
end
|
data/mailboxer.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "mailboxer"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.16"
|
4
4
|
s.authors = ["Eduardo Casanova Cuesta"]
|
5
5
|
s.summary = "Messaging system for rails apps."
|
6
6
|
s.description = "A Rails engine that allows any model to act as messageable, permitting it interchange messages with any other messageable model." +
|
@@ -32,30 +32,30 @@ describe "Mailboxer::Models::Messageable through User" do
|
|
32
32
|
it "should be able to unread an owned mail (mark as unread)" do
|
33
33
|
@receipt = @entity1.send_message(@entity2,"Body","Subject")
|
34
34
|
@receipt.read.should==true
|
35
|
-
@entity1.
|
35
|
+
@entity1.unread(@receipt)
|
36
36
|
@receipt.read.should==false
|
37
37
|
end
|
38
38
|
|
39
39
|
it "should be able to read an owned mail (mark as read)" do
|
40
40
|
@receipt = @entity1.send_message(@entity2,"Body","Subject")
|
41
41
|
@receipt.read.should==true
|
42
|
-
@entity1.
|
43
|
-
@entity1.
|
42
|
+
@entity1.unread(@receipt)
|
43
|
+
@entity1.read(@receipt)
|
44
44
|
@receipt.read.should==true
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should be able to unread anot owned mail (mark as unread)" do
|
48
48
|
@receipt = @entity1.send_message(@entity2,"Body","Subject")
|
49
49
|
@receipt.read.should==true
|
50
|
-
@entity2.
|
50
|
+
@entity2.unread(@receipt) #Should not change
|
51
51
|
@receipt.read.should==true
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should be able to read a not owned mail (mark as read)" do
|
55
55
|
@receipt = @entity1.send_message(@entity2,"Body","Subject")
|
56
56
|
@receipt.read.should==true
|
57
|
-
@entity1.
|
58
|
-
@entity2.
|
57
|
+
@entity1.unread(@receipt) #From read to unread
|
58
|
+
@entity2.read(@receipt) #Should not change
|
59
59
|
@receipt.read.should==false
|
60
60
|
end
|
61
61
|
|
@@ -20,8 +20,7 @@ describe Message do
|
|
20
20
|
|
21
21
|
#Check getting NOTIFICATION receipts only
|
22
22
|
@entity1.mailbox.notifications.size.should==1
|
23
|
-
|
24
|
-
notification = receipt.notification
|
23
|
+
notification = @entity1.mailbox.notifications.first
|
25
24
|
notification.subject.should=="Subject"
|
26
25
|
notification.body.should=="Body"
|
27
26
|
end
|
@@ -48,18 +47,15 @@ describe Message do
|
|
48
47
|
|
49
48
|
#Check getting NOTIFICATION receipts only
|
50
49
|
@entity1.mailbox.notifications.size.should==1
|
51
|
-
|
52
|
-
notification = receipt.notification
|
50
|
+
notification = @entity1.mailbox.notifications.first
|
53
51
|
notification.subject.should=="Subject"
|
54
52
|
notification.body.should=="Body"
|
55
53
|
@entity2.mailbox.notifications.size.should==1
|
56
|
-
|
57
|
-
notification = receipt.notification
|
54
|
+
notification = @entity2.mailbox.notifications.first
|
58
55
|
notification.subject.should=="Subject"
|
59
56
|
notification.body.should=="Body"
|
60
57
|
@entity3.mailbox.notifications.size.should==1
|
61
|
-
|
62
|
-
notification = receipt.notification
|
58
|
+
notification = @entity3.mailbox.notifications.first
|
63
59
|
notification.subject.should=="Subject"
|
64
60
|
notification.body.should=="Body"
|
65
61
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mailboxer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 63
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 16
|
10
|
+
version: 0.0.16
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Eduardo Casanova Cuesta
|
@@ -15,12 +15,10 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-04-
|
18
|
+
date: 2011-04-17 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
name: foreigner
|
23
|
-
prerelease: false
|
24
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
23
|
none: false
|
26
24
|
requirements:
|
@@ -33,10 +31,10 @@ dependencies:
|
|
33
31
|
- 1
|
34
32
|
version: 0.9.1
|
35
33
|
type: :runtime
|
34
|
+
name: foreigner
|
35
|
+
prerelease: false
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
-
name: rails
|
39
|
-
prerelease: false
|
40
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
39
|
none: false
|
42
40
|
requirements:
|
@@ -49,10 +47,10 @@ dependencies:
|
|
49
47
|
- 5
|
50
48
|
version: 3.0.5
|
51
49
|
type: :development
|
50
|
+
name: rails
|
51
|
+
prerelease: false
|
52
52
|
version_requirements: *id002
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name: sqlite3-ruby
|
55
|
-
prerelease: false
|
56
54
|
requirement: &id003 !ruby/object:Gem::Requirement
|
57
55
|
none: false
|
58
56
|
requirements:
|
@@ -63,10 +61,10 @@ dependencies:
|
|
63
61
|
- 0
|
64
62
|
version: "0"
|
65
63
|
type: :development
|
64
|
+
name: sqlite3-ruby
|
65
|
+
prerelease: false
|
66
66
|
version_requirements: *id003
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
|
-
name: ruby-debug
|
69
|
-
prerelease: false
|
70
68
|
requirement: &id004 !ruby/object:Gem::Requirement
|
71
69
|
none: false
|
72
70
|
requirements:
|
@@ -79,10 +77,10 @@ dependencies:
|
|
79
77
|
- 3
|
80
78
|
version: 0.10.3
|
81
79
|
type: :development
|
80
|
+
name: ruby-debug
|
81
|
+
prerelease: false
|
82
82
|
version_requirements: *id004
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name: rspec-rails
|
85
|
-
prerelease: false
|
86
84
|
requirement: &id005 !ruby/object:Gem::Requirement
|
87
85
|
none: false
|
88
86
|
requirements:
|
@@ -95,10 +93,10 @@ dependencies:
|
|
95
93
|
- 0
|
96
94
|
version: 2.5.0
|
97
95
|
type: :development
|
96
|
+
name: rspec-rails
|
97
|
+
prerelease: false
|
98
98
|
version_requirements: *id005
|
99
99
|
- !ruby/object:Gem::Dependency
|
100
|
-
name: factory_girl
|
101
|
-
prerelease: false
|
102
100
|
requirement: &id006 !ruby/object:Gem::Requirement
|
103
101
|
none: false
|
104
102
|
requirements:
|
@@ -111,10 +109,10 @@ dependencies:
|
|
111
109
|
- 2
|
112
110
|
version: 1.3.2
|
113
111
|
type: :development
|
112
|
+
name: factory_girl
|
113
|
+
prerelease: false
|
114
114
|
version_requirements: *id006
|
115
115
|
- !ruby/object:Gem::Dependency
|
116
|
-
name: forgery
|
117
|
-
prerelease: false
|
118
116
|
requirement: &id007 !ruby/object:Gem::Requirement
|
119
117
|
none: false
|
120
118
|
requirements:
|
@@ -127,10 +125,10 @@ dependencies:
|
|
127
125
|
- 6
|
128
126
|
version: 0.3.6
|
129
127
|
type: :development
|
128
|
+
name: forgery
|
129
|
+
prerelease: false
|
130
130
|
version_requirements: *id007
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
|
-
name: capybara
|
133
|
-
prerelease: false
|
134
132
|
requirement: &id008 !ruby/object:Gem::Requirement
|
135
133
|
none: false
|
136
134
|
requirements:
|
@@ -143,6 +141,8 @@ dependencies:
|
|
143
141
|
- 9
|
144
142
|
version: 0.3.9
|
145
143
|
type: :development
|
144
|
+
name: capybara
|
145
|
+
prerelease: false
|
146
146
|
version_requirements: *id008
|
147
147
|
description: A Rails engine that allows any model to act as messageable, permitting it interchange messages with any other messageable model.It also supports sending system notifications to messageable models.
|
148
148
|
email: ecasanovac@gmail.com
|