module Mongoid::SleepingKingStudios::Orderable
Adds an order field that stores the index of the record relative to the specified sort query. Storing the order in this fashion allows, for example, finding the next or previous records in the set without needing to perform the full sort query each time.
@example Order by Most Recently Created:
class SortedDocument include Mongoid::Document include Mongoid::SleepingKingStudios::Ordering cache_ordering :created_at.desc, :as => :most_recent_order end # class
@see ClassMethods#cache_ordering
@since 0.7.0
Public Class Methods
@api private
Sets up the orderable relation, creating fields, callbacks and helper methods.
@param [Class] base The base class into which the concern is mixed in. @param [Symbol, Array, Hash] sort_params The params used to sort the
collection, generating the cached order index.
@param [Hash] options The options for the relation.
# File lib/mongoid/sleeping_king_studios/orderable.rb, line 35 def self.apply base, sort_params, options validate_options name, options sort_params = Metadata.normalize_sort_params(sort_params) options.update :sort_params => sort_params name = options.fetch(:as, Metadata.default_field_name(sort_params)) meta = characterize name, options, Metadata relate base, name, meta define_fields base, meta define_callbacks base, meta define_helpers base, meta end
@api private
Adds an after_save callback to update the index of the record and all subsequent records in the ordering.
@param [Class] base The base class into which the concern is mixed in. @param [Metadata] metadata The metadata for the relation.
# File lib/mongoid/sleeping_king_studios/orderable.rb, line 69 def self.define_callbacks base, metadata base.after_save do criteria = metadata.sort_criteria(base) ordering = criteria.to_a order_index = ordering.index(self) if order_index.nil? unless send(metadata.field_was).nil? # The old value wasn't nil, so remember it and set the new value, # then start looping through the ordered collection at the old # value. order_index = send(metadata.field_was) # Update the current instance. self[metadata.field_name] = nil # Set the value in the datastore. set(metadata.field_name => order_index) # Atomically update the subsequent documents in the collection. ordering[order_index..-1].each_with_index do |object, i| object.set(metadata.field_name => (order_index + i)) end # each end # unless else # Update the current instance. self[metadata.field_name] = order_index # Atomically update the subsequent documents in the collection. ordering[order_index..-1].each_with_index do |object, i| object.set(metadata.field_name => (order_index + i)) end # each end # if end # callback end
@api private
Creates an order field of type Integer on the base class, and sets the writer to private.
@param [Class] base The base class into which the concern is mixed in. @param [Metadata] metadata The metadata for the relation.
# File lib/mongoid/sleeping_king_studios/orderable.rb, line 56 def self.define_fields base, metadata base.send :field, metadata.field_name, :type => Integer base.send :private, metadata.field_writer end
@api private
Adds a class-level reorder! helper that loops through the entire collection and updates the ordering of each item.
@param [Class] base The base class into which the concern is mixed in. @param [Metadata] metadata The metadata for the relation.
# File lib/mongoid/sleeping_king_studios/orderable.rb, line 112 def self.define_helpers base, metadata base_name = metadata.field_name.to_s.gsub(/_order\z/,'') filtered = metadata.filter_criteria(base) # Define instance-level helpers. instance_methods = Module.new instance_methods.send :define_method, :"next_#{base_name}" do |scope = base| metadata.filter_criteria(scope).asc(metadata.field_name). where(metadata.field_name.gt => send(metadata.field_name)).limit(1).first end # method instance_methods.send :define_method, :"prev_#{base_name}" do |scope = base| metadata.filter_criteria(scope).desc(metadata.field_name). where(metadata.field_name.lt => send(metadata.field_name)).limit(1).first end # method base.send :include, instance_methods # Define class-level helpers. class_methods = Module.new class_methods.send :define_method, :"first_#{base_name}" do |scope = base| metadata.filter_criteria(scope).asc(metadata.field_name).limit(1).first end # method class_methods.send :define_method, :"last_#{base_name}" do |scope = base| metadata.filter_criteria(scope).desc(metadata.field_name).limit(1).first end # method class_methods.send :define_method, :"reorder_#{base_name}!" do base.update_all(metadata.field_name => nil) criteria = metadata.sort_criteria(base) ordering = criteria.to_a ordering.each_with_index do |record, index| record.set(metadata.field_name => index) end # each end # method base.extend class_methods end
Returns a list of options that are valid for this concern.
@return [Array<Symbol>] The list of valid options.
# File lib/mongoid/sleeping_king_studios/orderable.rb, line 159 def self.valid_options super + %i( as filter ) # end array end