Migrating from Protected Attributes to Strong Parameters

In Rails3, we use attr_accessible or attr_protected(Protected Attributes) to white-list attributes of a model for mass assignment.
In Rails4, Protected Attributes was moved out as a Gem and similar feature was implemented at the controller level, which is now known as Strong Parameters.

We were migrating one of our project from Rails3 to Rails4 and decided to use Rails’ Strong Parameter instead of using the Protected Attributes Gem. While migrating from Protected Attributes to Strong Parameters we found ourself in a situation where we were repeating the same code. To elaborate, in Rails3 when we used Protected Attribute, all our white-listed attributes were at one place i.e. in the model itself. But when we used Strong Parameters, all these attributes of model came into the controllers, so if we had more than one controller dealing with same model, we were repeating the code for white-listing attributes in each controller.

For example:

We had controllers like OrdersController, Admin::OrdersController, Api::OrdersController etc. for the same resource, lets say Order. All these controllers were dealing with Order model, so we needed to white-list order’s attributes in these three controllers by something similar to:

# lib/controllers/orders_controllers.rb
class OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(:parameter1, :parameter2)
  end
end

# lib/controllers/admin/orders_controllers.rb
class Admin::OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(:parameter1, :parameter2)
  end
end

# lib/controllers/api/orders_controllers.rbo
class Api::OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(:parameter1, :parameter2)
  end
end

The pain point was that whenever there was a new column added or removed in Order model, we had to modify order_params in all these three controllers, as:

# lib/controllers/orders_controllers.rb
class OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(:parameter1, :parameter2, :parameter3)
  end
end

# lib/controllers/admin/orders_controllers.rb
class Admin::OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(:parameter1, :parameter2, :parameter3)
  end
end

# lib/controllers/api/orders_controllers.rb
class Api::OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(:parameter1, :parameter2, :parameter3)
  end
end

In order to manage white-listed attributes in a better way, we wanted to have a single place where we can manage all the white-listed attributes of all models instead of checking and updating every related controller. While discussing this problem with our team members, someone suggested to give a look at the way Spree has wrapped Strong Parameters into a module.

To leverage spree’s approach in a non-spree project:

We created a module, PermittedParameters, in lib/modules and created class variables as an array of permitted parameters of the resource. Something like this:


# lib/modules/permitted_parameters.rb
module PermittedParameters
  mattr_accessor :order_parameters

  @@order_parameters = [:parameter1, :parameter2]
end

Now we could use our PermittedParameters module in controllers as:

# lib/controllers/orders_controllers.rb
class OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(PermittedAttributes.order_parameters)
  end
end

# lib/controllers/admin/orders_controllers.rb
class Admin::OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(PermittedAttributes.order_parameters)
  end
end

# lib/controllers/api/orders_controllers.rb
class Api::OrdersController < ApplicationController
  def order_params
    params.require(:order).permit(PermittedAttributes.order_parameters)
  end
end

And to add or remove any permitted parameter, we just need to modify PermittedParameters Module.

# lib/modules/permitted_parameters.rb
module PermittedParameters
  mattr_accessor :order_parameters

  @@order_parameters = [:parameter1, :parameter2, :parameter3]
end

However, there can be some cases where we need to permit few attributes of associated model too. For example, to permit line_items attributes with order, we can define a StrongParameterHelpers module and create methods which will merge line_item_parameters in order_parameters and then we can use these methods in our controller.


# lib/modules/strong_parameter_helpers.rb
module StrongParameterHelpers
  def permitted_order_parameters
    PermittedParameters.order_parameters + [line_items_attributes: PermittedParameters.line_item_parameters]
  end
end

We can use the above module in the controllers as:

# lib/controllers/orders_controllers.rb
class OrdersController < ApplicationController
  include StrongParameterHelpers

  def order_params
    params.require(:order).permit(permitted_order_parameters)
  end
end

# lib/controllers/admin/orders_controllers.rb
class Admin::OrdersController < ApplicationController
  include StrongParameterHelpers

  def order_params
    params.require(:order).permit(permitted_order_parameters)
  end
end

# lib/controllers/api/orders_controllers.rb
class Api::OrdersController < ApplicationController
  include StrongParameterHelpers

  def order_params
    params.require(:order).permit(permitted_order_parameters)
  end
end

This way we can have all the attributes at one place which are easy to manage. Kindly let us know your thoughts on this approach, or if there is another better way to handle such cases.

One thought on “Migrating from Protected Attributes to Strong Parameters

Leave a Reply

Your email address will not be published. Required fields are marked *