lib/mongoid/locker/wrapper.rb in mongoid-locker-1.0.1 vs lib/mongoid/locker/wrapper.rb in mongoid-locker-2.0.0

- old
+ new

@@ -1,3 +1,121 @@ -raise "Incompatible Mongoid #{version} version" unless %w[4 5 6 7].include?(version = Mongoid::VERSION.split('.')[0]) +# frozen_string_literal: true -require "mongoid/locker/wrapper#{version}" +module Mongoid + module Locker + # Set of methods to interact with the database. + module Wrapper + # Finds provided document with provided options, locks (sets +locking_name_field+ and +locked_at_field+ fields), and returns the fields. + # + # @example + # Mongoid::Locker::Wrapper.find_and_lock(document, opts) + # #=> {"locked_at"=>2019-03-19 07:51:24 UTC, "locking_name"=>nil} + # + # @example + # Mongoid::Locker::Wrapper.find_and_lock(document, opts) + # # => nil + # + # @param doc [Mongoid::Document] + # @param opts [Hash] (see #with_lock) + # @return [Hash] with +locking_name_field+ and +locked_at_field+ fields + # @return [nil] if the document was not found, was already locked + def self.find_and_lock(doc, opts) + model = doc.class + filter = { + _id: doc.id, + '$or': [ + { + '$or': [ + { model.locking_name_field => { '$exists': false } }, + { model.locked_at_field => { '$exists': false } } + ] + }, + { + '$or': [ + { model.locking_name_field => { '$eq': nil } }, + { model.locked_at_field => { '$eq': nil } } + ] + }, + { + '$where': "new Date() - this.#{model.locked_at_field} >= #{model.lock_timeout * 1000}" + } + ] + } + update = { + '$set': { model.locking_name_field => opts[:locking_name] }, + '$currentDate': { model.locked_at_field => true } + } + options = { + return_document: :after, + projection: { _id: false, model.locking_name_field => true, model.locked_at_field => true }, + write_concern: model.locker_write_concern + } + + model.collection.find_one_and_update(filter, update, options) + end + + # Finds provided document with provided options, unlocks (sets +locking_name_field+ and +locked_at_field+ fields to +nil+). + # + # @example + # Mongoid::Locker::Wrapper.find_and_unlock(doc, opts) + # #=> true + # Mongoid::Locker::Wrapper.find_and_unlock(doc, opts) + # #=> false + # + # @param doc [Mongoid::Document] + # @param opts [Hash] (see #with_lock) + # @return [Boolean] + # @return [true] if the document was unlocked + # @return [false] if the document was not found, was not unlocked + def self.find_and_unlock(doc, opts) + model = doc.class + filter = { + _id: doc.id, + model.locking_name_field => opts[:locking_name] + } + update = { + '$set': { + model.locking_name_field => nil, + model.locked_at_field => nil + } + } + options = { write_concern: model.locker_write_concern } + + result = model.collection.update_one(filter, update, options) + result.ok? && result.written_count == 1 + end + + # Returns value of +locked_at_field+ field for provided document. + # + # @example + # Mongoid::Locker::Wrapper.locked_at(document) + # #=> 2019-06-03 13:50:46 UTC + # + # @param doc [Mongoid::Document] + # @return [Time] +locked_at_field+ field time + # @return [nil] if response was failed + def self.locked_at(doc) + result = doc.class.collection.find( + { _id: doc.id }, + projection: { _id: false, doc.locked_at_field => true }, + limit: 1 + ).first + + result[doc.locked_at_field.to_s] if result + end + + # Returns the local database server time in UTC. + # + # @example + # Mongoid::Locker::Wrapper.current_mongodb_time(User) + # #=> 2019-03-19 07:24:36 UTC + # + # @param model [Class] the model class + # @return [Time] current time + # @return [nil] if response was failed + def self.current_mongodb_time(model) + info = model.collection.database.command(isMaster: 1) + info.ok? ? info.documents.first['localTime'] : nil + end + end + end +end