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

Commit

Permalink
refactor Mingo with modular design, before/after callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
mislav committed Apr 26, 2011
1 parent a1cdabd commit 456b4bc
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 84 deletions.
105 changes: 21 additions & 84 deletions lib/mingo.rb
Expand Up @@ -14,12 +14,14 @@ def id
end

class Mingo < Hashie::Dash
# ActiveModel::Callbacks
include ActiveModel::Conversion
extend ActiveModel::Translation

autoload :Cursor, 'mingo/cursor'
autoload :ManyProxy, 'mingo/many_proxy'
autoload :Cursor, 'mingo/cursor'
autoload :ManyProxy, 'mingo/many_proxy'
autoload :Persistence, 'mingo/persistence'
autoload :Callbacks, 'mingo/callbacks'
autoload :Changes, 'mingo/changes'

class << self
attr_writer :db, :collection
Expand Down Expand Up @@ -78,13 +80,6 @@ def paginate_ids(object_ids, paginate_options, options = {})
end
end

def create(obj = nil)
new(obj).tap do |object|
yield object if block_given?
object.save
end
end

def many(property, *args, &block)
proxy_class = block_given?? Class.new(ManyProxy, &block) : ManyProxy
ivar = "@#{property}"
Expand All @@ -96,18 +91,23 @@ def many(property, *args, &block)
end
end

attr_reader :changes

def initialize(obj = nil)
@changes = {}
@destroyed = false

if obj and obj['_id'].is_a? BSON::ObjectId
# a doc loaded straight from the db
merge!(obj)
else
super
include Module.new {
def initialize(obj = nil)
if obj and obj['_id'].is_a? BSON::ObjectId
# a doc loaded straight from the db
merge!(obj)
else
super
end
end
}

include Persistence
include Callbacks
include Changes

def id
self['_id']
end

# overwrite these to avoid checking for declared properties
Expand All @@ -124,72 +124,9 @@ def []=(property, value)
def stringify_keys() self end
alias :stringify_keys! :stringify_keys

def id
self['_id']
end

def persisted?
!!id
end

def save(options = {})
if persisted?
update(values_for_update, options)
else
self['_id'] = self.class.collection.insert(self.to_hash, options)
end.
tap { changes.clear }
end

def update(doc, options = {})
self.class.collection.update({'_id' => self.id}, doc, options)
end

def reload
doc = self.class.first(id, :convert => nil)
replace doc
end

def destroy
self.class.collection.remove('_id' => self.id)
@destroyed = true
self.freeze
end

def destroyed?
@destroyed
end

def changed?
changes.any?
end

def ==(other)
other.is_a?(self.class) and other.id == self.id
end

private

def values_for_update
changes.inject('$set' => {}, '$unset' => {}) do |doc, (key, values)|
value = values[1]
value.nil? ? (doc['$unset'][key] = 1) : (doc['$set'][key] = value)
doc
end
end

def _regular_writer(key, value)
track_change(key, value)
super
end

def track_change(key, value)
old_value = _regular_reader(key)
unless value == old_value
memo = (changes[key.to_sym] ||= [old_value])
memo[0] == value ? changes.delete(key.to_sym) : (memo[1] = value)
end
end
end

if $0 == __FILE__
Expand Down
19 changes: 19 additions & 0 deletions lib/mingo/callbacks.rb
@@ -0,0 +1,19 @@
class Mingo
module Callbacks
def self.included(base)
base.extend ActiveModel::Callbacks
base.send :define_model_callbacks, :create, :save, :update, :destroy
end

def save(*args)
action = persisted? ? 'update' : 'create'
send(:"_run_#{action}_callbacks") do
_run_save_callbacks { super }
end
end

def destroy
_run_destroy_callbacks { super }
end
end
end
47 changes: 47 additions & 0 deletions lib/mingo/changes.rb
@@ -0,0 +1,47 @@
class Mingo
module Changes
def self.included(base)
base.after_save :clear_changes
end

attr_reader :changes

def initialize(*args)
@changes = {}
super
end

def changed?
changes.any?
end

private

def _regular_writer(key, value)
track_change(key, value)
super
end

def track_change(key, value)
old_value = _regular_reader(key)
unless value == old_value
memo = (changes[key.to_sym] ||= [old_value])
memo[0] == value ? changes.delete(key.to_sym) : (memo[1] = value)
end
end

def clear_changes
changes.clear
end

private

def values_for_update
changes.inject('$set' => {}, '$unset' => {}) do |doc, (key, values)|
value = values[1]
value.nil? ? (doc['$unset'][key] = 1) : (doc['$set'][key] = value)
doc
end
end
end
end
60 changes: 60 additions & 0 deletions lib/mingo/persistence.rb
@@ -0,0 +1,60 @@
require 'active_support/concern'

class Mingo
module Persistence
extend ActiveSupport::Concern

module ClassMethods
def create(obj = nil)
new(obj).tap do |object|
yield object if block_given?
object.save
end
end
end

module InstanceMethods
def initialize(*args)
@destroyed = false
super
end

def persisted?
!!id
end

def save(options = {})
if persisted?
update(values_for_update, options)
else
self['_id'] = self.class.collection.insert(self.to_hash, options)
end
end

def update(doc, options = {})
self.class.collection.update({'_id' => self.id}, doc, options)
end

def reload
doc = self.class.first(id, :convert => nil)
replace doc
end

def destroy
self.class.collection.remove('_id' => self.id)
@destroyed = true
self.freeze
end

def destroyed?
@destroyed
end

private

def values_for_update
self.to_hash
end
end
end
end

0 comments on commit 456b4bc

Please sign in to comment.