class Mongoid::Orderable::Handlers::Base
Attributes
doc[R]
Public Class Methods
new(doc)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 9 def initialize(doc) @doc = doc end
Protected Instance Methods
allowed?(field)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 133 def allowed?(field) cond_if = orderable_if(field) cond_unless = orderable_unless(field) (cond_if.nil? || resolve_condition(cond_if)) && (cond_unless.nil? || !resolve_condition(cond_unless)) end
any_field_changed?()
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 35 def any_field_changed? orderable_keys.any? {|field| changed?(field) } end
apply_all_positions()
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 39 def apply_all_positions orderable_keys.map {|field| apply_one_position(field, move_all[field]) } end
apply_one_position(field, target_position)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 43 def apply_one_position(field, target_position) return unless allowed?(field) && changed?(field) set_lock(field) if use_transactions f = orderable_field(field) scope = orderable_scope(field) scope_changed = orderable_scope_changed?(field) # Set scope-level lock if scope changed if use_transactions && persisted? && scope_changed set_lock(field, true) scope_changed = orderable_scope_changed?(field) end # Get the current position as exists in the database current = if !persisted? || scope_changed nil elsif persisted? && !embedded? scope.where(_id: _id).pluck(f).first else orderable_position(field) end # If scope changed, remove the position from the old scope if persisted? && !embedded? && scope_changed existing_doc = doc.class.unscoped.find(_id) self.class.new(existing_doc).send(:remove_one_position, field) end # Return if there is no instruction to change the position in_list = persisted? && current return if in_list && !target_position # Use $inc operator to shift the position of the other documents target = resolve_target_position(field, target_position, in_list) if !in_list scope.gte(f => target).inc(f => 1) elsif target < current scope.where(f => { '$gte' => target, '$lt' => current }).inc(f => 1) elsif target > current scope.where(f => { '$gt' => current, '$lte' => target }).inc(f => -1) end # If persisted, update the field in the database atomically doc.set({ f => target }.merge(changed_scope_hash(field))) if use_transactions && persisted? doc.send("orderable_#{field}_position=", target) end
changeable_keys(field)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 157 def changeable_keys(field) [orderable_field(field)] | scope_keys(field) end
changed?(field)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 152 def changed?(field) return true if new_record? || !doc.send(orderable_field(field)) || move_all[field] changeable_keys(field).any? {|f| doc.send("#{f}_changed?") } end
changed_scope_hash(field)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 167 def changed_scope_hash(field) scope_keys(field).each_with_object({}) do |f, hash| hash[f] = doc.send(f) if doc.send("#{f}_changed?") end end
lock_scope(field, generic = false)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 181 def lock_scope(field, generic = false) sel = orderable_scope(field).selector scope = ([collection_name] + (generic ? [field] : sel.to_a.flatten)).map(&:to_s).join('|') { scope: scope } end
move_all()
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 106 def move_all doc.send(:move_all) end
remove_all_positions()
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 92 def remove_all_positions orderable_keys.each do |field| remove_one_position(field) end end
remove_one_position(field)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 98 def remove_one_position(field) return unless allowed?(field) f = orderable_field(field) current = orderable_position(field) set_lock(field) if use_transactions orderable_scope(field).gt(f => current).inc(f => -1) end
resolve_condition(condition)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 141 def resolve_condition(condition) case condition when Proc condition.arity.zero? ? doc.instance_exec(&condition) : condition.call(doc) when Symbol doc.send(condition) else condition || false end end
resolve_target_position(field, target_position, in_list)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 110 def resolve_target_position(field, target_position, in_list) target_position ||= 'bottom' unless target_position.is_a? Numeric target_position = case target_position.to_s when 'top' then (min ||= orderable_top(field)) when 'bottom' then (max ||= orderable_bottom(field, in_list)) when 'higher' then orderable_position(field).pred when 'lower' then orderable_position(field).next when /\A\d+\Z/ then target_position.to_i else raise Mongoid::Orderable::Errors::InvalidTargetPosition.new(target_position) end end if target_position <= (min ||= orderable_top(field)) target_position = min elsif target_position > (max ||= orderable_bottom(field, in_list)) target_position = max end target_position end
scope_keys(field)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 161 def scope_keys(field) orderable_scope(field).selector.keys.map do |f| doc.fields[f]&.options&.[](:as) || f end end
set_lock(field, generic = false)
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 173 def set_lock(field, generic = false) return unless use_transactions model_name = doc.class.orderable_configs[field][:lock_collection].to_s.singularize.classify model = Mongoid::Orderable::Models.const_get(model_name) attrs = lock_scope(field, generic) model.where(attrs).find_one_and_update(attrs.merge(updated_at: Time.now), { upsert: true }) end
use_transactions()
click to toggle source
# File lib/mongoid/orderable/handlers/base.rb, line 31 def use_transactions false end