Topic: Rails Messaging (INBOX) system

Hi All..

    I need to implement messaging system in one of my rails
applications(To say simply it must be similar to orkut messaging
system). Please any one of you help me with a guidance to the way of approach. Could any one suggest me with good stuff or links which may guide me to implement the mentioned
feature. I greatly appreciate any replies

Thank you

Re: Rails Messaging (INBOX) system

The design varies depend on your requirements: for instance, can a message be addressed to multiple recipients? Can it have multiple senders? Can users rearrange messages into "folders" within their own inbox? Is "unread" state necessary?

In the most basic version, you want two models: a Message and a User. The message belongs_to an author (of class User) and also to a recipient (of class User). It has, presumably, a subject, body and created_at timestamp.

# app/models/message.rb
class Message < ActiveRecord::Base
  belongs_to :author, :class_name => "User"
  belongs_to :recipient, :class_name => "User"
end

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :sent_messages, :class_name => "Message", :foreign_key => "author_id"
  has_many :received_messages, :class_name => "Message", :foreign_key => "recipient_id"
  # presumably this class would be more complex to handle authentication, etc.
end

# db/migrate/001_add_messages.rb
class AddMessages < ActiveRecord::Migration
  def self.up
    create_table "messages" do |t|
      t.integer :author_id, :recipient_id
      t.string :subject
      t.text   :body
      t.timestamps!
    end
  end
 
  def self.down
    drop_table "messages"
  end
end

# db/migrate/002_add_users.rb
class AddUsers < ActiveRecord::Migration
  def self.up
    create_table "users" do |t|
      t.string :login
      # (whatever else you need for authentication, etc)
    end
  end
 
  def self.down
    drop_table "users"
  end
end


But you could certainly make it more complex. To implement all of the "bonus" features above, you'll need something considerably more complex. Something like this:

# app/models/message.rb
class Message < ActiveRecord::Base
  belongs_to  :author, :class_name => "User"
  has_many    :message_copies
  has_many    :recipients, :through => :message_copies
  before_save :prepare_copies
  attr_accessor  :to # array of people to send to
  attr_protected :author_id

  def prepare_copies
    return if to.nil?
   
    to.each do |recipient|
      message_copies.build(:recipient_id => recipient, :folder_id => recipient.inbox)
    end
  end
end

# app/models/message_copy.rb
# represents an individual recipient's copy of a message
class MessageCopy < ActiveRecord::Base
  belongs_to :message
  belongs_to :recipient, :class_name => "User"
  belongs_to :folder
  delegate   :author, :created_at, :subject, :body, :recipients, :to => :message
end

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :sent_messages, :class_name => "Message", :foreign_key => "author_id"
  has_many :received_messages, :class_name => "MessageCopy", :foreign_key => "recipient_id"
  has_many :folders

  def inbox; folders.find_by_name("Inbox"); end

  # presumably this class would be more complex to handle authentication, etc.
end

# app/models/folder.rb
class Folder < ActiveRecord::Base
  acts_as_tree
  belongs_to :user
  has_many :messages, :class_name => "MessageCopy"
end

# db/migrate/001_add_messages.rb
class AddMessages < ActiveRecord::Migration
  def self.up
    create_table "messages" do |t|
      t.integer :author_id
      t.string :subject
      t.text   :body
      t.timestamps!
    end
  end
 
  def self.down
    drop_table "messages"
  end
end

# db/migrate/002_add_users.rb
class AddUsers < ActiveRecord::Migration
  def self.up
    create_table "users" do |t|
      t.string :login
      # (whatever else you need for authentication, etc)
    end
  end
 
  def self.down
    drop_table "users"
  end
end

# db/migrate/003_add_message_copies.rb
class AddMessageCopies < ActiveRecord::Migration
  def self.up
    create_table "message_copies" do |t|
      t.integer :recipient_id, :folder_id, :message_id
    end
  end
 
  def self.down
    drop_table "message_copies"
  end
end


# db/migrate/004_add_message_copies.rb
class AddFolders < ActiveRecord::Migration
  def self.up
    create_table "folders" do |t|
      t.integer :user_id, :parent_id
      t.string  :name
    end
  end
 
  def self.down
    drop_table "folders"
  end
end


You could further extend this to allow To/Cc/Bcc, etc. It's more flexible than the first, but also more complex. It does, however, allow the usual pleasantries (deleting a message without deleting it for everyone else, for instance). I hope that gets you started.

P.S: This will allow you to create messages using mass-assignment, as I'm sure you're used to.

