mongoid_orderable 6.0.2 → 6.0.3

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
  SHA256:
3
- metadata.gz: b3a80c8eb058adeffe1ead4c0fa20ce10660ea49a2df1405d46048419f2af965
4
- data.tar.gz: d68f6f64f52f132295e588dfcd59ad27d22a68237e5b29ab7682a37e3c940ca5
3
+ metadata.gz: 8882038ec4e57486bd693bc1732f935f2d01f949aeedc7f71b3c54bca5ac0b44
4
+ data.tar.gz: c0c82c4802280dfe6005e89122e9f7905fabcdf261bc9b3864016d10a70c112c
5
5
  SHA512:
6
- metadata.gz: 6c2d9da294dda836b17047aaf3ebc6329b3c382fb793c83fe57dd63a07512d2e1da65af3559b12e75ce3924b1edfaaefad8317f4acaa488cb94ad2392d84b474
7
- data.tar.gz: 58e8c15554183674cef20639c2cb888cb6e793471ecf7f55ea76360f2ca7de351d218f417e34db220d2daa32c6ab5dc11da23bce7acec0f77a1f99718e713705
6
+ metadata.gz: 64332b6ae9d7786864d238accdd3ec81c8defa059ff1d7e1763e487a271c2dffb2f25b3a1a2332947a3b262ecc6cb8da33aae0acdab0cd04bd3ac6e0a2a2ca44
7
+ data.tar.gz: 918ea29f2b71caf6cfba5cdc7e145bba69d0d90d2561e1983676fa13a3517f87eaabd7f66d628fbae1ec45df56fe82a11a723de632bbd1650697f39939d10534
data/CHANGELOG.md CHANGED
@@ -1,7 +1,12 @@
1
- ### 6.0.3 (Next)
1
+ ### 6.0.4 (Next)
2
2
 
3
3
  * Your contribution here.
4
4
 
