Hashrocket.com / blog

Bridging ActiveRecord and Mongoid

posted on and written by Kevin Wang in

Image 100x100 bb1ab55dc8c1931091dd2aadc9058475

When we pick technologies for clients, we use the right tool for the job and quite often roll custom solutions when necessary. In a recent project, we used both SQL ( ActiveRecord over Postgres) and NoSQL (Mongoid over MongoDB) solutions for the data persistence layer, modeled after the client’s data characteristics, and it worked out quite well.

While working on this project, we found ourselves needing to traverse between the ActiveRecord side and the Mongoid side quite often as “1..many” relationships, so we wrote this piece of glue code to make that easier.

class ActiveRecord::Base
  def self.has_many_documents(association_name)
    class_eval %<
      def #{association_name}
        #{association_name.to_s.singularize.classify}.where(#{name.underscore}_id: id)
      end
    >
  end
end

module Mongoid::ActiveRecordBridge
  extend ActiveSupport::Concern

  included do
    def self.belongs_to_record(association_name, options={})
      association_class = options[:class_name] || association_name.to_s.singularize.classify
      class_eval %<
        field :#{association_name}_id, type: Integer
        index(#{association_name}_id: 1)

        def #{association_name}
          @#{association_name} ||= #{association_class}.where(id: #{association_name}_id).first if #{association_name}_id
        end

        def #{association_name}=(object)
          @#{association_name} = object
          self.#{association_name}_id = object.try :id
        end
      >
    end
end

Put this in an initializer, then in your ActiveRecord model, you can use “has_many_documents”, and in your Mongoid document, you can use “belongs_to_record” and it just works! You could easily expand this to include “1..1” and “many..many” relationships.

class Property < ActiveRecord::Base
  ...  
  has_many_documents :default_rules
  ... 
end
class Rule
  include Mongoid::Document
  include Mongoid::ActiveRecordBridge
  ...  
  belongs_to_record :property
  belongs_to_record :updated_by, class_name: 'User'
  belongs_to_record :approved_by, class_name: 'User'
  ... 
end

Posted in Development and tagged with Ruby