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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fdd1c69e257d2151ecdb439fc1f9ab974628c9ba
4
- data.tar.gz: cf273c392e0fde12e79b244819c1c880de8e11dd
3
+ metadata.gz: 218763356e4b6bc56393162ae45be295d7ccc478
4
+ data.tar.gz: 3fa4cfd1063e65e22322c5be1b183a6f04f10fc7
5
5
  SHA512:
6
- metadata.gz: 0e20060ad75456272818d391425dad43a7deca8babe64f104a586a946221db0aa0e57d227bb16acee3ac7a3921d272a12d8da2bde3a4d2794b85a6f8233965b8
7
- data.tar.gz: 6ab47ee0aed555756fd7ab7dadabe617985abe5c32d61efc1e4374546427abc0afebcb60185c84dfe50b87e083f8ca8ec10038120cc8480cca4adfdf7a54c8bf
6
+ metadata.gz: 1c2f385e2cc2184c33009e5907a15b8b052780dfbe90345dfa3bc9679a637e955df9acac88f2d429a207d53ba738d491db5d8ab491b2e4fa170b95f885803367
7
+ data.tar.gz: 7b579653a20b03b32db94736019d9c0caec715884948b9eff640397cae9d518c3f364d47c25e09a4bb2795151531f7f35316940aca01ee01629b1a4dd3c669d0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0
4
+
5
+ * Add Orderable concern.
6
+
3
7
  ## 0.6.2
4
8
 
5
9
  * Update dependencies (no user-facing changes).
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
@@ -3,6 +3,6 @@
3
3
  module Mongoid
4
4
  module SleepingKingStudios
5
5
  # The current version of the gem.
6
- VERSION = '0.6.2'
6
+ VERSION = '0.7.0'
7
7
  end # module
8
8
  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.6.2
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