5
+ ### 6.0.3 (2021/06/27)
6
+
7
+ * [#72](https://github.com/mongoid/mongoid_orderable/pull/72): Feature: Add :if and :unless conditions which disable callbacks.
8
+ * [#71](https://github.com/mongoid/mongoid_orderable/pull/71): Fix: Add TTL index to locks table.
9
+
5
10
  ### 6.0.2 (2021/01/26)
6
11
 
7
12
  * [#70](https://github.com/mongoid/mongoid_orderable/pull/70): Fix: Transactions should not use around callbacks - [@johnnyshields](https://github.com/johnnyshields).
data/README.md CHANGED
@@ -138,7 +138,8 @@ book.previous_#{field}_item
138
138
 
139
139
  where `#{field}` is either `position` or `serial_no`.
140
140
 
141
- When a model defines multiple orderable fields, the original helpers are also available and work on the first orderable field.
141
+ When a model defines multiple orderable fields, the original helpers are also available and work
142
+ on the first orderable field.
142
143
 
143
144
  ```ruby
144
145
  @book1 = Book.create!
@@ -181,6 +182,24 @@ end
181
182
  To ensure the position is written correctly, you will need to set
182
183
  `cascade_callbacks: true` on the relation.
183
184
 
185
+ ### Disable Ordering
186
+
187
+ You can disable position tracking for specific documents using the `:if` and `:unless` options.
188
+ This is in advanced scenarios where you want to control position manually for certain documents.
189
+ In general, the disable condition should match a specific scope.
190
+ **Warning:** If used improperly, this will cause your documents to become out-of-order.
191
+
192
+ ```ruby
193
+ class Book
194
+ include Mongoid::Document
195
+ include Mongoid::Orderable
196
+
197
+ field :track_position, type: Boolean
198
+
199
+ orderable if: :track_position, unless: -> { created_at < Date.parse('2020-01-01') }
200
+ end
201
+ ```
202
+
184
203
  ## Transaction Support
185
204
 
186
205
  By default, Mongoid Orderable does not guarantee ordering consistency
@@ -219,7 +238,7 @@ After `transaction_max_retries` has been exceeded, a
219
238
 
220
239
  When using transactions, Mongoid Orderable creates a collection
221
240
  `mongoid_orderable_locks` which is used to store temporary lock objects.
222
- This collection accumulate documents overtime; it is safe to delete it periodically.
241
+ Lock collections use a TTL index which auto-deletes objects older than 1 day.
223
242
 
224
243
  You can change the lock collection name globally or per model:
225
244
 
@@ -11,6 +11,8 @@ module Configs
11
11
  base
12
12
  index
13
13
  default
14
+ if
15
+ unless
14
16
  use_transactions
15
17
  transaction_max_retries
16
18
  lock_collection].freeze
@@ -17,6 +17,14 @@ module Generators
17
17
  field ||= default_orderable_field
18
18
  #{self_class}.orderable_configs[field][:field]
19
19
  end
20
+
21
+ def orderable_if(field)
22
+ #{self_class}.orderable_configs[field][:if]
23
+ end
24
+
25
+ def orderable_unless(field)
26
+ #{self_class}.orderable_configs[field][:unless]
27
+ end
20
28
  KLASS
21
29
 
22
30
  generate_method(:orderable_inherited_class) do
@@ -12,12 +12,14 @@ module Generators
12
12
  module Models
13
13
  class #{model_name}
14
14
  include Mongoid::Document
15
+ include Mongoid::Timestamps::Updated::Short
15
16
 
16
17
  store_in collection: :#{collection_name}
17
18
 
18
19
  field :scope, type: String
19
20
 
20
21
  index({ scope: 1 }, { unique: 1 })
22
+ index({ updated_at: 1 }, { expire_after_seconds: 86400 })
21
23
  end
22
24
  end
23
25
  KLASS
@@ -15,6 +15,8 @@ module Handlers
15
15
  delegate :orderable_keys,
16
16
  :orderable_field,
17
17
  :orderable_position,
18
+ :orderable_if,
19
+ :orderable_unless,
18
20
  :orderable_scope,
19
21
  :orderable_scope_changed?,
20
22
  :orderable_top,
@@ -39,7 +41,7 @@ module Handlers
39
41
  end
40
42
 
41
43
  def apply_one_position(field, target_position)
42
- return unless changed?(field)
44
+ return unless allowed?(field) && changed?(field)
43
45
 
44
46
  set_lock(field) if use_transactions
45
47
 
@@ -94,6 +96,7 @@ module Handlers
94
96
  end
95
97
 
96
98
  def remove_one_position(field)
99
+ return unless allowed?(field)
97
100
  f = orderable_field(field)
98
101
  current = orderable_position(field)
99
102
  set_lock(field) if use_transactions
@@ -127,6 +130,25 @@ module Handlers
127
130
  target_position
128
131
  end
129
132
 
133
+ def allowed?(field)
134
+ cond_if = orderable_if(field)
135
+ cond_unless = orderable_unless(field)
136
+
137
+ (cond_if.nil? || resolve_condition(cond_if)) &&
138
+ (cond_unless.nil? || !resolve_condition(cond_unless))
139
+ end
140
+
141
+ def resolve_condition(condition)
142
+ case condition
143
+ when Proc
144
+ condition.arity.zero? ? doc.instance_exec(&condition) : condition.call(doc)
145
+ when Symbol
146
+ doc.send(condition)
147
+ else
148
+ condition || false
149
+ end
150
+ end
151
+
130
152
  def changed?(field)
131
153
  return true if new_record? || !doc.send(orderable_field(field)) || move_all[field]
132
154
  changeable_keys(field).any? {|f| doc.send("#{f}_changed?") }
@@ -153,7 +175,7 @@ module Handlers
153
175
  model_name = doc.class.orderable_configs[field][:lock_collection].to_s.singularize.classify
154
176
  model = Mongoid::Orderable::Models.const_get(model_name)
155
177
  attrs = lock_scope(field, generic)
156
- model.where(attrs).find_one_and_update(attrs, { upsert: true })
178
+ model.where(attrs).find_one_and_update(attrs.merge(updated_at: Time.now), { upsert: true })
157
179
  end
158
180
 
159
181
  def lock_scope(field, generic = false)
@@ -35,7 +35,7 @@ module Orderable
35
35
  def add_db_index
36
36
  spec = [[config[:field], 1]]
37
37
  config[:scope].each {|field| spec.unshift([field, 1]) } if config[:scope].is_a?(Array)
38
- klass.index(Hash[spec])
38
+ klass.index(spec.to_h)
39
39
  end
40
40
 
41
41
  def save_config
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Mongoid
4
4
  module Orderable
5
- VERSION = '6.0.2'
5
+ VERSION = '6.0.3'
6
6
  end
7
7
  end
@@ -0,0 +1,36 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe ConditionalOrderable do
4
+
5
+ shared_examples_for 'conditional_orderable' do
6
+
7
+ before :each do
8
+ ConditionalOrderable.create!(cond_a: false, cond_b: nil)
9
+ ConditionalOrderable.create!(cond_a: false, cond_b: 1)
10
+ ConditionalOrderable.create!(cond_a: true, cond_b: 2)
11
+ ConditionalOrderable.create!(cond_a: false, cond_b: 3)
12
+ ConditionalOrderable.create!(cond_a: true, cond_b: 4)
13
+ ConditionalOrderable.create!(cond_a: true, cond_b: 5)
14
+ end
15
+
16
+ it 'should have proper position field' do
17
+ orderables = ConditionalOrderable.all.sort_by {|x| x.cond_b || 0 }
18
+
19
+ expect(orderables.map(&:pos_a)).to eq [nil, nil, 1, nil, 2, 3]
20
+ expect(orderables.map(&:pos_b)).to eq [nil, 1, 2, 3, 4, nil]
21
+ expect(orderables.map(&:pos_c)).to eq [1, 2, 3, 4, 5, 6]
22
+ end
23
+ end
24
+
25
+ context 'with transactions' do
26
+ enable_transactions!
27
+
28
+ it_behaves_like 'conditional_orderable'
29
+ end
30
+
31
+ context 'without transactions' do
32
+ disable_transactions!
33
+
34
+ it_behaves_like 'conditional_orderable'
35
+ end
36
+ end
@@ -13,6 +13,18 @@ class SimpleOrderable
13
13
  orderable
14
14
  end
15
15
 
16
+ class ConditionalOrderable
17
+ include Mongoid::Document
18
+ include Mongoid::Orderable
19
+
20
+ field :cond_a, type: Boolean
21
+ field :cond_b, type: Integer
22
+
23
+ orderable field: :pos_a, if: :cond_a, unless: ->(obj) { obj.cond_b&.<(2) }
24
+ orderable field: :pos_b, if: -> { cond_b&.<=(4) }
25
+ orderable field: :pos_c, unless: false
26
+ end
27
+
16
28
  class ScopedGroup
17
29
  include Mongoid::Document
18
30
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_orderable
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.2
4
+ version: 6.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - pyromaniac
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-27 00:00:00.000000000 Z
11
+ date: 2021-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -117,6 +117,7 @@ files:
117
117
  - lib/mongoid/orderable/version.rb
118
118
  - lib/mongoid_orderable.rb
119
119
  - spec/integration/concurrency_spec.rb
120
+ - spec/integration/conditional_spec.rb
120
121
  - spec/integration/customized_spec.rb
121
122
  - spec/integration/embedded_spec.rb
122
123
  - spec/integration/foreign_key_spec.rb
@@ -131,7 +132,8 @@ files:
131
132
  - spec/spec_helper.rb
132
133
  - spec/support/models.rb
133
134
  homepage: https://github.com/mongoid/mongoid_orderable
134
- licenses: []
135
+ licenses:
136
+ - MIT
135
137
  metadata: {}
136
138
  post_install_message:
137
139
  rdoc_options: []
@@ -148,12 +150,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
150
  - !ruby/object:Gem::Version
149
151
  version: '0'
150
152
  requirements: []
151
- rubygems_version: 3.1.2
153
+ rubygems_version: 3.2.9
152
154
  signing_key:
153
155
  specification_version: 4
154
156
  summary: Mongoid orderable list implementation
155
157
  test_files:
156
158
  - spec/integration/concurrency_spec.rb
159
+ - spec/integration/conditional_spec.rb
157
160
  - spec/integration/customized_spec.rb
158
161
  - spec/integration/embedded_spec.rb
159
162
  - spec/integration/foreign_key_spec.rb