Inbox messages in rails like facebook - ruby-on-rails

I am trying to create a personal inbox message system but couple question are on my mind. First let me explain my system.
Here is my table model
create_table "inboxmessages", :force => true do |t|
t.integer "receiver_id"
t.integer "sender_id"
t.integer "message_id"
t.boolean "isread"
t.boolean "isstarred"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "messages", :force => true do |t|
t.string "subject"
t.text "body"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
The relationship would be has follow
inboxmessages
belongs_to :message
belongs_to :user, :class_name => "User", :foreign_key => "sender_id"
belongs_to :user, :class_name => "User", :foreign_key => "receiver_id"
messages
has_many :inboxmessages
user
has_many :inboxmessages
The problem that i am having his i am uncertain on how to create a message which allows me multiple users. here the schema of the form i am trying to have
Message.subject
Inboxmessage.receiver # but have multiple different user
------------------------------------------------------------------------
Message.body
Inboxmessage.sender = current_user # hidden field
Here are the question that I have regarding building this model/controller/app
1 - Should my new form be in inboxmessages or messages?
2 - Should I use accept_nested_for or should I use nested resources
3 - Is my model/database is okay or not the best?
4 - Are my foreign_key relationship well define?
Thanks in advance!!

I would do something like this:
class User
has_many :mailboxes
has_many :messages, :through => :tags
has_many :tags
end
class Message
has_many :users, :through => :tags
has_many :mailboxes, :through => :tags
has_many :tags
end
class Mailbox
has_many :tags
has_many :messages, :through => :tags
has_many :users, :through => tags
end
class Tag
belongs_to :user
belongs_to :message
belongs_to :mailbox
# this class has "read?", "starred", etc.
end
This enables a message to appear in multiple mailboxes, for multiple users, and each user can have his/her own "read?", "starred", etc. You can limit the logic if you want to ensure that a user has only one copy of a message, i.e. the message is not in two or more mailboxes for the same user.
To improve your schema, read the Rails Guides, especially about associations like these:
belongs_to
has_one
has_many
has_many :through
inverse_of
Also look at the Rails gem acts-as-taggable-on
http://rubygems.org/gems/acts-as-taggable-on
One way to think of a message schema is "a message belongs to a mailbox, and a mailbox has many messages" (this is how Yahoo does it). Another way to think of a message is "a message has many tags, and a mailbox is simply a search by tag" (this is how Gmail does it).
By the way, the Ruby mail gem is excellent. You can use it for creating messages, and look at how it converts headers like from, to, cc, etc.
You asked good questions:
1 - Should my new form be in inboxmessages or messages?
My opinion is that you will get the most benefit if you have a model for the message that is like an email, have a model for a mailbox (or tag). To create a new message, the new form would typically be in ./messages/new.html.erb
2 - Should I use accept_nested_for or should I use nested resources?
Nested resources are fine; see the Rails guide here:
http://guides.rubyonrails.org/routing.html#nested-resources
Nested attributes are also fine; see the API here:
A good rule of thumb is to only nest one level down, because after that it gets more complicated than its worth.
3 - Is my model/database is okay or not the best?
Not the best. A real-world message will typically have a sender and receiver(s). But you're modeling the sender and receiver in the inboxmessages table. Better to model the message with has_many receivers, and use a separate model for the user's interaction with the message for example a table called "marks" with fields "starred", "isread", etc. A mark belongs to a message and belongs to a user. A message has_many marks, and a user has_many marks.
4 - Are my foreign_key relationship well define?
In general, yes. Just be aware that email is surprisingly hard to model. A good rule of thumb is to index your foreign keys. Also look at Rails associations "inverse_of" and use it; this will help with speed and memory use.

I would have my classes set up something like this.
class Inbox
has_many :messages
belongs_to :user
end
class User
has_many :messages
has_one :inbox
end
class Message
belongs_to :user
belongs_to :inbox
has_many recipients, class_name: "User", foreign_key: "recipient_id"
end

Related

Creating assoc record in HABTM through

Models:
cities.rb:
has_many :cities_users
has_many :users, :through => :cities_users
I have a HABTM (through) between cities and users. I want to view all cities associated with a user. Here's what I have and what the error is:
users.rb
has_many :cities_users
has_many :cities, :through => :cities_users
Controller:
#user = User.find(current_user.id)
#users_cities = #user.cities
I have written a migration that creates the JOIN table:
create_table "cities_users", :id => false, :force => true do |t|
t.integer "user_id"
t.integer "city_id"
end
This is my error (relating to second line of controller code):
uninitialized constant User::CitiesUser
I'm having similar problems creating a city that is associated with a user too.
Many thanks.
You should create new model if you want to use has_many :through association.
Please, consider using has_and_belongs_to_many for direct many-to-many connection with no intervening model.
For any details you can read http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many.

