Skip to content

Instantly share code, notes, and snippets.

@ravinggenius
Created August 4, 2010 22:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ravinggenius/508940 to your computer and use it in GitHub Desktop.
Save ravinggenius/508940 to your computer and use it in GitHub Desktop.

MongoMapper does not include HABTM relationships. This is my attempt to add them.

class Node
include MongoMapper::Document
include Relationship
key :is_published, Boolean, :default => false
key :title, String, :required => true
# provides Node#terms, Node#save_taggings
habtm :nodes, :terms, :tagging
after_save :save_taggings
end
module Relationship
module InstanceMethods
end
module ClassMethods
def habtm(this_model, that_model, glue_model, options = {})
define_method Private::plural(that_model) do
@those_models ||= Private::klass(glue_model).send("#{Private::plural(that_model)}_for", self.id)
end
define_method "save_#{Private::plural(glue_model)}" do
@those_models ||= []
@those_models.each { |om| Private::klass(glue_model).first_or_create(Private::id_key(this_model) => self.id, Private::id_key(that_model) => om.id) }
end
end
def habtm_glue(model_a, model_b, options = {})
{
model_a => model_b,
model_b => model_a
}.each do |this_model, that_model|
key Private::id_key(this_model), BSON::ObjectID, :required => true
define_method this_model do
Private::klass(this_model).find(self.send(Private::id_key(this_model)))
end
define_method "#{this_model}=" do |new_model|
self.send "#{Private::id_key(this_model)}=", new_model.id
new_model
end
this_model_for = lambda { |that_model_id| all(Private::id_key(that_model) => that_model_id).map { |tagging| tagging.send(this_model) } }
self.class.send(:define_method, "#{Private::plural(this_model)}_for", this_model_for)
end
end
end
module Private
def self.single(model_name)
model_name.to_s.singularize.to_sym
end
def self.plural(model_name)
model_name.to_s.pluralize.to_sym
end
def self.id_key(model_name)
"#{single(model_name)}_id".to_sym
end
def self.klass(model_name)
single(model_name).to_s.capitalize.constantize
end
end
def self.included(base)
base.send :extend, ClassMethods
base.send :include, InstanceMethods
end
end
class Tagging
include MongoMapper::Document
include Relationship
# provides Tagging#node, Tagging#term, Tagging#node=, Tagging#term=, Tagging.terms_for(node_id), Tagging.node_for(term_id)
habtm_glue :node, :term
end
class Term
include MongoMapper::Document
include Relationship
key :name, String, :required => true
# provides Term#nodes, Term#save_nodes
habtm :terms, :nodes, :tagging
after_save :save_taggings
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment