User Presence In a chat room (chat getting updated by simple polling) - ruby-on-rails

We have implemented a simple chat room feature in Rails using Simple Ajax updates. Now in every chat room a message belongs to particular user. We want to show the list of users(something like user presence). Please suggest ways. We are not using Jabber,XMPP etc.
The Chatroom model is:
class ChatRoom < ActiveRecord::Base
validates_presence_of :title
has_many :messages,:foreign_key=> "chat_room_id"
has_many :stories,:foreign_key=>"chat_room_id"
has_many :topics,:foreign_key=>"chat_room_id"
end
The messages are the chats sent of every user.
The message model is:
class Message < ActiveRecord::Base
belongs_to :user
end
The USer model is:
class User < ActiveRecord::Base
acts_as_authentic :crypto_provider => Authlogic::CryptoProviders::BCrypt
validates_presence_of :nick
validates_uniqueness_of :nick
has_many :questions
end
Please suggest ways

To keep track of which users are in which room, you could set up a HABTM relationship between the ChatRoom and User models. And, you could add a last_poll_datetime column to the User model to track the last time the user polled for messages (more on this part in a minute).
To show a list of all the users in a given room, use your HABTM join table, ChatRooms_Users. You'll be inserting/deleting from this table whenever a user joins or leaves a room.
If you want to expire users who close their browsers instead of clicking 'leave room', set up a sweeper task to run every minute that looks for users with last_poll_datetime older than one minute and remove their rows from the ChatRooms_Users join table.

Related

Manage newsletter subscribers from one or two models? - Rails 5

I want my Rails app to send a newsletter. I first implemented it so that Users can subscribe and receive the newsletter. But now I'd like people who are not a user to be able to join the newsletter as well. Therefore I created a new model: NewsletterSubscribers.
The thing I'm running in now though is that I want to make sure that whenever someone first joined the newsletter and later on signs up for an account, he doesn't get the newsletter twice.
Im doubting how to go forward with this. I see two options:
Send it to NewsLetterSubscribers and then before sending it to the Users, check if their email is present in a record of the NewsletterSubscriber table. And if it is present, not send it to that email again. My worry though is that this is very slow. Also it feels like bad practice to manage my newsletter subscription with two models.
To manage the newsletter only from the NewsletterSubscriber model. Everyone can subscribe (visitors and users). This would simplify it a lot, but I want to be able to check whether a user is subscribed to the newsletter or not in my views. So then I would need a belongs_to - has_many association between NewsletterSubscriber and Users. But then visitors can not subscribe anymore because of the validations that come with this association.
As you can read, I'm stuck... Does anyone have an idea how to best approach this?
ps. I use Devise with Twitter omniauth to authenticate users. Wanted to let you know, in case this simplifies or complicates a possible solution.
You can just disable the validation generated by belongs_to by passing the optional: true option:
class User < ApplicationRecord
has_many :newsletter_subscriptions
# or
has_one :newsletter_subscription
end
class NewsletterSubscription < ApplicationRecord
belongs_to :user, optional: :true
end
Another way you might want to consider is using Single Table Inheritance (STI) which will let you create different logic for guest and registered subscriptions while still using a single table. You can set this up by adding a string column named type to the newsletter_subscriptions table.
# rails g model NewsletterSubscription type:string email:string user:references
class NewsletterSubscription < ApplicationRecord
# shared logic
end
# app/models/users/newsletter_subscription.rb
module Users
class NewsletterSubscription < ::NewsletterSubscription
belongs_to :user
delegate :email, to: :user
end
end
# app/models/guests/newsletter_subscription.rb
module Guests
class NewsletterSubscription < ::NewsletterSubscription
validates_presence_of :email
end
end

How to edit a page of information and send a request to be updated for a mod?

I have a database full of movie information and i would want users to be able to edit that information, but i don't want the information to get updated just yet. I would want a request to be send to a moderator to review it first before anything.
How would i go with something like this? I was thinking of making another column with the edited information so mods can check and update.
So a user would go to /movies/1
User will then see the information and notice that some of the information is outdated.
User clicks on a edit information button and edits the info.
User presses save and it's sent to a column for a mod to check.
Your approach will probably work if only there are only one user. In general (as was mentioned by max pleaner) you should create new table with references to users table and store pending changes there.
Another approach would be using rails tables inheritance. Something like this should do the thing
class MoviesAbstract < ActiveRecord::Base
self.abstract_class = true
end
class Movies < MoviesAbstract
# your code
end
class MoviesPengingChanges < MoviesAbstract
belongs_to :user
validates_presence_of :user
# ensures user won't propose two changes of one field
validates_uniqueness_of :user, scope: [:title, :desctiption]
end
class User < ActiveRecord::Base
has_many :movies, dependent: :destroy
end
And when moderator approves some users' changes over some movie, user.movies_pending_changes.where(movie: movie) may be destroyed.

User owns a model and other users can join this model

