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 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