vidibus-inheritance

This gem is part of the open source SOA framework Vidibus: www.vidibus.org

It allows inheritance of objects and is depends on Rails 3 and Mongoid. It will update all attributes and embedded documents of inheritors when ancestor gets changed. Custom attributes (mutations) of inheritors will not be overridden, unless a :reset option is set.

Installation

Add the dependency to the Gemfile of your application:

gem "vidibus-inheritance"

Then call bundle install on your console.

Usage

Include the Vidibus::Uuid::Inheritance module in your Mongoid model:

class Model
  include Mongoid::Document
  include Vidibus::Uuid::Mongoid
  include Vidibus::Inheritance::Mongoid
  field :name
end

To establish an inheritance relationship, add ancestor to a model of same class:

ancestor = Model.create(:name => "Anna")

# To establish a relation, call #inherit_from!
inheritor = Model.new
inheritor.inherit_from!(ancestor)

# ...or set :ancestor attribute
inheritor = Model.create(:ancestor => ancestor)

Mongoid configuration

When inheriting, the attribute :_reference_id will be set on embedded documents of inherited objects. So make sure this field is available or Mongoid is configured to allow dynamic fields. Add to config/mongoid.yml:

allow_dynamic_fields: true

Acquired attributes

All attributes will be inherited, except these ACQUIRED_ATTRIBUTES:

_id
_type
uuid
ancestor_uuid
mutated_attributes
mutated
created_at
updated_at
version
versions

You may overwrite acquired attributes by defining a method on your inherited document and its embedded documents:

def acquired_attributes
  Vidibus::Inheritance::Mongoid::ACQUIRED_ATTRIBUTES + %w[my custom values]
end

Manage mutations of embedded documents

All custom changes on inherited objects will be stored in #mutated_attributes. On embedded documents of inherited objects, however, mutations of attributes will not be tracked. But you may flag a document as mutated when applying custom values:

class Job
  include Mongoid::Document
  field :salary
  field :mutated, :type => Boolean
  embedded_in :model, :inverse_of => :jobs

  def set_custom_salary(amount)
    self.salary = amount
    self.mutated = true
  end
end

To control how inherited data will be updated, you may define a callback method and check #mutated:

def update_inherited_attributes(attrs)
  attrs.delete("salary") if mutated? # preserve custom salary
  update_attributes(attrs)
end

TODO

  • Removed items will be re-added when inheritance is performed again. Introduce paranoid behaviour for embedded collection items? Or add a list of deleted associations like _destroyed_children?

  • Use delayed_job for inheritance across a huge pedigree.

  • Fix relations when deleting an ancestor.

  • Rewrite root_ancestor_id when deleting the root ancestor.

Copyright © 2010 Andre Pankratz. See LICENSE for details.

Thank you!

The development of this gem was sponsored by Käuferportal: www.kaeuferportal.de