Multiparameter Attributes Handler

Background

Rails form helpers for time, datetime and date return multiparameter attributes that ActiveRecord objects are able to convert into parent attributes.

For example

date_select(:thing, :last_read)

when set to 24th June 2004, will generate

'thing' => {
     "last_read(1i)" => "2004", 
     "last_read(2i)" => "6", 
     "last_read(3i)" => "24"
}

ActiveRecord understands this and converts these key/value pairs into a date object that is passed to thing#last_read= method.

A solution without ActiveRecord

Multiparameter Attributes Handler allows other objects to do the same.

thing_params = MultiparameterAttributesHandler.manipulate_all(params[:thing])

This creates a last_read key with a time object derived from the values

thing_params == {
    "last_read(1i)" => "2004", 
    "last_read(2i)" => "6", 
    "last_read(3i)" => "24",
    "last_read"     => Time.local('2004', '6', '24')
}

Modifying the output

If you need to modify the derived values, pass a block to manipulate_all, and that will be called on each output value.

thing_params = MultiparameterAttributesHandler.manipulate_all(params[:thing], &:to_s)

Will create

thing_params == {
    "last_read(1i)" => "2004", 
    "last_read(2i)" => "6", 
    "last_read(3i)" => "24",
    "last_read"     => Time.local('2004', '6', '24').to_s
}

An ActiveResource example

So for a ActiveResource Thing, this functionality can be added by over-riding the attributes setter:

class Thing < ActiveResource::Base
  def attributes=(params)
    super MultiparameterAttributesHandler.manipulate_all(params)
  end
end

Use within a Rails controller

However, as the params splitting and rebuilding is happening in the views and controller space, it might well make more sense to do the manipulation in the controller:

class ThingsController < ApplicationController

  ....

  def update
    @thing = Thing.find(params[:id])
    @property.update_attributes thing_params
    redirect_to @property
  end

  private
  def thing_params
    MultiparameterAttributesHandler.manipulate_all(params[:thing])
  end
end

Only setter affected

Note that this gem provides handling of the parameters received from the form submission. For the form helpers to work, they need to be passed a date or time.

One solution is to override the getter in the model. So for the Thing example above:

class Thing < ActiveResource::Base

  def last_read
    Time.parse(super)
  end
end

Rails form date helpers will then work for last_read:

date_select(:thing, :last_read)

Installation

This functionality is made available via the multiparameter_attributes_handler gem. So add this to your gemfile:

gem 'multiparameter_attributes_handler'