RobertJoseph
RobertJoseph

Reputation: 8158

Convert hours & minutes to seconds before saving a record

I am still very new to Rails. Thus far all of my form elements have mapped directly to their underlying Rails model counterparts. In my current situation though that isn't possible.

My model has an event_duration field which stores a duration in seconds (integer type).

In my form though I want to have two text fields: hours and minutes. When the form is submitted I want to convert those values into seconds and store the resulting value in the model.

What is the best way to accomplish this? (Thank you in advance)

Upvotes: 1

Views: 409

Answers (3)

Drenmi
Drenmi

Reputation: 8787

Another way to accomplish this is what's called a Form Object, which is essentially a "mock model" that you pass to your controller in lieu of your model, which then handles form rendering and persistence.

The benefits of this is you don't need any logic in your controllers, and you can eliminate pesky callbacks from your models. They also work exceptionally well for getting rid of those accepts_nested_attributes_for traps when dealing with multiple models.

It can look something like this:

# app/forms/event_form.rb
class EventForm
  include ActiveModel::Model
  include ActiveModel::Validations

  attr_accessor :duration_hours, :duration_minutes, ... # Event parameters

  validates ... # Your validations

  def save
    if valid?
      persist!
    else
      false
    end
  end

  private

  def persist!
    event = Event.new(...) # Event parameters
    event.event_duration = ((duration_hours * 60) + duration_minutes) * 60
    event.save
  end
end

By including some of the ActiveModel modules, you can borrow some functionality, like initializing from a Hash, and validations.

And in your controller:

# app/controllers/events_controller.rb
def new
  @event = EventForm.new
end

def create
  @event = EventForm.new(event_params).save
end

There are also Gems that you can use to create form objects, like reform, if you don't want to roll your own.

Upvotes: 1

Sergei Stralenia
Sergei Stralenia

Reputation: 865

You can create virtual attributes in your model:

class Model < ActiveRecord::Base
  attr_reader :hours_event_duration, :minutes_event_duration, :use_diff_event_duration_variables
  before_save :correct_save_event_duration

  def initialize(*args)
    @hours_event_duration = @minutes_event_duration = 0
    @use_diff_event_duration_variables = false
    super
  end

  ... 

  protected

  def correct_save_event_duration
    self.event_duration = @hours_event_duration * 1.hour + @minutes_event_duration * 1.minute if @use_diff_event_duration_variables
  end
end

And in your form:

f.hidden_field :use_diff_event_duration_variables, true
f.text_field :hours_event_duration
f.text_field :minutes_event_duration

Upvotes: 1

MrYoshiji
MrYoshiji

Reputation: 54902

You can create a 'hook' in your controller to update the params right before the record is created/updated:

class EventsController < ApplicationController
  before_filter :sanitize_durations_in_params, only: [:create, :update]

  # ...

  def sanitize_durations_in_params
    hours = params[:event].delete(:hours).to_i
    minutes = params[:event].delete(:minutes).to_i
    minutes = minutes + hours * 60
    seconds = minutes * 60
    params[:event][:event_duration] = seconds
  end
end

This code implies that you have an input with name event[:hours] and another one with event[:minutes] in your form. If you already have an input with name event[:event_duration], it will be overwriten.

Upvotes: 2

Related Questions