Module: Mongoid::SleepingKingStudios::Orderable

Extended by:
ActiveSupport::Concern, Concern
Defined in:
lib/mongoid/sleeping_king_studios/orderable.rb,
lib/mongoid/sleeping_king_studios/orderable/metadata.rb

Overview

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.

Examples:

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 Also:

Since:

  • 0.7.0

Defined Under Namespace

Modules: ClassMethods Classes: Metadata

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Concern

characterize, relate, valid_options, validate_options

Class Method Details

.apply(base, sort_params, options) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Sets up the orderable relation, creating fields, callbacks and helper methods.

Parameters:

  • base (Class)

    The base class into which the concern is mixed in.

  • sort_params (Symbol, Array, Hash)

    The params used to sort the collection, generating the cached order index.

  • options (Hash)

    The options for the relation.

Since:

  • 0.7.0


35
36
37
38
39
40
41
42
43
44
45
46
47
# 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

.define_callbacks(base, metadata) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds an after_save callback to update the index of the record and all subsequent records in the ordering.

Parameters:

  • base (Class)

    The base class into which the concern is mixed in.

  • metadata (Metadata)

    The metadata for the relation.

Since:

  • 0.7.0


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 69

def self.define_callbacks base, 
  base.after_save do
    criteria    = .sort_criteria(base)
    ordering    = criteria.to_a
    order_index = ordering.index(self)

    if order_index.nil?
      unless send(.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(.field_was)

        # Update the current instance.
        self[.field_name] = nil

        # Set the value in the datastore.
        set(.field_name => order_index)

        # Atomically update the subsequent documents in the collection.
        ordering[order_index..-1].each_with_index do |object, i|
          object.set(.field_name => (order_index + i))
        end # each
      end # unless
    else
      # Update the current instance.
      self[.field_name] = order_index

      # Atomically update the subsequent documents in the collection.
      ordering[order_index..-1].each_with_index do |object, i|
        object.set(.field_name => (order_index + i))
      end # each
    end # if
  end # callback
end

.define_fields(base, metadata) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Creates an order field of type Integer on the base class, and sets the writer to private.

Parameters:

  • base (Class)

    The base class into which the concern is mixed in.

  • metadata (Metadata)

    The metadata for the relation.

Since:

  • 0.7.0


56
57
58
59
60
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 56

def self.define_fields base, 
  base.send :field,   .field_name, :type => Integer

  base.send :private, .field_writer
end

.define_helpers(base, metadata) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Adds a class-level reorder! helper that loops through the entire collection and updates the ordering of each item.

Parameters:

  • base (Class)

    The base class into which the concern is mixed in.

  • metadata (Metadata)

    The metadata for the relation.

Since:

  • 0.7.0


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 112

def self.define_helpers base, 
  base_name = .field_name.to_s.gsub(/_order\z/,'')
  filtered  = .filter_criteria(base)

  # Define instance-level helpers.
  instance_methods = Module.new

  instance_methods.send :define_method, :"next_#{base_name}" do |scope = base|
    .filter_criteria(scope).asc(.field_name).
      where(.field_name.gt => send(.field_name)).limit(1).first
  end # method

  instance_methods.send :define_method, :"prev_#{base_name}" do |scope = base|
    .filter_criteria(scope).desc(.field_name).
      where(.field_name.lt => send(.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|
    .filter_criteria(scope).asc(.field_name).limit(1).first
  end # method

  class_methods.send :define_method, :"last_#{base_name}" do |scope = base|
    .filter_criteria(scope).desc(.field_name).limit(1).first
  end # method

  class_methods.send :define_method, :"reorder_#{base_name}!" do
    base.update_all(.field_name => nil)
    
    criteria = .sort_criteria(base)
    ordering = criteria.to_a

    ordering.each_with_index do |record, index|
      record.set(.field_name => index)
    end # each
  end # method

  base.extend class_methods
end

.valid_optionsArray<Symbol>

Returns a list of options that are valid for this concern.

Returns:

  • (Array<Symbol>)

    The list of valid options.

Since:

  • 0.7.0


159
160
161
162
163
164
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 159

def self.valid_options
  super + %i(
    as
    filter
  ) # end array
end

Instance Method Details

#next_ordering_nameMongoid::Document?

Finds the next document, based on the stored ordering values.

The generated name of this method will depend on the sort params or the :as option provided. For example, :as => :alphabetical_order will result in an instance method #next_alphabetical.

Returns:

  • (Mongoid::Document, nil)

    The next document in the order, or nil if there are no more documents in the collection.


# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 225

#prev_ordering_nameMongoid::Document?

Finds the previous document, based on the stored ordering values.

The generated name of this method will depend on the sort params or the :as option provided. For example, :as => :alphabetical_order will result in an instance method #prev_alphabetical.

Returns:

  • (Mongoid::Document, nil)

    The previous document in the order, or nil if there are no prior documents in the collection.


# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 235