Last edited by manitoba98 (2008-02-22 12:32:14)

Re: Rails Messaging (INBOX) system

Hi mani,

   First of all i like to thank you a lot for the response. Your reply helps a lot and i am thinking of implementing a basic inbox messaging system with sending , composing reading deleting functionalities. I believe you might have saw the messaging stuff, given in rails recipes book. With users table, inboxes table, messages table maintained separately.I appreciate you if you guide me with the entire stuff to do so..

I once again thank you a lot for your time and interest up on the newbies.

Thanks..

Re: Rails Messaging (INBOX) system

Take a look at this plugin: http://agilewebdevelopment.com/plugins/ … ssageable, it might help you.

Re: Rails Messaging (INBOX) system

Sure I can make a tutorial. It'll be up when it's done (i.e: not yet). tongue

Re: Rails Messaging (INBOX) system

Okay, I promised you a tutorial. It's up. smile I've still got more stuff I intend to add, but it should be enough to get you started.

http://www.novawave.net/public/rails_me … orial.html

Re: Rails Messaging (INBOX) system

Yeah it is a cool stuff and you did it well. Will you be blogging? If so could you please give me the address. Thanks for your reply and posting

Re: Rails Messaging (INBOX) system

The svn link for installing the will_paginate plugin is not giving me any thing.... I mean its not installing any thing. What do you suggest?

Re: Rails Messaging (INBOX) system

Are you on Windows, by chance? Windows doesn't include an SVN client by default (which is used to install will_paginate from the official source). A quick searchh here on Rails Forum turns this post about a week ago with a link to a frozen copy of will_paginate: just put the will_paginate folder in vendor/plugins.

http://railsforum.com/viewtopic.php?pid=54199

Re: Rails Messaging (INBOX) system

Yeah got you and got it too. Tons of thanks for your help........

Re: Rails Messaging (INBOX) system

Hi..

My Messaging system is working fine with your. Your postings helped me a lot.   
I am trying to implement pagination stuff, which does not require AJAX, as the requirement was specific that the back button must be working but with ajax pagination i believe that button does'nt work. Is there some useful stuff which allows me to paginate with out ajax approach.

Thanks a lot well in advance

Re: Rails Messaging (INBOX) system

Hello
chandrahasanreddy

I have a problem in using Action Mailer there is no error but it does not send a mail it just display the blank page.
the code added in app/views/products/show.html.erb

<p>Interested? <%= link_to_function('Contact the seller', "Element.show('contact_seller')") %></p>
<div id="contact_seller">
  <%= form_remote_tag(:url => {:action => 'contact', :id => @product.id }, :html => {:id => "contact_form"}) -%>
  Your e-mail: <%= text_field "contact", "email" -%><br />
  Message: <br />
  <%= text_area "contact", "message", {:rows => 10} -%><br />
  <%= submit_tag 'Contact seller' -%>
  <%=  -%>
</div>

code in app/models/product_mailer.rb

class ProductMailer < ActionMailer::Base
def contact(product, buyer, sent_at = Time.now)
  @subject    = 'Railslist: A potential buyer has contacted you'
@recipients = abc@proj.com
  @from       = 'no-reply@yourdomain.com'
  @sent_on    = sent_at
  @body["title"] = product.productname
  @body["message"] = buyer[ :message]
end
end

in app/controllers/product_controller.rb
def contact
  @product = Product.find(params[:id])
  ProductMailer.deliver_contact(@product,params[:contact])
  return if request.xhr?
  render :nothing => true
end

in app/views/product/contact.rjs
page.alert 'Message successfully sent to seller.'
page.form.reset 'contact_form'
page.hide 'contact_seller'




I have exactly followed the same steps as mentioned in rails solution book by Justin Williams
These are the code written please guide me i have very little time left to submit my project hope you could solve the problem


Thanks
Anup

Re: Rails Messaging (INBOX) system

Most pagination does not require AJAX. will_paginate, which I used in that tutorial, doesn't require AJAX, and the back button does in fact work. The same is true of classic_pagination and most pagination plugins.

Re: Rails Messaging (INBOX) system

Hi,

I'am trying to implement the basic version of the messaging system you posted first. But I get the following error:

"Unknown key(s): class"

Do you know what the problem is?

thanks.

Re: Rails Messaging (INBOX) system

Could you paste the full error, including line number (and a snippet of code surrounding that line)?

Re: Rails Messaging (INBOX) system

Hi, Here is the full error and my User.rb file.

