Rails 4, Postgres Array + Hstore, and Strong Params Guide by Someone That Likes You

by Zach Briggs

I swear that every guide dealing with Rails 4, Strong Parameters, and the new support for Postgres Array and Hstore types is written by somebody who doesn't like me very much. That's OK, we're in the trust tree now. This guide won't leave you stranded by the side of the road at 4 AM without any gas, cell reception, and silent failures. 

The default behavior of Rails 4's Strong Parameters combined with the new Postgres array support results in everybody's least favorite flavor of failure: the silent type.  If you're unfamiliar with how Strong Params work, head over to Remarkable Labs for a primer. The following example is pretty typical scaffold generated code found at the bottom of a Rails 4 controller. 

def due_date_params
params.require(:due_date).permit(:name, :tags, :recur)
end

Unfortunately it is flawed when :tags happens to be a Postgres array.

  DueDate Load (0.5ms)  SELECT "due_dates".* FROM "due_dates" WHERE "due_dates"."id" = $1 LIMIT 1  [["id", "2"]]
Unpermitted parameters: tags
(0.2ms) BEGIN
(0.2ms) COMMIT

Tracking this one down was devilishly hard, but thanks to Robert Gogolok for posting his slide deck, we now have our answer. We need to specify that :tags takes an array using this syntax:

def due_date_params
params.require(:due_date).permit(:name, { tags: [] }, :recur)
end

The tags array works now, but :recur actually refers to an hstore column in our table. Rather than needing to use custom getters and setters like savages, Rails 4 provides store_accessor for our convenience. Thanks once again to Remarkable Labs for posting the syntax.

class DueDate < ActiveRecord::Base
store_accessor :recur, :frequency
store_accessor :recur, :day_of_week
store_accessor :recur, :day_of_month
end

Back at whitelist ranch, we no longer care about the parent hstore column and just list the children right next to the actual top level attributes.

def due_date_params
params.require(:due_date).permit(
:name { :tags => [] },
:day_of_month, :day_of_week, :frequency
)
end

And that should be just enough to wire up basic CRUD to our fancy new Postgres types. I've put together a lengthy gist to provide some additional context over here.