in RubyGems
I built this gem based on my experience of implementing very similar (perhaps even identical) services in extremely different business domains. For example, I currently work in a Payroll SaaS, which calculates payslips using "concepts" (which are basically calculated formulas at a certain point in time, for example, a concept may be "health insurance discount", "performance bonus"). I supported a system like that when I worked at a delivery app, for supporting dynamic checkouts. I imagine you can use the same to calculate the premiums in a car insurance calculation, and a plethora of other domains.
The idea is simple; you mix the gem into your models
class Employee < ApplicationRecord
include ActsAsCalculator::Calculable
end
Then, you have two classes, ActsAsCalculator::Formula + ActsAsCalculator::FormulaVersion , so each formula version is accesible from a slice window (effective_from | effective_to),
formula = ActsAsCalculator::Formula.create!(key: "monthly_taxes", scope: "payroll")
puts employee.salary
5000.0
ActsAsCalculator::PublishFormulaVersion.(
formula: formula,
expression: "salary * 0.22",
effective_from: Date.new(2026, 1, 1),
effective_to: Date.new(2026, 6, 30),
variables: [{ name: "salary", source_type: "attribute" }]
)
You can now use these formulas to discount a tax from the employee salary. Important to note, formulas are parsed and calculated through Dentaku, which long ago already solved safe logic eval.
employee = Employee.last
result = employee.calculate(
:monthly_taxes,
as_of: Date.new(2026, 3, 15)
)
puts result.inspect
#<data ActsAsCalculator::Result value=0.11e4, breakdown={:expression=>"salary * 0.22", :inputs=>{"salary"=>0.5e4}, :value=>0.11e4}, formula_version=#<ActsAsCalculator::FormulaVersion id: 17, formula_id: 10, version_number: 6, expression: "salary * 0.22", effective_from: "2026-01-01", effective_to: "2026-06-30", status: "active", change_note: nil, created_at: "2026-09-01 01:26:15.204120000 +0000", updated_at: "2026-09-01 01:26:15.204120000 +0000">, as_of=Sun, 15 Mar 2026>
puts result.value
1100.0
(ActsAsCalculator::Result is a value object)
Then, a key feature, you can also template the result using sandboxed Liquid (this templating is functional but not yet feature-complete, I have a small roadmap still planned for this)
template = ActsAsCalculator::Template.create!(
key: "payslip", scope: "payroll", format: "text",
body: "Gross: {{ result.value | currency }}\n }}"
)
employee.render(:payslip, calculate: :monthly_taxes, as_of: Date.today)
=> "Gross: 1,100.00 "
The engine also has support (experimental for now) for JSON imports, and exports are still on the roadmap.
The engine also supports exposing HTTP endpoints for managing formulas/templates, but this is disabled by default — you opt in via a config flag before mounting it.ActsAsCalculator.configure { |c| c.enable_api = true }
The gem has also a supporting frontend engine, acts_as_calculator_editor, built upon Hotwire and Lexxy.
A couple of notes:
the base gem acts_as_calculator is human-made, written from my experience of building from scratch an identical but way-less generic production-used implementation of this, so most of the architecture is already proven at scale. I used AI to add a couple helper methods, to do code review, to complete documentation and to write several unit tests. The supporting gem acts_as_calculator_editor was fully built with an AI agentic workflow.
I haven't yet published the 1.0.0 version since I still have a key feature to implement (chaineable formulas, so a formula is able to call other formulas directly).
The gem also exposes a lot of apportionment, distribution and allocation helper strategies (:proportional, :equal, :largest_remainder), which are one of the main motivations I had for creating this gem. In the v1 version, I will add comprehensive api docs for them.
The gem is also intended to be used with ActiveRecord, but should be framework agnostic for most things.
If you have any feedback to give about the gem and the idea, please leave a comment. This is my first experience building something for the OSS space that has given me so much, so feel free to contribute and criticize.