ranked-model 0.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +67 -3
- data/Gemfile +17 -0
- data/Readme.mkd +24 -4
- data/lib/ranked-model.rb +8 -1
- data/lib/ranked-model/ranker.rb +52 -8
- data/lib/ranked-model/version.rb +1 -1
- data/ranked-model.gemspec +2 -1
- data/spec/duck-model/duck_spec.rb +365 -20
- data/spec/player-model/records_already_exist_spec.rb +22 -0
- data/spec/spec_helper.rb +4 -2
- data/spec/support/active_record.rb +13 -2
- data/spec/support/database.yml +26 -1
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 116f5fc0aeafe231cddba562c5135e868ba06604
|
4
|
+
data.tar.gz: c720ee45d4532e3c96fb2d829788a2c43cdd7f8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00ca1355412ab2cc161edc2831bc685862f4996b33903a7bf5a65077e2a638e2f385d665db6e20bdfe50f59d93c8517cdf68c8e694a325680c4a2314e4adb677
|
7
|
+
data.tar.gz: 72dbeebd7402e29238fb7219c0037f04ba588dba79e06ecbabf6ee9962cee67131b0b96246945639271b7585c63ebf6edb09274c7ba779ca864a336ba576af72
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,18 +1,82 @@
|
|
1
1
|
language: ruby
|
2
|
+
before_script:
|
3
|
+
- mysql -e 'create database ranked_model_test;'
|
4
|
+
- psql -c 'create database ranked_model_test;' -U postgres
|
2
5
|
rvm:
|
3
6
|
- "1.9.2"
|
4
7
|
- "1.9.3"
|
5
8
|
- "2.0.0"
|
9
|
+
- "2.1.0"
|
10
|
+
- "jruby-19mode"
|
11
|
+
- "rbx"
|
6
12
|
env:
|
7
|
-
- "ACTIVERECORD_VERSION=4.0.
|
8
|
-
- "ACTIVERECORD_VERSION=
|
13
|
+
- "ACTIVERECORD_VERSION=4.1.0.beta1"
|
14
|
+
- "ACTIVERECORD_VERSION=4.1.0.beta1 DB=mysql_travis"
|
15
|
+
- "ACTIVERECORD_VERSION=4.1.0.beta1 DB=postgresql_travis"
|
16
|
+
- "ACTIVERECORD_VERSION=4.0.2"
|
17
|
+
- "ACTIVERECORD_VERSION=4.0.2 DB=mysql_travis"
|
18
|
+
- "ACTIVERECORD_VERSION=4.0.2 DB=postgresql_travis"
|
19
|
+
- "ACTIVERECORD_VERSION=3.2.16"
|
20
|
+
- "ACTIVERECORD_VERSION=3.2.16 DB=mysql_travis"
|
21
|
+
- "ACTIVERECORD_VERSION=3.2.16 DB=postgresql_travis"
|
9
22
|
- "ACTIVERECORD_VERSION=3.1.12"
|
23
|
+
- "ACTIVERECORD_VERSION=3.1.12 DB=mysql_travis"
|
24
|
+
- "ACTIVERECORD_VERSION=3.1.12 DB=postgresql_travis"
|
10
25
|
- "ACTIVERECORD_VERSION=master"
|
26
|
+
- "ACTIVERECORD_VERSION=master DB=mysql_travis"
|
27
|
+
- "ACTIVERECORD_VERSION=master DB=postgresql_travis"
|
11
28
|
matrix:
|
12
29
|
exclude:
|
13
|
-
- env: "ACTIVERECORD_VERSION=4.0.
|
30
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1"
|
14
31
|
rvm: "1.9.2"
|
32
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1 DB=mysql_travis"
|
33
|
+
rvm: "1.9.2"
|
34
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1 DB=postgresql_travis"
|
35
|
+
rvm: "1.9.2"
|
36
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1"
|
37
|
+
rvm: "1.9.3"
|
38
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1 DB=mysql_travis"
|
39
|
+
rvm: "1.9.3"
|
40
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1 DB=postgresql_travis"
|
41
|
+
rvm: "1.9.3"
|
42
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1"
|
43
|
+
rvm: "jruby-19mode"
|
44
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1 DB=mysql_travis"
|
45
|
+
rvm: "jruby-19mode"
|
46
|
+
- env: "ACTIVERECORD_VERSION=4.1.0.beta1 DB=postgresql_travis"
|
47
|
+
rvm: "jruby-19mode"
|
15
48
|
- env: "ACTIVERECORD_VERSION=master"
|
49
|
+
rvm: "jruby-19mode"
|
50
|
+
- env: "ACTIVERECORD_VERSION=master DB=mysql_travis"
|
51
|
+
rvm: "jruby-19mode"
|
52
|
+
- env: "ACTIVERECORD_VERSION=master DB=postgresql_travis"
|
53
|
+
rvm: "jruby-19mode"
|
54
|
+
- env: "ACTIVERECORD_VERSION=4.0.2"
|
55
|
+
rvm: "1.9.2"
|
56
|
+
- env: "ACTIVERECORD_VERSION=4.0.2 DB=mysql_travis"
|
16
57
|
rvm: "1.9.2"
|
58
|
+
- env: "ACTIVERECORD_VERSION=4.0.2 DB=postgresql_travis"
|
59
|
+
rvm: "1.9.2"
|
60
|
+
- env: "ACTIVERECORD_VERSION=master"
|
61
|
+
rvm: "1.9.2"
|
62
|
+
- env: "ACTIVERECORD_VERSION=master DB=mysql_travis"
|
63
|
+
rvm: "1.9.2"
|
64
|
+
- env: "ACTIVERECORD_VERSION=master DB=postgresql_travis"
|
65
|
+
rvm: "1.9.2"
|
66
|
+
- env: "ACTIVERECORD_VERSION=master"
|
67
|
+
rvm: "1.9.3"
|
68
|
+
- env: "ACTIVERECORD_VERSION=master DB=mysql_travis"
|
69
|
+
rvm: "1.9.3"
|
70
|
+
- env: "ACTIVERECORD_VERSION=master DB=postgresql_travis"
|
71
|
+
rvm: "1.9.3"
|
17
72
|
allow_failures:
|
73
|
+
# Master may or may not pass
|
18
74
|
- env: "ACTIVERECORD_VERSION=master"
|
75
|
+
- env: "ACTIVERECORD_VERSION=master DB=mysql_travis"
|
76
|
+
- env: "ACTIVERECORD_VERSION=master DB=postgresql_travis"
|
77
|
+
# Rails 3.1 is incompatible with database cleaner 1.2
|
78
|
+
- env: "ACTIVERECORD_VERSION=3.1.12 DB=mysql_travis"
|
79
|
+
- env: "ACTIVERECORD_VERSION=3.1.12 DB=postgresql_travis"
|
80
|
+
# Postgres is not supported before Rails 4.0.
|
81
|
+
- env: "ACTIVERECORD_VERSION=3.1.12 DB=postgresql_travis"
|
82
|
+
- env: "ACTIVERECORD_VERSION=3.2.16 DB=postgresql_travis"
|
data/Gemfile
CHANGED
@@ -13,3 +13,20 @@ when "default"
|
|
13
13
|
else
|
14
14
|
gem "activerecord", "~> #{ar_version}"
|
15
15
|
end
|
16
|
+
|
17
|
+
platforms :rbx do
|
18
|
+
gem 'rubysl', '~> 2.0'
|
19
|
+
gem 'rubinius-developer_tools'
|
20
|
+
end
|
21
|
+
|
22
|
+
# SQLite
|
23
|
+
gem "activerecord-jdbcsqlite3-adapter", ">= 1.3.0", platforms: :jruby
|
24
|
+
gem "sqlite3", platforms: :ruby
|
25
|
+
|
26
|
+
# Postgres
|
27
|
+
gem "activerecord-jdbcpostgresql-adapter", platforms: :jruby
|
28
|
+
gem "pg", platforms: :ruby
|
29
|
+
|
30
|
+
# MySQL
|
31
|
+
gem "activerecord-jdbcmysql-adapter", platforms: :jruby
|
32
|
+
gem "mysql", platforms: :ruby
|
data/Readme.mkd
CHANGED
@@ -5,6 +5,10 @@
|
|
5
5
|
Installation
|
6
6
|
------------
|
7
7
|
|
8
|
+
ranked-model passes specs with Rails 3.1, 3.2, 4.0, and 4.1-beta for MySQL, Postgres, and SQLite on Ruby 1.9.2, 1.9.3, 2.0, 2.1, jruby-19mode, and rubinius where Rails supports the platform. This is with the exception of Postgres before Rails 4.0 on all platforms, which is unsupported (I'd gladly accept a PR to fix this).
|
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.
|
11
|
+
|
8
12
|
To install ranked-model, just add it to your `Gemfile`:
|
9
13
|
|
10
14
|
``` ruby
|
@@ -45,14 +49,14 @@ Duck.rank(:row_order).all
|
|
45
49
|
```
|
46
50
|
|
47
51
|
The ranking integers stored in the `row_order` column will be big and spaced apart. When you
|
48
|
-
implement a sorting UI, just update the resource with the position
|
52
|
+
implement a sorting UI, just update the resource by appending the column name with `_position` and indicating the desired position:
|
49
53
|
|
50
54
|
``` ruby
|
51
|
-
@duck.update_attribute :row_order_position, 0 # or 1, 2, 37. :first and :
|
55
|
+
@duck.update_attribute :row_order_position, 0 # or 1, 2, 37. :first, :last, :up and :down are also valid
|
52
56
|
```
|
53
57
|
|
54
58
|
Position numbers begin at zero. A position number greater than the number of records acts the
|
55
|
-
same as :last.
|
59
|
+
same as :last. :up and :down move the record up/down the ladder by one step.
|
56
60
|
|
57
61
|
So using a normal json controller where `@duck.attributes = params[:duck]; @duck.save`, JS can
|
58
62
|
look pretty elegant:
|
@@ -117,10 +121,17 @@ Contributing
|
|
117
121
|
|
118
122
|
Fork, clone, write a test, write some code, commit, push, send a pull request. Github FTW!
|
119
123
|
|
124
|
+
The specs can be run with sqlite, postgres, and mysql:
|
125
|
+
|
126
|
+
```
|
127
|
+
DB=postgres bundle exec rake
|
128
|
+
```
|
129
|
+
|
130
|
+
Is no DB is specified, the tests run against sqlite.
|
131
|
+
|
120
132
|
RankedModel is mostly the handiwork of Matthew Beale:
|
121
133
|
|
122
134
|
* [madhatted.com](http://madhatted.com) is where I blog. Also [@mixonic](http://twitter.com/mixonic).
|
123
|
-
* [Spinto](http://www.spintoapp.com) is a product I'm bootstrapping.
|
124
135
|
|
125
136
|
A hearty thanks to these contributors:
|
126
137
|
|
@@ -129,3 +140,12 @@ A hearty thanks to these contributors:
|
|
129
140
|
* [AndrewRadev](https://github.com/AndrewRadev)
|
130
141
|
* [adheerajkumar](https://github.com/adheerajkumar)
|
131
142
|
* [mikeycgto](https://github.com/mikeycgto)
|
143
|
+
* [robotex82](https://github.com/robotex82)
|
144
|
+
* [rociiu](https://github.com/rociiu)
|
145
|
+
* [codepodu](https://github.com/codepodu)
|
146
|
+
* [kakra](https://github.com/kakra)
|
147
|
+
* [metalon](https://github.com/metalon)
|
148
|
+
* [jamesalmond](https://github.com/jamesalmond)
|
149
|
+
* [jguyon](https://github.com/jguyon)
|
150
|
+
* [pehrlich](https://github.com/pehrlich)
|
151
|
+
* [petergoldstein](https://github.com/petergoldstein)
|
data/lib/ranked-model.rb
CHANGED
@@ -46,7 +46,14 @@ module RankedModel
|
|
46
46
|
self.rankers ||= []
|
47
47
|
ranker = RankedModel::Ranker.new(*args)
|
48
48
|
self.rankers << ranker
|
49
|
-
|
49
|
+
attr_reader "#{ranker.name}_position"
|
50
|
+
define_method "#{ranker.name}_position=" do |position|
|
51
|
+
if position.present?
|
52
|
+
send "#{ranker.column}_will_change!"
|
53
|
+
instance_variable_set "@#{ranker.name}_position", position
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
50
57
|
public "#{ranker.name}_position", "#{ranker.name}_position="
|
51
58
|
end
|
52
59
|
|
data/lib/ranked-model/ranker.rb
CHANGED
@@ -64,7 +64,9 @@ module RankedModel
|
|
64
64
|
def update_rank! value
|
65
65
|
# Bypass callbacks
|
66
66
|
#
|
67
|
-
instance_class.
|
67
|
+
instance_class.
|
68
|
+
where(instance_class.primary_key => instance.id).
|
69
|
+
update_all([%Q{#{ranker.column} = ?}, value])
|
68
70
|
end
|
69
71
|
|
70
72
|
def position
|
@@ -81,6 +83,10 @@ module RankedModel
|
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
86
|
+
def has_rank?
|
87
|
+
!rank.nil?
|
88
|
+
end
|
89
|
+
|
84
90
|
private
|
85
91
|
|
86
92
|
def instance_class
|
@@ -120,14 +126,28 @@ module RankedModel
|
|
120
126
|
end
|
121
127
|
when :middle, 'middle'
|
122
128
|
rank_at( ( ( RankedModel::MAX_RANK_VALUE - RankedModel::MIN_RANK_VALUE ).to_f / 2 ).ceil + RankedModel::MIN_RANK_VALUE )
|
129
|
+
when :down, 'down'
|
130
|
+
neighbors = find_next_two(rank)
|
131
|
+
if neighbors[:lower]
|
132
|
+
min = neighbors[:lower].rank
|
133
|
+
max = neighbors[:upper] ? neighbors[:upper].rank : RankedModel::MAX_RANK_VALUE
|
134
|
+
rank_at( ( ( max - min ).to_f / 2 ).ceil + min )
|
135
|
+
end
|
136
|
+
when :up, 'up'
|
137
|
+
neighbors = find_previous_two(rank)
|
138
|
+
if neighbors[:upper]
|
139
|
+
max = neighbors[:upper].rank
|
140
|
+
min = neighbors[:lower] ? neighbors[:lower].rank : RankedModel::MIN_RANK_VALUE
|
141
|
+
rank_at( ( ( max - min ).to_f / 2 ).ceil + min )
|
142
|
+
end
|
123
143
|
when String
|
124
144
|
position_at position.to_i
|
125
145
|
when 0
|
126
146
|
position_at :first
|
127
147
|
when Integer
|
128
148
|
neighbors = neighbors_at_position(position)
|
129
|
-
min = (neighbors[:lower] ? neighbors[:lower].rank : RankedModel::MIN_RANK_VALUE)
|
130
|
-
max = (neighbors[:upper] ? neighbors[:upper].rank : RankedModel::MAX_RANK_VALUE)
|
149
|
+
min = ((neighbors[:lower] && neighbors[:lower].has_rank?) ? neighbors[:lower].rank : RankedModel::MIN_RANK_VALUE)
|
150
|
+
max = ((neighbors[:upper] && neighbors[:upper].has_rank?) ? neighbors[:upper].rank : RankedModel::MAX_RANK_VALUE)
|
131
151
|
rank_at( ( ( max - min ).to_f / 2 ).ceil + min )
|
132
152
|
when NilClass
|
133
153
|
if !rank
|
@@ -157,15 +177,15 @@ module RankedModel
|
|
157
177
|
if current_first.rank && current_first.rank > RankedModel::MIN_RANK_VALUE && rank == RankedModel::MAX_RANK_VALUE
|
158
178
|
_scope.
|
159
179
|
where( instance_class.arel_table[ranker.column].lteq(rank) ).
|
160
|
-
update_all(
|
180
|
+
update_all( %Q{#{ranker.column} = #{ranker.column} - 1} )
|
161
181
|
elsif current_last.rank && current_last.rank < (RankedModel::MAX_RANK_VALUE - 1) && rank < current_last.rank
|
162
182
|
_scope.
|
163
183
|
where( instance_class.arel_table[ranker.column].gteq(rank) ).
|
164
|
-
update_all(
|
184
|
+
update_all( %Q{#{ranker.column} = #{ranker.column} + 1} )
|
165
185
|
elsif current_first.rank && current_first.rank > RankedModel::MIN_RANK_VALUE && rank > current_first.rank
|
166
186
|
_scope.
|
167
187
|
where( instance_class.arel_table[ranker.column].lt(rank) ).
|
168
|
-
update_all(
|
188
|
+
update_all( %Q{#{ranker.column} = #{ranker.column} - 1} )
|
169
189
|
rank_at( rank - 1 )
|
170
190
|
else
|
171
191
|
rebalance_ranks
|
@@ -194,7 +214,7 @@ module RankedModel
|
|
194
214
|
end
|
195
215
|
end
|
196
216
|
|
197
|
-
def finder
|
217
|
+
def finder(order = :asc)
|
198
218
|
@finder ||= begin
|
199
219
|
_finder = instance_class
|
200
220
|
columns = [instance_class.arel_table[instance_class.primary_key], instance_class.arel_table[ranker.column]]
|
@@ -226,7 +246,7 @@ module RankedModel
|
|
226
246
|
_finder = _finder.where \
|
227
247
|
instance_class.arel_table[instance_class.primary_key].not_eq(instance.id)
|
228
248
|
end
|
229
|
-
_finder.order(instance_class.arel_table[ranker.column].
|
249
|
+
_finder.order(instance_class.arel_table[ranker.column].send(order)).select(columns)
|
230
250
|
end
|
231
251
|
end
|
232
252
|
|
@@ -286,6 +306,30 @@ module RankedModel
|
|
286
306
|
end
|
287
307
|
end
|
288
308
|
|
309
|
+
def find_next_two _rank
|
310
|
+
ordered_instances = finder.where(instance_class.arel_table[ranker.column].gt _rank).limit(2)
|
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
|
+
{}
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def find_previous_two _rank
|
322
|
+
ordered_instances = finder(:desc).where(instance_class.arel_table[ranker.column].lt _rank).limit(2)
|
323
|
+
if ordered_instances[1]
|
324
|
+
{ :upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ),
|
325
|
+
:lower => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[1] ) }
|
326
|
+
elsif ordered_instances[0]
|
327
|
+
{ :upper => RankedModel::Ranker::Mapper.new( ranker, ordered_instances[0] ) }
|
328
|
+
else
|
329
|
+
{}
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
289
333
|
end
|
290
334
|
|
291
335
|
end
|
data/lib/ranked-model/version.rb
CHANGED
data/ranked-model.gemspec
CHANGED
@@ -11,13 +11,14 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.homepage = "https://github.com/mixonic/ranked-model"
|
12
12
|
s.summary = %q{An acts_as_sortable replacement built for Rails 3 & 4}
|
13
13
|
s.description = %q{ranked-model is a modern row sorting library built for Rails 3 & 4. It uses ARel aggressively and is better optimized than most other libraries.}
|
14
|
+
s.license = 'MIT'
|
14
15
|
|
15
16
|
s.add_dependency "activerecord", ">= 3.1.12"
|
16
17
|
s.add_development_dependency "rspec", "~> 2.13.0"
|
17
18
|
s.add_development_dependency "sqlite3", "~> 1.3.7"
|
18
19
|
s.add_development_dependency "genspec", "~> 0.2.8"
|
19
20
|
s.add_development_dependency "mocha", "~> 0.14.0"
|
20
|
-
s.add_development_dependency "database_cleaner", "~> 1.0
|
21
|
+
s.add_development_dependency "database_cleaner", "~> 1.2.0"
|
21
22
|
s.add_development_dependency "rake", "~> 10.1.0"
|
22
23
|
|
23
24
|
s.files = `git ls-files`.split("\n")
|
@@ -62,9 +62,9 @@ describe Duck do
|
|
62
62
|
subject { Duck.in_shin_pond.rank(:size).to_a }
|
63
63
|
|
64
64
|
its(:size) { should == 3 }
|
65
|
-
|
65
|
+
|
66
66
|
its(:first) { should == @ducks[:quacky] }
|
67
|
-
|
67
|
+
|
68
68
|
its(:last) { should == @ducks[:wingy] }
|
69
69
|
|
70
70
|
end
|
@@ -79,9 +79,9 @@ describe Duck do
|
|
79
79
|
subject { Duck.where(:pond => 'Shin').rank(:age).to_a }
|
80
80
|
|
81
81
|
its(:size) { should == 3 }
|
82
|
-
|
82
|
+
|
83
83
|
its(:first) { should == @ducks[:wingy] }
|
84
|
-
|
84
|
+
|
85
85
|
its(:last) { should == @ducks[:quacky] }
|
86
86
|
|
87
87
|
end
|
@@ -98,9 +98,9 @@ describe Duck do
|
|
98
98
|
subject { Duck.rank(:row).to_a }
|
99
99
|
|
100
100
|
its(:size) { should == 6 }
|
101
|
-
|
101
|
+
|
102
102
|
its(:first) { should == @ducks[:beaky] }
|
103
|
-
|
103
|
+
|
104
104
|
its(:last) { should == @ducks[:wingy] }
|
105
105
|
|
106
106
|
end
|
@@ -117,26 +117,26 @@ describe Duck do
|
|
117
117
|
@ducks[:webby].update_attribute :row_position, 6
|
118
118
|
}
|
119
119
|
|
120
|
-
describe "row" do
|
120
|
+
describe "row" do
|
121
121
|
|
122
122
|
subject { Duck.rank(:row).to_a }
|
123
123
|
|
124
124
|
its(:size) { should == 6 }
|
125
|
-
|
125
|
+
|
126
126
|
its(:first) { should == @ducks[:beaky] }
|
127
|
-
|
127
|
+
|
128
128
|
its(:last) { should == @ducks[:webby] }
|
129
129
|
|
130
130
|
end
|
131
131
|
|
132
|
-
describe "row" do
|
132
|
+
describe "row" do
|
133
133
|
|
134
134
|
subject { Duck.in_shin_pond.rank(:size).to_a }
|
135
135
|
|
136
136
|
its(:size) { should == 3 }
|
137
|
-
|
137
|
+
|
138
138
|
its(:first) { should == @ducks[:quacky] }
|
139
|
-
|
139
|
+
|
140
140
|
its(:last) { should == @ducks[:feathers] }
|
141
141
|
|
142
142
|
end
|
@@ -156,6 +156,27 @@ describe Duck do
|
|
156
156
|
|
157
157
|
end
|
158
158
|
|
159
|
+
describe "changing a related attribute" do
|
160
|
+
|
161
|
+
it "marks record as changed" do
|
162
|
+
duck = Duck.rank(:age)[2]
|
163
|
+
duck.age_position = 1
|
164
|
+
duck.changed?.should be_true
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "setting only truly values" do
|
170
|
+
|
171
|
+
subject { Duck.rank(:age).first }
|
172
|
+
|
173
|
+
it "doesnt set empty string" do
|
174
|
+
subject.age_position = ''
|
175
|
+
subject.age_position.should be_nil
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
159
180
|
describe "setting and fetching by positioning" do
|
160
181
|
|
161
182
|
describe "in the middle" do
|
@@ -178,7 +199,7 @@ describe Duck do
|
|
178
199
|
subject { Duck.rank(:row).collect {|duck| duck.id } }
|
179
200
|
|
180
201
|
it { subject[0..1].should == @ordered[0..1] }
|
181
|
-
|
202
|
+
|
182
203
|
it { subject[3..subject.length].should == @ordered[2..@ordered.length] }
|
183
204
|
|
184
205
|
}
|
@@ -213,7 +234,7 @@ describe Duck do
|
|
213
234
|
subject { Duck.rank(:row).collect {|duck| duck.id } }
|
214
235
|
|
215
236
|
it { subject[1..subject.length].should == @ordered }
|
216
|
-
|
237
|
+
|
217
238
|
}
|
218
239
|
|
219
240
|
end
|
@@ -254,7 +275,7 @@ describe Duck do
|
|
254
275
|
subject { Duck.rank(:row).collect {|duck| duck.id } }
|
255
276
|
|
256
277
|
it { subject[0..-2].should == @ordered }
|
257
|
-
|
278
|
+
|
258
279
|
}
|
259
280
|
|
260
281
|
end
|
@@ -295,11 +316,11 @@ describe Duck do
|
|
295
316
|
subject { Duck.rank(:row).collect {|duck| duck.id } }
|
296
317
|
|
297
318
|
it { subject[0..-2].should == @ordered }
|
298
|
-
|
319
|
+
|
299
320
|
}
|
300
321
|
|
301
322
|
end
|
302
|
-
|
323
|
+
|
303
324
|
describe "at the end with string" do
|
304
325
|
|
305
326
|
before {
|
@@ -336,11 +357,335 @@ describe Duck do
|
|
336
357
|
subject { Duck.rank(:row).collect {|duck| duck.id } }
|
337
358
|
|
338
359
|
it { subject[0..-2].should == @ordered }
|
339
|
-
|
360
|
+
|
340
361
|
}
|
341
362
|
|
342
363
|
end
|
343
364
|
|
365
|
+
describe "down with symbol" do
|
366
|
+
|
367
|
+
context "when in the middle" do
|
368
|
+
|
369
|
+
before {
|
370
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
371
|
+
@ducks[:wingy].update_attribute :row_position, :down
|
372
|
+
}
|
373
|
+
|
374
|
+
context {
|
375
|
+
|
376
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(4).instance }
|
377
|
+
|
378
|
+
its(:id) { should == @ducks[:wingy].id }
|
379
|
+
|
380
|
+
}
|
381
|
+
|
382
|
+
context {
|
383
|
+
|
384
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
385
|
+
|
386
|
+
it { subject[0..3].should == @ordered[0..3] }
|
387
|
+
|
388
|
+
it { subject[5..subject.length].should == @ordered[4..@ordered.length] }
|
389
|
+
|
390
|
+
}
|
391
|
+
|
392
|
+
end
|
393
|
+
|
394
|
+
context "when last" do
|
395
|
+
|
396
|
+
before {
|
397
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:quacky].id).collect { |duck| duck.id }
|
398
|
+
@ducks[:quacky].update_attribute :row_position, :down
|
399
|
+
}
|
400
|
+
|
401
|
+
context {
|
402
|
+
|
403
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 1).instance }
|
404
|
+
|
405
|
+
its(:id) { should == @ducks[:quacky].id }
|
406
|
+
|
407
|
+
}
|
408
|
+
|
409
|
+
context {
|
410
|
+
|
411
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
412
|
+
|
413
|
+
it { subject[0..-2].should eq(@ordered) }
|
414
|
+
|
415
|
+
}
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
context "when second last" do
|
420
|
+
|
421
|
+
before {
|
422
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:feathers].id).collect { |duck| duck.id }
|
423
|
+
@ducks[:feathers].update_attribute :row_position, :down
|
424
|
+
}
|
425
|
+
|
426
|
+
context {
|
427
|
+
|
428
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 1).instance }
|
429
|
+
|
430
|
+
its(:id) { should == @ducks[:feathers].id }
|
431
|
+
|
432
|
+
}
|
433
|
+
|
434
|
+
context {
|
435
|
+
|
436
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
437
|
+
|
438
|
+
it { subject[0..-2].should eq(@ordered) }
|
439
|
+
|
440
|
+
}
|
441
|
+
|
442
|
+
end
|
443
|
+
|
444
|
+
end
|
445
|
+
|
446
|
+
describe "down with string" do
|
447
|
+
|
448
|
+
context "when in the middle" do
|
449
|
+
|
450
|
+
before {
|
451
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
452
|
+
@ducks[:wingy].update_attribute :row_position, 'down'
|
453
|
+
}
|
454
|
+
|
455
|
+
context {
|
456
|
+
|
457
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(4).instance }
|
458
|
+
|
459
|
+
its(:id) { should == @ducks[:wingy].id }
|
460
|
+
|
461
|
+
}
|
462
|
+
|
463
|
+
context {
|
464
|
+
|
465
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
466
|
+
|
467
|
+
it { subject[0..3].should == @ordered[0..3] }
|
468
|
+
|
469
|
+
it { subject[5..subject.length].should == @ordered[4..@ordered.length] }
|
470
|
+
|
471
|
+
}
|
472
|
+
|
473
|
+
end
|
474
|
+
|
475
|
+
context "when last" do
|
476
|
+
|
477
|
+
before {
|
478
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:quacky].id).collect { |duck| duck.id }
|
479
|
+
@ducks[:quacky].update_attribute :row_position, 'down'
|
480
|
+
}
|
481
|
+
|
482
|
+
context {
|
483
|
+
|
484
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 1).instance }
|
485
|
+
|
486
|
+
its(:id) { should == @ducks[:quacky].id }
|
487
|
+
|
488
|
+
}
|
489
|
+
|
490
|
+
context {
|
491
|
+
|
492
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
493
|
+
|
494
|
+
it { subject[0..-2].should eq(@ordered) }
|
495
|
+
|
496
|
+
}
|
497
|
+
|
498
|
+
end
|
499
|
+
|
500
|
+
context "when second last" do
|
501
|
+
|
502
|
+
before {
|
503
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:feathers].id).collect { |duck| duck.id }
|
504
|
+
@ducks[:feathers].update_attribute :row_position, 'down'
|
505
|
+
}
|
506
|
+
|
507
|
+
context {
|
508
|
+
|
509
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 1).instance }
|
510
|
+
|
511
|
+
its(:id) { should == @ducks[:feathers].id }
|
512
|
+
|
513
|
+
}
|
514
|
+
|
515
|
+
context {
|
516
|
+
|
517
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
518
|
+
|
519
|
+
it { subject[0..-2].should eq(@ordered) }
|
520
|
+
|
521
|
+
}
|
522
|
+
|
523
|
+
end
|
524
|
+
|
525
|
+
end
|
526
|
+
|
527
|
+
describe "up with symbol" do
|
528
|
+
|
529
|
+
context "when in the middle" do
|
530
|
+
|
531
|
+
before {
|
532
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
533
|
+
@ducks[:wingy].update_attribute :row_position, :up
|
534
|
+
}
|
535
|
+
|
536
|
+
context {
|
537
|
+
|
538
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(2).instance }
|
539
|
+
|
540
|
+
its(:id) { should == @ducks[:wingy].id }
|
541
|
+
|
542
|
+
}
|
543
|
+
|
544
|
+
context {
|
545
|
+
|
546
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
547
|
+
|
548
|
+
it { subject[0..1].should == @ordered[0..1] }
|
549
|
+
|
550
|
+
it { subject[3..subject.length].should == @ordered[2..@ordered.length] }
|
551
|
+
|
552
|
+
}
|
553
|
+
|
554
|
+
end
|
555
|
+
|
556
|
+
context "when first" do
|
557
|
+
|
558
|
+
before {
|
559
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:beaky].id).collect { |duck| duck.id }
|
560
|
+
@ducks[:beaky].update_attribute :row_position, :up
|
561
|
+
}
|
562
|
+
|
563
|
+
context {
|
564
|
+
|
565
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(0).instance }
|
566
|
+
|
567
|
+
its(:id) { should == @ducks[:beaky].id }
|
568
|
+
|
569
|
+
}
|
570
|
+
|
571
|
+
context {
|
572
|
+
|
573
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
574
|
+
|
575
|
+
it { subject[1..subject.length].should eq(@ordered) }
|
576
|
+
|
577
|
+
}
|
578
|
+
|
579
|
+
end
|
580
|
+
|
581
|
+
context "when second" do
|
582
|
+
|
583
|
+
before {
|
584
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:waddly].id).collect { |duck| duck.id }
|
585
|
+
@ducks[:waddly].update_attribute :row_position, :up
|
586
|
+
}
|
587
|
+
|
588
|
+
context {
|
589
|
+
|
590
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(0).instance }
|
591
|
+
|
592
|
+
its(:id) { should == @ducks[:waddly].id }
|
593
|
+
|
594
|
+
}
|
595
|
+
|
596
|
+
context {
|
597
|
+
|
598
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
599
|
+
|
600
|
+
it { subject[1..subject.length].should eq(@ordered) }
|
601
|
+
|
602
|
+
}
|
603
|
+
|
604
|
+
end
|
605
|
+
|
606
|
+
end
|
607
|
+
|
608
|
+
describe "up with string" do
|
609
|
+
|
610
|
+
context "when in the middle" do
|
611
|
+
|
612
|
+
before {
|
613
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect { |duck| duck.id }
|
614
|
+
@ducks[:wingy].update_attribute :row_position, 'up'
|
615
|
+
}
|
616
|
+
|
617
|
+
context {
|
618
|
+
|
619
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(2).instance }
|
620
|
+
|
621
|
+
its(:id) { should == @ducks[:wingy].id }
|
622
|
+
|
623
|
+
}
|
624
|
+
|
625
|
+
context {
|
626
|
+
|
627
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
628
|
+
|
629
|
+
it { subject[0..1].should == @ordered[0..1] }
|
630
|
+
|
631
|
+
it { subject[3..subject.length].should == @ordered[2..@ordered.length] }
|
632
|
+
|
633
|
+
}
|
634
|
+
|
635
|
+
end
|
636
|
+
|
637
|
+
context "when first" do
|
638
|
+
|
639
|
+
before {
|
640
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:beaky].id).collect { |duck| duck.id }
|
641
|
+
@ducks[:beaky].update_attribute :row_position, 'up'
|
642
|
+
}
|
643
|
+
|
644
|
+
context {
|
645
|
+
|
646
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(0).instance }
|
647
|
+
|
648
|
+
its(:id) { should == @ducks[:beaky].id }
|
649
|
+
|
650
|
+
}
|
651
|
+
|
652
|
+
context {
|
653
|
+
|
654
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
655
|
+
|
656
|
+
it { subject[1..subject.length].should eq(@ordered) }
|
657
|
+
|
658
|
+
}
|
659
|
+
|
660
|
+
end
|
661
|
+
|
662
|
+
context "when second" do
|
663
|
+
|
664
|
+
before {
|
665
|
+
@ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:waddly].id).collect { |duck| duck.id }
|
666
|
+
@ducks[:waddly].update_attribute :row_position, 'up'
|
667
|
+
}
|
668
|
+
|
669
|
+
context {
|
670
|
+
|
671
|
+
subject { Duck.ranker(:row).with(Duck.new).current_at_position(0).instance }
|
672
|
+
|
673
|
+
its(:id) { should == @ducks[:waddly].id }
|
674
|
+
|
675
|
+
}
|
676
|
+
|
677
|
+
context {
|
678
|
+
|
679
|
+
subject { Duck.rank(:row).collect { |duck| duck.id } }
|
680
|
+
|
681
|
+
it { subject[1..subject.length].should eq(@ordered) }
|
682
|
+
|
683
|
+
}
|
684
|
+
|
685
|
+
end
|
686
|
+
|
687
|
+
end
|
688
|
+
|
344
689
|
end
|
345
690
|
|
346
691
|
end
|
@@ -392,9 +737,9 @@ describe Duck do
|
|
392
737
|
subject { Duck.in_lake_and_flock(0,0).rank(:landing_order).to_a }
|
393
738
|
|
394
739
|
its(:size) { should == 3 }
|
395
|
-
|
740
|
+
|
396
741
|
its(:first) { should == @ducks[:quacky] }
|
397
|
-
|
742
|
+
|
398
743
|
its(:last) { should == @ducks[:feathers] }
|
399
744
|
|
400
745
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Player do
|
4
|
+
|
5
|
+
before {
|
6
|
+
@players = {}
|
7
|
+
@players[:dave] = Player.create!(:name => "Dave", :city => "Detroit")
|
8
|
+
@players[:bob] = Player.create!(:name => "Bob", :city => "Portland")
|
9
|
+
@players[:nigel] = Player.create!(:name => "Nigel", :city => "New York")
|
10
|
+
Player.class_eval do
|
11
|
+
include RankedModel
|
12
|
+
ranks :score
|
13
|
+
end
|
14
|
+
|
15
|
+
}
|
16
|
+
|
17
|
+
describe "setting the position of a record that already exists" do
|
18
|
+
it "sets the rank without error" do
|
19
|
+
expect{@players[:bob].update_attributes! :score_position => 1}.to_not raise_error
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler/setup'
|
3
|
-
require 'database_cleaner'
|
4
3
|
|
5
|
-
require 'ranked-model'
|
4
|
+
require 'ranked-model'
|
6
5
|
|
7
6
|
Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each {|f| require f}
|
8
7
|
|
8
|
+
# After the DB connection is setup
|
9
|
+
require 'database_cleaner'
|
10
|
+
|
9
11
|
RSpec.configure do |config|
|
10
12
|
config.mock_with :mocha
|
11
13
|
|
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'active_record'
|
2
|
-
require 'sqlite3'
|
3
2
|
require 'logger'
|
4
3
|
|
5
4
|
ROOT = File.join(File.dirname(__FILE__), '..')
|
6
5
|
|
6
|
+
DB_CONFIG = "test" + (ENV['DB'] ? "_#{ENV['DB'].downcase}" : '')
|
7
|
+
|
7
8
|
ActiveRecord::Base.logger = Logger.new('tmp/ar_debug.log')
|
8
9
|
ActiveRecord::Base.configurations = YAML::load(IO.read('spec/support/database.yml'))
|
9
|
-
ActiveRecord::Base.establish_connection(
|
10
|
+
ActiveRecord::Base.establish_connection(DB_CONFIG)
|
10
11
|
|
11
12
|
ActiveRecord::Schema.define :version => 0 do
|
12
13
|
create_table :ducks, :force => true do |t|
|
@@ -49,6 +50,12 @@ ActiveRecord::Schema.define :version => 0 do
|
|
49
50
|
t.string :name
|
50
51
|
t.integer :size
|
51
52
|
end
|
53
|
+
|
54
|
+
create_table :players, :force => true do |t|
|
55
|
+
t.string :name
|
56
|
+
t.string :city
|
57
|
+
t.integer :score
|
58
|
+
end
|
52
59
|
end
|
53
60
|
|
54
61
|
class Duck < ActiveRecord::Base
|
@@ -129,3 +136,7 @@ class Ego < ActiveRecord::Base
|
|
129
136
|
include RankedModel
|
130
137
|
ranks :size
|
131
138
|
end
|
139
|
+
|
140
|
+
class Player < ActiveRecord::Base
|
141
|
+
# don't add rank yet, do it in the specs
|
142
|
+
end
|
data/spec/support/database.yml
CHANGED
@@ -1,5 +1,30 @@
|
|
1
|
-
|
1
|
+
test:
|
2
2
|
adapter: sqlite3
|
3
3
|
database: tmp/data.sqlite3
|
4
4
|
pool: 5
|
5
5
|
timeout: 5000
|
6
|
+
|
7
|
+
test_mysql:
|
8
|
+
adapter: mysql
|
9
|
+
database: ranked_model_test
|
10
|
+
pool: 5
|
11
|
+
timeout: 5000
|
12
|
+
|
13
|
+
test_postgresql:
|
14
|
+
adapter: postgresql
|
15
|
+
database: ranked_model_test
|
16
|
+
pool: 5
|
17
|
+
timeout: 5000
|
18
|
+
|
19
|
+
test_mysql_travis:
|
20
|
+
adapter: mysql
|
21
|
+
database: ranked_model_test
|
22
|
+
pool: 5
|
23
|
+
timeout: 5000
|
24
|
+
username: travis
|
25
|
+
|
26
|
+
test_postgresql_travis:
|
27
|
+
adapter: postgresql
|
28
|
+
database: ranked_model_test
|
29
|
+
pool: 5
|
30
|
+
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:
|
4
|
+
version: 0.4.0
|
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: 2014-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - ~>
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 1.0
|
89
|
+
version: 1.2.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - ~>
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 1.0
|
96
|
+
version: 1.2.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -133,6 +133,7 @@ files:
|
|
133
133
|
- spec/duck-model/lots_of_ducks_spec.rb
|
134
134
|
- spec/duck-model/wrong_ducks_spec.rb
|
135
135
|
- spec/ego-model/ego_spec.rb
|
136
|
+
- spec/player-model/records_already_exist_spec.rb
|
136
137
|
- spec/ranked-model/ranker_spec.rb
|
137
138
|
- spec/ranked-model/version_spec.rb
|
138
139
|
- spec/spec_helper.rb
|
@@ -142,7 +143,8 @@ files:
|
|
142
143
|
- spec/support/database.yml
|
143
144
|
- tmp/.gitignore
|
144
145
|
homepage: https://github.com/mixonic/ranked-model
|
145
|
-
licenses:
|
146
|
+
licenses:
|
147
|
+
- MIT
|
146
148
|
metadata: {}
|
147
149
|
post_install_message:
|
148
150
|
rdoc_options: []
|
@@ -169,6 +171,7 @@ test_files:
|
|
169
171
|
- spec/duck-model/lots_of_ducks_spec.rb
|
170
172
|
- spec/duck-model/wrong_ducks_spec.rb
|
171
173
|
- spec/ego-model/ego_spec.rb
|
174
|
+
- spec/player-model/records_already_exist_spec.rb
|
172
175
|
- spec/ranked-model/ranker_spec.rb
|
173
176
|
- spec/ranked-model/version_spec.rb
|
174
177
|
- spec/spec_helper.rb
|