rails: how to detect other clients browsing the same url - ruby-on-rails

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.

Related

In Rails 3, how can I save user search history to the database with no membership/authentication system?

Currently, the site is storing "previously viewed items" via cookies.
I need to take that a step further and not only store those items in the database, but save the user's most recent search, so that they can retrieve the search later by returning to the site (the cookie can handle this), but also through say a four-letter code and later a QR code.
When you get into writing these non-authenticated users' data to the database, what is the identifier to use as the key to differentiate them? And would it be ideal to create the actual model record upon visiting the home page, or perhaps after the initial search?
You could save the IP address, other than that, there is really no way to save something specific to a user.
Use the rails request object. You can access the IP with request.remote_ip.
This is the only thing I can think of that is request specific without auth.

How can I persist objects between requests with ASP.NET MVC?

I'm just starting to learn ASP.NET MVC and I'd like to know how I can retain model objects between subsequent requests to controller action methods?
For example say I'm creating a contact list web app. Users can create, update, rename, and delete contacts in their list. However, I also want users to be able to upload a contact list exported from other programs. Yet I don't want to just automatically add all the contacts in the uploaded file I want to give the user a secondary form where they can pick which uploaded contacts should be actualy added to their list.
So first I have a ContactController.Upload() method which shows an upload form. This submits to ContactController.Upload(HttpPostedFileBase file) which reads the file that was posted into a set of Contact model objects. Then I want to display a list of all the names of the contacts in the list and allow the user to select those that should be added to their contact list. This might be a long list that needs to be split up into multiple pages, and I might also want to allow the user to edit the details of the contacts before they are actually added to their contact list.
Where should I save the model objects between when a user uploads a file and when they finally submit the specific contacts they want? I'd rather not immediately load all the uploaded contacts into the back end database, as the user may end up only selecting a handful to actually add. Then the rest would need to be deleted. Also I would have to account for the case when a user uploads a file, but never actually completes the upload.
From what I understand an instance of a controller only lasts for one request. So should I create a static property on my Contact controller that contains all the latest uploaded contact model object collections? And then have some process that periodically checks the age of these collections and clears out any that are older then some specified expiration time?
A static property on the controller is trouble. First off, it won't work in a web farm and second it you'd have to deal with multiple requests from different users. If you really don't want to use your database you could use the ASP.NET Session.
No, you don't want a static property, as that would be static to all instances of the controller, even for other users.
Instead, you should create a table used to upload the data to. This table would be used as an intermediary between when the user uploads the data, and completes the process. Upon completion, you copy the contacts you want to keep into your permanent table, then delete the temporary data. You can then run a process every so often that purges incomplete data that is older than a specified time limit.
You could also use the HttpContext.Cache, which supports expiration (and sliding expiration) out-of-the box.
Alternatively, and perhaps even better (but more work) you could use cookies and have the user modify the data using javascript in her browser before finally posting it to you.
However, I'd strongly recommend to store the uploaded information in the database instead.
As you pointed out, it might be a lot of data and the user might want to edit it before clicking 'confirm'. What happens if the user's machine (or browser) crashes or she has to leave urgently?
Depending on the way you store the data the data in this scenario will probably be lost. Even if you used the user id as a cache key, a server restart, cache expiration or cache overflow would cause data loss.
The best solution is probably a combination of database and cookie storage where the DB keeps the information in a temporary collection. Every n minutes, or upon pagination, the modified data is sent to the server and updated in the DB.
The problem with storing the data in session or memory is what happens if the user uploads 50k contacts or more. You then have a very large data set in memory to deal with which depending on your platform may effect application performance.
If this is never going to be an issue and the size of the imported contacts list is manageable you can use either the session or cache to store the dataset for further modifications. Just remember to clear it when the user has committed the changes, you don't want a few heavy datasets hanging around in session.
If you store the dataset in session using your application controller then it will be available to all controllers while it is needed.

How do I see real-time activity of my users in Rails 3?

