In developing bracket-lab, I needed a 1-indexed bit field of 63 bits, stored as an unsigned 63-bit value in an int64 column. ActiveRecord’s custom types made this clean.

A 1-indexed bit field means the 64th bit is significant but not as a two’s complement sign bit. Languages like Rust have nice unsigned integer types, but that’s not an option here. The data itself fits in u63 (positive i64) space, so my solution is to right-shift before storing to the DB and left-shift when reading.

First, define the type at app/types. This folder is not auto-generated with rails new but is autoloaded at boot.

Add an initializer to register the type with ActiveRecord.

Then, declare the type on any model attribute that uses it. For example, Tournament has :game_decisions and :game_mask that are both stored as int64.

Now everything just works. The code uses the attributes as 1-indexed bit fields and AR takes care of casting and serialization. This works for all ActiveRecord operations like find_or_create_by.

Note: if you need the raw DB value, you can access it with read_attribute_before_type_cast, which is useful when you need bitwise logic executed in the DB engine.