Ruby on Rails: Date validations in a Booking model and disabling dates in the date-picker

Rui Freitas
Light the Fuse and Run
4 min readDec 15, 2018

Subjects covered in this article: Ruby on Rails custom validation rules, each validator, displaying a date-picker with flatpickr and writing a model instance method to list all unavailable dates to be passed into the date-picker.

For the past 2 years, I have been teaching web development to several batches of the Le Wagon coding bootcamp. We teach a full-stack curriculum and during the final two weeks the students work in teams to ship two different Rails apps to production, one of which is a two-sided marketplace concept, where there are users who put up something for rent or for sale, and users who book a product or a service (or make reservations). Because this project only lasts for one week, often we tell students to focus solely on one side of the story, and we ask them to assume, for the sake of simplicity, that the product/service is always available, regardless of whether or not there are bookings for selected dates. Well, I feel it’s time to address this issue and in this tutorial, I will show you how to handle date validation in Rails, display a date-picker with simple-form and flatpickr, and disable unavailable dates in the date-picker.

Our app has the following user stories and constraints:

  • Developers are available for hire and have a first_name, last_name, github_username and keep track of the total number of bookings in bookings_count via a counter cache.
  • Users can book developers for a period of time, which is determined by the date range between the start_date and the end_date.
  • The start_date cannot come after the end_date.
  • A Booking cannot be made if its start_date or end_date is an unavailable date (i.e., there is already another Booking for the same developer on that date).

Even though the date-picker will prevent the user from selecting disabled dates, it is still a good idea to implement these validation rules in the backend as well. We will use two custom validations in our Booking model:

The private method end_date_after_start_date validates that the end_date is not before the start_date. If it is, we add an error to the ActiveRecord object. The other custom validator (notice that the option being passed in line 5 of the above snippet availability: true is not a native rails validator) uses a different technique. We can write custom validators which inherit from ActiveModel::EachValidator:

I have placed this class in app/validators. Here, record is the object being validated, attribute is either start_date or end_date and value is whatever is currently assigned to start_date and end_date. We find all of the bookings for the record, and then build a list of ranges of unavailable dates (date_ranges is an Array of Arrays). If the value is included in any of these ranges, we add an error to the attribute with the indication that the said date is “not available”.

Adding a date-picker

Photo by Brooke Lark

For the form, we will use the simple_form gem and the flatpickr npm package. The form needs to be rendered with the date inputs as strings:

Flatpickr will plug into each of the date fields to display a nice date-picker, but before we do that, we need our backend to send to the front-end a list of all the unavailable dates. Accordingly to flatpickr’s documentation, it is possible to pass an option to disable dates as an array of ranges of dates [{ from: "2018-05-01, to: "2018-05-20"}]. Let’s add a method #unavailable_dates to our Developer model to do just that:

Finally, we add flatpickr to our project with yarn add flatpickr. We have two date inputs, so we need to generate two instances of flatpickr and we will disable the end_date input until we have a selected start_date. We will do this via flatpickr’s onChange event. Here, we will also implement the validation that the end_date needs to come after the start_date by specifying the minDate is the same as the selected start_date plus one. We stringify the unavailable dates and add them to the dataset of an HTML element as data-unavailable="<%= @developer.unavailable_dates.to_json %>. We then parse it in our JavaScript component and pass it into both flatpickr instances as the value for the option disable.

You can see a complete version of this app with tests for the Booking model here.

--

--

Rui Freitas
Light the Fuse and Run

Lead Teacher @ Le Wagon | Web Developer @ Light the Fuse and Run: http://lightthefuse.run/ | Photographer @ Rod Loboz: https://blog.rodloboz.com/