mongoid-sleeping_king_studios 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +52 -0
- data/lib/mongoid/sleeping_king_studios/orderable.rb +172 -0
- data/lib/mongoid/sleeping_king_studios/orderable/metadata.rb +111 -0
- data/lib/mongoid/sleeping_king_studios/version.rb +1 -1
- metadata +17 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 218763356e4b6bc56393162ae45be295d7ccc478
|
4
|
+
data.tar.gz: 3fa4cfd1063e65e22322c5be1b183a6f04f10fc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c2f385e2cc2184c33009e5907a15b8b052780dfbe90345dfa3bc9679a637e955df9acac88f2d429a207d53ba738d491db5d8ab491b2e4fa170b95f885803367
|
7
|
+
data.tar.gz: 7b579653a20b03b32db94736019d9c0caec715884948b9eff640397cae9d518c3f364d47c25e09a4bb2795151531f7f35316940aca01ee01629b1a4dd3c669d0
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -77,6 +77,58 @@ slower and more resource-intensive. Do not use this option outside of
|
|
77
77
|
read-heavy applications with very specific requirements, e.g. a directory
|
78
78
|
structure where you must access all parent directories on each page view.
|
79
79
|
|
80
|
+
### Orderable
|
81
|
+
|
82
|
+
require 'mongoid/sleeping_king_studios/orderable'
|
83
|
+
|
84
|
+
Adds a field that tracks the index of each record with respect to the provided
|
85
|
+
sort order parameters.
|
86
|
+
|
87
|
+
**How To Use:**
|
88
|
+
|
89
|
+
class OrderedDocument
|
90
|
+
include Mongoid::Document
|
91
|
+
include Mongoid::SleepingKingStudios::Orderable
|
92
|
+
|
93
|
+
cache_ordering :created_at.desc, :as => :most_recent_order
|
94
|
+
end # class
|
95
|
+
|
96
|
+
The ::cache_ordering method accepts a subset of the params for an Origin
|
97
|
+
\#order_by query operation, e.g.:
|
98
|
+
|
99
|
+
- :first_field.desc, :second_field
|
100
|
+
- { :first_field => -1, :second_field => :asc }
|
101
|
+
- [[:first_field, :desc], [:second_field, 1]]
|
102
|
+
|
103
|
+
#### Helpers
|
104
|
+
|
105
|
+
Creating an ordering cache also creates the following helpers:
|
106
|
+
|
107
|
+
##### ::reorder_ordering_name!
|
108
|
+
|
109
|
+
Loops through the collection and sets each item's field to the appropriate
|
110
|
+
ordered index. Normally, this is handled on item save, but this helper allows
|
111
|
+
a bulk update of the collection when adding the concern to an existing model,
|
112
|
+
or if data corruption or other issues have broken the existing values.
|
113
|
+
|
114
|
+
#### Options
|
115
|
+
|
116
|
+
##### As
|
117
|
+
|
118
|
+
cache_ordering sort_params, :as => :named_order
|
119
|
+
|
120
|
+
Sets the name of the generated order field and helpers. If no name is
|
121
|
+
specified, one will be automatically generated of the form
|
122
|
+
first_field_desc_second_field_asc_order.
|
123
|
+
|
124
|
+
##### Filter
|
125
|
+
|
126
|
+
cache_ordering sort_params, :filter => { :published => true }
|
127
|
+
|
128
|
+
Restricts which records from the collection will be sorted to generate the
|
129
|
+
ordering values. If a record is filtered out, its ordering field will be set
|
130
|
+
to nil.
|
131
|
+
|
80
132
|
### Sluggable
|
81
133
|
|
82
134
|
require 'mongoid/sleeping_king_studios/sluggable'
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# lib/mongoid/sleeping_king_studios/orderable.rb
|
2
|
+
|
3
|
+
require 'mongoid/sleeping_king_studios/orderable/metadata'
|
4
|
+
|
5
|
+
module Mongoid::SleepingKingStudios
|
6
|
+
# Adds an order field that stores the index of the record relative to the
|
7
|
+
# specified sort query. Storing the order in this fashion allows, for
|
8
|
+
# example, finding the next or previous records in the set without needing to
|
9
|
+
# perform the full sort query each time.
|
10
|
+
#
|
11
|
+
# @example Order by Most Recently Created:
|
12
|
+
# class SortedDocument
|
13
|
+
# include Mongoid::Document
|
14
|
+
# include Mongoid::SleepingKingStudios::Ordering
|
15
|
+
#
|
16
|
+
# cache_ordering :created_at.desc, :as => :most_recent_order
|
17
|
+
# end # class
|
18
|
+
#
|
19
|
+
# @see ClassMethods#cache_ordering
|
20
|
+
#
|
21
|
+
# @since 0.7.0
|
22
|
+
module Orderable
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
extend Mongoid::SleepingKingStudios::Concern
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
#
|
28
|
+
# Sets up the orderable relation, creating fields, callbacks and helper
|
29
|
+
# methods.
|
30
|
+
#
|
31
|
+
# @param [Class] base The base class into which the concern is mixed in.
|
32
|
+
# @param [Symbol, Array, Hash] sort_params The params used to sort the
|
33
|
+
# collection, generating the cached order index.
|
34
|
+
# @param [Hash] options The options for the relation.
|
35
|
+
def self.apply base, sort_params, options
|
36
|
+
name = :orderable
|
37
|
+
validate_options name, options
|
38
|
+
options.update :sort_params => sort_params
|
39
|
+
meta = characterize name, options, Metadata
|
40
|
+
|
41
|
+
relate base, name, meta
|
42
|
+
|
43
|
+
define_fields base, meta
|
44
|
+
define_callbacks base, meta
|
45
|
+
define_helpers base, meta
|
46
|
+
end # module method apply
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
#
|
50
|
+
# Creates an order field of type Integer on the base class, and sets the
|
51
|
+
# writer to private.
|
52
|
+
#
|
53
|
+
# @param [Class] base The base class into which the concern is mixed in.
|
54
|
+
# @param [Metadata] metadata The metadata for the relation.
|
55
|
+
def self.define_fields base, metadata
|
56
|
+
base.send :field, metadata.field_name, :type => Integer
|
57
|
+
|
58
|
+
base.send :private, metadata.field_writer
|
59
|
+
end # module method define_fields
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
#
|
63
|
+
# Adds an after_save callback to update the index of the record and all
|
64
|
+
# subsequent records in the ordering.
|
65
|
+
#
|
66
|
+
# @param [Class] base The base class into which the concern is mixed in.
|
67
|
+
# @param [Metadata] metadata The metadata for the relation.
|
68
|
+
def self.define_callbacks base, metadata
|
69
|
+
base.after_save do
|
70
|
+
criteria = metadata.sort_criteria(base)
|
71
|
+
ordering = criteria.to_a
|
72
|
+
order_index = ordering.index(self)
|
73
|
+
|
74
|
+
if order_index.nil?
|
75
|
+
if send(metadata.field_was).nil?
|
76
|
+
# Both the old and new values are nil, so mission accomplished.
|
77
|
+
return
|
78
|
+
else
|
79
|
+
# The old value wasn't nil, so remember it and set the new value,
|
80
|
+
# then start looping through the ordered collection at the old
|
81
|
+
# value.
|
82
|
+
order_index = send(metadata.field_was)
|
83
|
+
|
84
|
+
# Update the current instance.
|
85
|
+
self[metadata.field_name] = nil
|
86
|
+
|
87
|
+
# Set the value in the datastore.
|
88
|
+
set(metadata.field_name => order_index)
|
89
|
+
end # unless
|
90
|
+
else
|
91
|
+
# Update the current instance.
|
92
|
+
self[metadata.field_name] = order_index
|
93
|
+
end # if
|
94
|
+
|
95
|
+
ordering[order_index..-1].each_with_index do |object, i|
|
96
|
+
object.set(metadata.field_name => (order_index + i))
|
97
|
+
end # each
|
98
|
+
end # callback
|
99
|
+
end # module method define_callbacks
|
100
|
+
|
101
|
+
# @api private
|
102
|
+
#
|
103
|
+
# Adds a class-level reorder! helper that loops through the entire
|
104
|
+
# collection and updates the ordering of each item.
|
105
|
+
#
|
106
|
+
# @param [Class] base The base class into which the concern is mixed in.
|
107
|
+
# @param [Metadata] metadata The metadata for the relation.
|
108
|
+
def self.define_helpers base, metadata
|
109
|
+
name = :"reorder_#{metadata.field_name.to_s.gsub(/_order\z/,'')}!"
|
110
|
+
meta = class << base; self; end
|
111
|
+
meta.send :define_method, name do
|
112
|
+
base.update_all(metadata.field_name => nil)
|
113
|
+
|
114
|
+
criteria = metadata.sort_criteria(base)
|
115
|
+
ordering = criteria.to_a
|
116
|
+
|
117
|
+
ordering.each_with_index do |record, index|
|
118
|
+
record.set(metadata.field_name => index)
|
119
|
+
end # each
|
120
|
+
end # method
|
121
|
+
end # module method define_helpers
|
122
|
+
|
123
|
+
# Returns a list of options that are valid for this concern.
|
124
|
+
#
|
125
|
+
# @return [Array<Symbol>] The list of valid options.
|
126
|
+
def self.valid_options
|
127
|
+
super + %i(
|
128
|
+
as
|
129
|
+
filter
|
130
|
+
) # end array
|
131
|
+
end # module method valid options
|
132
|
+
|
133
|
+
# Class methods added to the base class via #extend.
|
134
|
+
module ClassMethods
|
135
|
+
# @overload cache_ordering sort_params, options = {}
|
136
|
+
# Creates the order field and sets up the callbacks and helpers.
|
137
|
+
#
|
138
|
+
# @param [Array] sort_params The sort query used to order the
|
139
|
+
# collection. Accepts a subset of the options for a default
|
140
|
+
# Origin sort operation:
|
141
|
+
# - :field_name.desc, :another_field
|
142
|
+
# - { :field_name => -1, :another_field => 1 }
|
143
|
+
# - \[[:field_name, -1], [:another_field, :asc]]
|
144
|
+
# @param [Hash] options The options for the relation.
|
145
|
+
# @option options [Symbol] :as
|
146
|
+
# Sets the name of the generated field and helpers. By default,
|
147
|
+
# uses the name(s) and direction(s) of the fields from the sort
|
148
|
+
# query, e.g. :field_name_asc_another_field_desc_order.
|
149
|
+
# @option options [Hash] :filter
|
150
|
+
# Sets a filter that excludes collection items from the ordering
|
151
|
+
# process. Accepts the same parameters as a Mongoid #where query.
|
152
|
+
#
|
153
|
+
# @raise [Mongoid::Errors::InvalidOptions] If any of the provided
|
154
|
+
# options are invalid.
|
155
|
+
def cache_ordering *sort_params, **options
|
156
|
+
concern = Mongoid::SleepingKingStudios::Orderable
|
157
|
+
concern.apply self, sort_params, options
|
158
|
+
end # class method slugify
|
159
|
+
|
160
|
+
# @!method reorder_ordering_name!
|
161
|
+
# Iterates through the entire collection and sets the cached order of
|
162
|
+
# each item to its current order index. Filtered items have their order
|
163
|
+
# set to nil. Normally, this should be taken care of when the items are
|
164
|
+
# saved, but this method allows the process to be reset in case of data
|
165
|
+
# corruption or other issues.
|
166
|
+
#
|
167
|
+
# The generated name of this method will depend on the sort params or
|
168
|
+
# the :as option provided. For example, :as => :alphabetical_order will
|
169
|
+
# result in a class method ::reorder_alphabetical!
|
170
|
+
end # module
|
171
|
+
end # module
|
172
|
+
end # module
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# lib/mongoid/sleeping_king_studios/orderable/metadata.rb
|
2
|
+
|
3
|
+
require 'mongoid/sleeping_king_studios/concern/metadata'
|
4
|
+
|
5
|
+
module Mongoid::SleepingKingStudios
|
6
|
+
module Orderable
|
7
|
+
# Stores information about an Orderable concern.
|
8
|
+
class Metadata < Mongoid::SleepingKingStudios::Concern::Metadata
|
9
|
+
# @param [Symbol, String] name The name of the concern or relation.
|
10
|
+
# @param [Hash] properties The properties of the concern or relation.
|
11
|
+
def initialize name, properties = {}
|
12
|
+
super
|
13
|
+
|
14
|
+
self[:sort_params] = case properties[:sort_params]
|
15
|
+
when Array
|
16
|
+
properties[:sort_params].reduce({}) do |hsh, param|
|
17
|
+
hsh.merge parse_sort_param(param)
|
18
|
+
end # each
|
19
|
+
when Hash
|
20
|
+
properties[:sort_params].each.with_object({}) do |(key, value), hsh|
|
21
|
+
hsh[key] = parse_sort_direction(value)
|
22
|
+
end # each
|
23
|
+
when Symbol, Origin::Key
|
24
|
+
parse_sort_param(properties[:sort_params])
|
25
|
+
end # case
|
26
|
+
end # method initialize
|
27
|
+
|
28
|
+
# The name of the field used to store the order.
|
29
|
+
#
|
30
|
+
# @return [Symbol] The field name.
|
31
|
+
def field_name
|
32
|
+
fetch(:as, default_field_name).intern
|
33
|
+
end # method field_name
|
34
|
+
|
35
|
+
# @return [Boolean] True if a custom field name is defined; otherwise
|
36
|
+
# false.
|
37
|
+
def field_name?
|
38
|
+
!!self[:as]
|
39
|
+
end # method field_name
|
40
|
+
|
41
|
+
# The name of the dirty tracking method for the order field.
|
42
|
+
#
|
43
|
+
# @return [Symbol] The method name.
|
44
|
+
def field_was
|
45
|
+
:"#{field_name}_was"
|
46
|
+
end # method field_was
|
47
|
+
|
48
|
+
# The name of the writer for the order field.
|
49
|
+
#
|
50
|
+
# @return [Symbol] The method name.
|
51
|
+
def field_writer
|
52
|
+
:"#{field_name}="
|
53
|
+
end # method field_writer
|
54
|
+
|
55
|
+
# The criteria to filter only the desired collection items to sort.
|
56
|
+
#
|
57
|
+
# @param [Mongoid::Criteria] criteria The base criteria to modify using
|
58
|
+
# the filter params.
|
59
|
+
#
|
60
|
+
# @return [Mongoid::Criteria]
|
61
|
+
def filter_criteria criteria
|
62
|
+
filter_params? ? criteria.where(filter_params) : criteria
|
63
|
+
end # method filter_criteria
|
64
|
+
|
65
|
+
# The options (if any) to filter the collection by prior to sorting.
|
66
|
+
#
|
67
|
+
# @return [Hash]
|
68
|
+
def filter_params
|
69
|
+
self[:filter]
|
70
|
+
end # method filter_params
|
71
|
+
|
72
|
+
# @return [Boolean] True if filter params are defined; otherwise false.
|
73
|
+
def filter_params?
|
74
|
+
!!self[:filter]
|
75
|
+
end # method filter_params?
|
76
|
+
|
77
|
+
# The criteria to be used when sorting the collection.
|
78
|
+
#
|
79
|
+
# @param [Mongoid::Criteria] criteria The base criteria to modify using
|
80
|
+
# the sort params.
|
81
|
+
#
|
82
|
+
# @return [Mongoid::Criteria]
|
83
|
+
def sort_criteria criteria
|
84
|
+
filter_criteria(criteria).order_by(self[:sort_params])
|
85
|
+
end # method sort_criteria
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def default_field_name
|
90
|
+
self[:sort_params].map { |key, value|
|
91
|
+
"#{key}_#{value == 1 ? 'asc' : 'desc'}"
|
92
|
+
}.join('_') + '_order'
|
93
|
+
end # method default_field_name
|
94
|
+
|
95
|
+
def parse_sort_param param
|
96
|
+
case param
|
97
|
+
when Array
|
98
|
+
{ param[0] => parse_sort_direction(param[1]) }
|
99
|
+
when Origin::Key
|
100
|
+
{ param.name => param.operator }
|
101
|
+
when Symbol
|
102
|
+
{ param => 1 }
|
103
|
+
end # case
|
104
|
+
end # method sort_param=
|
105
|
+
|
106
|
+
def parse_sort_direction direction
|
107
|
+
(direction == -1 || direction.to_s.downcase == 'desc') ? -1 : 1
|
108
|
+
end # method parse_sort_direction
|
109
|
+
end # class
|
110
|
+
end # module
|
111
|
+
end # module
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-sleeping_king_studios
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob "Merlin" Smith
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ~>
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '1.2'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: pry
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.9'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.9'
|
125
139
|
description: |2
|
126
140
|
A collection of concerns and extensions to add functionality to Mongoid
|
127
141
|
documents and collections. The features can be included individually or by
|
@@ -143,6 +157,8 @@ files:
|
|
143
157
|
- lib/mongoid/sleeping_king_studios/has_tree/metadata.rb
|
144
158
|
- lib/mongoid/sleeping_king_studios/has_tree/parent/metadata.rb
|
145
159
|
- lib/mongoid/sleeping_king_studios/has_tree.rb
|
160
|
+
- lib/mongoid/sleeping_king_studios/orderable/metadata.rb
|
161
|
+
- lib/mongoid/sleeping_king_studios/orderable.rb
|
146
162
|
- lib/mongoid/sleeping_king_studios/sluggable/metadata.rb
|
147
163
|
- lib/mongoid/sleeping_king_studios/sluggable.rb
|
148
164
|
- lib/mongoid/sleeping_king_studios/version.rb
|