Friday, October 24, 2008

REST: Some tips and implementing "Forgot your password?"

If someone asked me what was the best thing I had learnt over the last 12 months, I would say: REST.

I know that the RESTful approach requires changing our way of thinking, and it took me a while to "switch". The benefits are huge. From my experience the main advantages of REST are:
  • the consistency it brings to the app,
  • a standardized way of creating controllers,
  • thinking always in terms of resources - what is the 'thing' that the user wants to create/update/destroy?
  • a higher-lever abstraction that simplifies communication with other people in the team
  • very often there's only one RESTful way of implementing a certain feature
  • consistent API, in one project we use Flex for the UI, and being RESTful makes it very easy to work with Rails controllers.
"Forget your password" as an example

One of the examples that is hard/not worth (according to some people) to implement in a RESTful way is "Forgot your password" functionality. Some people feel like it could be just "/users/4/reset_password" and that we could use a custom action for the users controller. I know that it can work, but I see no reason to break the REST rules here (by adding a non-REST action to the controller).

My RESTful solution

The feature should let users provide an email, send them a link to a page, where they can change their password.

We have several steps to follow:
  1. Show a page where people can type their email, verify the email
  2. Generate a reset_password code.
  3. Send an email with this link.
  4. Clicking on this link should check if it's a valid reset_password code
  5. If it is valid, then show a page where a user can type the new password twice.
  6. Display a message appropriate for the result (error when the passwords don't match etc.)

Given these steps, I split the code into the following controllers and actions:

Email Verification

We want to let people create a new Email Verification, so:

I created an EmailVerificationsController with new/create actions.

It is responsible for:
  • finding the user by email,
  • generating the reset_password code,
  • sending the email,
  • handling errors
Passwords

After veryfing the email, we want people to change (update) their Password, so:

PasswordController with edit/update actions

it's responsible for:
  • authenticating the user by the reset_password code,
  • letting the user change the password,
  • displaying errors.

That's it.

Techniques

This example shows that a controller doesn't have to reflect a ActiveRecord class. There is no such model as EmailVerification in my code. It could be worth considering it, but in my case I don't think it was needed.

A useful technique for REST-izing your design is to turn verbs into nouns, thus making them easier to expose as resources. In this case, we wanted to verify an email, so I turned it into "creating an EmailVerification resource".

Sometimes changing the words slightly can help. Instead of resetting passwords, I use "update a Password".

As always, I try to move as much logic to the model as I can. Ideally I just call one model method and handle exceptions if there are any. See this article for more details about this approach.

A controller can use other models. In our case the PasswordsController could call User.update_attributes method to change the password.

Bonus

A friend of mine asked recently how I'd implement a feature, that let's an admin making another user a superuser.

My thinking here is that I turn the "making someone a superuser" into "creating a new superuser". This shows me that we now have a resource called Superuser. According to REST a Create action expects the params to be sent using POST. So we can just POST the id of the user to the Create action. In the Create action we just call admin.make_superuser(params[:id]).

Questions

What is your opinion about these solutions?
Would you solve them differently?

Feel free to ask me how I'd RESTfully implement other features.

Monday, July 7, 2008

MVC: How to write controllers

I've been working on many projects (web and desktop applications), and I must say that the way MVC is implemented varies a lot. That's probably a good thing, since every project is different. I believe, however, that there are some common patterns that could improve most of the MVC applications.

1. Keep it simple


Don't create additional tiers unless they are needed. Every class should clearly belong to either Controller, Model or a View.


2. Thin controller, fat model


This is probably the most useful lesson I learnt from the Rails world. I'm not going to elaborate on that here, as it was covered in many other places. Just move all your logic to models. It's that simple.


3. RESTful design

If you work on a web application, and still don't know what REST is, then go and read about it. It's easy and it will simplify your design a lot. Basically, it makes you think about your app as a set of resources. Every action in your controllers does one of the following with your resources: new, create, edit, update, show, destroy, index(list). At first, it can feel as a limitation, but it is the kind of limitation that actually helps you.

Many people argue that not everything can be RESTified. I agree that there are places where REST is not needed, however it's always a good exercise to find a RESTful solution.


4. Communication between controllers and models

As always, when you delegate some work further, you need to control the result somehow. The same happens when you move all the logic from controllers to models.

There are three ways (that I'm aware of) of implementing the communication between a controller and a model.

4a. Return codes

You use return codes when your model methods return not the data, but the result of an operation.

An example here is ActiveRecord::Base.save method which returns true when the save was successful, false otherwise.

order = Order.new(params[:order])
if order.save
flash[:message] = "The order was created!"
redirect_to order
else
flash[:errors] = "Something was wrong"
redirect_to new_order_path
end

In my opinion it's a good solution for simple situations. However, I don't like the "if" condition here and also it can go messy with more complex examples.

4b. Ask the object for its state


credit_card = CreditCard.new(params[:credit_card])
if credit_card.valid?
if order.capture_payment(credit_card)
flash[:message] = "success"
else
flash[:message] = "payment failure"
end
else
flash[:message] = "credit card validation error"
end
redirect_to order
This is slightly better than a), because the return code knowledge is hidden in method calls, but we still see the problem of many "if" statements.

4c. Don't ask, tell. Use custom exceptions

That's my favorite, because it eliminates all the "if"s from my controller code. I know it's just a syntactical change, but I prefer to see something like this in my controller, rather than the example above:

begin
order.pay(credit_card)
flash[:message] = "success"
rescue CreditCardNotValid
flash[:message] = "credit card validation error"
rescue PaymentFailed
flash[:message] = "payment failure"
end

Here we use custom exceptions to define a "protocol" for the communication between a model and a controller. Obviously we need to define the exceptions somewhere. I like to add them at the top of the associated model class:

class CreditCardNotValid < Exception
class PaymentFailed < Exception

class Order < ActiveRecord::Base
def pay(credit_card)
...
end
end


So, what are your favorite patterns for writing controllers?

Thursday, June 26, 2008

Git: working with branches

The way we work with git is that for every remote pair programming session we create a separate branch. We give it a name after the feature we're working on.


git checkout -b feature1


It automatically switches to this branch.

During our work we tend to checkin our changes quite often. We do it with:


git checkin -a -m "created some specs for class A"


After we finish our session, we do two things.
First, we merge our branch to master:


git checkout master
git merge feature1


Then, we delete the branch we were working on:


git branch -D feature1


That's it.

If you happen to delete the branch BEFORE you merge it, don't panic, there is a solution.
In order to undelete a branch just after you deleted it, do:


git checkout -b new_branch_name HEAD@{1}

Tuesday, June 10, 2008

Andrzej's Rails tips #11

Today I'm going to show you two tips, both related to Rails controllers.

Use the current_user object whenever you access its data

Instead of

@order = Order.find(params[:order_id])

do this:

@order = current_user.orders.find(params[:order_id])

Thanks to that, you don't have to check whether this order belongs to the user, you just need to handle ActiveRecord::RecordNotFound exception.

Move all the logic from your controller to the model

I know you already read this statement many times, but I will repeat it anyway.
In your actions you shouldn't manipulate your model objects, do it in the model class itself. Here's a simple example:

BAD CODE:

class OrdersController < ApplicationController
def update
@order = current_user.orders.find(params[:order_id])
if params[:order][:amount] > 0
@order.prepare_invoice
@order.send_email
@order.mark_as_paid
@order.notify_producers
end
end
end

BETTER CODE:

class OrdersController < ApplicationController
def update
@order = current_user.orders.find(params[:order_id])
@order.pay(params[:order][:amount])
end
end

class Order < ActiveRecord::Base
def pay(amount)
if amount > 0
prepare_invoice
send_email
mark_as_paid
notify_producers
end
end
end

Friday, June 6, 2008

Andrzej's Rails tips #10

form_for and namespace route

When you use a namespace route like the following:


map.namespace :admin do |a|
a.resources :users
end


then if you want to use form_for @user, this is the correct way:

<% form_for([:admin, @user]) do |f| %>

2 minutes with David Chelimsky and RSpec stories (video)

A short (2.34 minutes) description of RSpec stories.


David Chelimsky at Railsconf 2008 from Gregg Pollack on Vimeo.

Wednesday, May 21, 2008

Story Driven Development

If you are interested in testing and the TDD/BDD movements, then you may find Bryan Helmkamp presentation on "Story Driven Development" worth watching. Bryan explains the differences between unit testing and scenarios. He also shows Webrat, a tool he works on, which makes defining stories in Rails apps very easy.

I like the title that Bryan has chosen for the talk. The name "Story Driven Development" makes it clear that it's a lot about defining requirements, and not only about testing. Additionally, I think it's clear that it focuses on the acceptance level of tests without mentioning unit tests.

He also provides a quote from Robert Martin:


Unit tests: “Make sure you write the code right.”
Scenarios: “Make sure you write the right code.”

You can watch the slides here, or grab the PDF version (which has correct code examples).