Fun with mongomapper serialization

This post covers mongo_mapper v 0.8.2

One cool feature of mongomapper I understood recently is automatic object serialization. What it does is to allow developers to write code to map their own objects into a mongodb database.
It works in a very simple way: every object you want to save to mongodb has to implement a very simple interface, like this:

class MyOwnDatatype 
  def self.to_mongo(value)
  ...  
  end

  def self.from_mongo(value)
  ...
  end
end

Mongomapper by default extends some basic ruby data types to make them compliant to this interface.
You can find them here

The default data types are just the basic stuff, the kind of stuff a ruby developer expects to be able to save in any database. Extending someone of those data types, you can overcome little problems in an elegant way. Imagine you have the following model:

class BlogPost 
  include MongoMapper::Document 
  key :title, String
  key :content, String 
  key :tags, Array 
end

A classic blog post model with tags as an array in the document itself. It works fine, but in my controller I want to be able to pass tags as a comma separated string, like in the following example:

>> b = BlogPost.new(:title => 'a post',  :content => 'the post', 
  :tags => 'music,sport')
>> b.save  
>> b.tags 
=> ["music,sport"]

As you can see, we got an array with the full string passed.
Coming from a lot of ActiveRecord coding, I thought to implement a before_validation callback at first (yes, we have them in mongomapper too). But that’s the hard way. The easy way is to subclass Array into a SuperTagsArray:

class SuperTagsArray < Array
  
  def self.to_mongo(value)
    _new_value = []
    value.each do |array_element|
      array_element.split(',').each do |sub_item|
        _new_value << sub_item 
      end
    end
    _new_value
  end

  def self.from_mongo(value)
    value || []
  end
end

and in our model we can update the tags mapped object into:

class BlogPost 
  include MongoMapper::Document 
  key :title, String
  key :content, String 
  key :tags, SuperTagsArray 
end

From now on, we can save tags as either an array or a string:

>> b = BlogPost.new(:title => 'a post', :content => 'the post', :tags => 'music,sport')
=> #
>> b.tags
=> ["music", "sport"]
>> b.tags = ['rock', 'pop']
=> ["rock", "pop"]
>> b.tags = 'fun,serialization'
=> "fun,serialization"
>> b.save
=> true
>> b.tags
=> ["fun", "serialization"]

The end. Thanks for reading.

Some related slides

An interesting presentation of mongomapper, which also talks about this feature: http://www.slideshare.net/jnunemaker/mongomapper-mapping-ruby-to-and-from-mongo

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s