module Sequel tsk_require 'sequel/plugins/instance_filters' module Plugins # This plugin implements a simple database-independent locking mechanism # to ensure that concurrent updates do not override changes. This is # best implemented by a code example: # # class Person < Sequel::Model # plugin :optimistic_locking # end # p1 = Person[1] # p2 = Person[1] # p1.update(:name=>'Jim') # works # p2.update(:name=>'Bob') # raises Sequel::Plugins::OptimisticLocking::Error # # In order for this plugin to work, you need to make sure that the database # table has a lock_version column (or other column you name via the lock_column # class level accessor) that defaults to 0. # # This plugin relies on the instance_filters plugin. module OptimisticLocking # Exception class raised when trying to update or destroy a stale object. Error = InstanceFilters::Error # Load the instance_filters plugin into the model. def self.apply(model, opts={}) model.plugin :instance_filters end # Set the lock_column to the :lock_column option, or :lock_version if # that option is not given. def self.configure(model, opts={}) model.lock_column = opts[:lock_column] || :lock_version end module ClassMethods # The column holding the version of the lock attr_accessor :lock_column # Copy the lock_column value into the subclass def inherited(subclass) super subclass.lock_column = lock_column end end module InstanceMethods # Add the lock column instance filter to the object before destroying it. def before_destroy lock_column_instance_filter super end # Add the lock column instance filter to the object before updating it. def before_update lock_column_instance_filter super end private # Add the lock column instance filter to the object. def lock_column_instance_filter lc = model.lock_column instance_filter(lc=>send(lc)) end # Clear the instance filters when refreshing, so that attempting to # refresh after a failed save removes the previous lock column filter # (the new one will be added before updating). def _refresh(ds) clear_instance_filters super end # Only update the row if it has the same lock version, and increment the # lock version. def _update_columns(columns) lc = model.lock_column lcv = send(lc) columns[lc] = lcv + 1 super send("#{lc}=", lcv + 1) end end end end end