Developer’s journal: devise, Rails 3, RSpec, and more

I keep a text file open as I work, and when I learn something, or fix a bug, I make a note of that in the text file. I call it my journal. It’s pretty low-tech, but text search turns out to be a sufficient way to look up past experiences when I find myself thinking “this problem seems so familiar”. There’s another benefit too: I find keeping simple notes about what I’m doing—what problems I’m solving, what approaches I’m using, what’s working and what could work better—keeps me focused on my goals. It turns out learning a programming language or a web framework is not as hard as training your mind to solve programming challenges efficiently.

Here are some things from my journal: I’m working on a Rails 3 app right now, something I plan to flog and promote mercilessly in upcoming posts. I’ve encountered some mysteries for which there was no simple HOWTO information easily available via web search. I thought I’d share the answers I cobbled together:

How to get RSpec and RSpec-Rails for Rails 3;

How to make devise routing work with related nested routes;

How to spec controllers that use devise to restrict access to logged-in users;

Routing with devise

I’d prefer to use OpenID for my app, but I fear trying to use Ruby’s gems for OpenID could be a time sink, so I’m going to start off with a userid/password-only solution. Fortunately, I don’t have to roll my own. There are plenty of good plugin gems out there. Since I’m working with Rails 3, I can use devise, which builds on a library called warden to provide a Rack application for authentication in Rails. Using devise, you get easily-configurable sign-up, sing-in, “remember me” functionality, password retrieval, and more. There is lots of good information about devise out there, but I want to make a few points about routing and RSpec.

If you’re planning RESTful routes for your app, and you’re modeling your users as the owners of those resources, you’ll want to have Rails routing build URLs like

users/1/someresources/2

The Rails way to do this is to set up a simple declaration in routes.rb that someresources are nested resources of users:

resources :users do
resources :someresources
end

The tricky thing, though, is doing this and using devise to manage user authentication using the User model. You get a handy routing macro with devise: devise_for. You can tell Rails to use devise on your User model with this line in routes.rb:

devise_for :users

The actual controller that handles user authentication actions is inside the devise gem. You don’t need a UsersController class. However, you cannot treat devise_for as a resources declaration and pass it a block for nested resources. This doesn’t work:

devise_for :users do
resources :someresources
end

If you were to add a separate declaration for users, like this…

devise_for :users
resources :users do
resources :someresources
end

…then what if you decide you want a UsersController, to show and edit profiles for instance? Things could get complicated. The solution is to give devise_for an alternate path to use for user authentication, e.g. accounts:

It doesn’t matter what path you use, since devise is going to use its own controller to handle authentication requests.

RSpec, devise, and controllers

Extending the theme of its out-of-the-box ease of use, devise lets you specify that a user must be logged in to use the methods of a controller with this simple directive:

before_filter :authenticate_user!

That's great. But if you have specs written for this controller, they will all begin failing the moment you add this directive, and the error you get back will not be that helpful:

undefined method `authenticate!' for nil:NilClass

Obviously RSpec doesn't know enough to simulate a logged-in user for devise. How do we teach it to mock such a condition?

I read through a long issue ticket on this spec'ing problem, in which the conversation starts out for earlier versions of RSpec and devise and works towards a general solution that I've cobbled together from different folks' posts. It works for me:

Correction: In my original code sample, below, I mocked up an object of the Warden class with mock_model(). Bad idea. Recent versions of RSpec and rspec-rails throw an error when you try to do that, since Warden, being no kind of Rails class, does not extend ActiveModel::Naming. Instead, you can mock it simply with mock().

As of Rails 3.0, however, this verify method is now excluded from the Rails Core. You can, however, install it easily as a plugin:

> rails plugin install git://github.com/rails/verification.git

To do

As I wrote above, keeping a journal is a good way to keep track of how things could go better. From my notes, I read that RSpec's routing specs could better confirm nested routes. Also, some of devise's generators could be more expressive. Does devise work with reCAPTCHA? And, finally, an up-to-date OpenID authentication engine, possibly built on warden, would be teh hawtness. Thanks to the triumph of Open Source in the Ruby community, I don't have to just whine about these things. I could get working on projects and patches right now. If only blogging didn't take so much time in itself. :-)