Last Updated: February 25, 2016
·
3.501K
· we4tech

Active Record Attribute Serialization with Custom Class

When we were porting our old application to a brand new ruby stack keeping the underlying database intact. We could use rails active record serialization feature quite intensely. Here is a simple example -

We had a string attribute "width" which was expecting value something like this "10 Inches" or "20 Cm".

Previously it was manually taking two separate inputs and concatenating them together and store them in a single string attribute.

We have remade the same thing but in bit better way using ActiveRecord serialization, here goes the code -

class SomeModel < ActiveRecord::Base
  serialize :width, ValueAndUnit
end

Serializer class which dumps and loads the data based on our expected format -

class ValueAndUnit
  attr_reader :value, :unit

  def initialize(value = nil, unit = nil)
    @value, @unit = value, unit
  end

  class << self

    # Load serialized data into the model scope with our expected transformation. 
    # ie. we expect successful execution could return ValueAndUnit instance from width method.
    #
    # @return [ValueAndUnit] The instance of ValueAndUnit from cleaned out string data. 
    def load(data)
      # Make sure data is compliant with our expected data format 
      if data.present? && (matched_data = data.match(/([\d.]+)\s+(.+)/))
        self.new(matched_data[1], matched_data[2])
      else
        self.new
      end
    end

    # Dump the ValueAndUnit instance into the storable (serialized) format. In our case we want it to be stored as "{value} {unit}" ie. "10 Inches". 
    #
    # @return [String] The transformed string representation of the data.
    def dump(data)
      data.to_s
    end
  end

  def to_s
    "#{self.value} #{self.unit}"
  end
end

Here goes few sample usages -

record = SomeModel.new
record.width.unit = 'Inches'
record.width.value = 10
record.save

While we are pulling an existing record

record = SomeModel.find(1)
puts record.width.unit    # => 'Inches'
puts record.width.value  # => '10