I want to creat an application where a User can create a Room and is the only owner of it. Other users should be able to join a Room, just one at the time and only they should be able to see what is happening in this Room.
So i created a controller rooms_controller and the model Room.
Btw I'm using devise to handle all the Userstuff.
So what should i put into the user.rb file? has_one :room? belongs_to :rooms?
How can users join a model?
Does a User have an one.to-one relationship with Room? In that case User has_one Room. Or can a User create many Rooms? A User can join a Room, if, for example, there is a Rooms_Visitors table where the RoomId and the UserId identify a row.
Its hard to answer without knowing your broad use case.
I advice that you study a bit on SQL relations, then the answer for your use case will become clear to you.
You can design the models this way. You can actually have two types of users: Owner and User and they can inherit from the BaseUser where BaseUser being the class with all the common user attributes.
And, your model associations can look like this:
class BaseUser < ActiveRecord::Base
# all the common user attributes here
end
class Owner < User
has_one :room
end
class User < BaseUser
has_many :rooms
end
class Room < ActiveRecord::Base
belongs_to :owner
belongs_to :user
end
owner has one room and room belongs to owner.
user has many rooms and room belongs to user.
You have to add owner_id and user_id in your rooms table.
You can also have a user_type column in your base_users table to indicate what type of user is this (i.e. owner or user).
Also, take a look at the cancancan gem which is an authorization Gem for Ruby on Rails application. You will need to have different types of abilities for example: owner will be able to create the room but user can't create the room, they can only join. You need to handle these things using authorization.

Many to Many relation issue in Rails 4

I am a rails beginner and encountered the following issue
Models are setup as follows (many to many relation):
class Activity < ActiveRecord::Base
belongs_to :user
has_many :joinings
has_many :attendees, through: :joinings
end
class Joining < ActiveRecord::Base
belongs_to :activity
belongs_to :attendee
end
class Attendee < ActiveRecord::Base
has_many :joinings
has_many :activities, through: :joinings
end
This is one page test application for some users posting some activities, and other users to join the activities.
It is organized as single page format (activities index), and after each activity, there is a "Join" button users can click.
I am stuck at the point when a user needs to join a specific activity.
in the index.html.erb (of the activities), with the Join button code.
This will point to the attendee controller, to Create method, but I got no information regarding the Activity that I want to follow (eg. activity_id, or id)
Without this I cannot use the many to many relation to create the attendee.
What would be the correct button code, or any other suggestion to to get the corresponding activity ID in the attendees controller?
I tried a lot of alternatives, including even session[current_activity] , but is pointing (of course) always to the last activity.
Thanks so much !
If you have existing activities, and existing attendees, and you want to change the relationship between them, then you are editing the join table records. Therefore, you should have a controller for these. Like i said in my comment i'd strongly recomnmend renaming "joining" to "AttendeeActivity" so your schema looks like this:
class Activity < ActiveRecord::Base
belongs_to :user
has_many :attendee_activities
has_many :attendees, through: :attendee_activities
end
class AttendeeActivity < ActiveRecord::Base
belongs_to :activity
belongs_to :attendee
end
class Attendee < ActiveRecord::Base
has_many :attendee_activities
has_many :activities, through: :attendee_activities
end
Now, make an AttendeeActivitiesController, with the standard scaffoldy create/update/destroy methods.
Now, when you want to add an attendee to an activity, you're just creating an AttendeeActivity record. if you want to remove an attendee from an activity, you're destroying an AttendeeActivity record. Super simple.
EDIT
If you want to create an Attendee, and add them to an activity at the same time, then add a hidden field to the form triggered by the button:
<%= hidden_field_tag "attendee[activity_ids][]", activity.id %>
This will effectively call, when creating the attendee,
#attendee.activity_ids = [123]
thus adding them to activity 123 for example.
You have several options here. I'm assuming that the Join button will simply submit a hidden form to the attendees controller's create action. So the simplest solution would be to include the activity_id as a hidden form tag that gets submitted along with the rest of the form. This activity_id will be available in the params hash in the controller.
The other option is to setup Nested routing so that the activity_id is exposed via the path.
Thanks for all the details. I will change the naming of the join table for the future.
My problem was that I could not find the specific activity for attendee create method. Finally I found something like this for the JOIN button:
<%= button_to 'Join', attendees_path(:id => activity) %>
This will store the Activity ID, so I am able to find it in the Attendees controller:
def create
activity = Activity.find(params[:id])
#attendee = activity.attendees.create user_id: current_user.id
redirect_to activities_path
end
this updates also the Joinings (AttendeeActivity) table.
I will study about the hidden_field_tag solution, as is not clear to me yet.

How to create a Many-to-One relationship with existing records in Rails

I'm trying to implement something similar to that below. Basic setup is a Schedule model
class Schedule < ActiveRecord::Base
has_many :events
end
The Event model will then belong_to a Schedule.
Events are comprised of a name, datetime, running length and a Room
A Room is chosen from a select list when the event is created, all Rooms are created beforehand through seeding or admin interface. Room will store information like seating count.
I've been trying to find a method implementing this that feels right. Solutions seem to range between habtm or has_many :through but I can't see the need for a join table for this situation.
Theoretically, an Event has_one :room but the reverse relationship isn't belongs_to :event as a Room may be used for many events, this also requires the foreign key to be on the rooms table.
I've considered handling this manually in the model using a room_id foreign key in the event, I could then query for the relevant Room. I think this would work since I currently cannot see a requirement for finding all events using a Room.
class Event < ActiveRecord::Base
belongs_to :schedule
def room
Room.find(room_id)
end
end
Everything I've done so far in Rails has felt 'right', but all the solutions I've come up with here doesn't and I feel like I'm missing something; or maybe I'm just expecting that extra bit of magic.
Is there a "Rails" way to do this?
Wouldn't Event just have a belongs_to relationship to Room in this case?
class Event < ActiveRecord::Base
belongs_to :schedule
belongs_to :room
end
If you think about it from a schema perspective, your "events" table almost certainly has "schedule_id" and "room_id" columns.
The other side of the relationship is that a Room has_many Events.

Resources