ranked-model 0.4.2 → 0.4.7
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 +5 -5
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.travis.yml +27 -23
- data/Appraisals +27 -36
- data/Gemfile +12 -0
- data/Readme.mkd +70 -12
- data/gemfiles/rails_4_2.gemfile +9 -8
- data/gemfiles/rails_5_0.gemfile +9 -9
- data/gemfiles/rails_5_1.gemfile +9 -9
- data/gemfiles/rails_5_2.gemfile +8 -5
- data/gemfiles/rails_6_0.gemfile +22 -0
- data/lib/ranked-model.rb +16 -1
- data/lib/ranked-model/ranker.rb +38 -27
- data/lib/ranked-model/version.rb +1 -1
- data/ranked-model.gemspec +3 -3
- data/spec/duck-model/column_default_ducks_spec.rb +29 -0
- data/spec/duck-model/duck_spec.rb +115 -46
- data/spec/duck-model/lots_of_ducks_spec.rb +29 -33
- data/spec/ego-model/ego_spec.rb +3 -3
- data/spec/number-model/number_spec.rb +10 -2
- data/spec/player-model/records_already_exist_spec.rb +1 -1
- data/spec/ranked-model/ranker_spec.rb +18 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/sti-model/element_spec.rb +24 -24
- data/spec/sti-model/vehicle_spec.rb +4 -4
- data/spec/support/active_record.rb +13 -5
- metadata +10 -10
- data/gemfiles/rails_3_2.gemfile +0 -22
- data/gemfiles/rails_4_1.gemfile +0 -22
data/gemfiles/rails_5_2.gemfile
CHANGED
|
@@ -5,15 +5,18 @@ source "https://rubygems.org"
|
|
|
5
5
|
gem "activerecord", "~> 5.2.0"
|
|
6
6
|
|
|
7
7
|
group :sqlite do
|
|
8
|
-
gem "sqlite3", platform: :ruby
|
|
8
|
+
gem "sqlite3", "~> 1.3.13", platform: :ruby
|
|
9
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 52.0", platform: :jruby
|
|
9
10
|
end
|
|
10
11
|
|
|
11
|
-
group :
|
|
12
|
-
gem "
|
|
12
|
+
group :postgresql do
|
|
13
|
+
gem "pg", "~> 1.2.0", platform: :ruby
|
|
14
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 52.0", platform: :jruby
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
group :
|
|
16
|
-
gem "
|
|
17
|
+
group :mysql do
|
|
18
|
+
gem "mysql2", "~> 0.5.0", platform: :ruby
|
|
19
|
+
gem "activerecord-jdbcmysql-adapter", "~> 52.0", platform: :jruby
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
gemspec path: "../"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# This file was generated by Appraisal
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gem "activerecord", "~> 6.0.0"
|
|
6
|
+
|
|
7
|
+
group :sqlite do
|
|
8
|
+
gem "sqlite3", "~> 1.4", platform: :ruby
|
|
9
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 60.0", platform: :jruby
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
group :postgresql do
|
|
13
|
+
gem "pg", "~> 1.2.0", platform: :ruby
|
|
14
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 60.0", platform: :jruby
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
group :mysql do
|
|
18
|
+
gem "mysql2", "~> 0.5.0", platform: :ruby
|
|
19
|
+
gem "activerecord-jdbcmysql-adapter", "~> 60.0", platform: :jruby
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
gemspec path: "../"
|
data/lib/ranked-model.rb
CHANGED
|
@@ -3,6 +3,8 @@ require File.dirname(__FILE__)+'/ranked-model/railtie' if defined?(Rails::Railti
|
|
|
3
3
|
|
|
4
4
|
module RankedModel
|
|
5
5
|
|
|
6
|
+
class NonNilColumnDefault < StandardError; end
|
|
7
|
+
|
|
6
8
|
# Signed INT in MySQL
|
|
7
9
|
#
|
|
8
10
|
MAX_RANK_VALUE = 2147483647
|
|
@@ -18,7 +20,7 @@ module RankedModel
|
|
|
18
20
|
before_save :handle_ranking
|
|
19
21
|
|
|
20
22
|
scope :rank, lambda { |name|
|
|
21
|
-
|
|
23
|
+
reorder ranker(name.to_sym).column
|
|
22
24
|
}
|
|
23
25
|
end
|
|
24
26
|
|
|
@@ -45,6 +47,11 @@ module RankedModel
|
|
|
45
47
|
def ranks *args
|
|
46
48
|
self.rankers ||= []
|
|
47
49
|
ranker = RankedModel::Ranker.new(*args)
|
|
50
|
+
|
|
51
|
+
if column_default(ranker)
|
|
52
|
+
raise NonNilColumnDefault, %Q{Your ranked model column "#{ranker.name}" must not have a default value in the database.}
|
|
53
|
+
end
|
|
54
|
+
|
|
48
55
|
self.rankers << ranker
|
|
49
56
|
attr_reader "#{ranker.name}_position"
|
|
50
57
|
define_method "#{ranker.name}_position=" do |position|
|
|
@@ -54,9 +61,17 @@ module RankedModel
|
|
|
54
61
|
end
|
|
55
62
|
end
|
|
56
63
|
|
|
64
|
+
define_method "#{ranker.name}_rank" do
|
|
65
|
+
ranker.with(self).relative_rank
|
|
66
|
+
end
|
|
67
|
+
|
|
57
68
|
public "#{ranker.name}_position", "#{ranker.name}_position="
|
|
58
69
|
end
|
|
59
70
|
|
|
71
|
+
def column_default ranker
|
|
72
|
+
column_defaults[ranker.name.to_s] if ActiveRecord::Base.connected? && table_exists?
|
|
73
|
+
end
|
|
74
|
+
|
|
60
75
|
end
|
|
61
76
|
|
|
62
77
|
end
|
data/lib/ranked-model/ranker.rb
CHANGED
|
@@ -69,10 +69,20 @@ module RankedModel
|
|
|
69
69
|
update_all(ranker.column => value)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
def reset_ranks!
|
|
73
|
+
finder.update_all(ranker.column => nil)
|
|
74
|
+
end
|
|
75
|
+
|
|
72
76
|
def position
|
|
73
77
|
instance.send "#{ranker.name}_position"
|
|
74
78
|
end
|
|
75
79
|
|
|
80
|
+
def relative_rank
|
|
81
|
+
escaped_column = instance_class.connection.quote_column_name ranker.column
|
|
82
|
+
|
|
83
|
+
finder.where("#{escaped_column} < #{rank}").count(:all)
|
|
84
|
+
end
|
|
85
|
+
|
|
76
86
|
def rank
|
|
77
87
|
instance.send "#{ranker.column}"
|
|
78
88
|
end
|
|
@@ -171,7 +181,7 @@ module RankedModel
|
|
|
171
181
|
|
|
172
182
|
def assure_unique_position
|
|
173
183
|
if ( new_record? || rank_changed? )
|
|
174
|
-
if (rank > RankedModel::MAX_RANK_VALUE) ||
|
|
184
|
+
if (rank > RankedModel::MAX_RANK_VALUE) || rank_taken?
|
|
175
185
|
rearrange_ranks
|
|
176
186
|
end
|
|
177
187
|
end
|
|
@@ -179,7 +189,7 @@ module RankedModel
|
|
|
179
189
|
|
|
180
190
|
def rearrange_ranks
|
|
181
191
|
_scope = finder
|
|
182
|
-
escaped_column =
|
|
192
|
+
escaped_column = instance_class.connection.quote_column_name ranker.column
|
|
183
193
|
# If there is room at the bottom of the list and we're added to the very top of the list...
|
|
184
194
|
if current_first.rank && current_first.rank > RankedModel::MIN_RANK_VALUE && rank == RankedModel::MAX_RANK_VALUE
|
|
185
195
|
# ...then move everyone else down 1 to make room for us at the end
|
|
@@ -205,29 +215,35 @@ module RankedModel
|
|
|
205
215
|
end
|
|
206
216
|
|
|
207
217
|
def rebalance_ranks
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
218
|
+
ActiveRecord::Base.transaction do
|
|
219
|
+
if rank && instance.persisted?
|
|
220
|
+
origin = current_order.index { |item| item.instance.id == instance.id }
|
|
221
|
+
if origin
|
|
222
|
+
destination = current_order.index { |item| rank <= item.rank }
|
|
223
|
+
destination -= 1 if origin < destination
|
|
224
|
+
|
|
225
|
+
current_order.insert destination, current_order.delete_at(origin)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
212
228
|
|
|
213
|
-
current_order.
|
|
214
|
-
|
|
229
|
+
gaps = current_order.size + 1
|
|
230
|
+
range = (RankedModel::MAX_RANK_VALUE - RankedModel::MIN_RANK_VALUE).to_f
|
|
231
|
+
gap_size = (range / gaps).ceil
|
|
215
232
|
|
|
216
|
-
|
|
217
|
-
range = (RankedModel::MAX_RANK_VALUE - RankedModel::MIN_RANK_VALUE).to_f
|
|
218
|
-
gap_size = (range / gaps).ceil
|
|
233
|
+
reset_ranks!
|
|
219
234
|
|
|
220
|
-
|
|
221
|
-
|
|
235
|
+
current_order.each.with_index(1) do |item, position|
|
|
236
|
+
new_rank = (gap_size * position) + RankedModel::MIN_RANK_VALUE
|
|
222
237
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
238
|
+
if item.instance.id == instance.id
|
|
239
|
+
rank_at new_rank
|
|
240
|
+
else
|
|
241
|
+
item.update_rank! new_rank
|
|
242
|
+
end
|
|
227
243
|
end
|
|
228
|
-
end
|
|
229
244
|
|
|
230
|
-
|
|
245
|
+
reset_cache
|
|
246
|
+
end
|
|
231
247
|
end
|
|
232
248
|
|
|
233
249
|
def finder(order = :asc)
|
|
@@ -256,7 +272,7 @@ module RankedModel
|
|
|
256
272
|
_finder = _finder.where.not instance_class.primary_key.to_sym => instance.id
|
|
257
273
|
end
|
|
258
274
|
|
|
259
|
-
_finder.
|
|
275
|
+
_finder.reorder(ranker.column.to_sym => order).select(columns)
|
|
260
276
|
end
|
|
261
277
|
end
|
|
262
278
|
|
|
@@ -286,13 +302,8 @@ module RankedModel
|
|
|
286
302
|
end
|
|
287
303
|
end
|
|
288
304
|
|
|
289
|
-
def
|
|
290
|
-
|
|
291
|
-
except( :order ).
|
|
292
|
-
where( ranker.column => _rank ).
|
|
293
|
-
first)
|
|
294
|
-
RankedModel::Ranker::Mapper.new ranker, ordered_instance
|
|
295
|
-
end
|
|
305
|
+
def rank_taken?
|
|
306
|
+
finder.except(:order).where(ranker.column => rank).exists?
|
|
296
307
|
end
|
|
297
308
|
|
|
298
309
|
def neighbors_at_position _pos
|
data/lib/ranked-model/version.rb
CHANGED
data/ranked-model.gemspec
CHANGED
|
@@ -9,11 +9,11 @@ Gem::Specification.new do |s|
|
|
|
9
9
|
s.authors = ["Matthew Beale"]
|
|
10
10
|
s.email = ["matt.beale@madhatted.com"]
|
|
11
11
|
s.homepage = "https://github.com/mixonic/ranked-model"
|
|
12
|
-
s.summary = %q{An acts_as_sortable replacement built for Rails
|
|
13
|
-
s.description = %q{ranked-model is a modern row sorting library built for Rails
|
|
12
|
+
s.summary = %q{An acts_as_sortable replacement built for Rails 4.2+}
|
|
13
|
+
s.description = %q{ranked-model is a modern row sorting library built for Rails 4.2+. It uses ARel aggressively and is better optimized than most other libraries.}
|
|
14
14
|
s.license = 'MIT'
|
|
15
15
|
|
|
16
|
-
s.add_dependency "activerecord", ">= 4.
|
|
16
|
+
s.add_dependency "activerecord", ">= 4.2"
|
|
17
17
|
s.add_development_dependency "rspec", "~> 3"
|
|
18
18
|
s.add_development_dependency "rspec-its"
|
|
19
19
|
s.add_development_dependency "mocha"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'ColumnDefaultDuck' do
|
|
4
|
+
|
|
5
|
+
it "should raise an error if we try to initialise ranked_model on a column with a default value" do
|
|
6
|
+
expect {
|
|
7
|
+
class ColumnDefaultDuck < ActiveRecord::Base
|
|
8
|
+
include RankedModel
|
|
9
|
+
ranks :size, :with_same => :pond
|
|
10
|
+
end
|
|
11
|
+
}.to raise_error(RankedModel::NonNilColumnDefault, 'Your ranked model column "size" must not have a default value in the database.')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should not raise an error if we don't have a database connection when checking for default value" do
|
|
15
|
+
begin
|
|
16
|
+
ActiveRecord::Base.remove_connection
|
|
17
|
+
|
|
18
|
+
expect {
|
|
19
|
+
class ColumnDefaultDuck < ActiveRecord::Base
|
|
20
|
+
include RankedModel
|
|
21
|
+
ranks :size, :with_same => :pond
|
|
22
|
+
end
|
|
23
|
+
}.not_to raise_error
|
|
24
|
+
ensure
|
|
25
|
+
ActiveRecord::Base.establish_connection(ENV['DB'].to_sym)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
@@ -10,12 +10,16 @@ describe Duck do
|
|
|
10
10
|
|
|
11
11
|
it { expect(subject).to respond_to(:row_position) }
|
|
12
12
|
it { expect(subject).to respond_to(:row_position=) }
|
|
13
|
+
it { expect(subject).to respond_to(:row_rank) }
|
|
13
14
|
it { expect(subject).to respond_to(:size_position) }
|
|
14
15
|
it { expect(subject).to respond_to(:size_position=) }
|
|
16
|
+
it { expect(subject).to respond_to(:size_rank) }
|
|
15
17
|
it { expect(subject).to respond_to(:age_position) }
|
|
16
18
|
it { expect(subject).to respond_to(:age_position=) }
|
|
19
|
+
it { expect(subject).to respond_to(:age_rank) }
|
|
17
20
|
it { expect(subject).to respond_to(:landing_order_position) }
|
|
18
21
|
it { expect(subject).to respond_to(:landing_order_position=) }
|
|
22
|
+
it { expect(subject).to respond_to(:landing_order_rank) }
|
|
19
23
|
|
|
20
24
|
end
|
|
21
25
|
|
|
@@ -44,9 +48,9 @@ describe Duck do
|
|
|
44
48
|
}
|
|
45
49
|
@ducks.each { |name, duck|
|
|
46
50
|
duck.reload
|
|
47
|
-
duck.
|
|
48
|
-
duck.
|
|
49
|
-
duck.
|
|
51
|
+
duck.update :row_position => 0
|
|
52
|
+
duck.update :size_position => 0
|
|
53
|
+
duck.update :age_position => 0
|
|
50
54
|
duck.save!
|
|
51
55
|
}
|
|
52
56
|
@ducks.each {|name, duck| duck.reload }
|
|
@@ -55,8 +59,8 @@ describe Duck do
|
|
|
55
59
|
describe "sorting by size on in_shin_pond" do
|
|
56
60
|
|
|
57
61
|
before {
|
|
58
|
-
@ducks[:quacky].
|
|
59
|
-
@ducks[:wingy].
|
|
62
|
+
@ducks[:quacky].update :size_position => 0
|
|
63
|
+
@ducks[:wingy].update :size_position => 2
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
subject { Duck.in_shin_pond.rank(:size).to_a }
|
|
@@ -72,8 +76,8 @@ describe Duck do
|
|
|
72
76
|
describe "sorting by age on Shin pond" do
|
|
73
77
|
|
|
74
78
|
before {
|
|
75
|
-
@ducks[:feathers].
|
|
76
|
-
@ducks[:wingy].
|
|
79
|
+
@ducks[:feathers].update :age_position => 0
|
|
80
|
+
@ducks[:wingy].update :age_position => 0
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
subject { Duck.where(:pond => 'Shin').rank(:age).to_a }
|
|
@@ -89,10 +93,10 @@ describe Duck do
|
|
|
89
93
|
describe "sorting by row" do
|
|
90
94
|
|
|
91
95
|
before {
|
|
92
|
-
@ducks[:beaky].
|
|
93
|
-
@ducks[:webby].
|
|
94
|
-
@ducks[:waddly].
|
|
95
|
-
@ducks[:wingy].
|
|
96
|
+
@ducks[:beaky].update :row_position => 0
|
|
97
|
+
@ducks[:webby].update :row_position => 2
|
|
98
|
+
@ducks[:waddly].update :row_position => 2
|
|
99
|
+
@ducks[:wingy].update :row_position => 6
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
subject { Duck.rank(:row).to_a }
|
|
@@ -108,13 +112,13 @@ describe Duck do
|
|
|
108
112
|
describe "mixed sorting by" do
|
|
109
113
|
|
|
110
114
|
before {
|
|
111
|
-
@ducks[:quacky].
|
|
112
|
-
@ducks[:beaky].
|
|
113
|
-
@ducks[:webby].
|
|
114
|
-
@ducks[:wingy].
|
|
115
|
-
@ducks[:waddly].
|
|
116
|
-
@ducks[:wingy].
|
|
117
|
-
@ducks[:webby].
|
|
115
|
+
@ducks[:quacky].update :size_position => 0
|
|
116
|
+
@ducks[:beaky].update :row_position => 0
|
|
117
|
+
@ducks[:webby].update :row_position => 2
|
|
118
|
+
@ducks[:wingy].update :size_position => 1
|
|
119
|
+
@ducks[:waddly].update :row_position => 2
|
|
120
|
+
@ducks[:wingy].update :row_position => 6
|
|
121
|
+
@ducks[:webby].update :row_position => 6
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
describe "row" do
|
|
@@ -149,7 +153,7 @@ describe Duck do
|
|
|
149
153
|
# puts Duck.rank(:age).collect {|duck| "#{duck.name} #{duck.age}" }
|
|
150
154
|
duck = Duck.rank(:age)[2]
|
|
151
155
|
expect(->{
|
|
152
|
-
duck.
|
|
156
|
+
duck.update :name => 'New Name'
|
|
153
157
|
}).to_not change(duck.reload, :age)
|
|
154
158
|
# puts Duck.rank(:age).collect {|duck| "#{duck.name} #{duck.age}" }
|
|
155
159
|
end
|
|
@@ -183,7 +187,7 @@ describe Duck do
|
|
|
183
187
|
|
|
184
188
|
before {
|
|
185
189
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
|
|
186
|
-
@ducks[:wingy].
|
|
190
|
+
@ducks[:wingy].update :row_position => 2
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
context {
|
|
@@ -210,7 +214,7 @@ describe Duck do
|
|
|
210
214
|
|
|
211
215
|
before {
|
|
212
216
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
|
|
213
|
-
@ducks[:wingy].
|
|
217
|
+
@ducks[:wingy].update :row_position => 0
|
|
214
218
|
}
|
|
215
219
|
|
|
216
220
|
context {
|
|
@@ -250,7 +254,7 @@ describe Duck do
|
|
|
250
254
|
|
|
251
255
|
context {
|
|
252
256
|
|
|
253
|
-
before { @ducks[:wingy].
|
|
257
|
+
before { @ducks[:wingy].update :row_position => (@ducks.size - 2) }
|
|
254
258
|
|
|
255
259
|
subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 2).instance }
|
|
256
260
|
|
|
@@ -260,7 +264,7 @@ describe Duck do
|
|
|
260
264
|
|
|
261
265
|
context {
|
|
262
266
|
|
|
263
|
-
before { @ducks[:wingy].
|
|
267
|
+
before { @ducks[:wingy].update :row_position => :down }
|
|
264
268
|
|
|
265
269
|
subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 2).instance }
|
|
266
270
|
|
|
@@ -274,7 +278,7 @@ describe Duck do
|
|
|
274
278
|
|
|
275
279
|
before {
|
|
276
280
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
|
|
277
|
-
@ducks[:wingy].
|
|
281
|
+
@ducks[:wingy].update :row_position => (@ducks.size - 1)
|
|
278
282
|
}
|
|
279
283
|
|
|
280
284
|
context {
|
|
@@ -315,7 +319,7 @@ describe Duck do
|
|
|
315
319
|
|
|
316
320
|
before {
|
|
317
321
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
|
|
318
|
-
@ducks[:wingy].
|
|
322
|
+
@ducks[:wingy].update :row_position => :last
|
|
319
323
|
}
|
|
320
324
|
|
|
321
325
|
context {
|
|
@@ -356,7 +360,7 @@ describe Duck do
|
|
|
356
360
|
|
|
357
361
|
before {
|
|
358
362
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
|
|
359
|
-
@ducks[:wingy].
|
|
363
|
+
@ducks[:wingy].update :row_position => 'last'
|
|
360
364
|
}
|
|
361
365
|
|
|
362
366
|
context {
|
|
@@ -399,7 +403,7 @@ describe Duck do
|
|
|
399
403
|
|
|
400
404
|
before {
|
|
401
405
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
|
402
|
-
@ducks[:wingy].
|
|
406
|
+
@ducks[:wingy].update :row_position => :down
|
|
403
407
|
}
|
|
404
408
|
|
|
405
409
|
context {
|
|
@@ -426,7 +430,7 @@ describe Duck do
|
|
|
426
430
|
|
|
427
431
|
before {
|
|
428
432
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:quacky].id).collect { |duck| duck.id }
|
|
429
|
-
@ducks[:quacky].
|
|
433
|
+
@ducks[:quacky].update :row_position => :down
|
|
430
434
|
}
|
|
431
435
|
|
|
432
436
|
context {
|
|
@@ -451,7 +455,7 @@ describe Duck do
|
|
|
451
455
|
|
|
452
456
|
before {
|
|
453
457
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:feathers].id).collect { |duck| duck.id }
|
|
454
|
-
@ducks[:feathers].
|
|
458
|
+
@ducks[:feathers].update :row_position => :down
|
|
455
459
|
}
|
|
456
460
|
|
|
457
461
|
context {
|
|
@@ -480,7 +484,7 @@ describe Duck do
|
|
|
480
484
|
|
|
481
485
|
before {
|
|
482
486
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
|
483
|
-
@ducks[:wingy].
|
|
487
|
+
@ducks[:wingy].update :row_position => 'down'
|
|
484
488
|
}
|
|
485
489
|
|
|
486
490
|
context {
|
|
@@ -507,7 +511,7 @@ describe Duck do
|
|
|
507
511
|
|
|
508
512
|
before {
|
|
509
513
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:quacky].id).collect { |duck| duck.id }
|
|
510
|
-
@ducks[:quacky].
|
|
514
|
+
@ducks[:quacky].update :row_position => 'down'
|
|
511
515
|
}
|
|
512
516
|
|
|
513
517
|
context {
|
|
@@ -532,7 +536,7 @@ describe Duck do
|
|
|
532
536
|
|
|
533
537
|
before {
|
|
534
538
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:feathers].id).collect { |duck| duck.id }
|
|
535
|
-
@ducks[:feathers].
|
|
539
|
+
@ducks[:feathers].update :row_position => 'down'
|
|
536
540
|
}
|
|
537
541
|
|
|
538
542
|
context {
|
|
@@ -561,7 +565,7 @@ describe Duck do
|
|
|
561
565
|
|
|
562
566
|
before {
|
|
563
567
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
|
564
|
-
@ducks[:wingy].
|
|
568
|
+
@ducks[:wingy].update :row_position => :up
|
|
565
569
|
}
|
|
566
570
|
|
|
567
571
|
context {
|
|
@@ -588,7 +592,7 @@ describe Duck do
|
|
|
588
592
|
|
|
589
593
|
before {
|
|
590
594
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:beaky].id).collect { |duck| duck.id }
|
|
591
|
-
@ducks[:beaky].
|
|
595
|
+
@ducks[:beaky].update :row_position => :up
|
|
592
596
|
}
|
|
593
597
|
|
|
594
598
|
context {
|
|
@@ -613,7 +617,7 @@ describe Duck do
|
|
|
613
617
|
|
|
614
618
|
before {
|
|
615
619
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:waddly].id).collect { |duck| duck.id }
|
|
616
|
-
@ducks[:waddly].
|
|
620
|
+
@ducks[:waddly].update :row_position => :up
|
|
617
621
|
}
|
|
618
622
|
|
|
619
623
|
context {
|
|
@@ -641,7 +645,7 @@ describe Duck do
|
|
|
641
645
|
Duck.where(id: @ducks[name].id).update_all(row: i)
|
|
642
646
|
@ducks[name].reload
|
|
643
647
|
end
|
|
644
|
-
@ducks[:wingy].
|
|
648
|
+
@ducks[:wingy].update :row_position => :up
|
|
645
649
|
}
|
|
646
650
|
|
|
647
651
|
context {
|
|
@@ -671,7 +675,7 @@ describe Duck do
|
|
|
671
675
|
|
|
672
676
|
before {
|
|
673
677
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
|
674
|
-
@ducks[:wingy].
|
|
678
|
+
@ducks[:wingy].update :row_position => 'up'
|
|
675
679
|
}
|
|
676
680
|
|
|
677
681
|
context {
|
|
@@ -698,7 +702,7 @@ describe Duck do
|
|
|
698
702
|
|
|
699
703
|
before {
|
|
700
704
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:beaky].id).collect { |duck| duck.id }
|
|
701
|
-
@ducks[:beaky].
|
|
705
|
+
@ducks[:beaky].update :row_position => 'up'
|
|
702
706
|
}
|
|
703
707
|
|
|
704
708
|
context {
|
|
@@ -723,7 +727,7 @@ describe Duck do
|
|
|
723
727
|
|
|
724
728
|
before {
|
|
725
729
|
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:waddly].id).collect { |duck| duck.id }
|
|
726
|
-
@ducks[:waddly].
|
|
730
|
+
@ducks[:waddly].update :row_position => 'up'
|
|
727
731
|
}
|
|
728
732
|
|
|
729
733
|
context {
|
|
@@ -748,6 +752,71 @@ describe Duck do
|
|
|
748
752
|
|
|
749
753
|
end
|
|
750
754
|
|
|
755
|
+
describe "fetching rank for an instance" do
|
|
756
|
+
before {
|
|
757
|
+
[:quacky, :feathers, :wingy, :webby, :waddly, :beaky].each_with_index do |name, i|
|
|
758
|
+
Duck.where(id: @ducks[name].id).update_all(row: RankedModel::MAX_RANK_VALUE - i)
|
|
759
|
+
@ducks[name].reload
|
|
760
|
+
end
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
context {
|
|
764
|
+
subject { Duck.find_by(id: @ducks[:beaky]).row_rank }
|
|
765
|
+
|
|
766
|
+
it { should == 0 }
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
context {
|
|
770
|
+
subject { Duck.find_by(id: @ducks[:wingy]).row_rank }
|
|
771
|
+
|
|
772
|
+
it { should == 3 }
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
context {
|
|
776
|
+
subject { Duck.find_by(id: @ducks[:quacky]).row_rank }
|
|
777
|
+
|
|
778
|
+
it { should == 5 }
|
|
779
|
+
}
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
describe "when moving between ponds should work when rebalancing" do
|
|
783
|
+
before do
|
|
784
|
+
[:feathers, :wingy, :webby, :waddly, :beaky].each_with_index do |name, i|
|
|
785
|
+
Duck.where(id: @ducks[name].id)
|
|
786
|
+
.update_all(age: RankedModel::MIN_RANK_VALUE + i, pond: "Boyden")
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
@ducks[:quacky].update!(age_position: 2, pond: "Boyden")
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
it 'rebalances ranks correctly' do
|
|
793
|
+
expect(@ducks[:feathers].reload.age_rank).to eq 0
|
|
794
|
+
expect(@ducks[:quacky].reload.age_rank).to eq 2
|
|
795
|
+
expect(@ducks[:beaky].reload.age_rank).to eq 5
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
context 'when attempting to update position to a non-unique value' do
|
|
799
|
+
before do
|
|
800
|
+
@duck_one = Duck.create(landing_order: RankedModel::MIN_RANK_VALUE,
|
|
801
|
+
lake_id: 42, flock_id: 42)
|
|
802
|
+
# Duck one's landing order will be rebalanced to -715_827_883.
|
|
803
|
+
# Given a unique index on [:landing_order, :lake_id, :flock_id] we
|
|
804
|
+
# verify that the operation succeeds despite the value already being
|
|
805
|
+
# occupied by duck two.
|
|
806
|
+
@duck_two = Duck.create(landing_order: -715_827_883,
|
|
807
|
+
lake_id: 42, flock_id: 42)
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
it 'rebalances ranks correctly' do
|
|
811
|
+
@ducks[:quacky].update!(landing_order_position: :first,
|
|
812
|
+
lake_id: 42, flock_id: 42)
|
|
813
|
+
expect(@ducks[:quacky].reload.landing_order_rank).to eq 0
|
|
814
|
+
expect(@duck_one.reload.landing_order_rank).to eq 1
|
|
815
|
+
expect(@duck_two.reload.landing_order_rank).to eq 2
|
|
816
|
+
end
|
|
817
|
+
end
|
|
818
|
+
end
|
|
819
|
+
|
|
751
820
|
end
|
|
752
821
|
|
|
753
822
|
describe Duck do
|
|
@@ -781,7 +850,7 @@ describe Duck do
|
|
|
781
850
|
}
|
|
782
851
|
@ducks.each { |name, duck|
|
|
783
852
|
duck.reload
|
|
784
|
-
duck.
|
|
853
|
+
duck.update :landing_order_position => 0
|
|
785
854
|
duck.save!
|
|
786
855
|
}
|
|
787
856
|
@ducks.each {|name, duck| duck.reload }
|
|
@@ -790,8 +859,8 @@ describe Duck do
|
|
|
790
859
|
describe "sorting by landing_order" do
|
|
791
860
|
|
|
792
861
|
before {
|
|
793
|
-
@ducks[:quacky].
|
|
794
|
-
@ducks[:wingy].
|
|
862
|
+
@ducks[:quacky].update :landing_order_position => 0
|
|
863
|
+
@ducks[:wingy].update :landing_order_position => 1
|
|
795
864
|
}
|
|
796
865
|
|
|
797
866
|
subject { Duck.in_lake_and_flock(0,0).rank(:landing_order).to_a }
|
|
@@ -815,10 +884,10 @@ describe Duck do
|
|
|
815
884
|
|
|
816
885
|
@previous_ranks = @untouchable_ranks.call
|
|
817
886
|
|
|
818
|
-
@ducks[:quacky].
|
|
819
|
-
@ducks[:wingy].
|
|
820
|
-
@ducks[:feathers].
|
|
821
|
-
@ducks[:wingy].
|
|
887
|
+
@ducks[:quacky].update :landing_order_position => 0
|
|
888
|
+
@ducks[:wingy].update :landing_order_position => 1
|
|
889
|
+
@ducks[:feathers].update :landing_order_position => 0
|
|
890
|
+
@ducks[:wingy].update :landing_order_position => 1
|
|
822
891
|
}
|
|
823
892
|
|
|
824
893
|
subject { @untouchable_ranks.call }
|