Rails private message system - ruby-on-rails

Hey,
I'm trying to implement a message system as used in social networks. The problem I'm facing is first about the database structure and second how to implement it in rails.
My first idea is I'm using 3 tables:
messages: id|subject|text|created_at
receivers: id|message_id|read:boolean
creators: id|message_id|read:boolean
now I'm wondering how to implement following features:
1.) a user can delete his message. but as both want to read the message, how to make sure the message is only deleted when both users have deleted it.
2.) how do I implement a reply? or actually how do I find the corresponding creator?
3.) how to find out whether a mail was read by the receiver?
another idea is:
creator_messages: id|creator_id|receiver_id|subject|text|read|created_at
receiver_messages: same as creator_messages
this distinguishes between the users, so they can delete individually their messages. but how do i find out, whether the mail was read or not?
my third approach was basicly my second but only one table messages and then displaying this to the user.
1. the message is deleted as soon as one of the user deletes it.
2. actually how do I represent the relationships as has_many and belongs to?
I thought it would work like this:
model User
:has_many :send_messages, :class_name=>"messages", :foreign_key=>"creator_id"
:has_many :received_messages, :class_name=>"messages", :foreign_key=>"receiver_id"
end
model Messages
belongs_to :user
end
but somehow I didn't get it to work. guess I'm missing something basic here.
Hope someone can help me =) thanks a lot

ok, if i understand it correctly the messages would have maximum of 1 receiver and 1 sender.
In that case i would do the following:
I would create just a messages model, this would have the extra fields
- receiver_read
- receiver_deleted
- sender_deleted
Now you can add hooks to the model like "after_save", "after_create" in here you can check if the receiver_read has just been set to true via for example with the receiver_read_changed? method, if this is true you can notify the sender or do something else with it.
With this after_save hook you can also check that if the sender_deleted is just set to true and the receiver_deleted is already true you delete the whole message.
When you have multiple receivers I would create a join model for the receivers and have the sender_deleted and sender_id in the message model.
In the join model i would add the columns receiver_id, read and deleted.
Now i would use the before_save method on the messages and the join models to check if the message needs to be deleted or if the sender has to be notified of the message that has been read.

The solution might be:
1) I would create a deleted(_at) flag in the receivers table as well, if you really want to hard-remove the messages from the database if all of the receivers deleted it you could setup a cronjob or something.
2) have a creator_id in the messages model instead of a creators table, i mean how can multiple people create the same message?
3) I don't really get this one, i guess you set the "read" flag on the receivers table to "true" when that user opens up the message, after this you can make a scope in the users model like "scope :read, where(:read, true)" and also a scope unread.
I hope this is what you mean.

Related

Ruby - Ensure only one class object

I have a Model Bot and I would like to ensure that there is only one Bot object in my database. I also need to make sure it is persisted and not tampered with.
My original thought was to do this in a migration, one that would follow the :bots table migration. It would include a line that is something like:
Bot.all.size == 0 ? Bot.create! : nil
Maybe this would prevent the AR object from being messed with in future migrations?
BONUS: Would be awesome to be able to have instant and global access to this class object. I was thinking using a singleton module in my Bot class that way I can always reference Bot.instance and have access to that specific object.
USE CASE:
I have 4 types of users in my DB and this bot will be the facilitator to delivery role-specific messages to them through our in-app messaging feature.
The Class Bot will have a has_many association with BotMessage/bot_messages. On the bot_messages table will be an enum field for user_role.
Messages will be created by company admins and stored in these tables because we want them to be viewable at any time by looking at the "conversation" thread between the User and the Bot.
When it comes to only having 1 bot, it's just that. I have no need for an additional Bot object. Additionally, since there is only one object it would be nice to be able to have a way of explicitly targeting that object without having to run a query to find it.
For example, unlike User where there could be 1000 records and in order to find the specific one you would do something like #user = User.find_by_email('foo#bar.com'), doing something like that for the bot would be unnecessary since there is only one record to find. That is what lead me to believe having a singleton object may be worthwhile here, so whenever I need to pull up a message for a specific role, I could run Bot.instance.bot_messages.where(user_role: 1) or something similar
Based on your Use Case, I see no reason for Bot to be a model.
Let's say you have a role called cool_user and you want to get all the bot_messages for that role, you might do something like:
class Bot
class << self
def bot_messages(user_role)
BotMessage.send(user_role)
end
end
end
As a very thoughtful but potentially anonymous super code monkey notes in the comments, you could also do:
class Bot
def self.bot_messages(user_role)
BotMessage.send(user_role)
end
end
Which some folks might find more readable. IMO, it is a bit of an issue of personal preference.
In either case, you should be able to do
Bot.bot_messages(:cool_user)
Since, as stated in the docs,
Scopes based on the allowed values of the enum field will be provided as well.
So, I believe BotMessage, with the properly set enum, should respond to cool_user and return all the bot_messages for that role.
You may need to check the docs to get the syntax exactly right.
I believe this should also satisfy your BONUS requirement.
A proven solution would be to use an STI on User (with a user_type column)
class User < ApplicationRecord
...
end
class Bot < User
has_many :bot_messages, foreign_key: :user_id
end
Is it what you're looking for ?

