Permalink
Browse files

refactor Mingo with modular design, before/after callbacks

  • Loading branch information...
1 parent a1cdabd commit 456b4bc5770d4966267ffcb84e820450d529da39 @mislav committed Apr 26, 2011
Showing with 147 additions and 84 deletions.
  1. +21 −84 lib/mingo.rb
  2. +19 −0 lib/mingo/callbacks.rb
  3. +47 −0 lib/mingo/changes.rb
  4. +60 −0 lib/mingo/persistence.rb
View
@@ -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
@@ -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}"
@@ -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
@@ -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__
View
@@ -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
View
@@ -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
View
@@ -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.