Polymorphic relationships and counter cache

So I have an app with a 2 different models, Comments and Replies, each of which you can either Agree or Disagree, so I have a polymorphic model called Emotion. Here is my code for these:
class Comment < ActiveRecord::Base
belongs_to :user
has_many :replies
has_many :emotions, :as => :emotionable
end
class Reply < ActiveRecord::Base
belongs_to :user
belongs_to :comment
has_many :emotions, :as => :emotionable
end
class Emotion < ActiveRecord::Base
belongs_to :emotionable, :polymorphic => :true
end
So this all works fine, but I'm going to need to add a counter cache for both Comment and Reply in order to get the size of the Agrees and Disagree for each Object. In all of the docs, it has examples for doing counter cache with normal polymorphic associations, not one with an extra condition in it. For reference, by schema for Emotion looks like this:
create_table "emotions", :force => true do |t|
t.integer "user_id"
t.string "emotion"
t.integer "emotionable_id"
t.string "emotionable_type"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
TL:DR - I need to be able to call #commet.agrees_count, #comment.disagrees_count, #reply.agrees_count and #reply.disagrees_count on a polymorphic association through a counter cache. So Comment and Reply will need 2 counter caches.
My suggestion would be to manually increment or decrement your counter cache in an after_commit callback so that you can test if the record was persisted and it updates outside of the transaction. This is because it will make your code more explicit, and less mysterious on how and when the cache is updated or invalidated.
Also manually updating the cache gives you extra flexibility if for example you wanted to give some users more authority when they agree or disagree with a comment (e.g. karma systems).
you may want to add the counter cache attribute to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end). http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/belongs_to
:polymorphic
Specify this association is a polymorphic association by passing true.
Note: If you’ve enabled the counter cache, then you may
want to add the counter cache attribute to the attr_readonly list in
the associated classes
(e.g. class Post; attr_readonly :comments_count; end).
It's none business of 'Polymorphic relationships and counter cache', it's about Multiple counter_cache in Rails model
By the way, for 'Polymorphic relationships and counter cache'
class Code < ActiveRecord::Base
has_many :notes, :as => :noteable
end
class Issue < ActiveRecord::Base
has_many :notes, :as => :noteable
end
class Note < ActiveRecord::Base
belongs_to :noteable, polymorphic: true, counter_cache: :noteable_count
end
in your table 'issues', you should have the column 'noteable_count', same as your table 'codes'

Making ActiveRecord join model attributes available in query results

Given User and Book models, I've created a join model, ViewedBook, that contains additional attributes. Below is the essence of what I've come up with:
create_table "users"
t.string "username"
end
create_table "books"
t.string "title"
t.integer "user_id"
t.date "authored_date"
end
create_table "books_viewings"
t.integer "book_id"
t.integer "user_id"
t.boolean "finished"
t.date "last_viewed_date"
end
class User
belongs_to :book_viewing
has_many :authored_books,
:class_name => "Book",
:source => :book
has_many :book_viewings
has_many :viewed_books :through => :book_viewings
:order => "book_viewings.last_viewed_date DESC"
has_many :finished_books :through => :book_viewings
:conditions => "book_viewings.finished = TRUE",
:order => "book_viewings.last_viewed_date DESC"
end
class Book
belongs_to :user
has_one :author, :class_name => "User"
end
class BookViewing
belongs_to :book
belongs_to :user
end
I think this works for most of the queries I need to create. However, I want each of the Book objects returned by user.viewed_books to include the finished attribute as well. Further, I will have additional queries like Book.best_sellers that I would also like to scope to a given user so that they also include the finished attribute.
From my limited exposure to ActiveRecord, it appears there's probably an elegant way to manage this and generate efficient queries, but I have yet to find an example that clarifies this scenario.
EDIT: to clarify, the other queries I'm looking for will not themselves be restricted to books that have been finished, but I need to have the finished attribute appended to each book if it exists for the given book and scoped user in book_viewings.
See my answer here https://stackoverflow.com/a/8874831/365865
Pretty much, no there isn't a specific way to do this, but you can pass a select option to your has_many association. In your case I'd do this:
has_many :books, :through => :book_viewings, :select => 'books.*, book_viewings.finished as finished'

