Thursday, October 4, 2007

acts_as_messageable plugin released!

Problem:

Private messaging in Rails.

Solution:

acts_as_messageable

What it do?

acts_as_messageable is a plugin for enabling private messaging between users. Conversations, multiple recipients, trash, sentbox... its all in there.

to install:
> script/plugin install http://actsasmessageable.googlecode.com/svn/tags/acts_as_messageable-1.0.1 
> script/generate messageable 
> rake db:migrate
to use:
class User < ActiveRecord::Base 
  acts_as_messageable 
end
sending:
phil = User.find(3123) 
todd = User.find(4141) 
phil.send_message(todd, "whats up for tonight?", "hey guy")

sends a mail to todd's inbox and puts the sent message in phil's sentbox.

retrieving:
todd.mailbox[:inbox].unread_mail

returns an array of all unread mail messages in todd's inbox.

replying:
mail = todd.mailbox[:inbox].unread_mail[0] 
todd.reply_to_sender(mail, "not sure, probably having a few dozen cocktails.")

replys to the sender of the mail message. (Messages can be sent to multiple users, and are conversation based. See the RDoc.)

moving / deleting:
convo = mail.conversation
todd.mailbox.move_to(:trash, :conversation => convo)

moves all mail messages to the 'trash' that are part of the given conversation. There are 3 default mailboxes for each user (:inbox, :sentbox, :trash), although any name can be passed to this method if you wanted to implement folders.

bonus:
todd.mailbox[:inbox].latest_mail

returns the last message received for each conversation you are involved in. Pretty sweet for an inbox view.

Check the RDoc.

23 comments:

josh said...

Phil,

Are you currently using this in a project, and if so how large? If I may ask.

Phil said...

yes I am currently using this on bandsintown.com.

Peter said...

Awesome! Thanks Phil! This is a really nice little plugin and fairly well documented. I'm trying to implement a little message system with it now and I think I'm missing something (like confusing Messages and Mail) because I can't figure out how to get individual Message objects directly from the mailbox...

I'd like to do something like this:

@messages = current_user.mailbox[:inbox].messages

but, instead I'm using the very cludgy:

current_user.mailbox[:inbox].mail.each do |mail|
@messages << Message.find(mail.message_id)
end

Can someone post an example of the best way to get an individual message or array of messages to send to an inbox or message view?

Thanks!
Peter

ps. Bandsintown.com is pretty sweet...

Phil said...

A mail is just basically a wrapper for a message which holds individual info on the message (read, trashed, mailbox). So if someone sent a message to 20 recipients there would only be 1 message created and 20 mails.

It was confusing for me building it too but I believe this is the correct approach.

I think what you are actually after are the mail objects. If you go directly to the messages then you can not see the read state.

so you could just render the mail array as a collection and access the info with:

mail.message.message
mail.message.subject
mail.read

I also use CachedModel on my messages which really speeds this up.

latest_mail() is also really nice for an inbox view if you are going with the conversation approach... similar to how gmail inbox works, only showing you the latest message for each convo you are involved in.

Im on no sleep so I hope im making sense... let me know if you have any other questions.


glad you like bandsintown.

Peter said...

Aahhh, I see... yes, that's exactly what I wanted. I'll check out CachedModel next and maybe dive into the folder & conversation features later... Thanks again!

Phil said...

no problem... glad to see youre using it.

xxx said...
This post has been removed by the author.
xxx said...

Could you please post some examples how to write the controller and views? The stuff posted above doesn't really say anything. Nor does the Rdoc...

Phil said...

I will post some more examples later on tonight.

xxx said...

I don't want to push you or anything... ;) Put you got some examples up your sleeve? Else I move on to another plugin that I know works out-of-the-box.

Phil said...

It does work out of the box i just chose not to include controller/views because they tend to be very custom... anyway here are some examples:

example url:
-------------
#user/:id/mail/:type
/user/3/mail/inbox

controller:
-------------
def mail()
@user = User.find(params[:id])
@mail = @user.mailbox[params[:type]].latest_mail
end

view:
------------
<%= render(:partial => '/shared/mail', :collection => @mail %>

in that partial you could just access the mail info through:

mail.message.message
mail.message.subject
mail.read


Just a simple example but that should get you going.

-Phil

Aroop said...

looks cool. I am going to use it right way in my project. Will post the experience

Aroop said...

I have posted an issue that was occurring in migration

http://code.google.com/p/actsasmessageable/issues/detail?id=1

Also can you plz post the routes that you are using in your project esp the nested one.

Thanks,
Aroop

Phil said...

@aroop

Thanks for reporting that bug, I have applied a fix and the new version is available here:

http://actsasmessageable.googlecode.com/svn/tags/acts_as_messageable-1.0.1

also for the routes im using something like this:

/user/:id/mail/:mailbox_type
(for inbox, sentbox, trash, etc.)

/user/:id/convo/:convo_id
(for reading conversations)

Phil said...

here is the new version link if you can't see it in my last comment:

new version

joy said...

Phil,

Your site (BandsInTown) absolutely rocks! Congrats!

Is the acts_as_messageable plugin already Rails 2.0 compatible?

Cheers, Sazima

Phil said...

I am not sure, I havent made the switch yet. If you find out please let me know.

Saza said...

Phil,

Apparently "acts_as_messageable" is Rails 2.0.2 compatible!

But unfortunately I'm very confused on how to use it...

Any chance you could post or send me an example application (basically routes, controllers and views)?

Cheers, Sazima

Celular said...

Hello. This post is likeable, and your blog is very interesting, congratulations :-). I will add in my blogroll =). If possible gives a last there on my blog, it is about the Celular, I hope you enjoy. The address is http://telefone-celular-brasil.blogspot.com. A hug.

dmarc said...

Phil,

This is a wicked plugin, great job!!! I am however, trying to implement this plugin along with the Goldberg authentication plugin, and running into problems. The Goldberg plugin has a user table, with a user.rb which includes a Goldberg module. I basically tried to change reference class_names from User to Goldberg::User and encounter an error stating 'undefined method 'mail' for Goldberg::User:class..I also tried to create a user.rb with the following inheritance
User < Goldberg::User and get the same error..but stating 'undefined method 'mail' for User:Class?
Is this due to the line in the mail.rb? self.table_name = "mail", any clues on how to integrate both.

Thanks, in advance,
Dmarc

Mustafa Ekim said...

Phil, it is a good plugin. good work.

I wonder if it is possible that you share your views also. I know views could vary very much but you know, it is messaging, it will be almost same everywhere, if not it will present a good example for the people.

simply the views in bandsintown.com will do..

thanks!

MB said...
This post has been removed by the author.
mickey said...

I am trying to get this plugin to work within a rails 2.0.2 application but i run into a weird problem.

When trying to access the mailbox and its mails from the rails console everything works as expected:

u = User.find(:first)
mb = u.mailbox[:inbox]
mb.mail.each { |mail| puts mail.message.subject }

But when used within a controller/view in a similar way only the first call works.

Every call after throws

undefined method `message' for #< Mail:0x6878614 >

Any Ideas whats going wrong?