Reputation: 287390
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
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
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
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
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
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
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
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