Rails: Polymorphic User Table a good idea with AuthLogic?

I have a system where I need to login three user types: customers, companies, and vendors from one login form on the home page.
I have created one User table that works according to AuthLogic's example app at http://github.com/binarylogic/authlogic_example. I have added a field called "User Type" that currently contains either 'Customer', 'Company', or 'Vendor'.
Note: each user type contains many disparate fields so I'm not sure if Single Table Inheritance is the best way to go (would welcome corrections if this conclusion is invalid).
Is this a polymorphic association where each of the three types is 'tagged' with a User record? How should my models look so I have the right relationships between my User table and my user types Customer, Company, Vendor?
Thanks very much!
------UPDATE------
Current setup:
#User model
class User < ActiveRecord::Base
acts_as_authentic
belongs_to :authenticable, :polymorphic => true
end
#Customer model (Company and Vendor models are similar)
class Customer < ActiveRecord::Base
has_many :users, :as => :authenticable
acts_as_authentic
end
Is this a valid way to set them up?
This is what it sounds like you are trying to do:
You have users that can be in one of three groups. If that isn't quite right a little clarification would help.
Since AuthLogic only cares about it's special fields, making and using other fields in your user model is no biggy.
Each user might be made up something like this:
#The Authlogic Fields
t.string :username, :null => false
t.string :crypted_password, :null => false
t.string :password_salt, :null => false
t.string :persistence_token, :null => false
#Your field to identify user type
t.integer :user_type
#If your user is a customer, you would populate these fields
t.integer :customer_name
...
#If your user is a company, you would populate these fields
t.integer :company_name
...
#If your user is a vendor, you would populate these fields
t.integer :vendor_name
...
I'm not sure what you mean by "single table inheritance" but if you want to keep information about a vendor in a table separate from the users table (really no reason to unless you are REALLY concerned about performance) you can just link up your models like this:
class User < ActiveRecord::Base
has_many :customers, :companies, :vendors
end
class Customer < ActiveRecord::Base
belongs_to :user
end
class Company < ActiveRecord::Base
belongs_to :user
end
class Vendor < ActiveRecord::Base
belongs_to :user
end
In this case, since a user would have no associations to 2 of the 3 user types, you are pushed into using the has_many association which works nicely with the 0 association case. You would just have to do some checks in your code to make sure you don't double up.

how do I get foreign_key to work in this simple has_many, belongs_to relationship?

I'm pulling data from Harvest. Here are my two models and schema:
# schema
create_table "clients", :force => true do |t|
t.string "name"
t.integer "harvest_id"
end
create_table "projects", :force => true do |t|
t.string "name"
t.integer "client_id"
t.integer "harvest_id"
end
# Client.rb
has_many :projects, :foreign_key => 'client_id' # not needed, I know
# Project.rb
belongs_to :client, :foreign_key => 'harvest_id'
I'm trying to get the Projects to find their client by matching Project.client_id to a Client.harvest_id. Here is what I'm getting instead.
> Project.first.client_id
=> 187259
Project.first.client
=> nil
Client.find(187259).projects
=> []
Is this possible? Thanks!
Might not seem intuitive, but the foreign_key for both relations has to be the same. Let's say you decide to use harvest_id as the foreign key. It should be set up like this:
# Client.rb
has_many :projects, :foreign_key => 'harvest_id'
# Project.rb
belongs_to :client, :foreign_key => 'harvest_id'
You would also only have the harvest_id field in the projects table, since the client has_many projects.
Since your belongs_to relationship in Project model is on harvest_id, you have to ensure the harvest_id attribute is set in the project object.
> Project.first.harvest_id
=> ??
Your problem can occur if the harvest_id is not set.
Projects to find their client by matching Project.client_id to a Client.harvest_id
This does not seem to make sense as client and harvest are supposed to be different objects/records and you cannot match them.
It is like "Find apples where there are Orange-like seeds".
So we probably need more context.
You defined your relations the following way:
On the Project side you say "it is related to client via client_id", but on the Client you say that "it is related to Project via harvest_id"
There you have discrepancy.
So it seems you just have incorrect mappings defined.
Not sure how harvest_id is supposed to be used, so will make the assumption it is just association:
# Client.rb
has_many :projects
belongs_to :harvest
# Project.rb
belongs_to :client
belongs_to :harvest
# Harvest
has_one :client
has_one :project

Resources