Is there such a thing as a listener in Ruby? I know they have session listeners for JSP, so I was wondering if there was something similar. Ultimately, I would like to create a listener to listen to when session are created or destroyed so I can display a count of how many users are currently online.
You do not know how many users are online on a webserver, unless they have a permanent active connection (for example something like long-polling, comet).
Your best bet is to look into your session store and count the users that are active in the last X minutes.
A listener will not fire reliably when a user disconnects. For example with 'login' and 'logout' logic you will not get all logout actions, because internet connections can fail, users can forget to press the logout button, etcetera.
Update: for Rails, check this forum post: http://railsforum.com/viewtopic.php?id=18480 which explains the solution in more detail.
Related
Consider scenario in the App:
User submits an answer then gets taken to a page where they get subscribed to an ActionCable.
Every time a user submits an answer, a broadcast also happens so that subscribed users can be updated with the new user and their answer.
Problem: When two users submits an answer at almost the same time (within 1 second)
one user ends up seeing two answers, theirs and the other user
the other user ends up only seeing their answer
Question:
Is there a way to make it so that both players see each other's answers?
I've been searching for solutions but have not found any. I may have stumbled upon the cause of the issue from rubyonrails.org:
If you're not streaming a broadcasting at the very moment it sends out an update, you will not get that update, even if you connect after it has been sent.
Is there a way to make it so that both players see each other's answers?
Load the answers from the database on page load, and only use broadcasts to "update" what gets seen on-screen in real-time.
If updates are broadcasted for data already on-screen, either do nothing or use that to replace the answer already shown.
Subscribing to or listening to WebSocket messages is like tuning into a radio station. You have no idea what may have broadcasted before you started listening, so you'll need to get that data from elsewhere.
Alternatively, if you'd prefer to start with a blank slate, you could instruct the client-side code that after it is subscribed, to make an API call (or ActionCable request) to get all the relevant data needed, and also use broadcasts to "update" that data.
I really don't have a clue on how to do this. So when admin, deletes or adds a posts, I want people who are viewing the app to automatically see the new content without them having to refresh. I researched and some say websockets. But is there any other way to do this? or What is the best way?
Indeed, what you are looking for is websocket.
The app will "subscribe" users to a channel (which can be attached to a page for example).
Then you create events for this channel, in your case:
- when an admin create a post, create a create_post event
- when an admin delete a post, create a delete_post event
Then, you can trigger actions when a new event is created.
When a delete_post event is created, hide the post in the page. When a create_post event is created, append it.
You have multiple solutions for this. Rails 5 includes ActionCable, which is a websocket "system" integrated into rails (Here is a tutorial).
Or you have external solutions, such as pusher.
I have Users that can create DinnerEvent that contain Food. User specify preferred Food using a join table. Would like to create an internal message system that automatically sends out a notice to other Users who "prefer" the Food in the DinnerEvent that was created. Can anyone provide some guidance as to how I can go about approaching this or if there are any good reference resources out there (haven't had much luck searching)? Thought about ActiveMailer but decided I wouldn't want people to get spammed all the time in their email inbox. Would prefer to only use Rails to achieve this.
There's a lot of options here and many use cases to think through. Maybe you can start with something very simple that:
Tracks the last date/time of login for each user
On some page (specific to the logged in user), display all DinnerEvents created since last login that match their Food preferences. Should be simple Active Record to pull this.
Continue to show this list until they dismiss it (record this date/time) or login again
A full blown messaging system will probably require more complex stuff like queues for each user that are subscribed to a master queue. And, possibly an additional backend data store like Redis. I'm purposefully leaving out the details of something like this for now; it's a much bigger topic.
so I have designed this voting thing which does not let somebody vote for the same article twice in 24 hours. However, suppose a person votes and after seeing the person was able to cast vote or that he is falling in that 24 hour window, I disable the vote-casting button (and this is all Ajax btw).
But what to do when a person closes his/her browser and comes back up or even refreshes the page? Obviously, he would not be able to cast vote, because of my algorithm, but the person would still end up succeeding in making call to the server. So if he really wanted, he would keep refreshing the page and clicking on the vote and put unnecessary load on the server. How to avoid that by doing some sort of client-side thing or something?
I am using ASP.NET MVC, so session variables are out of question.
Am I being over-concerned by this?
If voting happens only from logged in (known) members then you shouldn't have any problem.
If, on the other hand, everyone can vote then you need to store all user vote events:
timestamp
poll
poll_vote
ip
user agent
user uniqueness cookie
So you'll need a random hash sent out as cookie. This will ensure that you don't accept another vote for the same poll from the same person.
If the user deletes his cookies you fallback to plan B, where you don't allow more than (say) 10 votes from the same IP and user agent combination for 24 hours.
The system is not perfect since users can change IPs and (more easily) user agents. You'd need advanced pattern detection algorithms to detect suspicious votes. The good thing about storing all user vote events is that you can process these later on using a scheduler, or outsource the votes to someone else who can process them for you.
Good luck
Refreshing is not a problem
If you're doing all this voting using Ajax, refreshing a page won't do anything except load the page using GET.
If you're not using Ajax you should make sure you call RedirectToAction/RedirectToRoute action result, that would as well help you avoid refresh problems.
How do you recognise users
If you use some sort of user authentication this re-voting is not a problem. But if your users are plain anonymous, you should store IP address with your votes. This is how things are usually done. This makes it possible to avoid session variables as well. But you have to be aware of this technique because it's not 100% perfect.
Cookies?
You could of course also use absolute expiration cookies. They'd expire in an day. Advanced users would of course be able to avoid your voting restrictions, but they would be able to avoid other ways as well. Sessions BTW are also based on cookies anyway.
Combination
But when you'd like to make you system as great as possible, you'll probably use a combination of the above.
The best way would be to track who voted for what and when on the server (probably storing it in a database). In order to do this you must use an authentication system on your site (probably forms authentication) to identify users. So every time someone tries to vote you check first in your data storage if he already voted and when and decide whether to validate the vote or not. This is the most reliable way.
If your site is anonymous (no authentication required to vote) then you could store a persistent cookie on the client computer that will last for 24 hours and indicate that a vote has already been cast from this computer. Remember though that cookies might be disabled, removed and are not a reliable way to identify a given user.
I am using ASP.NET MVC, so session
variables are out of question.
Any reason for that? Sessions are perfectly fine in ASP.NET MVC applications. It is in your case that they won't work because if the user closes the browser he will lose the session.
Obviously, he would not be able to
cast vote, because of my algorithm,
but the person would still end up
succeeding in making call to the
server. So if he really wanted, he
would keep refreshing the page and
clicking on the vote and put
unnecessary load on the server
Automated bots could also put unnecessary load to your server which is much more important than a single user clicking on F5.
If you just want to ensure the user can only vote once on an article then you just need to store a Set (i.e. HashSet) of all article id's that they've already voted on, then just check before allowing the vote.
If you still wanted a 24hr limit then you need to store a Dictionary<articleId,DateTime> then you can check if he has already voted for that article and if he has when it was.
What want to be accomplished is:
I want to "synchronize web browsers". my site has a "wait URL" where when browser gets there it will be kept waiting till another browser also go there and then both will be presented with a quiz-like game.
Right now the wait url will call each second to Rails to check if other player came to the game. How can in the Rails framework detect a different client connecting to the same URL?
As the controller is recreated per request looks like is not the place, not the view for sure and storing this in the model looks really clumsy.
Also, after the pairing I need to check and compare every answer of the paired users so somehow that information must be retained
What you're trying to do is share information between users. So the database or memcached are the most sensible.
Simplest: I'd create an ActiveRecord object, perhaps called Quiz, instances of which people join by virtue of going to a URL, e.g using default routes:
http://yoursite.com/quizes/join/3434
You'd need an ajax poller poller to notify the others; use periodically_call_remote for this -- you could use render :nothing => true by default and render something else if there was an error to keep it efficient. You can also use the frequency method as a basis to determine whether people leave the quiz as well (e.g. if frequency is 1s, then assume someone has left if they didn't ping after 5-10s).
Assuming these users are not registered with the site so don't have some kind of user id you could store I would suggest using the session. It is a per user data store. By default the session is stored in an encrypted cookie on the users machine. However you can use ActiveRecord as the session store and could maybe query that table directly?
Store the URL in the session and do a search for it at a later time. You can normally only access the current users session using the Rails 'session' hash but maybe (untested) if you created a model called Session (or maybe something more specific like 'WaitingGamers') which used the sessions table you could lookup the information you need.
I would guess when using ActiveRecord as the session store the session data is stored as a serialised hash. Use Marshall to turn it back in to a regular hash and find the data you stored in there.
I'm not a rails expert, but since all the state resides in your database that would be the place to keep this information.
You could keep a "waiting users" table, and in the "wait URL" view check if the user is already in the table. If not, add him to the table. Then, check if there is another user waiting (maybe there's more than one?) and if so, match them up and delete them from the table.
Another improvement would be to keep a timestamp for each user in the "waiting users" table, which gets updated in the view - this would serve as a keep-alive that will enable you to detect users that left the "wait URL" page or closed the browser.