What I would like to do is have my admin user be able to see - in real time (via some AJAX/jQuery niceness) - what my user's are doing.
How do I go about doing that ?
I assume it has something to do with session activity - and I have started saving the session to the db, rather than the cookie.
But generally speaking, how do I take that info and parse it in real time ?
I looked at my session table and aside from the ids (id and session_id), I see a 'data' field. That data field stores a hash - which I can't make any sense of (looks like an md5 hash).
How would I use that to see that User A just clicked on Link B, and right after that User B clicked on link A, etc. ?
Is there a gem - aside from rackamole - that might be able to help me?
You might want to check out Mixpanel. They are easy to setup and have some of what you are asking for.
The session data only contains the values stored in the session[]-hash from the user. It doesn't store which action/controller was called, so you don't know which "link was clicked".
Get the activity of your users:
Besides rackamole you have two options IMHO.
Use a before_filter in your ApplicationController to store the relevant info you are interested in. (Name of controller, action or URI, additional parameters and id of the logged in user for example).
Use an AJAX-call at the bottom of each page which posts back the info you are interested in (URI, id of logged in user, etc.) to your server. This allows faster response times from the server, as the info is stored after the page has already been delivered. Plus, you don't have to use a Rails-request to store it. The AJAX-request could also be calling a simple PHP-script writing the data to disk. This is much faster.
Storing this activity:
Store this data/info either in the database or in a logfile. The database will give your more flexibility like showing all actions from one user, or all visitors for one page, etc. The logfile solution will give you better performance.
Realtime vs. Oldschool:
As for pulling out your collected data in realtime, you have to build your own solution. To do this elegantly (without querying your server once a second to look if new data has arrived) you'll need another server process. Search for AJAX Push for more info.
Depending on your application I'd ask myself if realtime notifications for this are really necessary (because of all the hassles of setting this up).
To monitor the activity on your site, it should be enough to have a page listing the latest actions and manually refresh it (or refresh it automatically every ten seconds).
Maybe you can test https://github.com/raid5/acts_as_scribe#readme
It works with Rails 3 too.

When do Symfony's user attributes get written to session?

I have a Symfony app that populates the "widgets" of a portal application and I'm noticing something (that seems) odd. The portal app has iframes that make calls to the Symfony app. On each of those calls, a random user key is passed on the query string. The Symfony app stores that key its session using myUser->setAttribute(). If the incoming value is different from what it has in session, it overwrites the session value.
In pseudo-code (and applying a synchronous nature for clarity even though it may not exist):
# Widget request arrives with ?foo=bar
if the user attribute 'foo' does not equal 'bar'
overwrite the user attribute 'foo' with 'bar'
end
What I'm noticing is that, on a portal page with multiple widgets (read: multiple requests coming in more or less simultaneously) where the value needs to be overwritten, each request is trying to overwrite. Is this a timing problem? When I look at the log prints, I'd expect the first request that arrives to overwrite and subsequent requests to see that the user attribute they received matches what was just put into cache by the initial request.
In this scenario, it could be that subsequent requests begin (and are checked) even before the first one--the one that should overwrite the cached value--has completely finished. Are session values not really available to subsequent requests until one request has completed entirely or could there be something else that I'm missing?
Thanks.
Attributes of the user do not get written to storage until the end of the request (in sfUser::shutdown). Attributes get loaded into sfUser at the beginning of a request. So in this case, the second request would have to be initiated after the first request is finished. Your best options are probably
Add hardRead and hardWrite methods to sfUser (look at what sfUser::initialize and sfUser::shutdown do respectively).
Use another method of storing the information that has better support for concurrency. The database or potentially the caching system you're using could work. For example, I think this could be done using APC cache.
Note that depending on what class you're using for storage, user attributes may not get written to $_SESSION at all. Symfony supports using many methods for storing user attributes (e.g. database, cache).

Rails w/ ActiveRecord Sessions and Restful-authentication: when does the sessions table get written to?

Warning: some of this may be very wrong-headed, so please let me know if my assumptions are incorrect.
Here's what I'm trying to accomplish:
I'm using restful-authentication for login. However, as I am using flex/ruby_amf for my UI, I have to separately authenticate each connection from flex.
The way I decided to do that was by having the log-in screen redirect to the embedded flash page, inserting the session-id as a flashvar. The flash app sends the session-id with every request, and a before filter on all of the relevant controllers checks to see if the user associated with the session identified by the session-id is logged on.
The way I associate a user with session is by adding a 'user_id' column to the sessions table, and doing an sql "update sessions set user_id...'" type query called from the login function.
However, the user_id only gets updated the 2nd time the user logs in. A little investigating showed that the record in the sessions table does not yet exist during execution of the login function.
So, if everything up to this point makes sense, and conforms to best-practices, etc., then my question is:
At what point in time is the record in the sessions table created? Is there a way to update the session object in the login function and have rails write the user_id to the database for me?
The behavior of sessions in rails is a real mystery to me. I'd appreciate any help.
Thank you.
In Rails 2.3, the session is saved after the Rack application has finished its processing. In traditional Rails applications, this will be after the request is fully processed: before filters, controller action, view rendering, and after filters. Look in actionpack/lib/action_dispatch/vendor/rack-1.1.pre/rack/session/abstract/id.rb.
If you think about it, this makes perfect sense. Writing the session to its store every time you place something in the session would incur a lot of extra overhead.
It's Rails, so if you want to mess with it enough, sure, you can monkeypatch yourself a way to write the session to store anytime you wish. I don't recommend it. You'll end up having to rework the code constantly as Rails evolves.
You are right that for ActiveRecord::SessionStore, one row does map to one session. The data column is an encoded form of every object you put in the session. Each time a request comes in, Rails has to reconstitute the session as it existed by creating new instances of all the objects you previously stored in it.

Resources