Ruby on rails relies on cookies. Perhaps ironically, it doesn’t actually check that a user has cookies enabled. This, as you might imagine, can lead to problems.
I was charged with implementing some sort of cookies test for a project at work. Letting google be my co-pilot, I ran across a blog post that mentioned a way to do it. The method is simple: given a request, check the session for the _session_id variable. If it isn’t there, set it, and redirect the user to the same URL, but with a GET parameter appended to it. The _session_id variable should be set automatically by rails. The GET parameter will indicate that cookie detection is in progress. When the browser follows the redirect, the GET parameter is detected, and if the _session_id still doesn’t exist, then the user has cookies disabled.
Why is all of this required you ask? Because on the first visit to a site, the user can’t possibly have any cookies nor session variables (which are accessed via cookies). Hence the need to set a session variable and reload the page.
I implemented the methodology laid out in the blog post, and while effective, it proved awkward for my company’s use. Because it involved redirecting to a page with a GET argument, e.g. blah.com/my_page?cookies_enabled=testing.
This in and of itself was fine, except that if one restarted the server or deleted the sessions, then reloaded that particular page via its URL, the cookie test would see the GET argument, but not the _session_id session variable that was supposed to be set, and fail. As URLs get cut and pasted around a lot at my work—especially by QA—this didn’t go over well. I needed something different. Here’s what I came up with:
in application.rb:
before_filter :cookies_required, :except => ["cookies_test"]def cookies_testif request.cookies["_session_id"].to_s.blank?logger.warn("=== cookies are disabled")render :template => "shared/cookies_required"elseredirect_back_or_default(:controller => "foo")endend
def cookies_requiredreturn unless request.cookies["_session_id"].to_s.blank?session[:return_to] = request.request_uriredirect_to(:controller => "foo",:action => "cookies_test")return falseend
Don’t forget to write up some scathing admonishment for the shared/cookies_required view, and you’re half-way there.
I say half-way, because no changes can be made to a rails installation without two things; passing your current tests, and writing some new ones. When I ran my tests, all my functional tests failed. Why? Say it with me class, “the cookie test was redirecting all the functional tests to the page with the scathing admonishment for disabling cookies.” That’s right. Fortunately, this is easily worked around, by adding the following cookie line to your functional tests:
in foo_controller_test.rb:
def setup@controller = FooController.new@request = ActionController::TestRequest.new@response = ActionController::TestResponse.new@request.cookies["_session_id"] = "fake cookie bypasses filter"end
This way, all your functional tests’ requests have a fake session id, which fakes out the cookie detection test. Voile! All the tests pass. Now, don’t forget to take the time to write a few that don’t set the session id, and make sure that they do get redirected like they’re supposed to.
The advantage of this method, is that there are never any GET parameters passed around, so it’s safe to load any page at any time. Both methods make some assumptions about how rails handles sessions, but I think this is a limited risk, as the session information is integral to rails, so they can’t go changing it willy nilly without upsetting a lot of people anyway.
As an added bonus, the redirects are all done behind the scenes, so the user is ne’er the wiser. A slight delay on their first page load is all they could ever notice.
This is interesting, I found your page on google when looking for a good solution to this problem.
I don’t entirely understand your solution, or what your solution has over your original attempt? How does it fix the “server restart or deleted sessions” problem you first identify? I don’t really understand. More explanation would be welcome!
Let me explain more fully then.
With the original solution, a GET parameter was used. So when a user first came to your site, a cookie would be set on their browser, and they would be redirected to test for the existence of that cookie. The page they were redirected to would have a URL like this: http://kill-0.com/?cookie_detect=true. Now, let’s say they bookmarked that page. Then they cleared their cookies, or you restarted your server, or just cleared your server sessions out. When the user returns to that bookmark, the original solution will see that GET parameter (?cookie_detect=true), but not the cookie that is supposed to be with it. This will cause the site to assume the user does not have cookies enabled. It will then redirect the user to a page saying as much. Because my design does not use a GET parameter, there is no threat of that happening.
Does that make it clearer?
e.
I couldn’t understand some parts of this article Rails Cookie Detection, but I guess I just need to check some more resources regarding this, because it sounds interesting.
[...] I decided to implement a redirect, thanks to ideas given by this blog post. It worked, but it left me with a parameter in my URL the first time the user visited the site. It [...]
The article mentioned in the comment above, while it does misstate how my solution works, does find an alternative solution, that is likely to be better and faster. You should check it out.
e.