Skip to content

Instantly share code, notes, and snippets.

@stan
Created September 27, 2012 10:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stan/3793263 to your computer and use it in GitHub Desktop.
Save stan/3793263 to your computer and use it in GitHub Desktop.
Undo for mongoid
module Mongoid
module Undo
extend ActiveSupport::Concern
included do
include Mongoid::Document
field :version, type: Integer, default: 0
before_save :prepare_versioning
after_save :create_version
before_destroy :version_destroy
end
def versions; ::Version.where(obj_id: id) end
def versions_gt(version = self.version); versions.where(:version.gt => version) end
def version_at(version = self.version); versions.where(version: version).first end
def current_version; version_at end
def previous_version; version_at version - 1 end
def versioned?; version > 0 end
def has?(ver)
ver.obj_id == id && ver.obj_class == self.class.to_s
end
def matches?(ver)
has?(ver) &&
ver.obj_attributes == attributes &&
ver.version == version
end
def undo!; rollback_to!(version - 1) end
def rollback_to!(version)
unless persisted?; return false end
if version == 0
versions.destroy
without_versioning :destroy
attributes
else
ver = version_at(version)
unless ver; return false end
ver.rollback_to(self)
end
end
def without_versioning(method = nil)
@without_versioning = true
method ? method.to_proc.call(self) : yield
ensure
@without_versioning = false
end
private
def match_persisted?
current_version && attributes == current_version.obj_attributes
end
def prepare_versioning
if !@without_versioning
if version == 0 && persisted?
self.version += 1
old_self = self.class.find(id)
old_self.version += 1
old_self.send(:create_version)
end
if !match_persisted?; self.version += 1 end
end
end
def create_version(destroy = false)
if !@without_versioning && (destroy || !match_persisted?)
ver = ::Version.new(obj_id: id, obj_class: self.class.to_s, version: version)
unless destroy; ver.attributes = ver.attributes.merge({:obj_attributes => attributes}) end
ver.save!
end
end
def version_destroy
if !@without_versioning
self.version += 1
without_versioning(:save)
create_version(true)
end
end
end
end
module Mongoid::Undo
module Version
extend ActiveSupport::Concern
included do
include Mongoid::Document
include Mongoid::Timestamps::Created
field :obj_id
field :obj_class
field :obj_attributes, type: Hash
field :version, type: Integer
validates_presence_of(fields.keys - ['_id', '_type', 'created_at', 'obj_attributes'])
end
def obj; obj_class.constantize.find(obj_id) end
def created?; version == 1 end
def deleted?; !obj_attributes end
def modified?; !(created? || deleted?) end
def latest?(obj = nil)
unless obj; obj = self.obj end
obj.nil? || version == obj.version
end
def rollback_to(obj = nil)
if !deleted?
if obj.nil? || obj.id != obj_id; obj = self.obj end
if obj.nil?; obj = obj_class.constantize.new end
obj.versions_gt(version).each {|ver| ver.destroy }
rollback(obj)
else
false
end
end
private
def rollback(obj = nil)
old_attributes = obj.attributes
obj.attributes = obj_attributes
obj.without_versioning :save
old_attributes
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment