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
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 35

def self.apply base, sort_params, options
  name = :orderable
  validate_options    name, options
  options.update :sort_params => 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



68
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
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 68

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?
      if send(.field_was).nil?
        # Both the old and new values are nil, so mission accomplished.
        return
      else
        # 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)
      end # unless
    else
      # Update the current instance.
      self[.field_name] = order_index
    end # if

    ordering[order_index..-1].each_with_index do |object, i|
      object.set(.field_name => (order_index + i))
    end # each
  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



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

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



108
109
110
111
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
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 108

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

  base.send :define_method, :"first_#{base_name}" do
    filtered.order_by(.field_name.asc).limit(1).first
  end # method

  base.send :define_method, :"last_#{base_name}" do
    filtered.order_by(.field_name.desc).limit(1).first
  end # method
  
  base.send :define_method, :"next_#{base_name}" do
    filtered.order_by(.field_name.asc).where(.field_name.gt => send(.field_name)).limit(1).first
  end # method

  base.send :define_method, :"prev_#{base_name}" do
    filtered.order_by(.field_name.desc).where(.field_name.lt => send(.field_name)).limit(1).first
  end # method

  meta = class << base; self; end
  meta.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
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



144
145
146
147
148
149
# File 'lib/mongoid/sleeping_king_studios/orderable.rb', line 144

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

Instance Method Details

#first_ordering_nameMongoid::Document?

Finds the first 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 #first_alphabetical.

Returns:

  • (Mongoid::Document, nil)

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



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

#last_ordering_nameMongoid::Document?

Finds the last 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 #last_alphabetical.

Returns:

  • (Mongoid::Document, nil)

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



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

#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 210

#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 220