ranked-model 0.4.8 → 0.4.9
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/.github/FUNDING.yml +3 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +90 -0
- data/.gitignore +1 -0
- data/Appraisals +14 -45
- data/Readme.mkd +31 -0
- data/gemfiles/rails_5_2.gemfile +3 -3
- data/gemfiles/rails_6_0.gemfile +2 -2
- data/gemfiles/rails_6_1.gemfile +2 -2
- data/gemfiles/rails_7_0.gemfile +22 -0
- data/lib/ranked-model/ranker.rb +357 -356
- data/lib/ranked-model/version.rb +1 -1
- data/ranked-model.gemspec +1 -1
- data/spec/duck-model/inferred_ducks_spec.rb +71 -0
- data/spec/support/database.yml +2 -0
- metadata +10 -7
- data/gemfiles/rails_4_2.gemfile +0 -23
- data/gemfiles/rails_5_0.gemfile +0 -22
- data/gemfiles/rails_5_1.gemfile +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61451e859060c7d8a3f889bd54327b103797d728312cedef66efa1b25d11767f
|
4
|
+
data.tar.gz: 2e1df0f514decf2e43667c2d0139e510202fc0ebc32aed758831ea20497aaf1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d046ed8e55533b8f453a67e0c705738e9353b22b20b01310381a56195803ee066cd647e1b8801484ca79518a8ea2394577841d4b9c4eb97d12f0b36246c1bfc7
|
7
|
+
data.tar.gz: ca05b38d74cbe11544cabb70890c64d26c9a94c4608d2b4d7da1253535efc03775ef3fed80fc9b93709f503f3f53a8cd725b7f2b8c43ad8565a72139f0ec53e5
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1,90 @@
|
|
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.4
|
14
|
+
- 2.5
|
15
|
+
- 2.6
|
16
|
+
- 2.7
|
17
|
+
- '3.0'
|
18
|
+
- 3.1
|
19
|
+
gemfile:
|
20
|
+
- gemfiles/rails_5_2.gemfile
|
21
|
+
- gemfiles/rails_6_0.gemfile
|
22
|
+
- gemfiles/rails_6_1.gemfile
|
23
|
+
- gemfiles/rails_7_0.gemfile
|
24
|
+
db:
|
25
|
+
- sqlite
|
26
|
+
- mysql
|
27
|
+
- postgresql
|
28
|
+
exclude:
|
29
|
+
- ruby: 2.4
|
30
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
31
|
+
- ruby: 2.4
|
32
|
+
gemfile: gemfiles/rails_6_1.gemfile
|
33
|
+
- ruby: 2.4
|
34
|
+
gemfile: gemfiles/rails_7_0.gemfile
|
35
|
+
- ruby: 2.5
|
36
|
+
gemfile: gemfiles/rails_7_0.gemfile
|
37
|
+
- ruby: 2.6
|
38
|
+
gemfile: gemfiles/rails_7_0.gemfile
|
39
|
+
- ruby: '3.0'
|
40
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
41
|
+
- ruby: 3.1
|
42
|
+
gemfile: gemfiles/rails_5_2.gemfile
|
43
|
+
- ruby: 3.1
|
44
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
45
|
+
os:
|
46
|
+
- ubuntu-latest
|
47
|
+
services:
|
48
|
+
mysql:
|
49
|
+
image: mysql:5.7
|
50
|
+
env:
|
51
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
52
|
+
ports:
|
53
|
+
- 3306:3306
|
54
|
+
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
55
|
+
|
56
|
+
postgres:
|
57
|
+
# Docker Hub image
|
58
|
+
image: postgres
|
59
|
+
# Provide the password for postgres
|
60
|
+
env:
|
61
|
+
POSTGRES_USER: postgres
|
62
|
+
POSTGRES_HOST_AUTH_METHOD: trust
|
63
|
+
ports:
|
64
|
+
- 5432:5432
|
65
|
+
# Set health checks to wait until postgres has started
|
66
|
+
options: >-
|
67
|
+
--health-cmd pg_isready
|
68
|
+
--health-interval 10s
|
69
|
+
--health-timeout 5s
|
70
|
+
--health-retries 5
|
71
|
+
env:
|
72
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
73
|
+
DB: ${{ matrix.db }}
|
74
|
+
steps:
|
75
|
+
- uses: actions/checkout@v3
|
76
|
+
- name: Set up Ruby
|
77
|
+
uses: ruby/setup-ruby@v1
|
78
|
+
with:
|
79
|
+
ruby-version: ${{ matrix.ruby }}
|
80
|
+
bundler-cache: true
|
81
|
+
- name: "Create MySQL database"
|
82
|
+
if: ${{ env.DB == 'mysql' }}
|
83
|
+
run: |
|
84
|
+
mysql -h 127.0.0.1 -u root -e 'create database ranked_model_test;'
|
85
|
+
- name: "Create PostgreSQL database"
|
86
|
+
if: ${{ env.DB == 'postgresql' }}
|
87
|
+
run: |
|
88
|
+
psql -c 'create database ranked_model_test;' -h localhost -U postgres
|
89
|
+
- name: Run tests
|
90
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/Appraisals
CHANGED
@@ -1,48 +1,3 @@
|
|
1
|
-
appraise "rails-4-2" do
|
2
|
-
group :sqlite do
|
3
|
-
gem "activerecord-jdbcsqlite3-adapter", "~> 1.3.24", platform: :jruby
|
4
|
-
end
|
5
|
-
group :mysql do
|
6
|
-
gem "mysql2", "~> 0.4.0", platform: :ruby
|
7
|
-
gem "jdbc-mysql", "~> 5.1.47", platform: :jruby
|
8
|
-
gem "activerecord-jdbcmysql-adapter", "~> 1.3.24", platform: :jruby
|
9
|
-
end
|
10
|
-
group :postgresql do
|
11
|
-
gem "pg", "~> 0.18.4", platform: :ruby
|
12
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 1.3.24", platform: :jruby
|
13
|
-
end
|
14
|
-
|
15
|
-
gem "activerecord", "~> 4.2.0"
|
16
|
-
end
|
17
|
-
|
18
|
-
appraise "rails-5-0" do
|
19
|
-
group :sqlite do
|
20
|
-
gem "activerecord-jdbcsqlite3-adapter", "~> 50.0", platform: :jruby
|
21
|
-
end
|
22
|
-
group :mysql do
|
23
|
-
gem "activerecord-jdbcmysql-adapter", "~> 50.0", platform: :jruby
|
24
|
-
end
|
25
|
-
group :postgresql do
|
26
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 50.0", platform: :jruby
|
27
|
-
end
|
28
|
-
|
29
|
-
gem "activerecord", "~> 5.0.0"
|
30
|
-
end
|
31
|
-
|
32
|
-
appraise "rails-5-1" do
|
33
|
-
group :sqlite do
|
34
|
-
gem "activerecord-jdbcsqlite3-adapter", "~> 51.0", platform: :jruby
|
35
|
-
end
|
36
|
-
group :mysql do
|
37
|
-
gem "activerecord-jdbcmysql-adapter", "~> 51.0", platform: :jruby
|
38
|
-
end
|
39
|
-
group :postgresql do
|
40
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 51.0", platform: :jruby
|
41
|
-
end
|
42
|
-
|
43
|
-
gem "activerecord", "~> 5.1.0"
|
44
|
-
end
|
45
|
-
|
46
1
|
appraise "rails-5-2" do
|
47
2
|
group :sqlite do
|
48
3
|
gem "activerecord-jdbcsqlite3-adapter", "~> 52.0", platform: :jruby
|
@@ -84,3 +39,17 @@ appraise "rails-6-1" do
|
|
84
39
|
end
|
85
40
|
gem "activerecord", "~> 6.1.0"
|
86
41
|
end
|
42
|
+
|
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/Readme.mkd
CHANGED
@@ -136,6 +136,37 @@ Pond.first.ducks.rank(:swimming_order)
|
|
136
136
|
Duck.walking.rank(:walking)
|
137
137
|
```
|
138
138
|
|
139
|
+
Drawbacks
|
140
|
+
---------
|
141
|
+
|
142
|
+
While ranked-model is performant when storing data, it might cause N+1s depending on how you write your code. Consider this snippet:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
ducks = Duck.all
|
146
|
+
ducks.map do |duck|
|
147
|
+
{
|
148
|
+
id: duck.id,
|
149
|
+
position: duck.row_order_rank # This causes N+1!
|
150
|
+
}
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
Every call to `duck.row_order_rank` will make a call to the DB to check the rank of that
|
155
|
+
particular element. If you have a long list of elements this might cause issues to your DB.
|
156
|
+
|
157
|
+
In order to avoid that, you can use the `rank(:your_rank)` scope and some Ruby code to get
|
158
|
+
the element's position:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
ducks = Duck.rank(:row_order).all
|
162
|
+
ducks.map.with_index do |duck, index|
|
163
|
+
{
|
164
|
+
id: duck.id,
|
165
|
+
position: index
|
166
|
+
}
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
139
170
|
Single Table Inheritance (STI)
|
140
171
|
------------------------------
|
141
172
|
|
data/gemfiles/rails_5_2.gemfile
CHANGED
@@ -5,17 +5,17 @@ source "https://rubygems.org"
|
|
5
5
|
gem "activerecord", "~> 5.2.0"
|
6
6
|
|
7
7
|
group :sqlite do
|
8
|
-
gem "sqlite3",
|
8
|
+
gem "sqlite3", platform: :ruby
|
9
9
|
gem "activerecord-jdbcsqlite3-adapter", "~> 52.0", platform: :jruby
|
10
10
|
end
|
11
11
|
|
12
12
|
group :postgresql do
|
13
|
-
gem "pg",
|
13
|
+
gem "pg", platform: :ruby
|
14
14
|
gem "activerecord-jdbcpostgresql-adapter", "~> 52.0", platform: :jruby
|
15
15
|
end
|
16
16
|
|
17
17
|
group :mysql do
|
18
|
-
gem "mysql2",
|
18
|
+
gem "mysql2", platform: :ruby
|
19
19
|
gem "activerecord-jdbcmysql-adapter", "~> 52.0", platform: :jruby
|
20
20
|
end
|
21
21
|
|
data/gemfiles/rails_6_0.gemfile
CHANGED
@@ -10,12 +10,12 @@ group :sqlite do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
group :postgresql do
|
13
|
-
gem "pg",
|
13
|
+
gem "pg", platform: :ruby
|
14
14
|
gem "activerecord-jdbcpostgresql-adapter", "~> 60.0", platform: :jruby
|
15
15
|
end
|
16
16
|
|
17
17
|
group :mysql do
|
18
|
-
gem "mysql2",
|
18
|
+
gem "mysql2", platform: :ruby
|
19
19
|
gem "activerecord-jdbcmysql-adapter", "~> 60.0", platform: :jruby
|
20
20
|
end
|
21
21
|
|
data/gemfiles/rails_6_1.gemfile
CHANGED
@@ -10,12 +10,12 @@ group :sqlite do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
group :postgresql do
|
13
|
-
gem "pg",
|
13
|
+
gem "pg", platform: :ruby
|
14
14
|
gem "activerecord-jdbcpostgresql-adapter", "~> 61.0", platform: :jruby
|
15
15
|
end
|
16
16
|
|
17
17
|
group :mysql do
|
18
|
-
gem "mysql2",
|
18
|
+
gem "mysql2", platform: :ruby
|
19
19
|
gem "activerecord-jdbcmysql-adapter", "~> 61.0", platform: :jruby
|
20
20
|
end
|
21
21
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "activerecord", "~> 7.0.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: "../"
|
data/lib/ranked-model/ranker.rb
CHANGED
@@ -1,356 +1,357 @@
|
|
1
|
-
module RankedModel
|
2
|
-
|
3
|
-
class InvalidScope < StandardError; end
|
4
|
-
class InvalidField < StandardError; end
|
5
|
-
|
6
|
-
class Ranker
|
7
|
-
attr_accessor :name, :column, :scope, :with_same, :class_name, :unless
|
8
|
-
|
9
|
-
def initialize name, options={}
|
10
|
-
self.name = name.to_sym
|
11
|
-
self.column = options[:column] || name
|
12
|
-
self.class_name = options[:class_name]
|
13
|
-
|
14
|
-
[ :scope, :with_same, :unless ].each do |key|
|
15
|
-
self.send "#{key}=", options[key]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def with instance
|
20
|
-
Mapper.new self, instance
|
21
|
-
end
|
22
|
-
|
23
|
-
class Mapper
|
24
|
-
attr_accessor :ranker, :instance
|
25
|
-
|
26
|
-
def initialize ranker, instance
|
27
|
-
self.ranker = ranker
|
28
|
-
self.instance = instance
|
29
|
-
|
30
|
-
validate_ranker_for_instance!
|
31
|
-
end
|
32
|
-
|
33
|
-
def validate_ranker_for_instance!
|
34
|
-
if ranker.scope && !instance_class.respond_to?(ranker.scope)
|
35
|
-
raise RankedModel::InvalidScope, %Q{No scope called "#{ranker.scope}" found in model}
|
36
|
-
end
|
37
|
-
|
38
|
-
if ranker.with_same
|
39
|
-
if (case ranker.with_same
|
40
|
-
when Symbol
|
41
|
-
!instance.respond_to?(ranker.with_same)
|
42
|
-
when Array
|
43
|
-
array_element = ranker.with_same.detect {|attr| !instance.respond_to?(attr) }
|
44
|
-
else
|
45
|
-
false
|
46
|
-
end)
|
47
|
-
raise RankedModel::InvalidField, %Q{No field called "#{array_element || ranker.with_same}" found in model}
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def handle_ranking
|
53
|
-
case ranker.unless
|
54
|
-
when Proc
|
55
|
-
return if ranker.unless.call(instance)
|
56
|
-
when Symbol
|
57
|
-
return if instance.send(ranker.unless)
|
58
|
-
end
|
59
|
-
|
60
|
-
update_index_from_position
|
61
|
-
assure_unique_position
|
62
|
-
end
|
63
|
-
|
64
|
-
def update_rank! value
|
65
|
-
# Bypass callbacks
|
66
|
-
#
|
67
|
-
instance_class.
|
68
|
-
where(instance_class.primary_key => instance.id).
|
69
|
-
update_all(ranker.column => value)
|
70
|
-
end
|
71
|
-
|
72
|
-
def reset_ranks!
|
73
|
-
finder.update_all(ranker.column => nil)
|
74
|
-
end
|
75
|
-
|
76
|
-
def position
|
77
|
-
instance.send "#{ranker.name}_position"
|
78
|
-
end
|
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
|
-
|
86
|
-
def rank
|
87
|
-
instance.send "#{ranker.column}"
|
88
|
-
end
|
89
|
-
|
90
|
-
def current_at_position _pos
|
91
|
-
if (ordered_instance = finder.offset(_pos).first)
|
92
|
-
RankedModel::Ranker::Mapper.new ranker, ordered_instance
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def has_rank?
|
97
|
-
!rank.nil?
|
98
|
-
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
def reset_cache
|
103
|
-
@finder, @current_order, @current_first, @current_last = nil
|
104
|
-
end
|
105
|
-
|
106
|
-
def instance_class
|
107
|
-
ranker.class_name.nil? ? instance.class : ranker.class_name.constantize
|
108
|
-
end
|
109
|
-
|
110
|
-
def position_at value
|
111
|
-
instance.send "#{ranker.name}_position=", value
|
112
|
-
update_index_from_position
|
113
|
-
end
|
114
|
-
|
115
|
-
def rank_at value
|
116
|
-
instance.send "#{ranker.column}=", value
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
destination
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
@finder
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
1
|
+
module RankedModel
|
2
|
+
|
3
|
+
class InvalidScope < StandardError; end
|
4
|
+
class InvalidField < StandardError; end
|
5
|
+
|
6
|
+
class Ranker
|
7
|
+
attr_accessor :name, :column, :scope, :with_same, :class_name, :unless
|
8
|
+
|
9
|
+
def initialize name, options={}
|
10
|
+
self.name = name.to_sym
|
11
|
+
self.column = options[:column] || name
|
12
|
+
self.class_name = options[:class_name]
|
13
|
+
|
14
|
+
[ :scope, :with_same, :unless ].each do |key|
|
15
|
+
self.send "#{key}=", options[key]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def with instance
|
20
|
+
Mapper.new self, instance
|
21
|
+
end
|
22
|
+
|
23
|
+
class Mapper
|
24
|
+
attr_accessor :ranker, :instance
|
25
|
+
|
26
|
+
def initialize ranker, instance
|
27
|
+
self.ranker = ranker
|
28
|
+
self.instance = instance
|
29
|
+
|
30
|
+
validate_ranker_for_instance!
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate_ranker_for_instance!
|
34
|
+
if ranker.scope && !instance_class.respond_to?(ranker.scope)
|
35
|
+
raise RankedModel::InvalidScope, %Q{No scope called "#{ranker.scope}" found in model}
|
36
|
+
end
|
37
|
+
|
38
|
+
if ranker.with_same
|
39
|
+
if (case ranker.with_same
|
40
|
+
when Symbol
|
41
|
+
!instance.respond_to?(ranker.with_same)
|
42
|
+
when Array
|
43
|
+
array_element = ranker.with_same.detect {|attr| !instance.respond_to?(attr) }
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end)
|
47
|
+
raise RankedModel::InvalidField, %Q{No field called "#{array_element || ranker.with_same}" found in model}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def handle_ranking
|
53
|
+
case ranker.unless
|
54
|
+
when Proc
|
55
|
+
return if ranker.unless.call(instance)
|
56
|
+
when Symbol
|
57
|
+
return if instance.send(ranker.unless)
|
58
|
+
end
|
59
|
+
|
60
|
+
update_index_from_position
|
61
|
+
assure_unique_position
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_rank! value
|
65
|
+
# Bypass callbacks
|
66
|
+
#
|
67
|
+
instance_class.
|
68
|
+
where(instance_class.primary_key => instance.id).
|
69
|
+
update_all(ranker.column => value)
|
70
|
+
end
|
71
|
+
|
72
|
+
def reset_ranks!
|
73
|
+
finder.update_all(ranker.column => nil)
|
74
|
+
end
|
75
|
+
|
76
|
+
def position
|
77
|
+
instance.send "#{ranker.name}_position"
|
78
|
+
end
|
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
|
+
|
86
|
+
def rank
|
87
|
+
instance.send "#{ranker.column}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def current_at_position _pos
|
91
|
+
if (ordered_instance = finder.offset(_pos).first)
|
92
|
+
RankedModel::Ranker::Mapper.new ranker, ordered_instance
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def has_rank?
|
97
|
+
!rank.nil?
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def reset_cache
|
103
|
+
@finder, @current_order, @current_first, @current_last = nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def instance_class
|
107
|
+
ranker.class_name.nil? ? instance.class : ranker.class_name.constantize
|
108
|
+
end
|
109
|
+
|
110
|
+
def position_at value
|
111
|
+
instance.send "#{ranker.name}_position=", value
|
112
|
+
update_index_from_position
|
113
|
+
end
|
114
|
+
|
115
|
+
def rank_at value
|
116
|
+
instance.send "#{ranker.column}=", value
|
117
|
+
instance.send "#{ranker.name}_position=", relative_rank unless position.is_a?(Integer)
|
118
|
+
end
|
119
|
+
|
120
|
+
def rank_changed?
|
121
|
+
instance.send "#{ranker.column}_changed?"
|
122
|
+
end
|
123
|
+
|
124
|
+
def new_record?
|
125
|
+
instance.new_record?
|
126
|
+
end
|
127
|
+
|
128
|
+
def update_index_from_position
|
129
|
+
case position
|
130
|
+
when :first, 'first'
|
131
|
+
if current_first && current_first.rank
|
132
|
+
rank_at_average current_first.rank, RankedModel::MIN_RANK_VALUE
|
133
|
+
else
|
134
|
+
position_at :middle
|
135
|
+
end
|
136
|
+
when :last, 'last'
|
137
|
+
if current_last && current_last.rank
|
138
|
+
rank_at_average current_last.rank, RankedModel::MAX_RANK_VALUE
|
139
|
+
else
|
140
|
+
position_at :middle
|
141
|
+
end
|
142
|
+
when :middle, 'middle'
|
143
|
+
rank_at_average RankedModel::MIN_RANK_VALUE, RankedModel::MAX_RANK_VALUE
|
144
|
+
when :down, 'down'
|
145
|
+
neighbors = find_next_two(rank)
|
146
|
+
if neighbors[:lower]
|
147
|
+
min = neighbors[:lower].rank
|
148
|
+
max = neighbors[:upper] ? neighbors[:upper].rank : RankedModel::MAX_RANK_VALUE
|
149
|
+
rank_at_average min, max
|
150
|
+
end
|
151
|
+
when :up, 'up'
|
152
|
+
neighbors = find_previous_two(rank)
|
153
|
+
if neighbors[:upper]
|
154
|
+
max = neighbors[:upper].rank
|
155
|
+
min = neighbors[:lower] ? neighbors[:lower].rank : RankedModel::MIN_RANK_VALUE
|
156
|
+
rank_at_average min, max
|
157
|
+
end
|
158
|
+
when String
|
159
|
+
position_at position.to_i
|
160
|
+
when 0
|
161
|
+
position_at :first
|
162
|
+
when Integer
|
163
|
+
neighbors = neighbors_at_position(position)
|
164
|
+
min = ((neighbors[:lower] && neighbors[:lower].has_rank?) ? neighbors[:lower].rank : RankedModel::MIN_RANK_VALUE)
|
165
|
+
max = ((neighbors[:upper] && neighbors[:upper].has_rank?) ? neighbors[:upper].rank : RankedModel::MAX_RANK_VALUE)
|
166
|
+
rank_at_average min, max
|
167
|
+
when NilClass
|
168
|
+
if !rank
|
169
|
+
position_at :last
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def rank_at_average(min, max)
|
175
|
+
if (max - min).between?(-1, 1) # No room at the inn...
|
176
|
+
rebalance_ranks
|
177
|
+
position_at position
|
178
|
+
else
|
179
|
+
rank_at( ( ( max - min ).to_f / 2 ).ceil + min )
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def assure_unique_position
|
184
|
+
if ( new_record? || rank_changed? )
|
185
|
+
if (rank > RankedModel::MAX_RANK_VALUE) || rank_taken?
|
186
|
+
rearrange_ranks
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def rearrange_ranks
|
192
|
+
_scope = finder
|
193
|
+
escaped_column = instance_class.connection.quote_column_name ranker.column
|
194
|
+
# If there is room at the bottom of the list and we're added to the very top of the list...
|
195
|
+
if current_first.rank && current_first.rank > RankedModel::MIN_RANK_VALUE && rank == RankedModel::MAX_RANK_VALUE
|
196
|
+
# ...then move everyone else down 1 to make room for us at the end
|
197
|
+
_scope.
|
198
|
+
where( instance_class.arel_table[ranker.column].lteq(rank) ).
|
199
|
+
update_all( "#{escaped_column} = #{escaped_column} - 1" )
|
200
|
+
# If there is room at the top of the list and we're added below the last value in the list...
|
201
|
+
elsif current_last.rank && current_last.rank < (RankedModel::MAX_RANK_VALUE - 1) && rank < current_last.rank
|
202
|
+
# ...then move everyone else at or above our desired rank up 1 to make room for us
|
203
|
+
_scope.
|
204
|
+
where( instance_class.arel_table[ranker.column].gteq(rank) ).
|
205
|
+
update_all( "#{escaped_column} = #{escaped_column} + 1" )
|
206
|
+
# If there is room at the bottom of the list and we're added above the lowest value in the list...
|
207
|
+
elsif current_first.rank && current_first.rank > RankedModel::MIN_RANK_VALUE && rank > current_first.rank
|
208
|
+
# ...then move everyone else below us down 1 and change our rank down 1 to avoid the collission
|
209
|
+
_scope.
|
210
|
+
where( instance_class.arel_table[ranker.column].lt(rank) ).
|
211
|
+
update_all( "#{escaped_column} = #{escaped_column} - 1" )
|
212
|
+
rank_at( rank - 1 )
|
213
|
+
else
|
214
|
+
rebalance_ranks
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def rebalance_ranks
|
219
|
+
ActiveRecord::Base.transaction do
|
220
|
+
if rank && instance.persisted?
|
221
|
+
origin = current_order.index { |item| item.instance.id == instance.id }
|
222
|
+
if origin
|
223
|
+
destination = current_order.index { |item| rank <= item.rank }
|
224
|
+
destination -= 1 if origin < destination
|
225
|
+
|
226
|
+
current_order.insert destination, current_order.delete_at(origin)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
gaps = current_order.size + 1
|
231
|
+
range = (RankedModel::MAX_RANK_VALUE - RankedModel::MIN_RANK_VALUE).to_f
|
232
|
+
gap_size = (range / gaps).ceil
|
233
|
+
|
234
|
+
reset_ranks!
|
235
|
+
|
236
|
+
current_order.each.with_index(1) do |item, position|
|
237
|
+
new_rank = (gap_size * position) + RankedModel::MIN_RANK_VALUE
|
238
|
+
|
239
|
+
if item.instance.id == instance.id
|
240
|
+
instance.send "#{ranker.column}=", new_rank
|
241
|
+
else
|
242
|
+
item.update_rank! new_rank
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
reset_cache
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def finder(order = :asc)
|
251
|
+
@finder ||= {}
|
252
|
+
@finder[order] ||= begin
|
253
|
+
_finder = instance_class
|
254
|
+
columns = [instance_class.primary_key.to_sym, ranker.column]
|
255
|
+
|
256
|
+
if ranker.scope
|
257
|
+
_finder = _finder.send ranker.scope
|
258
|
+
end
|
259
|
+
|
260
|
+
case ranker.with_same
|
261
|
+
when Symbol
|
262
|
+
columns << ranker.with_same
|
263
|
+
_finder = _finder.where \
|
264
|
+
ranker.with_same => instance.attributes[ranker.with_same.to_s]
|
265
|
+
when Array
|
266
|
+
ranker.with_same.each do |column|
|
267
|
+
columns << column
|
268
|
+
_finder = _finder.where column => instance.attributes[column.to_s]
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
unless new_record?
|
273
|
+
_finder = _finder.where.not instance_class.primary_key.to_sym => instance.id
|
274
|
+
end
|
275
|
+
|
276
|
+
_finder.reorder(ranker.column.to_sym => order).select(columns)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def current_order
|
281
|
+
@current_order ||= begin
|
282
|
+
finder.unscope(where: instance_class.primary_key.to_sym).collect { |ordered_instance|
|
283
|
+
RankedModel::Ranker::Mapper.new ranker, ordered_instance
|
284
|
+
}
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def current_first
|
289
|
+
@current_first ||= begin
|
290
|
+
if (ordered_instance = finder.first)
|
291
|
+
RankedModel::Ranker::Mapper.new ranker, ordered_instance
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def current_last
|
297
|
+
@current_last ||= begin
|
298
|
+
if (ordered_instance = finder.last)
|
299
|
+
RankedModel::Ranker::Mapper.new ranker, ordered_instance
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def rank_taken?
|
305
|
+
finder.except(:order).where(ranker.column => rank).exists?
|
306
|
+
end
|
307
|
+
|
308
|
+
def neighbors_at_position _pos
|
309
|
+
if _pos > 0
|
310
|
+
if (ordered_instances = finder.offset(_pos-1).limit(2).to_a)
|
311
|
+
if ordered_instances[1]
|
312
|
+
{ :lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ),
|
313
|
+
:upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[1] ) }
|
314
|
+
elsif ordered_instances[0]
|
315
|
+
{ :lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ) }
|
316
|
+
else
|
317
|
+
{ :lower => current_last }
|
318
|
+
end
|
319
|
+
end
|
320
|
+
else
|
321
|
+
if (ordered_instance = finder.first)
|
322
|
+
{ :upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instance ) }
|
323
|
+
else
|
324
|
+
{}
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def find_next_two _rank
|
330
|
+
ordered_instances = finder.where(instance_class.arel_table[ranker.column].gt _rank).limit(2)
|
331
|
+
if ordered_instances[1]
|
332
|
+
{ :lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ),
|
333
|
+
:upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[1] ) }
|
334
|
+
elsif ordered_instances[0]
|
335
|
+
{ :lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ) }
|
336
|
+
else
|
337
|
+
{}
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def find_previous_two _rank
|
342
|
+
ordered_instances = finder(:desc).where(instance_class.arel_table[ranker.column].lt _rank).limit(2)
|
343
|
+
if ordered_instances[1]
|
344
|
+
{ :upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ),
|
345
|
+
:lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[1] ) }
|
346
|
+
elsif ordered_instances[0]
|
347
|
+
{ :upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ) }
|
348
|
+
else
|
349
|
+
{}
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
end
|
data/lib/ranked-model/version.rb
CHANGED
data/ranked-model.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
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", ">=
|
16
|
+
s.add_dependency "activerecord", ">= 5.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,71 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Duck do
|
4
|
+
before do
|
5
|
+
5.times do |i|
|
6
|
+
Duck.create(name: "Duck #{i + 1}")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "updating a duck order with last" do
|
11
|
+
it "should maintain the order after creating a new duck" do
|
12
|
+
duck = Duck.first
|
13
|
+
duck.update(row_position: :last)
|
14
|
+
expect(duck.row_rank).to eq(4)
|
15
|
+
|
16
|
+
Duck.create(name: "Wacky")
|
17
|
+
|
18
|
+
expect(duck.row_rank).to eq(4)
|
19
|
+
|
20
|
+
duck.update(pond: 'Shin')
|
21
|
+
expect(duck.row_rank).to eq(4)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "updating a duck order with first" do
|
26
|
+
it "should maintain the order after creating a new duck" do
|
27
|
+
duck = Duck.last
|
28
|
+
duck.update(row_position: :first)
|
29
|
+
expect(duck.row_rank).to eq(0)
|
30
|
+
|
31
|
+
Duck.create(name: "Wacky")
|
32
|
+
|
33
|
+
expect(duck.row_rank).to eq(0)
|
34
|
+
|
35
|
+
duck.update(pond: 'Shin')
|
36
|
+
expect(duck.row_rank).to eq(0)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "updating a duck order with up" do
|
41
|
+
it "should maintain the order after creating a new duck" do
|
42
|
+
duck_id = Duck.ranker(:row).with(Duck.new).current_at_position(2).instance.id
|
43
|
+
duck = Duck.find(duck_id)
|
44
|
+
duck.update(row_position: :up)
|
45
|
+
expect(duck.row_rank).to eq(1)
|
46
|
+
|
47
|
+
Duck.create(name: "Wacky")
|
48
|
+
|
49
|
+
expect(duck.row_rank).to eq(1)
|
50
|
+
|
51
|
+
duck.update(pond: 'Shin')
|
52
|
+
expect(duck.row_rank).to eq(1)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "updating a duck order with down" do
|
57
|
+
it "should maintain the order after creating a new duck" do
|
58
|
+
duck_id = Duck.ranker(:row).with(Duck.new).current_at_position(2).instance.id
|
59
|
+
duck = Duck.find(duck_id)
|
60
|
+
duck.update(row_position: :down)
|
61
|
+
expect(duck.row_rank).to eq(3)
|
62
|
+
|
63
|
+
Duck.create(name: "Wacky")
|
64
|
+
|
65
|
+
expect(duck.row_rank).to eq(3)
|
66
|
+
|
67
|
+
duck.update(pond: 'Shin')
|
68
|
+
expect(duck.row_rank).to eq(3)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/support/database.yml
CHANGED
@@ -6,6 +6,7 @@ sqlite:
|
|
6
6
|
|
7
7
|
mysql:
|
8
8
|
adapter: mysql2
|
9
|
+
host: 127.0.0.1
|
9
10
|
database: ranked_model_test
|
10
11
|
pool: 5
|
11
12
|
timeout: 5000
|
@@ -14,6 +15,7 @@ mysql:
|
|
14
15
|
|
15
16
|
postgresql:
|
16
17
|
adapter: postgresql
|
18
|
+
host: 127.0.0.1
|
17
19
|
database: ranked_model_test
|
18
20
|
pool: 5
|
19
21
|
timeout: 5000
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ranked-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Beale
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,6 +130,9 @@ executables: []
|
|
130
130
|
extensions: []
|
131
131
|
extra_rdoc_files: []
|
132
132
|
files:
|
133
|
+
- ".github/FUNDING.yml"
|
134
|
+
- ".github/dependabot.yml"
|
135
|
+
- ".github/workflows/ci.yml"
|
133
136
|
- ".gitignore"
|
134
137
|
- ".rspec"
|
135
138
|
- ".travis.yml"
|
@@ -138,12 +141,10 @@ files:
|
|
138
141
|
- LICENSE
|
139
142
|
- Rakefile
|
140
143
|
- Readme.mkd
|
141
|
-
- gemfiles/rails_4_2.gemfile
|
142
|
-
- gemfiles/rails_5_0.gemfile
|
143
|
-
- gemfiles/rails_5_1.gemfile
|
144
144
|
- gemfiles/rails_5_2.gemfile
|
145
145
|
- gemfiles/rails_6_0.gemfile
|
146
146
|
- gemfiles/rails_6_1.gemfile
|
147
|
+
- gemfiles/rails_7_0.gemfile
|
147
148
|
- lib/ranked-model.rb
|
148
149
|
- lib/ranked-model/railtie.rb
|
149
150
|
- lib/ranked-model/ranker.rb
|
@@ -152,6 +153,7 @@ files:
|
|
152
153
|
- ranked-model.gemspec
|
153
154
|
- spec/duck-model/column_default_ducks_spec.rb
|
154
155
|
- spec/duck-model/duck_spec.rb
|
156
|
+
- spec/duck-model/inferred_ducks_spec.rb
|
155
157
|
- spec/duck-model/lots_of_ducks_spec.rb
|
156
158
|
- spec/duck-model/wrong_ducks_spec.rb
|
157
159
|
- spec/ego-model/ego_spec.rb
|
@@ -191,6 +193,7 @@ summary: An acts_as_sortable replacement built for Rails 4.2+
|
|
191
193
|
test_files:
|
192
194
|
- spec/duck-model/column_default_ducks_spec.rb
|
193
195
|
- spec/duck-model/duck_spec.rb
|
196
|
+
- spec/duck-model/inferred_ducks_spec.rb
|
194
197
|
- spec/duck-model/lots_of_ducks_spec.rb
|
195
198
|
- spec/duck-model/wrong_ducks_spec.rb
|
196
199
|
- spec/ego-model/ego_spec.rb
|
data/gemfiles/rails_4_2.gemfile
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# This file was generated by Appraisal
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
gem "activerecord", "~> 4.2.0"
|
6
|
-
|
7
|
-
group :sqlite do
|
8
|
-
gem "sqlite3", "~> 1.3.13", platform: :ruby
|
9
|
-
gem "activerecord-jdbcsqlite3-adapter", "~> 1.3.24", platform: :jruby
|
10
|
-
end
|
11
|
-
|
12
|
-
group :postgresql do
|
13
|
-
gem "pg", "~> 0.18.4", platform: :ruby
|
14
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 1.3.24", platform: :jruby
|
15
|
-
end
|
16
|
-
|
17
|
-
group :mysql do
|
18
|
-
gem "mysql2", "~> 0.4.0", platform: :ruby
|
19
|
-
gem "jdbc-mysql", "~> 5.1.47", platform: :jruby
|
20
|
-
gem "activerecord-jdbcmysql-adapter", "~> 1.3.24", platform: :jruby
|
21
|
-
end
|
22
|
-
|
23
|
-
gemspec path: "../"
|
data/gemfiles/rails_5_0.gemfile
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# This file was generated by Appraisal
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
gem "activerecord", "~> 5.0.0"
|
6
|
-
|
7
|
-
group :sqlite do
|
8
|
-
gem "sqlite3", "~> 1.3.13", platform: :ruby
|
9
|
-
gem "activerecord-jdbcsqlite3-adapter", "~> 50.0", platform: :jruby
|
10
|
-
end
|
11
|
-
|
12
|
-
group :postgresql do
|
13
|
-
gem "pg", "~> 1.2.0", platform: :ruby
|
14
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 50.0", platform: :jruby
|
15
|
-
end
|
16
|
-
|
17
|
-
group :mysql do
|
18
|
-
gem "mysql2", "~> 0.5.0", platform: :ruby
|
19
|
-
gem "activerecord-jdbcmysql-adapter", "~> 50.0", platform: :jruby
|
20
|
-
end
|
21
|
-
|
22
|
-
gemspec path: "../"
|
data/gemfiles/rails_5_1.gemfile
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# This file was generated by Appraisal
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
gem "activerecord", "~> 5.1.0"
|
6
|
-
|
7
|
-
group :sqlite do
|
8
|
-
gem "sqlite3", "~> 1.3.13", platform: :ruby
|
9
|
-
gem "activerecord-jdbcsqlite3-adapter", "~> 51.0", platform: :jruby
|
10
|
-
end
|
11
|
-
|
12
|
-
group :postgresql do
|
13
|
-
gem "pg", "~> 1.2.0", platform: :ruby
|
14
|
-
gem "activerecord-jdbcpostgresql-adapter", "~> 51.0", platform: :jruby
|
15
|
-
end
|
16
|
-
|
17
|
-
group :mysql do
|
18
|
-
gem "mysql2", "~> 0.5.0", platform: :ruby
|
19
|
-
gem "activerecord-jdbcmysql-adapter", "~> 51.0", platform: :jruby
|
20
|
-
end
|
21
|
-
|
22
|
-
gemspec path: "../"
|