Ruby on Rails: Polymorphic/Object-agnostic conditions on Trigger setups?

*I'm not really sure what concept this problem would go under - would this be considered polymorphic associations?
I want to create a system where users can set up triggers so that after a certain event happens, another event happens.
For example, I want the user to be able to define a trigger action as:
"This [Email, Document] must be sent to all users after they [Sign up, read email A, sign document B]."
As you can see within the brackets "[]", the conditions can be tied to various objects and actions.
My thought was to create a model TriggerCondition and storing columns
:object_id, :assigned_to, :activated_when
but the object_id(Email, Document) can be different models.
Would adding a column :object_type that can be either email or document be the best solution? Or is there a more sophisticated way of designing so that I can pass in an object and it will automatically know what I'm referring to?
Regarding the :activated_when condition, I'm completely unsure how I would store and check for condition. Would I be able to use a syntax similar to "Email#marked_as_read(A)?"
I'm trying to use Rails Observers to trigger the action
http://api.rubyonrails.org/v3.2.13/classes/ActiveRecord/Observer.html
Rails is built to handle polymorphic associations just like this.

How do I model a "Connections" relation between Doctor-Patient on Ruby on Rails

I'm using Ruby on Rails for my dissertation creating a E-health monitoring web application for Doctors and Patients. My question is:
Is there any simple approach to creating an Connection option as seen on LinkedIn that I can use between Doctors and Patients?
I have been thinking about it and my approach would be to create a new entity in my database called Connection that will store the doctor_id and the patient_id attributes.
How would I create the necessary validations required?
E.g For example if a patient is not connected with a doctor they cannot send a message to the doctor or if not connected they cannot see certain information.
I'm looking for an approach or a guide to how I can solve this problem.
Thank you
One way you could approach it is to write a method in the model to make checking the relationship nice and clean. For example, if you wanted to check if a patient was connected with a doctor, you could add something like this to your Patient model:
Patient Model
def connected?(doc)
return true if Connection.where(:patient_id => self.id, :doctor_id => doc.id).count > 0
false
end
Then, whenever you have an instance of a patient and doctor in your app, you can check if they are connected by passing the connected? method. For example, assume you have your objects as #patient and #doctor. You could check their connection like so:
if #patient.connected?(#doctor)
# do stuff, like send message
end
Of course, how you approach using this method can vary greatly depending on what you're trying to accomplish. It can become really messy to keep writing if statements all over the place. But, I tend to get the app working first and then focus on cleaning the code.
EDIT
As Phlip mentions, if your Connection model doesn't contain any data, it is simpler to get rid of the model and use a has_and_belongs_to_many relationship between Patient and Doctor.
I would still use a method in the Patient and/or Doctor models to check whether the connection exists. Then, if you ever need to add data to the connection, you could easily create a Connection model and not have to edit your controllers/views.

