Fencepost


When it comes to writing and maintianing software, I’m lazy. I don’t like doing mindless work. If I find myself doing the same thing more than twice, I will find a way to automate that thing in some way so I don’t have to keep repeating it.

The Problem

Ruby on Rails changed the way it handles input from a web request from version 3.2 to 4.0. It used to be that you defined what you allowed to be set via update_attributes in the model itself by using attr_accessible. This was all well and good until the new version of Rails removed that mechanism for model security in favor of something called strong_parameters.

Strong Parameters are a good thing. They increase the flexibility of the application by letting you define which parameters are allowed to be passed to your models outside the models themselves.

The Problem is that I have more than a few applications I need to upgrade to Rails 4, and that means I have to go into every controller in each of those applications and create my strong parameters declarations. Not only that: I’m lazy. And that means I need to automate this in some way or I will go insane.

Enter Fencepost

I solved this problem by creating a gem called Fencepost. This gem solves the strong parameters problem in an elegant way, freeing you from needless declarations in your controllers, and giving you access to those declarations in an intuitive way. The following is from the README for the gem: on Github

Installation

Add fencepost to your Gemfile

gem 'fencepost'

Configuration

rails g fencepost_config

This creates a yaml map of your models in config/fencepost.yml.

You can re-run the initializer at any time. You will be asked if you want to overwrite the existing config. “Y” will force an overwrite of the file, and you will need to re-comment out any attributes you want to remove by default.

Default Configuration

The yaml map is where you can edit the allowable attributes for your models. In the 80/20 rule, this would be the %80. Removing attributes in the configuration yaml lets you set reasonable defaults for strong parameter behavior. This map is read one time during intialization and stored in the Fencepost model graph (a class-level variable)

Dev Mode

During the early stages of development where your code is in flux, you can set dev_mode=true in config/initializers/fencepost.rb. dev mode will eager load and read in all your models dynamically every time the class is instantiated. (Ignoring the yaml in the initializer)

Usage

The gem creates a fencepost method in your contollers. This returns a Fencepost object that has read your models and given you access to strong params for any ActiveRecord model in your application.

Simplest Case

# app/controllers/people_controller
def create
  @person = Person.create(fencepost.person_params)
end

Simple allow / deny for top level model

In this example, the Person model allows height and weight by default, but does NOT allow dob (date of birth). In this example we want to allow date of birth but deny weight.

# app/controllers/people_controller
def create
  @person = Person.create(fencepost.allow(:dob).deny(:weight).person_params)
end

More complex allow / deny for nested models

In this example, the Person model has a collection of addresses. We want to deny latitude and longitude from the acceptable attributes.

# app/controllers/people_controller
def create
  @person = Person.create(fencepost.deny(addresses_attributes: [:latitude, :longitude]).person_params)
end

TLDR

This gem creates strong parameter declarations based on you ActiveRecord models. It makes this configuration available in all your controllers, and gives you the flexibility to allow or deny attributes on the fly.

Please have a look at the gem and feel free to submit a pull request if you can make it better.


comments powered by Disqus