One DateTime, two inputs… Doing it in Rails

Daniel Zanzini
Goiabada
Published in
3 min readMay 20, 2019

--

Let's assume you have an Appointment model and want to store the date and time it is scheduled. Since you need both date and time values the obvious choice is to go with a single datetime attribute named starts_at.

Now, you need to display it in a way the user can enter this value. Rails has a built-in datetime_select helper. It splits the datetime attribute into five smaller parts: year, month, day, hour and minute and creates one picker for each of these parts.

This would be enough if we wanted our users to select five different inputs, but our application design is different, it requires only two inputs: one for date and another one for time.

Our date and time inputs

This design prevented us of using Rails' built in solution, so we decided to create two virtual attributes to represent both date and time. Let's dive into this solution.

First of all, let's check our view with these two inputs. I'll skip the date picker part for clarity purposes.

Our new appointment view using two virtual attributes

In the snippet above there are two attributes which we’ll need to define: starts_at_time and starts_at_date. We'll do so in the Appointment model by creating a getter and a setter for each:

Our appointment model with virtual attributes defined

If we load our view now it should render correctly 🎉. But if you try to create the appointment using the view above you'll see that the values chosen for starts_at_date and starts_at_time are not being stored in starts_at. This happens because we still need to map the persistentstarts_at to the values stored into the two virtual attributes. The most common way of doing this is defining a before_save callback:

Our model using a callback to define the real value of starts_at

Now we've done it. Our inputs are working and we're saving the correct value to our starts_at attribute. We could stop right here, but I don't like this approach very much for a couple os reasons. Firstly, it can create problems with validations. In addition to that, we could see some unexpected behavior on other parts of the system due to the callback. Lastly, the starts_at attribute won't be set/overwritten until we actually save the appointment.

For this reason I've decided to go with a more straightforward solution that updates the value into starts_at whenever we set one of the values of starts_at_date or starts_at_time:

And that's it 🎉. A simple and easy solution when dealing with datetime fields with multiple inputs.

In our full solution we used these virtual attributes in a React component using the react-rails gem. In future posts I'll explain why and how we did it, so make sure to follow Guava's blog Goiabada and keep track of all our posts :)

--

--

Daniel Zanzini
Goiabada

Developer @ Guava Software. Passionate about Pão de Queijo. From Minas Gerais. Star Wars fan. Constantly happy, listening The Doors and drinking beer.