The way to implement User Email Preferences?

I want to implement a feature which lets every user decide the kind of emails he/she will receive. So far, I can only see the user receiving emails when he/she receives a friendship request and when he/she receives a new message. The way I plan to implement this is as follows:
Each User has_one EmailPreference
EmailPreferences table will have 2 columns: Friendship (Boolean) and Message (Boolean).
By default, they will be true. So the User will receive emails for both new messages and new requests received.
The user can go to the Edit action and update the values as per his choice.
I plan on using an if statement which will check if #user.emailpreference.message? or #user.emailpreference.friendship? before the send email method.
I'd like to know if this is the best way to go about it.
A couple considerations - I'd question whether you want to do the has_one or simply add the columns to the user. I also tend to use dates instead of booleans, so you can see when the boolean was set. For naming, consider something other than 'friendship' and 'message'. If it is an attribute on the user, I'd consider something like 'subscribed_to_friendships' and 'subscribed_to_messages'.
The reason I avoid has_one's in general is to make very simple queries and reduce the need for maintenance. It's likely you'll be getting all users that should receive a message and looping through them, I prefer to avoid the joins and keep it simple. I also don't really like how false and null are the same on the child. This will help you avoid deleting/adding preference records, especially if the default is true and you're going to create preferences for most users by default.
I see one issue in the approach is that if tomorrow you have more type as preference , means when somebody joins than also you want to send the email , in that case you have to add one more column. Why do not you normalize it further and use more table to store the Preference type
Id Name
1 Friendship
2 Message
Id User Id Flag
1 1 TRUE
2 1 False
It means user 1 is opted for Friendship but not for Message. Now you can easily add any new preference.
the approach by https://stackoverflow.com/users/177489/swards is for me , the best option because that has_one queries can be a mess later.
add columns to user model and gg!

Creating a dashboard in Rails

Let's say I had an app that was an address book. I'd like to have a page dedicated to a "dashboard". On this page, I'd like to have a running list of the events that happen within the app itself.
Event examples could be:
A user adds a contact.
A user deletes a contact.
A user updates a contact.
What would be the best way to create this type of functionality? Originally I felt that I could do some creative database calls with existing data, but I wouldn't be able to deal with events that deleted data, like when a contact is deleted.
So now I'm thinking it would have to be a separate table that simply stored the events as they occurred. Would this be how most sites accomplish this?
I could go throughout my app, and each time a CRUD operation is performed I could create a new item in the table detailing what happened, but that doesn't seem very DRY.
I supposed my question would be - what's the best way to create the dashboard functionality within an already existing application such as an address book?
Any guidance would be greatly appreciated.
The easiest way to do this is to user Observers in addition to a "logger" table in your database.
Logger
id
model_name
model_id
message
This way you can set up an Observer for all models that you want to log, and do something like this:
after_delete(contact)
Logger.create({:model_name => contact.class.to_s,
:model_id => contact.id,
:message => "Contact was deleted at #{Time.now}"})
end
Now you can log any event in a way you deem fit. Another great addition to this kind of structure is to implement "Logical Deletes", which means you never really delete a record from the table, you simple give it a flag so that it no longer shows up in regular result sets. There's a plugin that does this called acts_as_paranoid.
If you implement both things above, the dashboard can log all important actions, and if you ever need to see what happened or view the data of those events, it's all in the system and can be accessed via the Console (or controllers, if you set them up).
You may want to check out Timeline Fu: http://github.com/jamesgolick/timeline_fu:
class Post < ActiveRecord::Base
belongs_to :author, :class_name => 'Person'
fires :new_post, :on => :create,
:actor => :author
end
I've created similar functionality in the past using acts_as_audited.
This helps you track changes to your models, which you can then present to the user.
It basically just tracks the events in a separate table, as you suggested.
You can use Observers in order to handle the events.
Then just store event with the information needed in the database from those Observers.
Here is a quick link to get you started.
user paper_trail plugin, it is awesome!. We modified it though, it is used for all our audit system for complicated release process.

Resources