Strong Parameters The Right Way

StrongParameters is a great gem and it comes with Rails 4 by default. Currently, there are two patterns to work with your attributes.

1. Creating a private method on your controller which returns the whitelisted attributes. #

# app/controllers/people_controller.rb
class PeopleController < ActionController::Base

  def update
    person = current_account.people.find(params[:id])
    person.update_attributes!(person_params)
    redirect_to person
  end

  private

    def person_params
      params.require(:person).permit(:name, :age)
    end
end
Pros #
  1. If your person_params logic is simple as the example shows. That’s pretty much, just call the method and it will do the job.
Cons #

If your person_params logic grows because, let say you want to permit certain params attributes if your user is an admin and another params attributes if it’s just a regular user:

  1. You’ll be adding more complexity to your controller.
  2. You wont be able to reuse these logic.
  3. Your controller will be harder to test.

2. Extracting your params logic into a class. #

# app/models/permitted_params.rb
class PermittedParams < Struct.new(:params)
  def person
    valid_keys = [:name, :email, :message]
    params.slice(*valid_keys).require(:person).permit(*valid_keys)
  end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery

  private
    def permitted_params
      @permitted_params ||= PermittedParams.new(params)
    end
end
# app/controllers/people_controller.rb
class PeopleController < ActionController::Base

  def update
    person = current_account.people.find(params[:id])
    person.update_attributes!(permitted_params.person)
    redirect_to person
  end

end

These approach, in my opinion is better than the first one. You should checkout the StrongParameters’ Railscasts for more deatils if you liked these approach.

Pros #
  1. You’ll decouple your controller’s logic from your params logic, in which case, you’ll keep your controllers dry.
  2. Your controllers will be easier to test.
  3. You’ll avoid code duplication.
Cons #

If you have a lot a resources, because applications usually grows and not the other way:

  1. Your PermittedParams class will end up being a huge and will be hard to maintain.
  2. You’ll be breaking the Single Responsability Principle.

So, which is exactly the best way to handle my parameters? #

The best way to handle your resource params would be to have a PermittedParams class type for each resource. Wouldn’t be better to have your resources params’ logic in separate classes like this?:

# app/parameters/user_parameters.rb
class UserParameters < Struct.new(:params)

  def permit
    params.require(:user).permit(:name, :age)
  end

end
class ApplicationController < ActionController::Base
  protect_from_forgery

  # Returns an instance of the params class related to the Controller from which was called
  def permitted_params
    @permitted_params ||= permitted_params_class.new(params)
  end

  # Returns the params class related to the Controller from which was called
  def permitted_params_class
    class_name                = params[:controller].to_s.singularize
    formatted_class_name      = class_name.camelcase
    @permitted_params_class ||= "#{formatted_class_name}Parameters".constantize
  end
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController

  def create
    User.create(permitted_params.permit)
  end

end
Pros #
  1. You’ll decouple your controller’s logic from your params logic, in which case, you’ll keep your controllers dry.
  2. Your controllers will be easier to test.
  3. You’ll avoid code duplication.
  4. Your params classes will be smaller, easier to test and maintain.
  5. Your params classes will not violate the Single Responsability Principle.
Cons #

Some people might think that this is an over-engineering work if your controllers are simple and you have to deal with params like the first approach I described.

ActionParameter #

It’s a gem I created to implement this last pattern with some other features. I encourage you to take a look at it and try it, I’m pretty sure it would be helpful.

Conclusion #

All three patterns are good, you should choose the one that fits your needs. But bear in mind that applications and their logic always tends to grow and to get more complex.

 
107
Kudos
 
107
Kudos

Now read this

Most Common Mistakes On Legacy Rails Apps

Lately I’ve been supporting several legacy projects. As many of you may know, working on legacy projects sucks most of the time, usually because most of the code is ugly and difficult to understand or read. I decided to make a list of... Continue →