Database engines like PostgreSQL support creating custom data types. To use a real example here, I have a custom type for storing monetary amounts that include a currency. I can then use this in any table.
CREATE DOMAIN public.iso3char AS character varying(3)
CONSTRAINT iso3char_check CHECK (((VALUE IS NULL) OR ((VALUE)::text ~ '^[A-Z]{3}$'::text)));
CREATE TYPE public.currency_amount AS (fractional bigint, currency public.iso3char);
CREATE TABLE accounts (id bigint, balance currency_amount);
Rails supports this. We can create our own type derived from ActiveRecord::Type::Value
that implements cast
and serialize
.
The documented way to use this is with the attribute
macro in your model:
class Account
attribute :amount, CurrencyAmountType.new
end
This works, but it’s not DRY. You end up having to do this for every attribute in every model that uses the custom type. Something pretty generic like my currency_amount
could end up with dozens of uses.
I pored over the Rails source and couldn’t find a way to globally register custom types. So, I rolled my own. Question is, is there a reason not to be doing this? I’m wondering if it would make sense to submit a PR to make this standard functionality.
# config/initializers/active_record_custom_types.rb
ActiveSupport.on_load(:active_record) do
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.singleton_class.prepend(Module.new do
def initialize_type_map(m)
super(m)
m.register_type("currency_amount", CurrencyAmountType.new)
end
end)
end