lib/mongoid/locker.rb in mongoid-locker-0.3.6 vs lib/mongoid/locker.rb in mongoid-locker-1.0.0
- old
+ new
@@ -1,26 +1,30 @@
require File.expand_path(File.join(File.dirname(__FILE__), 'locker', 'version'))
require File.expand_path(File.join(File.dirname(__FILE__), 'locker', 'wrapper'))
module Mongoid
module Locker
+ # The field names used by default.
+ @locked_at_field = :locked_at
+ @locked_until_field = :locked_until
+
# Error thrown if document could not be successfully locked.
- class LockError < Exception; end
+ class LockError < RuntimeError; end
module ClassMethods
# A scope to retrieve all locked documents in the collection.
#
# @return [Mongoid::Criteria]
def locked
- where :locked_until.gt => Time.now
+ where locked_until_field.gt => Time.now.utc
end
# A scope to retrieve all unlocked documents in the collection.
#
# @return [Mongoid::Criteria]
def unlocked
- any_of({ locked_until: nil }, :locked_until.lte => Time.now)
+ any_of({ locked_until_field => nil }, locked_until_field.lte => Time.now.utc)
end
# Set the default lock timeout for this class. Note this only applies to new locks. Defaults to five seconds.
#
# @param [Fixnum] new_time the default number of seconds until a lock is considered "expired", in seconds
@@ -34,32 +38,72 @@
# @return [Fixnum] the default number of seconds until a lock is considered "expired", in seconds
def lock_timeout
# default timeout of five seconds
@lock_timeout || 5
end
+
+ # Set locked_at_field and locked_until_field names for this class
+ def locker(locked_at_field: nil, locked_until_field: nil)
+ class_variable_set(:@@locked_at_field, locked_at_field) if locked_at_field
+ class_variable_set(:@@locked_until_field, locked_until_field) if locked_until_field
+ end
+
+ # Returns field name used to set locked at time for this class.
+ def locked_at_field
+ class_variable_get(:@@locked_at_field)
+ end
+
+ # Returns field name used to set locked until time for this class.
+ def locked_until_field
+ class_variable_get(:@@locked_until_field)
+ end
end
- # @api private
- def self.included(mod)
- mod.extend ClassMethods
+ class << self
+ attr_accessor :locked_at_field, :locked_until_field
- mod.field :locked_at, type: Time
- mod.field :locked_until, type: Time
+ # @api private
+ def included(mod)
+ mod.extend ClassMethods
+
+ mod.class_variable_set(:@@locked_at_field, locked_at_field)
+ mod.class_variable_set(:@@locked_until_field, locked_until_field)
+
+ mod.send(:define_method, :locked_at_field) { mod.class_variable_get(:@@locked_at_field) }
+ mod.send(:define_method, :locked_until_field) { mod.class_variable_get(:@@locked_until_field) }
+ end
+
+ # Sets configuration using a block
+ #
+ # Mongoid::Locker.configure do |config|
+ # config.locked_at_field = :mongoid_locker_locked_at
+ # config.locked_until_field = :mongoid_locker_locked_until
+ # end
+ def configure
+ yield(self) if block_given?
+ end
+
+ # Resets to default configuration.
+ def reset!
+ # The field names used by default.
+ @locked_at_field = :locked_at
+ @locked_until_field = :locked_until
+ end
end
# Returns whether the document is currently locked or not.
#
# @return [Boolean] true if locked, false otherwise
def locked?
- !!(locked_until && locked_until > Time.now)
+ !!(self[locked_until_field] && self[locked_until_field] > Time.now.utc)
end
# Returns whether the current instance has the lock or not.
#
# @return [Boolean] true if locked, false otherwise
def has_lock?
- !!(@has_lock && self.locked?)
+ !!(@has_lock && locked?)
end
# Primary method of plugin: execute the provided code once the document has been successfully locked.
#
# @param [Hash] opts for the locking mechanism
@@ -68,11 +112,11 @@
# @option opts [Float] :retry_sleep How long to sleep between attempts to acquire lock - defaults to time left until lock is available
# @option opts [Boolean] :wait If the document is currently locked, wait until the lock expires and try again - defaults to false. If set, :retries will be ignored
# @option opts [Boolean] :reload After acquiring the lock, reload the document - defaults to true
# @return [void]
def with_lock(opts = {})
- had_lock = self.has_lock?
+ had_lock = has_lock?
unless had_lock
opts[:retries] = 1 if opts[:wait]
lock(opts)
end
@@ -85,38 +129,36 @@
end
protected
def acquire_lock(opts = {})
- time = Time.now
+ time = Time.now.utc
timeout = opts[:timeout] || self.class.lock_timeout
expiration = time + timeout
# lock the document atomically in the DB without persisting entire doc
locked = Mongoid::Locker::Wrapper.update(
self.class,
{
:_id => id,
'$or' => [
# not locked
- { locked_until: nil },
+ { locked_until_field => nil },
# expired
- { locked_until: { '$lte' => time } }
+ { locked_until_field => { '$lte' => time } }
]
},
-
'$set' => {
- locked_at: time,
- locked_until: expiration
+ locked_at_field => time,
+ locked_until_field => expiration
}
-
)
if locked
# document successfully updated, meaning it was locked
- self.locked_at = time
- self.locked_until = expiration
+ self[locked_at_field] = time
+ self[locked_until_field] = expiration
reload unless opts[:reload] == false
@has_lock = true
else
@has_lock = false
end
@@ -133,37 +175,35 @@
attempts_left -= 1
if attempts_left > 0
# if not passed a retry_sleep value, we sleep for the remaining life of the lock
- unless opts[:retry_sleep]
+ unless retry_sleep
locked_until = Mongoid::Locker::Wrapper.locked_until(self)
# the lock might be released since the last check so make another attempt
next unless locked_until
- retry_sleep = locked_until - Time.now
+ retry_sleep = locked_until - Time.now.utc
end
sleep retry_sleep if retry_sleep > 0
else
- fail LockError.new('could not get lock')
+ raise LockError.new('could not get lock')
end
end
end
def unlock
# unlock the document in the DB without persisting entire doc
Mongoid::Locker::Wrapper.update(
self.class,
{ _id: id },
-
'$set' => {
- locked_at: nil,
- locked_until: nil
+ locked_at_field => nil,
+ locked_until_field => nil
}
-
)
- self.attributes = { locked_at: nil, locked_until: nil } unless destroyed?
+ self.attributes = { locked_at_field => nil, locked_until_field => nil } unless destroyed?
@has_lock = false
end
end
end