Pablo Fernandez
Pablo Fernandez

Reputation: 287390

Custom serialization for fields in Rails

Is there a way to have a custom serialization for fields in rails, a method that runs when a field is saved and loaded to convert from/to a string which is what ultimately is saved on the database.

Specifically what I want to do is have a field of type symbol like gender, with possible values :male and :female storing "male" and "female" on the database. There are some workarounds, like:

def gender
  read_attribute(:gender).try(:to_sym)
end

but that leaves obj.attributes unchanged, so it's a leaky abstraction.

Upvotes: 9

Views: 11427

Answers (7)

JacobEvelyn
JacobEvelyn

Reputation: 3971

We just released a gem (AttributeHelpers) that does exactly this. Disclaimer: I am a maintainer for the gem.

It allows you to call attr_symbol :gender in your class definition and the serialization happens automagically.

Upvotes: 0

Maciej Biłas
Maciej Biłas

Reputation: 1976

You can do it in Rails 3.1. The object you want to serialize has to reply to load and dump methods.

Here is an example of serializing a string in Base64.

class User < ActiveRecord::Base
  class Base64
    def load(text)
      return unless text
      text.unpack('m').first
    end

    def dump(text)
      [text].pack 'm'
    end
  end

  serialize :bank_account_number, Base64.new
end

For more details see: http://archives.edgerails.info/articles/what-s-new-in-edge-rails/2011/03/09/custom-activerecord-attribute-serialization/index.html

Upvotes: 11

Cory Foy
Cory Foy

Reputation: 7212

From http://blog.quov.is/2012/05/01/custom-activerecord-attribute-serializers/

class Recipe < ActiveRecord::Base
  serialize :ingredients, IngredientsList
end

class IngredientsList < Array
  def self.dump(ingredients)
    ingredients ? ingredients.join("\n") : nil
  end

  def self.load(ingredients)
    ingredients ? new(ingredients.split("\n")) : nil
  end
end

Upvotes: 3

loosecannon
loosecannon

Reputation: 7803

def whachamacallit
  read_attribute("whachamacallit").to_sym
end
def whachamacallit=(name)
  write_attribute("whachamacallit", name.to_s)
end

store them as stings in the database, but extract them as symbols when you pull them out then convert back before you save. would work with any number or combination of strings / symbols.

to limit it to only a select few

validates_inclusion_of :whachamacallit, :in => [ :male, :female, :unknown, :hidden ]

Upvotes: 4

loosecannon
loosecannon

Reputation: 7803

well for just male/female you could just do a Boolean column like male and if it was false assume that meant female, add wrapper methods for it

def female? 
  return !self.male?
end

Upvotes: 0

PeterWong
PeterWong

Reputation: 16011

You could use serialize method inside the model. Please reference to this link: http://api.rubyonrails.org/classes/ActiveRecord/Base.html (ps. search keyword "serialize" in that page ;D)

In short, you could do this:

class YourModel < ActiveRecord::Base
  serialize :db_field
end

Rails would automatically serialize the field before saving to database, and deserialize it after fetched from the database.

Upvotes: 0

loosecannon
loosecannon

Reputation: 7803

you can define the models to_xml for a model and it will do that http://api.rubyonrails.org/classes/ActiveRecord/Serialization.html

its possible to define Marshall.dump and put in that way i think, but its something to look into

Upvotes: 0

Related Questions