Processing SigninController#signin (for 127.0.0.1 at 2008-02-22 10:16:38) [GET]
  Session ID: 3d24ae8e00052c04bece5b08cda33180
  Parameters: {"action"=>"signin", "controller"=>"signin"}
Rendering  within layouts/application
Rendering signin/signin
Completed in 0.03676 (27 reqs/sec) | Rendering: 0.03595 (97%) | DB: 0.00000 (0%) | 200 OK [http://localhost/]


Processing SigninController#signin (for 127.0.0.1 at 2008-02-22 10:16:42) [POST]
  Session ID: 3d24ae8e00052c04bece5b08cda33180
  Parameters: {"commit"=>"Sign in", "username"=>"shipper", "action"=>"signin", "controller"=>"signin", "password"=>"secret"}


ArgumentError (Unknown key(s): class):
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/core_ext/hash/keys.rb:48:in `assert_valid_keys'
    /Users/fedegl/rutanet/vendor/rails/activerecord/lib/active_record/associations.rb:1109:in `create_has_many_reflection'
    /Users/fedegl/rutanet/vendor/rails/activerecord/lib/active_record/associations.rb:564:in `has_many'
    /Users/fedegl/rutanet/app/models/user.rb:30
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:203:in `load_without_new_constant_marking'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:203:in `load_file'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:342:in `new_constants_in'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:202:in `load_file'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:94:in `require_or_load'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:248:in `load_missing_constant'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:452:in `const_missing'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:464:in `const_missing'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:260:in `load_missing_constant'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:468:in `const_missing'
    /Users/fedegl/rutanet/app/controllers/signin_controller.rb:22:in `signin'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/base.rb:1101:in `send'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/base.rb:1101:in `perform_action_without_filters'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/filters.rb:696:in `call_filters'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/filters.rb:688:in `perform_action_without_benchmark'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
    /usr/local/lib/ruby/1.8/benchmark.rb:293:in `measure'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/benchmarking.rb:66:in `perform_action_without_rescue'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/rescue.rb:83:in `perform_action'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/base.rb:435:in `send'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/base.rb:435:in `process_without_filters'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/filters.rb:684:in `process_without_session_management_support'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/session_management.rb:114:in `process'
    /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/base.rb:334:in `process'
    /Users/fedegl/rutanet/vendor/rails/railties/lib/dispatcher.rb:41:in `dispatch'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:78:in `process'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `synchronize'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `process'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:618:in `process_client'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `each'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `process_client'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `initialize'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `new'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `initialize'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `new'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `run'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:271:in `run'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `each'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `run'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:127:in `run'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run'
    /usr/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:488:in `load'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:488:in `load'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:342:in `new_constants_in'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:488:in `load'
    /Users/fedegl/rutanet/vendor/rails/railties/lib/commands/servers/mongrel.rb:60
    /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
    /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:495:in `require'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:342:in `new_constants_in'
    /Users/fedegl/rutanet/vendor/rails/activesupport/lib/active_support/dependencies.rb:495:in `require'
    /Users/fedegl/rutanet/vendor/rails/railties/lib/commands/server.rb:39
    script/server:3:in `require'
    script/server:3


Rendering /Users/fedegl/rutanet/vendor/rails/actionpack/lib/action_controller/templates/rescues/layout.rhtml (500 Internal Error)

##### User.rb

require 'digest/sha2'
class User < ActiveRecord::Base
 
  has_and_belongs_to_many   :roles
    has_many                  :freights
    has_many                  :tickets
    has_many                  :search_logs
    has_many                    :frequents
    has_many                  :feedbacks
   
  has_many                  :sent_messages,     :class => "Message", :foreign_key => "author_id"
  has_many                  :received_messages, :class => "Message", :foreign_key => "recipient_id"
   
    belongs_to                :company


Thanks.

Re: Rails Messaging (INBOX) system

My sincerest apologies. That should have been ":class_name", not ":class". I'll edit my post above.

Re: Rails Messaging (INBOX) system

No worries, and thanks for the correction.

Re: Rails Messaging (INBOX) system

Great tutorial thank you very much. However, I had a quick question about messages that have been marked as deleted still remain as new messages.

Mainly I was trying to find out if I goofed somewhere along the line or if this was something that wasn't reviewed in the tutorial.

Thanks again.

Re: Rails Messaging (INBOX) system

I figured it out I just sort of wrapped the view like

for message in @messages
if message.deleted

kind of deal. thanks for the great tutorial