I have a Rails app and I want to validate some of the incoming parameters. My Task model has a User relation and only certain Users are allowed to be attached to a Task; likewise only permitted Users can complete or delete a Task. Should I filter the user ID parameters or attach a before-filter to deal with it?
One of the key things with Object-Orientated programming is you need to think in terms of who is telling whom to do what.
In this case, your user is sending a message to the router, which in turn is forwarding that message to the controller. What is the controller then doing?
It’s telling the Task model to create a new instance, with the given User ID. It’s telling a Task instance to complete itself. It’s telling a Task instance to delete itself.
I like to think that the job of a Controller is of mediating input and output – it receives a request from the outside world (via a Router), it finds the relevant Models, tells them to do stuff, then takes those Models and sends them to a view, so that the view can render a response.
In this case, I don’t think it’s the Controller’s job to validate the parameters; in a traditional Rails app, it would be the Task’s job to decide if the given user can be attached to a new task, if the given user can complete a task, if the given user can delete a task. So I would add a validation to the Task model to only allow the correct Users to be attached, and I would add custom “complete” and “delete” methods that check the incoming User is allowed to perform those tasks:
class Task < ActiveRecord::Base
belongs_to :completed_by, class_name: 'User'
def complete user: nil
update_attributes! completed_at: Date.today, completed_by: user
def delete_task user: nil
raise CannotDelete unless valid_users.include? user
class CannotDelete < StandardError
errors.add :user, "is not allowed" unless valid_users.include? self.user
[#some array of users]
However, in my current apps, I wouldn’t use that pattern at all. It strikes me that what we are talking about here is a permissions issue – does the current user have permission to create/complete/delete a Task? So I would create a Command for each of those actions, get the Controller to tell the Command to do its stuff (telling it which Task to act on and who the User is) and it’s then up to the Command to figure out the permissions (probably by invoking a sub-Command, breaking a complex process into lots of individual simple steps). I then use Discovery Tests to figure out how those Commands relate to each other.