Search code examples
ruby-on-railsactiverecordactivemodel

Register an ActiveModel attribute type with the ability to pass options to it?


Is it possible to use ActiveModel::Type.register in a way that still lets you pass arguments?

To demonstrate, is it possible to turn this:

class Action < ApplicationRecord
  attribute :type, Types::VirtualStringType.new(suffix: 'Action')
end

into something like

class Action < ApplicationRecord
  attribute :type, :virtual_string, suffix: 'Action'
end

Solution

  • You can register a new type by creating config/initializers/active_record_types.rb

    Rails.application.config.to_prepare do
      ActiveRecord::Type.register(:virtual_string, Types::VirtualStringType)
    end
    

    The type class would have to accept the options/arguments in initialize like this:

    def initialize(**args)
      @suffix = args[:suffix].to_s
    end
    

    Here's a sample implementation:

    module Types
      class VirtualStringType < ActiveRecord::Type::String
        def initialize(**args)
          @suffix = args[:suffix].to_s
        end
    
        def cast_value(value)
          value.to_s + @suffix unless value.nil?
        end
    
        def serialize(value)
          value.chomp(@suffix) unless value.nil?
        end
    
        def changed_in_place?(original_value_for_database, value)
          original_value_for_database != serialize(value)
        end
      end
    end