One of the biggest mental shifts for me while upgrading to Rails 4 was how model attributes are handled. Attributes are now whitelisted in the controller before being passed to the model for persistence, as opposed to being declared in the model via the attr_accessible method. This makes perfect sense from an application architecture and security standpoint — it addresses one of the biggest security flaws in previous versions of Rails — but I found myself having to constantly refer back to the docs to understand the syntax of strong parameters. These are my notes from researching strong parameters, which I will continue to update. Perhaps someone else will find them useful.
Let’s take a look at a simple model first.
class ContactsController < ActionController::Base def create Contact.create(contact_params) end private def contact_params params.require(:contact).permit(:name, :email, :phone) end end
Referencing params directly in any of the public methods will raise a mass-assignment exception. Instead, we encapsulate the required and optional parameters in a private method with the new require and permit methods.
One of the advantages to this new method of whitelisting attributes is that conditional logic based on user authentication and permissions can be applied directly to the params. This is especially useful if our application makes use of roles and permissions. For example, say we want to control how users of our application are allowed to be updated.
class UsersController < ActionController::Base def update User.save(user_params) end private def user_params if current_user.is_admin? params.require(:user).permit(:name, :email, :role) elsif current_user.id == params[:user][:id] params.require(:user).permit(:name, :email) end end end
Naturally, we will allow an admin to update anything about any user. However, if the current user is not an admin, but is trying to edit their own user record, we allow them to edit some, but not all attributes (in this case, updating the role attribute is restricted to admins).
Strong parameters also are capable of handling nested parameters, which may be present if one model’s controller accepts parameters for a related model. In our Contact example above, say we wanted a contact to have many contact methods, rather than a single email and phone attribute. We can achieve this with acceptsnestedattributes_for in the model, but how do we represent this with strong parameters?
class ContactsController < ActionController::Base def create Contact.create(contact_params) end private def contact_params params.require(:contact).permit(:name, :contact_methods => [:type, :value]) end end
In this example,
:contact_methods => [...] signifies that it permits an array of contact method objects to be present in the params. The keys within the array represent the attributes that a contact method may contain.
To be continued…