ranked-model 0.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dcff2571a186c754b8b163ed3d80709dcaaf81c8
4
- data.tar.gz: c51f12aea789f0b771cac3d84d0fee6d3524607e
3
+ metadata.gz: 116f5fc0aeafe231cddba562c5135e868ba06604
4
+ data.tar.gz: c720ee45d4532e3c96fb2d829788a2c43cdd7f8f
5
5
  SHA512:
6
- metadata.gz: 7b91a45c679724c3625e587a8118441f53de967e2c614e00bcd76f8103bc4007d2e1149dc159222c4c4839b7ffa86ab3f03f9e8d42e7863d3840bf7dad40a1d5
7
- data.tar.gz: 46e6dca0c5f42e0b11a7e32b8f152675a336e15061657676ae32c80cfee4c8fffda441740bc66729c4f0daff19851be18ccdcd9c84e0b40eb8c2c551b522fef6
6
+ metadata.gz: 00ca1355412ab2cc161edc2831bc685862f4996b33903a7bf5a65077e2a638e2f385d665db6e20bdfe50f59d93c8517cdf68c8e694a325680c4a2314e4adb677
7
+ data.tar.gz: 72dbeebd7402e29238fb7219c0037f04ba588dba79e06ecbabf6ee9962cee67131b0b96246945639271b7585c63ebf6edb09274c7ba779ca864a336ba576af72
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ *.un~
4
5
 
5
6
  Gemfile.lock
@@ -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.0"
8
- - "ACTIVERECORD_VERSION=3.2.13"
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.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 instead:
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 :last are also valid
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)
@@ -46,7 +46,14 @@ module RankedModel
46
46
  self.rankers ||= []
47
47
  ranker = RankedModel::Ranker.new(*args)
48
48
  self.rankers << ranker
49
- attr_accessor "#{ranker.name}_position"
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
 
@@ -64,7 +64,9 @@ module RankedModel
64
64
  def update_rank! value
65
65
  # Bypass callbacks
66
66
  #
67
- instance_class.where(instance_class.primary_key => instance.id).update_all ["#{ranker.column} = ?", value]
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( "#{ranker.column} = #{ranker.column} - 1" )
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( "#{ranker.column} = #{ranker.column} + 1" )
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( "#{ranker.column} = #{ranker.column} - 1" )
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].asc).select(columns)
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
@@ -1,3 +1,3 @@
1
1
  module RankedModel
2
- VERSION = "0.3"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -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.1"
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
@@ -1,11 +1,13 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
- require 'database_cleaner'
4
3
 
5
- require 'ranked-model' # and any other gems you need
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('development')
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
@@ -1,5 +1,30 @@
1
- development:
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: '0.3'
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: 2013-07-18 00:00:00.000000000 Z
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.1
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.1
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