Skip to content
This repository has been archived by the owner on Sep 7, 2021. It is now read-only.

Commit

Permalink
Mingo.many method for declaring one-to-many
Browse files Browse the repository at this point in the history
  • Loading branch information
mislav committed Sep 6, 2010
1 parent 0bbc22c commit 07f5ef0
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
11 changes: 11 additions & 0 deletions lib/mingo.rb
Expand Up @@ -14,6 +14,7 @@ class Mingo < Hashie::Dash
extend ActiveModel::Translation

autoload :Cursor, 'mingo/cursor'
autoload :ManyProxy, 'mingo/many_proxy'

class << self
attr_writer :db, :collection
Expand Down Expand Up @@ -56,6 +57,16 @@ def find(selector = {}, options = {}, &block)
def create(obj = nil)
new(obj).tap { |doc| doc.save }
end

def many(property, model, &block)
proxy_class = block_given?? Class.new(ManyProxy, &block) : ManyProxy
ivar = "@#{property}"

define_method(property) {
(instance_variable_defined?(ivar) && instance_variable_get(ivar)) ||
instance_variable_set(ivar, proxy_class.new(self, property, model))
}
end
end

attr_reader :changes
Expand Down
88 changes: 88 additions & 0 deletions lib/mingo/many_proxy.rb
@@ -0,0 +1,88 @@
class Mingo
class ManyProxy
def self.decorate_with(mod = nil, &block)
if mod or block_given?
@decorate_with = mod || Module.new(&block)
else
@decorate_with
end
end

def self.decorate_each(&block)
if block_given?
@decorate_each = block
else
@decorate_each
end
end

def initialize(parent, property, model)
@parent = parent
@property = property
@model = model
@collection = nil
@embedded = (@parent[@property] ||= [])
@parent.changes.delete(@property)
end

def find_options
@find_options ||= begin
decorator = self.class.decorate_with
decorate_block = self.class.decorate_each

if decorator or decorate_block
{:convert => lambda { |doc|
@model.new(doc).tap do |obj|
obj.extend decorator if decorator
decorate_block.call(obj, @embedded) if decorate_block
end
}}
else
{}
end
end
end

undef :to_a, :inspect

def object_ids
@embedded
end

def convert(doc)
doc.id
end

def <<(doc)
doc = convert(doc)
@parent.update '$addToSet' => { @property => doc }
unload_collection
@embedded << doc
self
end

def delete(doc)
doc = convert(doc)
@parent.update '$pull' => { @property => doc }
unload_collection
@embedded.delete doc
end

private

def method_missing(method, *args, &block)
load_collection
@collection.send(method, *args, &block)
end

def unload_collection
@collection = nil
end

def load_collection
@collection ||= if @embedded.empty? then []
else @model.find({:_id => {'$in' => self.object_ids}}, find_options)
end
end
end
end

0 comments on commit 07f5ef0

Please sign in to comment.