mailboxer 0.10.3 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -5
- data/Appraisals +19 -0
- data/LICENSE.txt +1 -0
- data/README.md +45 -26
- data/Rakefile +4 -0
- data/app/mailers/message_mailer.rb +4 -4
- data/app/models/conversation.rb +114 -91
- data/app/models/mailbox.rb +16 -15
- data/app/models/message.rb +24 -22
- data/app/models/notification.rb +37 -24
- data/app/models/receipt.rb +37 -15
- data/gemfiles/rails3.0.gemfile +8 -0
- data/gemfiles/rails3.1.gemfile +8 -0
- data/gemfiles/rails3.2.gemfile +8 -0
- data/gemfiles/rails4.0.gemfile +8 -0
- data/lib/mailboxer.rb +13 -7
- data/lib/mailboxer/engine.rb +1 -2
- data/lib/mailboxer/models/messageable.rb +44 -22
- data/mailboxer.gemspec +9 -4
- data/spec/dummy/config/environments/test.rb +1 -3
- data/spec/mailers/message_mailer_spec.rb +69 -70
- data/spec/models/conversation_spec.rb +22 -0
- data/spec/models/mailbox_spec.rb +36 -0
- data/spec/models/mailboxer_models_messageable_spec.rb +8 -5
- data/spec/models/message_spec.rb +6 -0
- data/spec/models/receipt_spec.rb +13 -1
- metadata +25 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b22e110378d5da7e6e327c807f46a5f0475b2fa1
|
4
|
+
data.tar.gz: bc9242e076e3a10d2e3d9ca1106a42200ca7bc12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f67c3839eb84ce3153e7e9b1b50b012f80a1d17bdb56589e18279ba1043e3e4fc4f443c17b2ed9cff7a0e4bcbc9ebef10139bd63de67b520ac12f3fcd40bc05e
|
7
|
+
data.tar.gz: 277d0c3921688613e407b095665d7d2b2afe783294f1500d9ba2a423c83460b5854711520a9cfaddcec876b864924c65136edcd78dbdad4fbbd8286eb5d5405f
|
data/.travis.yml
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 1.8.7
|
4
|
-
- 1.9.2
|
5
3
|
- 1.9.3
|
6
|
-
-
|
7
|
-
- rbx-18mode
|
4
|
+
- 2.0.0
|
8
5
|
- rbx-19mode
|
9
|
-
- jruby-18mode
|
10
6
|
- jruby-19mode
|
7
|
+
gemfile:
|
8
|
+
#- gemfiles/rails3.0.gemfile
|
9
|
+
- gemfiles/rails3.1.gemfile
|
10
|
+
- gemfiles/rails3.2.gemfile
|
11
|
+
- gemfiles/rails4.0.gemfile
|
data/Appraisals
ADDED
@@ -0,0 +1,19 @@
|
|
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
|
+
appraise "rails3.2" do
|
12
|
+
gem "rails", "~> 3.2.6"
|
13
|
+
gem "mailboxer", :path => "../"
|
14
|
+
end
|
15
|
+
|
16
|
+
appraise "rails4.0" do
|
17
|
+
gem "rails", ">=4.0.0"
|
18
|
+
gem "mailboxer", :path => "../"
|
19
|
+
end
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
# Mailboxer 0.10.x [![](https://secure.travis-ci.org/ging/mailboxer.png)](http://travis-ci.org/ging/mailboxer) [![](https://gemnasium.com/ging/mailboxer.png)](https://gemnasium.com/ging/mailboxer)
|
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)
|
2
2
|
|
3
|
-
This project is based on the need
|
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
|
5
|
-
dependent on our development we are trying to implement a generic and
|
5
|
+
dependent on our development, we are trying to implement a generic and
|
6
6
|
potent messaging gem.
|
7
7
|
|
8
|
-
After looking for a good gem to use we
|
9
|
-
and functionality in them. Mailboxer tries to fill this
|
10
|
-
a
|
11
|
-
conversations with two or more
|
12
|
-
|
13
|
-
new comments”, “John Doe has updated
|
14
|
-
messageable model (if configured to do so). It has a complete
|
15
|
-
`Mailbox` object for each messageable with `inbox`, `sentbox` and
|
8
|
+
After looking for a good gem to use we noticed the lack of messaging gems
|
9
|
+
and functionality in them. Mailboxer tries to fill this void delivering
|
10
|
+
a powerful and flexible message system. It supports the use of
|
11
|
+
conversations with two or more participants, sending notifications to
|
12
|
+
recipients (intended to be used as system notifications “Your picture has
|
13
|
+
new comments”, “John Doe has updated his document”, etc.), and emailing the
|
14
|
+
messageable model (if configured to do so). It has a complete implementation
|
15
|
+
of a `Mailbox` object for each messageable with `inbox`, `sentbox` and
|
16
16
|
`trash`.
|
17
17
|
|
18
18
|
The gem is constantly growing and improving its functionality. As it is
|
19
19
|
used with our parallel development [ging / social\_stream](https://github.com/ging/social_stream) we are finding and fixing bugs continously. If you want
|
20
20
|
some functionality not supported yet or marked as TODO, you can create
|
21
|
-
an [issue](https://github.com/ging/mailboxer/issues) to ask for it. It will be
|
22
|
-
will know what you may find useful
|
21
|
+
an [issue](https://github.com/ging/mailboxer/issues) to ask for it. It will be great feedback for us, and we
|
22
|
+
will know what you may find useful in the gem.
|
23
23
|
|
24
24
|
Mailboxer was born from the great, but outdated, code from [lpsergi /
|
25
25
|
acts*as*messageable](https://github.com/psergi/acts_as_messageable).
|
26
26
|
|
27
|
-
We are now working to make
|
28
|
-
pages in order to make even easier to use the gem
|
27
|
+
We are now working to make exhaustive documentation and some wiki
|
28
|
+
pages in order to make it even easier to use the gem to its full potencial.
|
29
29
|
Please, give us some time if you find something missing or [ask for
|
30
30
|
it](https://github.com/ging/mailboxer/issues).
|
31
31
|
|
@@ -50,7 +50,7 @@ Run install script:
|
|
50
50
|
$ rails g mailboxer:install
|
51
51
|
```
|
52
52
|
|
53
|
-
And don't forget to migrate
|
53
|
+
And don't forget to migrate your database:
|
54
54
|
|
55
55
|
```sh
|
56
56
|
$ rake db:migrate
|
@@ -60,19 +60,19 @@ $ rake db:migrate
|
|
60
60
|
|
61
61
|
### Emails
|
62
62
|
|
63
|
-
We are now adding support for sending emails when a Notification or a Message is sent to one or more recipients. You should modify mailboxer initializer (/config/initializer/mailboxer.rb) to edit
|
63
|
+
We are now adding support for sending emails when a Notification or a Message is sent to one or more recipients. You should modify the mailboxer initializer (/config/initializer/mailboxer.rb) to edit these settings.
|
64
64
|
|
65
65
|
```ruby
|
66
66
|
Mailboxer.setup do |config|
|
67
|
-
#
|
67
|
+
#Enables or disables email sending for Notifications and Messages
|
68
68
|
config.uses_emails = true
|
69
|
-
#Configures the default from for the email sent for Messages and Notifications of Mailboxer
|
69
|
+
#Configures the default `from` address for the email sent for Messages and Notifications of Mailboxer
|
70
70
|
config.default_from = "no-reply@dit.upm.es"
|
71
71
|
...
|
72
72
|
end
|
73
73
|
```
|
74
74
|
|
75
|
-
You can change the way in which emails are delivered by specifying a custom implementation
|
75
|
+
You can change the way in which emails are delivered by specifying a custom implementation of notification and message mailers
|
76
76
|
|
77
77
|
```ruby
|
78
78
|
Mailboxer.setup do |config|
|
@@ -84,7 +84,7 @@ end
|
|
84
84
|
|
85
85
|
### User identities
|
86
86
|
|
87
|
-
Users must have an identity defined by a `name` and an `email`. We must
|
87
|
+
Users must have an identity defined by a `name` and an `email`. We must ensure that Messageable models have some specific methods. These methods are:
|
88
88
|
|
89
89
|
```ruby
|
90
90
|
#Returning any kind of identification you want for the model
|
@@ -126,7 +126,7 @@ config.name_method = :display_name
|
|
126
126
|
|
127
127
|
Will use the method `notification_email(object)` instead of `mailboxer_email(object)` and `display_name` for `name`.
|
128
128
|
|
129
|
-
Using default or custom method names, if your model doesn't implement them, Mailboxer will use dummy methods
|
129
|
+
Using default or custom method names, if your model doesn't implement them, Mailboxer will use dummy methods so as to notify you of missing methods rather than crashing.
|
130
130
|
|
131
131
|
## Preparing your models
|
132
132
|
|
@@ -138,7 +138,7 @@ class User < ActiveRecord::Base
|
|
138
138
|
end
|
139
139
|
```
|
140
140
|
|
141
|
-
You are not limited to User model. You can use Mailboxer in any other model and use it in serveral different models. If you have ducks and cylons in your application and you want to
|
141
|
+
You are not limited to the User model. You can use Mailboxer in any other model and use it in serveral different models. If you have ducks and cylons in your application and you want to exchange messages as if they were the same, just add `acts_as_messageable` to each one and you will be able to send duck-duck, duck-cylon, cylon-duck and cylon-cylon messages. Of course, you can extend it for as many classes as you need.
|
142
142
|
|
143
143
|
Example:
|
144
144
|
|
@@ -199,6 +199,25 @@ alfa.mailbox.sentbox
|
|
199
199
|
alfa.mailbox.trash
|
200
200
|
```
|
201
201
|
|
202
|
+
### How can I delete a message from trash?
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
#delete conversations forever for one receipt (still in database)
|
206
|
+
receipt.mark_as_deleted
|
207
|
+
|
208
|
+
#you can mark conversation as deleted for one participant
|
209
|
+
conversation.mark_as_deleted participant
|
210
|
+
|
211
|
+
#Mark the object as deleted for messageable
|
212
|
+
#Object can be:
|
213
|
+
#* A Receipt
|
214
|
+
#* A Conversation
|
215
|
+
#* A Notification
|
216
|
+
#* A Message
|
217
|
+
#* An array with any of them
|
218
|
+
alfa.mark_as_deleted conversation
|
219
|
+
```
|
220
|
+
|
202
221
|
### How can I paginate conversations?
|
203
222
|
|
204
223
|
You can use Kaminari to paginate the conversations as normal. Please, make sure you use the last version as mailboxer uses `select('DISTINCT conversations.*')` which was not respected before Kaminari 0.12.4 according to its changelog. Working corretly on Kaminari 0.13.0.
|
@@ -219,7 +238,7 @@ conversations = alfa.mailbox.trash.page(params[:page]).per(9)
|
|
219
238
|
|
220
239
|
### How can I read the messages of a conversation?
|
221
240
|
|
222
|
-
As a messageable, what you receive receipts
|
241
|
+
As a messageable, what you receive are receipts, which are associated with the message itself. You should retrieve your receipts for the conversation a get the message associated with them.
|
223
242
|
|
224
243
|
This is done this way because receipts save the information about the relation between messageable and the messages: is it read?, is it trashed?, etc.
|
225
244
|
|
@@ -239,7 +258,7 @@ receipts.each do |receipt|
|
|
239
258
|
end
|
240
259
|
```
|
241
260
|
|
242
|
-
You can take a look at the full documentation
|
261
|
+
You can take a look at the full documentation for Mailboxer in [rubydoc.info](http://rubydoc.info/gems/mailboxer/frames).
|
243
262
|
|
244
263
|
## Do you want to test Mailboxer?
|
245
264
|
|
@@ -247,7 +266,7 @@ Thanks to [Roman Kushnir (@RKushnir)](https://github.com/RKushnir/) you can test
|
|
247
266
|
|
248
267
|
## I need a GUI!
|
249
268
|
|
250
|
-
If you need a GUI you should take a look a
|
269
|
+
If you need a GUI you should take a look a these links:
|
251
270
|
|
252
271
|
* The [rails-messaging](https://github.com/frodefi/rails-messaging) project.
|
253
272
|
* The wiki page [GUI Example on a real application](https://github.com/ging/mailboxer/wiki/GUI-Example-on-a-real-application).
|
data/Rakefile
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
class MessageMailer < ActionMailer::Base
|
2
2
|
default :from => Mailboxer.default_from
|
3
|
-
#Sends and email for indicating a new message or a reply to a receiver.
|
3
|
+
#Sends and email for indicating a new message or a reply to a receiver.
|
4
4
|
#It calls new_message_email if notifing a new message and reply_message_email
|
5
5
|
#when indicating a reply to an already created conversation.
|
6
|
-
def send_email(message,receiver)
|
7
|
-
if message.conversation.messages.size > 1
|
6
|
+
def send_email(message, receiver)
|
7
|
+
if message.conversation.messages.size > 1
|
8
8
|
reply_message_email(message,receiver)
|
9
9
|
else
|
10
10
|
new_message_email(message,receiver)
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
include ActionView::Helpers::SanitizeHelper
|
15
15
|
|
16
16
|
#Sends an email for indicating a new message for the receiver
|
data/app/models/conversation.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
class Conversation < ActiveRecord::Base
|
2
|
-
attr_accessible :subject
|
2
|
+
attr_accessible :subject if Mailboxer.protected_attributes?
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
has_many :messages, :dependent => :destroy
|
5
|
+
has_many :receipts, :through => :messages
|
6
6
|
|
7
|
-
|
7
|
+
validates_presence_of :subject
|
8
8
|
|
9
|
-
|
9
|
+
before_validation :clean
|
10
10
|
|
11
11
|
scope :participant, lambda {|participant|
|
12
12
|
select('DISTINCT conversations.*').
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
where('notifications.type'=> Message.name).
|
14
|
+
order("conversations.updated_at DESC").
|
15
|
+
joins(:receipts).merge(Receipt.recipient(participant))
|
16
16
|
}
|
17
17
|
scope :inbox, lambda {|participant|
|
18
|
-
participant(participant).merge(Receipt.inbox.not_trash)
|
18
|
+
participant(participant).merge(Receipt.inbox.not_trash.not_deleted)
|
19
19
|
}
|
20
20
|
scope :sentbox, lambda {|participant|
|
21
|
-
participant(participant).merge(Receipt.sentbox.not_trash)
|
21
|
+
participant(participant).merge(Receipt.sentbox.not_trash.not_deleted)
|
22
22
|
}
|
23
23
|
scope :trash, lambda {|participant|
|
24
24
|
participant(participant).merge(Receipt.trash)
|
@@ -31,113 +31,136 @@ class Conversation < ActiveRecord::Base
|
|
31
31
|
}
|
32
32
|
|
33
33
|
#Mark the conversation as read for one of the participants
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
def mark_as_read(participant)
|
35
|
+
return if participant.nil?
|
36
|
+
self.receipts_for(participant).mark_as_read
|
37
|
+
end
|
38
38
|
|
39
39
|
#Mark the conversation as unread for one of the participants
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
def mark_as_unread(participant)
|
41
|
+
return if participant.nil?
|
42
|
+
self.receipts_for(participant).mark_as_unread
|
43
|
+
end
|
44
44
|
|
45
45
|
#Move the conversation to the trash for one of the participants
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
def move_to_trash(participant)
|
47
|
+
return if participant.nil?
|
48
|
+
self.receipts_for(participant).move_to_trash
|
49
|
+
end
|
50
50
|
|
51
51
|
#Takes the conversation out of the trash for one of the participants
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
def untrash(participant)
|
53
|
+
return if participant.nil?
|
54
|
+
self.receipts_for(participant).untrash
|
55
|
+
end
|
56
56
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
return recps
|
63
|
-
end
|
64
|
-
return []
|
65
|
-
end
|
57
|
+
#Mark the conversation as deleted for one of the participants
|
58
|
+
def mark_as_deleted(participant)
|
59
|
+
return if participant.nil?
|
60
|
+
return self.receipts_for(participant).mark_as_deleted
|
61
|
+
end
|
66
62
|
|
67
63
|
#Returns an array of participants
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#First message of the conversation.
|
79
|
-
def original_message
|
80
|
-
@original_message = self.messages.find(:first, :order => 'created_at') if @original_message.nil?
|
81
|
-
return @original_message
|
82
|
-
end
|
83
|
-
|
84
|
-
#Sender of the last message.
|
85
|
-
def last_sender
|
86
|
-
@last_sender = self.last_message.sender if @last_sender.nil?
|
87
|
-
return @last_sender
|
88
|
-
end
|
64
|
+
def recipients
|
65
|
+
if self.last_message
|
66
|
+
recps = self.last_message.recipients
|
67
|
+
recps = recps.is_a?(Array) ? recps : [recps]
|
68
|
+
recps
|
69
|
+
else
|
70
|
+
[]
|
71
|
+
end
|
72
|
+
end
|
89
73
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
74
|
+
#Returns an array of participants
|
75
|
+
def participants
|
76
|
+
recipients
|
77
|
+
end
|
78
|
+
|
79
|
+
#Originator of the conversation.
|
80
|
+
def originator
|
81
|
+
@originator ||= self.original_message.sender
|
82
|
+
end
|
83
|
+
|
84
|
+
#First message of the conversation.
|
85
|
+
def original_message
|
86
|
+
@original_message ||= self.messages.order('created_at').first
|
87
|
+
end
|
88
|
+
|
89
|
+
#Sender of the last message.
|
90
|
+
def last_sender
|
91
|
+
@last_sender ||= self.last_message.sender
|
92
|
+
end
|
93
|
+
|
94
|
+
#Last message in the conversation.
|
95
|
+
def last_message
|
96
|
+
@last_message ||= self.messages.order('created_at DESC').first
|
97
|
+
end
|
95
98
|
|
96
99
|
#Returns the receipts of the conversation for one participants
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
+
def receipts_for(participant)
|
101
|
+
Receipt.conversation(self).recipient(participant)
|
102
|
+
end
|
100
103
|
|
101
104
|
#Returns the number of messages of the conversation
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
+
def count_messages
|
106
|
+
Message.conversation(self).count
|
107
|
+
end
|
105
108
|
|
106
109
|
#Returns true if the messageable is a participant of the conversation
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
+
def is_participant?(participant)
|
111
|
+
return false if participant.nil?
|
112
|
+
self.receipts_for(participant).count != 0
|
113
|
+
end
|
114
|
+
|
115
|
+
#Adds a new participant to the conversation
|
116
|
+
def add_participant(participant)
|
117
|
+
messages = self.messages
|
118
|
+
messages.each do |message|
|
119
|
+
receipt = Receipt.new
|
120
|
+
receipt.notification = message
|
121
|
+
receipt.is_read = false
|
122
|
+
receipt.receiver = participant
|
123
|
+
receipt.mailbox_type = 'inbox'
|
124
|
+
receipt.updated_at = message.updated_at
|
125
|
+
receipt.created_at = message.created_at
|
126
|
+
receipt.save
|
127
|
+
end
|
110
128
|
end
|
111
129
|
|
112
130
|
#Returns true if the participant has at least one trashed message of the conversation
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
131
|
+
def is_trashed?(participant)
|
132
|
+
return false if participant.nil?
|
133
|
+
self.receipts_for(participant).trash.count != 0
|
134
|
+
end
|
135
|
+
|
136
|
+
#Returns true if the participant has deleted the conversation
|
137
|
+
def is_deleted?(participant)
|
138
|
+
return false if participant.nil?
|
139
|
+
return self.receipts_for(participant).trash.count==0
|
140
|
+
end
|
117
141
|
|
118
142
|
#Returns true if the participant has trashed all the messages of the conversation
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
143
|
+
def is_completely_trashed?(participant)
|
144
|
+
return false if participant.nil?
|
145
|
+
self.receipts_for(participant).trash.count == self.receipts_for(participant).count
|
146
|
+
end
|
123
147
|
|
124
|
-
|
125
|
-
|
126
|
-
|
148
|
+
def is_read?(participant)
|
149
|
+
!self.is_unread?(participant)
|
150
|
+
end
|
127
151
|
|
128
152
|
#Returns true if the participant has at least one unread message of the conversation
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
153
|
+
def is_unread?(participant)
|
154
|
+
return false if participant.nil?
|
155
|
+
self.receipts_for(participant).not_trash.is_unread.count != 0
|
156
|
+
end
|
133
157
|
|
134
|
-
|
158
|
+
protected
|
135
159
|
|
136
|
-
|
160
|
+
include ActionView::Helpers::SanitizeHelper
|
137
161
|
|
138
162
|
#Use the default sanitize to clean the conversation subject
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
163
|
+
def clean
|
164
|
+
self.subject = sanitize self.subject
|
165
|
+
end
|
143
166
|
end
|