lib/mongoid/orderable.rb in mongoid_orderable-4.1.0 vs lib/mongoid/orderable.rb in mongoid_orderable-4.1.1
- old
+ new
@@ -1,239 +1,22 @@
module Mongoid::Orderable
extend ActiveSupport::Concern
- module ClassMethods
- def orderable options = {}
- configuration = {
- :column => :position,
- :index => true,
- :scope => nil,
- :base => 1
- }
+ included do
+ include Mongoid::Orderable::Helpers
+ include Mongoid::Orderable::Callbacks
+ include Mongoid::Orderable::Movable
+ include Mongoid::Orderable::Listable
- configuration.merge! options if options.is_a?(Hash)
-
- if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
- scope_relation = self.relations[configuration[:scope].to_s]
- if scope_relation
- configuration[:scope] = scope_relation.key.to_sym
- else
- configuration[:scope] = "#{configuration[:scope]}_id".to_sym
- end
- elsif configuration[:scope].is_a?(String)
- configuration[:scope] = configuration[:scope].to_sym
- end
-
- field configuration[:column], orderable_field_opts(configuration)
- if configuration[:index]
- if MongoidOrderable.mongoid2?
- index configuration[:column]
- else
- index(configuration[:column] => 1)
- end
- end
-
- case configuration[:scope]
- when Symbol then
- scope :orderable_scope, lambda { |document|
- where(configuration[:scope] => document.send(configuration[:scope])) }
- when Proc then
- scope :orderable_scope, configuration[:scope]
- else
- scope :orderable_scope, lambda { |document| where({}) }
- end
-
- define_method :orderable_column do
- configuration[:column]
- end
-
- define_method :orderable_base do
- configuration[:base]
- end
-
- self_class = self
- define_method :orderable_inherited_class do
- self_class if configuration[:inherited]
- end
-
- before_save :add_to_list
- after_destroy :remove_from_list
- end
-
- private
-
- def orderable_field_opts(configuration)
- field_opts = { :type => Integer }
- field_opts.merge!(configuration.slice(:as))
- field_opts
- end
+ class_attribute :orderable_configurations
end
- ##
- # Returns items above the current document.
- # Items with a position lower than this document's position.
- def previous_items
- orderable_scoped.where(orderable_column.lt => self.position)
- end
- alias_method :prev_items, :previous_items
+ module ClassMethods
- ##
- # Returns items below the current document.
- # Items with a position greater than this document's position.
- def next_items
- orderable_scoped.where(orderable_column.gt => self.position)
- end
+ def orderable options = {}
+ configuration = Mongoid::Orderable::Configuration.build(self, options)
- # returns the previous item in the list
- def previous_item
- if previous_items.present?
- previous_position = self.position - 1
- orderable_scoped.where(:position => previous_position).first
- else
- nil
+ Mongoid::Orderable::OrderableClass.setup(self, configuration)
end
- end
- alias_method :prev_item, :previous_item
- # returns the next item in the list
- def next_item
- if next_items.present?
- next_position = self.position + 1
- orderable_scoped.where(:position => next_position).first
- else
- nil
- end
end
-
- def move_to! target_position
- @move_to = target_position
- save
- end
- alias_method :insert_at!, :move_to!
-
- def move_to target_position
- @move_to = target_position
- end
- alias_method :insert_at, :move_to
-
- def move_to= target_position
- @move_to = target_position
- end
- alias_method :insert_at=, :move_to=
-
- [:top, :bottom].each do |symbol|
- define_method "move_to_#{symbol}" do
- move_to symbol
- end
-
- define_method "move_to_#{symbol}!" do
- move_to! symbol
- end
- end
-
- [:higher, :lower].each do |symbol|
- define_method "move_#{symbol}" do
- move_to symbol
- end
-
- define_method "move_#{symbol}!" do
- move_to! symbol
- end
- end
-
- def first?
- in_list? && orderable_position == orderable_base
- end
-
- def last?
- in_list? && orderable_position == bottom_orderable_position
- end
-
- def in_list?
- !orderable_position.nil?
- end
-
- def add_to_list
- apply_position @move_to
- end
-
- def remove_from_list
- MongoidOrderable.inc orderable_scoped.where(orderable_column.gt => orderable_position), orderable_column, -1
- end
-
-private
-
- def orderable_position
- send orderable_column
- end
-
- def orderable_position= value
- send "#{orderable_column}=", value
- end
-
- def orderable_scoped
- if embedded?
- send(MongoidOrderable.metadata(self).inverse).send(MongoidOrderable.metadata(self).name).orderable_scope(self)
- else
- (orderable_inherited_class || self.class).orderable_scope(self)
- end
- end
-
- def orderable_scope_changed?
- if Mongoid.respond_to?(:unit_of_work)
- Mongoid.unit_of_work do
- orderable_scope_changed_query
- end
- else
- orderable_scope_changed_query
- end
- end
-
- def orderable_scope_changed_query
- !orderable_scoped.where(:_id => _id).exists?
- end
-
- def apply_position target_position
- if persisted? && !embedded? && orderable_scope_changed?
- self.class.unscoped.find(_id).remove_from_list
- self.orderable_position = nil
- end
-
- return if !target_position && in_list?
-
- target_position = target_position_to_position target_position
-
- unless in_list?
- MongoidOrderable.inc orderable_scoped.where(orderable_column.gte => target_position), orderable_column, 1
- else
- MongoidOrderable.inc(orderable_scoped.where(orderable_column.gte => target_position, orderable_column.lt => orderable_position), orderable_column, 1) if target_position < orderable_position
- MongoidOrderable.inc(orderable_scoped.where(orderable_column.gt => orderable_position, orderable_column.lte => target_position), orderable_column, -1) if target_position > orderable_position
- end
-
- self.orderable_position = target_position
- end
-
- def target_position_to_position target_position
- target_position = :bottom unless target_position
-
- target_position = case target_position.to_sym
- when :top then orderable_base
- when :bottom then bottom_orderable_position
- when :higher then orderable_position.pred
- when :lower then orderable_position.next
- end unless target_position.is_a? Numeric
-
- target_position = orderable_base if target_position < orderable_base
- target_position = bottom_orderable_position if target_position > bottom_orderable_position
- target_position
- end
-
- def bottom_orderable_position
- @bottom_orderable_position = begin
- positions_list = orderable_scoped.distinct(orderable_column)
- return orderable_base if positions_list.empty?
- max = positions_list.map(&:to_i).max.to_i
- in_list? ? max : max.next
- end
- end
-
end