ranked-model 0.4.1 → 0.4.11
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/.github/FUNDING.yml +3 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +107 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.travis.yml +40 -22
- data/Appraisals +30 -46
- data/CHANGELOG.md +9 -0
- data/Gemfile +11 -2
- data/Readme.mkd +136 -17
- data/gemfiles/rails_5_2.gemfile +22 -0
- data/gemfiles/rails_6_0.gemfile +22 -0
- data/gemfiles/rails_6_1.gemfile +22 -0
- data/gemfiles/rails_7_0.gemfile +22 -0
- data/lib/ranked-model/ranker.rb +100 -71
- data/lib/ranked-model/version.rb +1 -1
- data/lib/ranked-model.rb +17 -2
- data/ranked-model.gemspec +11 -9
- data/spec/duck-model/column_default_ducks_spec.rb +29 -0
- data/spec/duck-model/duck_spec.rb +179 -79
- data/spec/duck-model/inferred_ducks_spec.rb +71 -0
- data/spec/duck-model/lots_of_ducks_spec.rb +48 -36
- data/spec/ego-model/ego_spec.rb +3 -3
- data/spec/notifications_spec.rb +89 -0
- data/spec/number-model/number_spec.rb +39 -0
- data/spec/player-model/records_already_exist_spec.rb +1 -1
- data/spec/ranked-model/ranker_spec.rb +18 -0
- data/spec/ranked-model/version_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- data/spec/sti-model/element_spec.rb +24 -24
- data/spec/sti-model/vehicle_spec.rb +6 -6
- data/spec/support/active_record.rb +23 -5
- data/spec/support/database.yml +2 -0
- metadata +54 -41
- data/gemfiles/rails_3_2.gemfile +0 -24
- data/gemfiles/rails_4_1.gemfile +0 -24
- data/gemfiles/rails_4_2.gemfile +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c219aee4691eca3a788bd590e056349623685e2dac26405247eccc807b3b580b
|
|
4
|
+
data.tar.gz: 7d50bd015aa1f38ead1a4a4c795d3fc3f5f7ad101e416f37c66415d50251e010
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75a9f1e64091a37289aa2c3f6f35bd30be059237121e8f00af61196be089de2df645304854fb9a80bd1fc1de91d6014e741839b91714cc70ab9e7562e6367451
|
|
7
|
+
data.tar.gz: f8a6ac22945db1b7c609c776db78445f3dd60c550e7b421b4530c974af51fb4e0040375c676f72c7920240cff143b8ac236ec258f7c76f5cdbd418748e493243
|
data/.github/FUNDING.yml
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: CI
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
tests:
|
|
7
|
+
name: Ruby ${{ matrix.ruby }}, ${{ matrix.gemfile }}, DB ${{ matrix.db }}
|
|
8
|
+
runs-on: ${{ matrix.os }}
|
|
9
|
+
strategy:
|
|
10
|
+
fail-fast: false
|
|
11
|
+
matrix:
|
|
12
|
+
ruby:
|
|
13
|
+
- 2.5
|
|
14
|
+
- 2.6
|
|
15
|
+
- 2.7
|
|
16
|
+
- '3.0'
|
|
17
|
+
- 3.1
|
|
18
|
+
- 3.2
|
|
19
|
+
- 3.3
|
|
20
|
+
gemfile:
|
|
21
|
+
- gemfiles/rails_5_2.gemfile
|
|
22
|
+
- gemfiles/rails_6_0.gemfile
|
|
23
|
+
- gemfiles/rails_6_1.gemfile
|
|
24
|
+
- gemfiles/rails_7_0.gemfile
|
|
25
|
+
db:
|
|
26
|
+
- sqlite
|
|
27
|
+
- mysql
|
|
28
|
+
- postgresql
|
|
29
|
+
exclude:
|
|
30
|
+
- ruby: 2.5
|
|
31
|
+
gemfile: gemfiles/rails_7_0.gemfile
|
|
32
|
+
- ruby: 2.6
|
|
33
|
+
gemfile: gemfiles/rails_7_0.gemfile
|
|
34
|
+
- ruby: '3.0'
|
|
35
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
|
36
|
+
- ruby: 3.1
|
|
37
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
|
38
|
+
- ruby: 3.2
|
|
39
|
+
gemfile: gemfiles/rails_4_2.gemfile
|
|
40
|
+
- ruby: 3.2
|
|
41
|
+
gemfile: gemfiles/rails_5_0.gemfile
|
|
42
|
+
- ruby: 3.2
|
|
43
|
+
gemfile: gemfiles/rails_5_1.gemfile
|
|
44
|
+
- ruby: 3.2
|
|
45
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
|
46
|
+
- ruby: 3.2
|
|
47
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
|
48
|
+
- ruby: 3.2
|
|
49
|
+
gemfile: gemfiles/rails_6_1.gemfile
|
|
50
|
+
- ruby: 3.3
|
|
51
|
+
gemfile: gemfiles/rails_4_2.gemfile
|
|
52
|
+
- ruby: 3.3
|
|
53
|
+
gemfile: gemfiles/rails_5_0.gemfile
|
|
54
|
+
- ruby: 3.3
|
|
55
|
+
gemfile: gemfiles/rails_5_1.gemfile
|
|
56
|
+
- ruby: 3.3
|
|
57
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
|
58
|
+
- ruby: 3.3
|
|
59
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
|
60
|
+
- ruby: 3.3
|
|
61
|
+
gemfile: gemfiles/rails_6_1.gemfile
|
|
62
|
+
os:
|
|
63
|
+
- ubuntu-latest
|
|
64
|
+
services:
|
|
65
|
+
mysql:
|
|
66
|
+
image: mysql:5.7
|
|
67
|
+
env:
|
|
68
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
|
69
|
+
ports:
|
|
70
|
+
- 3306:3306
|
|
71
|
+
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
|
72
|
+
|
|
73
|
+
postgres:
|
|
74
|
+
# Docker Hub image
|
|
75
|
+
image: postgres
|
|
76
|
+
# Provide the password for postgres
|
|
77
|
+
env:
|
|
78
|
+
POSTGRES_USER: postgres
|
|
79
|
+
POSTGRES_HOST_AUTH_METHOD: trust
|
|
80
|
+
ports:
|
|
81
|
+
- 5432:5432
|
|
82
|
+
# Set health checks to wait until postgres has started
|
|
83
|
+
options: >-
|
|
84
|
+
--health-cmd pg_isready
|
|
85
|
+
--health-interval 10s
|
|
86
|
+
--health-timeout 5s
|
|
87
|
+
--health-retries 5
|
|
88
|
+
env:
|
|
89
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
|
90
|
+
DB: ${{ matrix.db }}
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v4
|
|
93
|
+
- name: Set up Ruby
|
|
94
|
+
uses: ruby/setup-ruby@v1
|
|
95
|
+
with:
|
|
96
|
+
ruby-version: ${{ matrix.ruby }}
|
|
97
|
+
bundler-cache: true
|
|
98
|
+
- name: "Create MySQL database"
|
|
99
|
+
if: ${{ env.DB == 'mysql' }}
|
|
100
|
+
run: |
|
|
101
|
+
mysql -h 127.0.0.1 -u root -e 'create database ranked_model_test;'
|
|
102
|
+
- name: "Create PostgreSQL database"
|
|
103
|
+
if: ${{ env.DB == 'postgresql' }}
|
|
104
|
+
run: |
|
|
105
|
+
psql -c 'create database ranked_model_test;' -h localhost -U postgres
|
|
106
|
+
- name: Run tests
|
|
107
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.travis.yml
CHANGED
|
@@ -1,38 +1,56 @@
|
|
|
1
|
-
sudo: false # use newer travis infrastructure
|
|
2
1
|
language: ruby
|
|
3
2
|
cache: bundler
|
|
4
3
|
before_install:
|
|
5
|
-
- gem
|
|
4
|
+
- gem update --system
|
|
5
|
+
- gem install bundler
|
|
6
6
|
before_script:
|
|
7
7
|
- mysql -e 'create database ranked_model_test;'
|
|
8
8
|
- psql -c 'create database ranked_model_test;' -U postgres
|
|
9
9
|
rvm:
|
|
10
|
-
-
|
|
11
|
-
- 2.
|
|
12
|
-
- 2.
|
|
13
|
-
- 2.
|
|
14
|
-
-
|
|
15
|
-
- 2.5.1
|
|
10
|
+
- 2.4
|
|
11
|
+
- 2.5
|
|
12
|
+
- 2.6
|
|
13
|
+
- 2.7
|
|
14
|
+
- 3.0
|
|
16
15
|
- jruby-9.1.17.0
|
|
17
|
-
- rbx-3.100
|
|
18
16
|
env:
|
|
19
17
|
- DB=sqlite
|
|
20
18
|
- DB=mysql
|
|
21
19
|
- DB=postgresql
|
|
20
|
+
services:
|
|
21
|
+
- mysql
|
|
22
|
+
- postgresql
|
|
22
23
|
gemfile:
|
|
23
|
-
- gemfiles/rails_3_2.gemfile
|
|
24
|
-
- gemfiles/rails_4_1.gemfile
|
|
25
24
|
- gemfiles/rails_4_2.gemfile
|
|
25
|
+
- gemfiles/rails_5_0.gemfile
|
|
26
|
+
- gemfiles/rails_5_1.gemfile
|
|
27
|
+
- gemfiles/rails_5_2.gemfile
|
|
28
|
+
- gemfiles/rails_6_0.gemfile
|
|
29
|
+
- gemfiles/rails_6_1.gemfile
|
|
26
30
|
matrix:
|
|
27
31
|
exclude:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- rvm:
|
|
31
|
-
gemfile: gemfiles/
|
|
32
|
-
- rvm:
|
|
33
|
-
gemfile: gemfiles/
|
|
34
|
-
- rvm:
|
|
35
|
-
gemfile: gemfiles/
|
|
36
|
-
-
|
|
37
|
-
gemfile: gemfiles/
|
|
38
|
-
|
|
32
|
+
# Rails <6 does not support Ruby 3, see:
|
|
33
|
+
# https://github.com/rails/rails/issues/40938#issuecomment-751898275
|
|
34
|
+
- rvm: 3.0
|
|
35
|
+
gemfile: gemfiles/rails_4_2.gemfile
|
|
36
|
+
- rvm: 3.0
|
|
37
|
+
gemfile: gemfiles/rails_5_0.gemfile
|
|
38
|
+
- rvm: 3.0
|
|
39
|
+
gemfile: gemfiles/rails_5_1.gemfile
|
|
40
|
+
- rvm: 3.0
|
|
41
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
|
42
|
+
# Ruby ≥2.7 uses a `bigdecimal` version that doesn't support BigDecimal.new
|
|
43
|
+
# that Rails 4.2 uses. See also:
|
|
44
|
+
# https://github.com/ruby/bigdecimal#which-version-should-you-select
|
|
45
|
+
- rvm: 2.7
|
|
46
|
+
gemfile: gemfiles/rails_4_2.gemfile
|
|
47
|
+
- rvm: jruby-9.1.17.0
|
|
48
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
|
49
|
+
- rvm: 2.4
|
|
50
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
|
51
|
+
- rvm: jruby-9.1.17.0
|
|
52
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
|
53
|
+
- rvm: 2.4
|
|
54
|
+
gemfile: gemfiles/rails_6_1.gemfile
|
|
55
|
+
- rvm: jruby-9.1.17.0
|
|
56
|
+
gemfile: gemfiles/rails_6_1.gemfile
|
data/Appraisals
CHANGED
|
@@ -1,71 +1,55 @@
|
|
|
1
|
-
appraise "rails-
|
|
1
|
+
appraise "rails-5-2" do
|
|
2
2
|
group :sqlite do
|
|
3
|
-
gem "
|
|
4
|
-
gem "activerecord-jdbcsqlite3-adapter", "~> 1.3.24", platform: :jruby
|
|
3
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 52.0", platform: :jruby
|
|
5
4
|
end
|
|
6
5
|
group :mysql do
|
|
7
|
-
gem "
|
|
8
|
-
gem "activerecord-jdbcmysql-adapter", "~> 1.3.24", platform: :jruby
|
|
6
|
+
gem "activerecord-jdbcmysql-adapter", "~> 52.0", platform: :jruby
|
|
9
7
|
end
|
|
10
8
|
group :postgresql do
|
|
11
|
-
gem "
|
|
12
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 1.3.24", platform: :jruby
|
|
9
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 52.0", platform: :jruby
|
|
13
10
|
end
|
|
14
11
|
|
|
15
|
-
gem "activerecord", "~>
|
|
12
|
+
gem "activerecord", "~> 5.2.0"
|
|
16
13
|
end
|
|
17
14
|
|
|
18
|
-
appraise "rails-
|
|
15
|
+
appraise "rails-6-0" do
|
|
19
16
|
group :sqlite do
|
|
20
|
-
gem "sqlite3", platform: :ruby
|
|
21
|
-
gem "activerecord-jdbcsqlite3-adapter", "~>
|
|
17
|
+
gem "sqlite3", "~> 1.4", platform: :ruby
|
|
18
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 60.0", platform: :jruby
|
|
22
19
|
end
|
|
23
20
|
group :mysql do
|
|
24
|
-
gem "
|
|
25
|
-
gem "activerecord-jdbcmysql-adapter", "~> 1.3.24", platform: :jruby
|
|
21
|
+
gem "activerecord-jdbcmysql-adapter", "~> 60.0", platform: :jruby
|
|
26
22
|
end
|
|
27
23
|
group :postgresql do
|
|
28
|
-
gem "
|
|
29
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 1.3.24", platform: :jruby
|
|
24
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 60.0", platform: :jruby
|
|
30
25
|
end
|
|
31
|
-
|
|
32
|
-
gem "activerecord", "~> 4.1.16"
|
|
26
|
+
gem "activerecord", "~> 6.0.0"
|
|
33
27
|
end
|
|
34
28
|
|
|
35
|
-
appraise "rails-
|
|
29
|
+
appraise "rails-6-1" do
|
|
36
30
|
group :sqlite do
|
|
37
|
-
gem "sqlite3", platform: :ruby
|
|
38
|
-
gem "activerecord-jdbcsqlite3-adapter", "~>
|
|
31
|
+
gem "sqlite3", "~> 1.4", platform: :ruby
|
|
32
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 61.0", platform: :jruby
|
|
39
33
|
end
|
|
40
34
|
group :mysql do
|
|
41
|
-
gem "
|
|
42
|
-
gem "activerecord-jdbcmysql-adapter", "~> 1.3.24", platform: :jruby
|
|
35
|
+
gem "activerecord-jdbcmysql-adapter", "~> 61.0", platform: :jruby
|
|
43
36
|
end
|
|
44
37
|
group :postgresql do
|
|
45
|
-
gem "
|
|
46
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 1.3.24", platform: :jruby
|
|
38
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 61.0", platform: :jruby
|
|
47
39
|
end
|
|
48
|
-
|
|
49
|
-
gem "activerecord", "~> 4.2.10"
|
|
40
|
+
gem "activerecord", "~> 6.1.0"
|
|
50
41
|
end
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# appraise "rails-5-2" do
|
|
67
|
-
# group :mysql do
|
|
68
|
-
# gem "mysql2", "~> 0.4.10", platform: :ruby
|
|
69
|
-
# end
|
|
70
|
-
# gem "activerecord", "~> 5.2.0.rc1"
|
|
71
|
-
# end
|
|
43
|
+
appraise "rails-7-0" do
|
|
44
|
+
group :sqlite do
|
|
45
|
+
gem "sqlite3", "~> 1.4", platform: :ruby
|
|
46
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 61.0", platform: :jruby
|
|
47
|
+
end
|
|
48
|
+
group :mysql do
|
|
49
|
+
gem "activerecord-jdbcmysql-adapter", "~> 61.0", platform: :jruby
|
|
50
|
+
end
|
|
51
|
+
group :postgresql do
|
|
52
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 61.0", platform: :jruby
|
|
53
|
+
end
|
|
54
|
+
gem "activerecord", "~> 7.0.0"
|
|
55
|
+
end
|
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
|
@@ -3,5 +3,14 @@ source "https://rubygems.org"
|
|
|
3
3
|
# Specify your gem's dependencies in ranked-model.gemspec
|
|
4
4
|
gemspec
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
gem
|
|
6
|
+
group :sqlite do
|
|
7
|
+
gem "sqlite3", platform: :ruby
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
group :postgresql do
|
|
11
|
+
gem "pg", platform: :ruby
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
group :mysql do
|
|
15
|
+
gem "mysql2", platform: :ruby
|
|
16
|
+
end
|
data/Readme.mkd
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
**ranked-model** is a modern row sorting library built for Rails
|
|
1
|
+
**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.
|
|
2
2
|
|
|
3
|
-
[](https://github.com/brendon/ranked-model/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
ANNOUNCING: Positioning, the gem
|
|
6
|
+
--------------------------------
|
|
7
|
+
|
|
8
|
+
As maintainer of both Acts As List and the Ranked Model gems, I've become intimately aquainted with the strengths and weaknesses of each. I ended up writing a small scale Rails Concern for positioning database rows for a recent project and it worked really well so I've decided to release it as a gem: [Positioning](https://github.com/brendon/positioning)
|
|
9
|
+
|
|
10
|
+
Positioning works similarly to Acts As List in that it maintains a sequential list of integer values as positions. It differs in that it encourages a unique constraints on the position column and supports multiple lists per database table. It borrows Ranked Model's concept of relative positioning. I encourage you to check it out and give it a whirl on your project!
|
|
4
11
|
|
|
5
12
|
Installation
|
|
6
13
|
------------
|
|
7
14
|
|
|
8
|
-
ranked-model passes specs with Rails
|
|
9
|
-
|
|
10
|
-
TL;DR, if you are using Rails 4 and up you are 100% good to go. Before Rails 4, be wary of Postgres.
|
|
15
|
+
ranked-model passes specs with Rails 4.2, 5.0, 5.1, 5.2, 6.0 and 6.1 for MySQL, Postgres, and SQLite on Ruby 2.4 through 3.0 (with exceptions, please check the CI setup for supported combinations), and jruby-9.1.17.0 where Rails supports the platform.
|
|
11
16
|
|
|
12
17
|
To install ranked-model, just add it to your `Gemfile`:
|
|
13
18
|
|
|
@@ -16,7 +21,7 @@ gem 'ranked-model'
|
|
|
16
21
|
|
|
17
22
|
# Or pin ranked-model to git
|
|
18
23
|
# gem 'ranked-model',
|
|
19
|
-
# :
|
|
24
|
+
# git: 'git@github.com:mixonic/ranked-model.git'
|
|
20
25
|
```
|
|
21
26
|
|
|
22
27
|
Then use `bundle install` to update your `Gemfile.lock`.
|
|
@@ -31,6 +36,18 @@ class Duck < ActiveRecord::Base
|
|
|
31
36
|
end
|
|
32
37
|
```
|
|
33
38
|
|
|
39
|
+
Give them an order (integer column):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
rails g migration AddRowOrderToDucks row_order:integer
|
|
43
|
+
rails db:migrate
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**IMPORTANT: The `_order` table column MUST allow null values. For the reason behind this requirement see [issue#167](https://github.com/mixonic/ranked-model/issues/167)**
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
34
51
|
Put your ducks in a row:
|
|
35
52
|
|
|
36
53
|
``` ruby
|
|
@@ -42,7 +59,7 @@ class Duck < ActiveRecord::Base
|
|
|
42
59
|
end
|
|
43
60
|
```
|
|
44
61
|
|
|
45
|
-
|
|
62
|
+
Order the Ducks by this order:
|
|
46
63
|
|
|
47
64
|
``` ruby
|
|
48
65
|
Duck.rank(:row_order).all
|
|
@@ -52,9 +69,11 @@ The ranking integers stored in the `row_order` column will be big and spaced apa
|
|
|
52
69
|
implement a sorting UI, just update the resource by appending the column name with `_position` and indicating the desired position:
|
|
53
70
|
|
|
54
71
|
``` ruby
|
|
55
|
-
@duck.
|
|
72
|
+
@duck.update row_order_position: 0 # or 1, 2, 37. :first, :last, :up and :down are also valid
|
|
56
73
|
```
|
|
57
74
|
|
|
75
|
+
**IMPORTANT: Note that you MUST append _position to the column name when setting a new position on an instance. This is a fake column that can take relative as well as absolute index-based values for position.**
|
|
76
|
+
|
|
58
77
|
Position numbers begin at zero. A position number greater than the number of records acts the
|
|
59
78
|
same as :last. :up and :down move the record up/down the ladder by one step.
|
|
60
79
|
|
|
@@ -70,6 +89,15 @@ $.ajax({
|
|
|
70
89
|
});
|
|
71
90
|
```
|
|
72
91
|
|
|
92
|
+
If you need to find the rank of an item with respect to other ranked items, you can use the `{column_name}_rank` method on the model instance. `{column_name}` is your resource ranking column.
|
|
93
|
+
|
|
94
|
+
Following on from our examples above, the `row_order_rank` method will return the position of the duck object in the list with respect to the order defined by the row_order column.
|
|
95
|
+
|
|
96
|
+
``` ruby
|
|
97
|
+
Duck.rank(:row_order).first.row_order_rank # => 0
|
|
98
|
+
Duck.rank(:row_order).third.row_order_rank # => 2
|
|
99
|
+
```
|
|
100
|
+
|
|
73
101
|
Complex Use
|
|
74
102
|
-----------
|
|
75
103
|
|
|
@@ -80,17 +108,28 @@ class Duck < ActiveRecord::Base
|
|
|
80
108
|
|
|
81
109
|
include RankedModel
|
|
82
110
|
|
|
83
|
-
ranks :row_order,
|
|
84
|
-
:
|
|
85
|
-
|
|
111
|
+
ranks :row_order, # Name this ranker, used with rank()
|
|
112
|
+
column: :sort_order # Override the default column, which defaults to the name
|
|
113
|
+
|
|
86
114
|
belongs_to :pond
|
|
87
115
|
ranks :swimming_order,
|
|
88
|
-
:
|
|
89
|
-
|
|
90
|
-
|
|
116
|
+
with_same: :pond_id # Ducks belong_to Ponds, make the ranker scoped to one pond
|
|
117
|
+
|
|
118
|
+
ranks :row_order,
|
|
119
|
+
with_same: [:pond_id, :breed] # Lets rank them by breed
|
|
120
|
+
|
|
121
|
+
scope :walking, where(walking: true )
|
|
91
122
|
ranks :walking_order,
|
|
92
|
-
:
|
|
123
|
+
scope: :walking # Narrow this ranker to a scope
|
|
93
124
|
|
|
125
|
+
belongs_to :parent, class_name: 'Duck', optional: true
|
|
126
|
+
ranks :child_order,
|
|
127
|
+
unless: :has_no_parent?, # Rank only ducks that have a parent. Alternatively a Proc or lambda can be passed, e.g. proc { parent.nil? }
|
|
128
|
+
with_same: :parent_id
|
|
129
|
+
|
|
130
|
+
def has_no_parent?
|
|
131
|
+
parent.nil?
|
|
132
|
+
end
|
|
94
133
|
end
|
|
95
134
|
```
|
|
96
135
|
|
|
@@ -104,6 +143,37 @@ Pond.first.ducks.rank(:swimming_order)
|
|
|
104
143
|
Duck.walking.rank(:walking)
|
|
105
144
|
```
|
|
106
145
|
|
|
146
|
+
Drawbacks
|
|
147
|
+
---------
|
|
148
|
+
|
|
149
|
+
While ranked-model is performant when storing data, it might cause N+1s depending on how you write your code. Consider this snippet:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
ducks = Duck.all
|
|
153
|
+
ducks.map do |duck|
|
|
154
|
+
{
|
|
155
|
+
id: duck.id,
|
|
156
|
+
position: duck.row_order_rank # This causes N+1!
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Every call to `duck.row_order_rank` will make a call to the DB to check the rank of that
|
|
162
|
+
particular element. If you have a long list of elements this might cause issues to your DB.
|
|
163
|
+
|
|
164
|
+
In order to avoid that, you can use the `rank(:your_rank)` scope and some Ruby code to get
|
|
165
|
+
the element's position:
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
ducks = Duck.rank(:row_order).all
|
|
169
|
+
ducks.map.with_index do |duck, index|
|
|
170
|
+
{
|
|
171
|
+
id: duck.id,
|
|
172
|
+
position: index
|
|
173
|
+
}
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
107
177
|
Single Table Inheritance (STI)
|
|
108
178
|
------------------------------
|
|
109
179
|
|
|
@@ -158,6 +228,35 @@ truck.row_order
|
|
|
158
228
|
=> 4194304
|
|
159
229
|
```
|
|
160
230
|
|
|
231
|
+
Migrations for existing data
|
|
232
|
+
----------------------------
|
|
233
|
+
|
|
234
|
+
If you use `ranked_model` with existing data, the following migration (for Rails
|
|
235
|
+
6) can be a starting point. Make sure to declare `include RankedModel` and
|
|
236
|
+
`ranks :row_order` in your `Duck` before running the migration.
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
rails g migration AddRowOrderToDucks row_order:integer
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Then, adjust the migration:
|
|
243
|
+
```ruby
|
|
244
|
+
# e.g. file db/migrate/20200325095038_add_row_order_to_ducks.rb
|
|
245
|
+
class AddRowOrderToDucks < ActiveRecord::Migration[6.0]
|
|
246
|
+
def change
|
|
247
|
+
add_column :ducks, :row_order, :integer
|
|
248
|
+
|
|
249
|
+
# Newest Duck shall rank "highest"" (be last).
|
|
250
|
+
Duck.update_all('row_order = EXTRACT(EPOCH FROM created_at)')
|
|
251
|
+
|
|
252
|
+
# Alternatively, implement any other sorting default
|
|
253
|
+
# Duck.order(created_at: :desc).each do |duck|
|
|
254
|
+
# duck.update!(row_order: duck.created_at.to_i + duck.age / 2)
|
|
255
|
+
# end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
```
|
|
259
|
+
|
|
161
260
|
Internals
|
|
162
261
|
---------
|
|
163
262
|
|
|
@@ -170,18 +269,37 @@ this occurs, ranked-model will try to shift other records out of the way. If ite
|
|
|
170
269
|
shifted anymore, it will rebalance the distribution of rank numbers across all members
|
|
171
270
|
of the ranked group.
|
|
172
271
|
|
|
272
|
+
Record updates to rebalance ranks do not trigger ActiveRecord callbacks. If you need to react to these updates
|
|
273
|
+
(to index them in a secondary data store, for example), you can subscribe to the `ranked_model.ranks_updated`
|
|
274
|
+
[ActiveSupport notification](https://api.rubyonrails.org/v7.1/classes/ActiveSupport/Notifications.html).
|
|
275
|
+
Subscribed consumers receive an event for each rearrangement or rebalancing, the payload of which includes the
|
|
276
|
+
triggering instance and the `scope` and `with_same` options for the ranking, which can be used to retrieve the
|
|
277
|
+
affected records.
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
ActiveSupport::Notifications.subscribe("ranked_model.ranks_updated") do |_name, _start, _finish, _id, payload|
|
|
281
|
+
# payload[:instance] - the instance whose update triggered the rebalance
|
|
282
|
+
# payload[:scope] - the scope applied to the ranking
|
|
283
|
+
# payload[:with_same] - the with_same option applied to the ranking
|
|
284
|
+
end
|
|
285
|
+
```
|
|
286
|
+
|
|
173
287
|
Contributing
|
|
174
288
|
------------
|
|
175
289
|
|
|
176
290
|
Fork, clone, write a test, write some code, commit, push, send a pull request. Github FTW!
|
|
177
291
|
|
|
292
|
+
The code is published under the [MIT License](LICENSE).
|
|
293
|
+
|
|
178
294
|
The specs can be run with sqlite, postgres, and mysql:
|
|
179
295
|
|
|
180
296
|
```
|
|
181
|
-
|
|
297
|
+
bundle
|
|
298
|
+
appraisal install
|
|
299
|
+
DB=postgresql bundle exec appraisal rake
|
|
182
300
|
```
|
|
183
301
|
|
|
184
|
-
|
|
302
|
+
If no DB is specified (`sqlite`, `mysql`, or `postgresql`), the tests run against sqlite.
|
|
185
303
|
|
|
186
304
|
RankedModel is mostly the handiwork of Matthew Beale:
|
|
187
305
|
|
|
@@ -203,3 +321,4 @@ A hearty thanks to these contributors:
|
|
|
203
321
|
* [jguyon](https://github.com/jguyon)
|
|
204
322
|
* [pehrlich](https://github.com/pehrlich)
|
|
205
323
|
* [petergoldstein](https://github.com/petergoldstein)
|
|
324
|
+
* [brendon](https://github.com/brendon)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# This file was generated by Appraisal
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gem "activerecord", "~> 5.2.0"
|
|
6
|
+
|
|
7
|
+
group :sqlite do
|
|
8
|
+
gem "sqlite3", platform: :ruby
|
|
9
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 52.0", platform: :jruby
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
group :postgresql do
|
|
13
|
+
gem "pg", platform: :ruby
|
|
14
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 52.0", platform: :jruby
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
group :mysql do
|
|
18
|
+
gem "mysql2", platform: :ruby
|
|
19
|
+
gem "activerecord-jdbcmysql-adapter", "~> 52.0", platform: :jruby
|
|
20
|
+
end
|
|
21
|
+
|
|
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", platform: :ruby
|
|
14
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 60.0", platform: :jruby
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
group :mysql do
|
|
18
|
+
gem "mysql2", platform: :ruby
|
|
19
|
+
gem "activerecord-jdbcmysql-adapter", "~> 60.0", platform: :jruby
|
|
20
|
+
end
|
|
21
|
+
|
|
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.1.0"
|
|
6
|
+
|
|
7
|
+
group :sqlite do
|
|
8
|
+
gem "sqlite3", "~> 1.4", platform: :ruby
|
|
9
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 61.0", platform: :jruby
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
group :postgresql do
|
|
13
|
+
gem "pg", platform: :ruby
|
|
14
|
+
gem "activerecord-jdbcpostgresql-adapter", "~> 61.0", platform: :jruby
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
group :mysql do
|
|
18
|
+
gem "mysql2", platform: :ruby
|
|
19
|
+
gem "activerecord-jdbcmysql-adapter", "~> 61.0", platform: :jruby
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
gemspec path: "../"
|