How many times have you cursed at a date form field because it rejected 03/01/22 and wanted 03/01/2023? Number inputs are the same—what if instead of copy & pasting from the calculator app you could enter 79 / 2 in a field and have it evaluate to 39.5? Inputomatic makes this possible in your Rails app, which means less friction in your UIs for the people who use your app.
It’s 2023 and here’s the best answer web browsers have given us to enter dates & times on popular browsers like Safari.

And Chrome.

What’s maddening about them is they force people to conform to the computer. It should be the other way around—the computer should be more forgiving to the messiness of the human condition and conform to our imprecise inputs.
Let’s see if we can do better in one of the worlds most exciting and reviled class of applications: the expense reports!
An Expense Reporting App
By far one of the most frustrating experiences of our lives is entering expense report data. Setting the date involves clicking on a bunch of different arrows, buttons, etc. to set the date on some poorly designed picker. Then, there’s always some policy that says “expense half of your cellphone bill”, we have to open a calculator to do simple calculations like 124.99 / 2.
What if we could build forms that made sense of inputs like Last week for dates and 124.99 / 2 for numbers?

That’s a little better! When we save it we get what we’d expect: a date from last week and the calculated cost.

Let’s try creating an expense from 900 days ago, which is outside of the “90 days ago” policy in our expense account:

Rails preserves the input value until the validation is cleared, then it persists the actual date or number to the database.
Try the demo app and enter a new expense. I promise it will be one of the most luxurious expenses you’ve ever entered. You can also clone the demo app and run it in your own machine or deploy it to Fly.io.
How does that work exactly?
The ActiveModel attribute class method
When we look at the expense.rb Active Record model code, we see the class method attribute and pass it an Inputomatic class the casts the values to and from the model, form, and the database.
class Expense < ApplicationRecord
attribute :purchased_at, Inputomatic::DateTime.new
attribute :amount, Inputomatic::Number.new
validates :purchased_at,
inclusion: {
in: Range.new(90.days.ago, 90.days.from_now),
message: -> (object, data) { "#{data[:value].to_date} is not between #{90.days.ago.to_date} and #{90.days.from_now.to_date}" }
}
end
Inputomatic is a gem I created that defines a few casting behaviors between the value in the Active Record model and the database. Turns out you can do some pretty nifty things with ActiveRecord::Type classes that make forms more of a joy to use.
The ActiveRecord::Type::Value#cast_value method
Let’s have a look at the Inputomatic::Number class, which is a subclass of activerecord::Type::Value:
class Number < ActiveRecord::Type::Value
def cast_value(value)
value.is_a?(String) ? ArithmeticInterpreter.new(value).parse : value
end
end
The cast_value method is where all the magic happens. If the value is a String, it’s coming from a form as an arithmetic expression. Inputomatic’s arithmetic interpreter (which was written by my pair bear, GPT-4) parses the string 124.99/2 into an expression that it can safely evaluate (never ever run form input through Kernel#eval, which Stack Overflow would tell you to do) into a number.
Once we have a number, case closed! We have a float, integer, or decimal that can be persisted to the database.
If the value is not a String, then we just return the value assuming it will be handled by any upstream handlers. In this case we’ll assume its an Integer or Float.
Fly.io ❤️ Rails
Fly.io is a great way to run your Rails app close to your users. It’s really easy to get started. You can be running in minutes.
Deploy a Rails app today! →