ranked-model 0.0.5 → 0.1.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.
data/Readme.mkd CHANGED
@@ -5,11 +5,13 @@ Installation
5
5
 
6
6
  To install ranked-model, just add it to your `Gemfile`:
7
7
 
8
- gem 'ranked-model'
9
-
10
- # Or pin ranked-model to git
11
- # gem 'ranked-model',
12
- # :git => 'git@github.com:harvesthq/ranked-model.git'
8
+ ``` ruby
9
+ gem 'ranked-model'
10
+
11
+ # Or pin ranked-model to git
12
+ # gem 'ranked-model',
13
+ # :git => 'git@github.com:mixonic/ranked-model.git'
14
+ ```
13
15
 
14
16
  Then use `bundle install` to update your `Gemfile.lock`.
15
17
 
@@ -18,66 +20,83 @@ Simple Use
18
20
 
19
21
  Use of ranked-model is straight ahead. Get some ducks:
20
22
 
21
- class Duck < ActiveRecord::Base
22
- end
23
+ ``` ruby
24
+ class Duck < ActiveRecord::Base
25
+ end
26
+ ```
23
27
 
24
28
  Put your ducks in a row:
25
29
 
26
- class Duck < ActiveRecord::Base
27
-
28
- include RankedModel
29
- ranks :row_order
30
-
31
- end
30
+ ``` ruby
31
+ class Duck < ActiveRecord::Base
32
+
33
+ include RankedModel
34
+ ranks :row_order
35
+
36
+ end
37
+ ```
32
38
 
33
39
  This simple example assumes an integer column called `row_order`. To order Ducks by this order:
34
40
 
35
- Duck.rank(:row_order).all
41
+ ``` ruby
42
+ Duck.rank(:row_order).all
43
+ ```
36
44
 
37
45
  The ranking integers stored in the `row_order` column will be big and spaced apart. When you
38
46
  implement a sorting UI, just update the resource with the position instead:
39
47
 
40
- @duck.update_attribute :row_order_position, 0 # or 1, 2, 37. :first and :last are also valid
48
+ ``` ruby
49
+ @duck.update_attribute :row_order_position, 0 # or 1, 2, 37. :first and :last are also valid
50
+ ```
51
+
52
+ Position numbers begin at zero. A position number greater than the number of records acts the
53
+ same as :last.
41
54
 
42
55
  So using a normal json controller where `@duck.attributes = params[:duck]; @duck.save`, JS can
43
56
  look pretty elegant:
44
57
 
45
- $.ajax({
46
- type: 'PUT',
47
- url: '/ducks',
48
- dataType: 'json',
49
- data: { duck: { row_order_position: 0 } }, // or whatever your new position is
50
- });
58
+ ``` javascript
59
+ $.ajax({
60
+ type: 'PUT',
61
+ url: '/ducks',
62
+ dataType: 'json',
63
+ data: { duck: { row_order_position: 0 } }, // or whatever your new position is
64
+ });
65
+ ```
51
66
 
52
67
  Complex Use
53
68
  -----------
54
69
 
55
70
  The `ranks` method takes serveral arguments:
56
71
 
57
- class Duck < ActiveRecord::Base
58
-
59
- include RankedModel
60
-
61
- ranks :row_order, # Name this ranker, used with rank()
62
- :column => :sort_order # Override the default column, which defaults to the name
63
-
64
- belongs_to :pond
65
- ranks :swimming_order,
66
- :with_same => :pond_id # Ducks belong_to Ponds, make the ranker scoped to one pond
67
-
68
- scope :walking, where(:walking => true )
69
- ranks :walking_order,
70
- :scope => :walking # Narrow this ranker to a scope
71
-
72
- end
72
+ ``` ruby
73
+ class Duck < ActiveRecord::Base
74
+
75
+ include RankedModel
76
+
77
+ ranks :row_order, # Name this ranker, used with rank()
78
+ :column => :sort_order # Override the default column, which defaults to the name
79
+
80
+ belongs_to :pond
81
+ ranks :swimming_order,
82
+ :with_same => :pond_id # Ducks belong_to Ponds, make the ranker scoped to one pond
83
+
84
+ scope :walking, where(:walking => true )
85
+ ranks :walking_order,
86
+ :scope => :walking # Narrow this ranker to a scope
87
+
88
+ end
89
+ ```
73
90
 
74
91
  When you make a query, add the rank:
75
92
 
76
- Duck.rank(:row_order)
77
-
78
- Pond.first.ducks.rank(:swimming_order)
79
-
80
- Duck.walking.rank(:walking)
93
+ ``` ruby
94
+ Duck.rank(:row_order)
95
+
96
+ Pond.first.ducks.rank(:swimming_order)
97
+
98
+ Duck.walking.rank(:walking)
99
+ ```
81
100
 
82
101
  Internals
83
102
  ---------
@@ -96,10 +115,13 @@ Contributing
96
115
 
97
116
  Fork, clone, write a test, write some code, commit, push, send a pull request. Github FTW!
98
117
 
99
- This project was open-sourced by [Harvest](http://getharvest.com/). [We're hiring!](http://www.getharvest.com/careers)
118
+ RankedModel is mostly the handiwork of Matthew Beale:
119
+
120
+ * [madhatted.com](http://madhatted.com) is where I blog. Also [@mixonic](http://twitter.com/mixonic).
121
+ * [Spinto](http://www.spintoapp.com) is a product I'm bootstrapping.
100
122
 
101
123
  A hearty thanks to these contributors:
102
124
 
125
+ * [Harvest](http://getharvest.com) where this Gem started. They are great, great folks.
103
126
  * [yabawock](https://github.com/yabawock)
104
127
  * [AndrewRadev](https://github.com/AndrewRadev/ranked-model)
105
-
@@ -18,7 +18,7 @@ module RankedModel
18
18
  before_save :handle_ranking
19
19
 
20
20
  scope :rank, lambda { |name|
21
- order arel_table[ ranker(name.to_sym).column ]
21
+ order ranker(name.to_sym).column
22
22
  }
23
23
  end
24
24
 
@@ -94,19 +94,19 @@ module RankedModel
94
94
 
95
95
  def update_index_from_position
96
96
  case position
97
- when :first
97
+ when :first, 'first'
98
98
  if current_first && current_first.rank
99
99
  rank_at( ( ( RankedModel::MIN_RANK_VALUE - current_first.rank ).to_f / 2 ).ceil + current_first.rank)
100
100
  else
101
101
  position_at :middle
102
102
  end
103
- when :last
103
+ when :last, 'last'
104
104
  if current_last && current_last.rank
105
105
  rank_at( ( ( RankedModel::MAX_RANK_VALUE - current_last.rank ).to_f / 2 ).ceil + current_last.rank )
106
106
  else
107
107
  position_at :middle
108
108
  end
109
- when :middle
109
+ when :middle, 'middle'
110
110
  rank_at( ( ( RankedModel::MAX_RANK_VALUE - RankedModel::MIN_RANK_VALUE ).to_f / 2 ).ceil + RankedModel::MIN_RANK_VALUE )
111
111
  when String
112
112
  position_at position.to_i
@@ -137,19 +137,21 @@ module RankedModel
137
137
  end
138
138
 
139
139
  def rearrange_ranks
140
+ _scope = finder
141
+ unless instance.id.nil?
142
+ # Never update ourself, shift others around us.
143
+ _scope = _scope.where( instance.class.arel_table[:id].not_eq(instance.id) )
144
+ end
140
145
  if current_first.rank && current_first.rank > RankedModel::MIN_RANK_VALUE && rank == RankedModel::MAX_RANK_VALUE
141
- instance.class.
142
- where( instance.class.arel_table[:id].not_eq(instance.id) ).
146
+ _scope.
143
147
  where( instance.class.arel_table[ranker.column].lteq(rank) ).
144
148
  update_all( "#{ranker.column} = #{ranker.column} - 1" )
145
149
  elsif current_last.rank && current_last.rank < (RankedModel::MAX_RANK_VALUE - 1) && rank < current_last.rank
146
- instance.class.
147
- where( instance.class.arel_table[:id].not_eq(instance.id) ).
150
+ _scope.
148
151
  where( instance.class.arel_table[ranker.column].gteq(rank) ).
149
152
  update_all( "#{ranker.column} = #{ranker.column} + 1" )
150
153
  elsif current_first.rank && current_first.rank > RankedModel::MIN_RANK_VALUE && rank > current_first.rank
151
- instance.class.
152
- where( instance.class.arel_table[:id].not_eq(instance.id) ).
154
+ _scope.
153
155
  where( instance.class.arel_table[ranker.column].lt(rank) ).
154
156
  update_all( "#{ranker.column} = #{ranker.column} - 1" )
155
157
  rank_at( rank - 1 )
@@ -209,7 +211,7 @@ module RankedModel
209
211
  _finder = _finder.where \
210
212
  instance.class.arel_table[:id].not_eq(instance.id)
211
213
  end
212
- _finder.order(instance.class.arel_table[ranker.column].asc).select([:id, ranker.column])
214
+ _finder.order(instance.class.arel_table[ranker.column].asc).select([instance.class.arel_table[:id], instance.class.arel_table[ranker.column]])
213
215
  end
214
216
  end
215
217
 
@@ -1,3 +1,3 @@
1
1
  module RankedModel
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -286,6 +286,47 @@ describe Duck do
286
286
  }
287
287
 
288
288
  end
289
+
290
+ describe "at the end with string" do
291
+
292
+ before {
293
+ @ordered = Duck.rank(:row).where(Duck.arel_table[:id].not_eq @ducks[:wingy].id).collect {|duck| duck.id }
294
+ @ducks[:wingy].update_attribute :row_position, 'last'
295
+ }
296
+
297
+ context {
298
+
299
+ subject { Duck.ranker(:row).with(Duck.new).current_at_position(@ducks.size - 1).instance }
300
+
301
+ its(:id) { should == @ducks[:wingy].id }
302
+
303
+ }
304
+
305
+ context {
306
+
307
+ subject { Duck.rank(:row).last }
308
+
309
+ its(:id) { should == @ducks[:wingy].id }
310
+
311
+ }
312
+
313
+ context {
314
+
315
+ subject { Duck.ranker(:row).with(Duck.new).instance_eval { current_last }.instance }
316
+
317
+ its(:id) { should == @ducks[:wingy].id }
318
+
319
+ }
320
+
321
+ context {
322
+
323
+ subject { Duck.rank(:row).collect {|duck| duck.id } }
324
+
325
+ it { subject[0..-2].should == @ordered }
326
+
327
+ }
328
+
329
+ end
289
330
 
290
331
  end
291
332
 
@@ -87,6 +87,32 @@ describe Duck do
87
87
 
88
88
  end
89
89
 
90
+ describe "with max value and with_same pond" do
91
+
92
+ before {
93
+ Duck.first(50).each_with_index do |d, index|
94
+ d.update_attributes :age => index % 10, :pond => "Pond #{index / 10}"
95
+ end
96
+ @duck_11 = Duck.offset(10).first
97
+ @duck_12 = Duck.offset(11).first
98
+ @ordered = Duck.where(:pond => 'Pond 1').rank(:age).where(Duck.arel_table[:id].not_in([@duck_11.id, @duck_12.id])).collect {|d| d.id }
99
+ @duck_11.update_attribute :age, RankedModel::MAX_RANK_VALUE
100
+ @duck_12.update_attribute :age, RankedModel::MAX_RANK_VALUE
101
+ }
102
+
103
+ context {
104
+ subject { Duck.where(:pond => 'Pond 1').rank(:age).collect {|d| d.id } }
105
+
106
+ it { should == (@ordered[0..-2] + [@ordered[-1], @duck_11.id, @duck_12.id]) }
107
+ }
108
+
109
+ context {
110
+ subject { Duck.first.age }
111
+ it { should == 0}
112
+ }
113
+
114
+ end
115
+
90
116
  describe "with min value" do
91
117
 
92
118
  before {
metadata CHANGED
@@ -1,118 +1,120 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ranked-model
3
- version: !ruby/object:Gem::Version
4
- hash: 21
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 5
10
- version: 0.0.5
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Matthew Beale
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-08-22 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- prerelease: false
22
- requirement: &id001 !ruby/object:Gem::Requirement
12
+ date: 2012-06-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
23
17
  none: false
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- hash: 1
28
- segments:
29
- - 3
30
- - 0
31
- - 3
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
32
21
  version: 3.0.3
33
22
  type: :runtime
34
- version_requirements: *id001
35
- name: activerecord
36
- - !ruby/object:Gem::Dependency
37
23
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
39
25
  none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- hash: 3
44
- segments:
45
- - 0
46
- version: "0"
47
- type: :development
48
- version_requirements: *id002
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.3
30
+ - !ruby/object:Gem::Dependency
49
31
  name: rspec
50
- - !ruby/object:Gem::Dependency
51
- prerelease: false
52
- requirement: &id003 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
53
33
  none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- hash: 3
58
- segments:
59
- - 0
60
- version: "0"
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
61
38
  type: :development
62
- version_requirements: *id003
63
- name: rspec-rails
64
- - !ruby/object:Gem::Dependency
65
39
  prerelease: false
66
- requirement: &id004 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec-rails
48
+ requirement: !ruby/object:Gem::Requirement
67
49
  none: false
68
- requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- hash: 3
72
- segments:
73
- - 0
74
- version: "0"
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
75
54
  type: :development
76
- version_requirements: *id004
77
- name: sqlite3
78
- - !ruby/object:Gem::Dependency
79
55
  prerelease: false
80
- requirement: &id005 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
81
57
  none: false
82
- requirements:
83
- - - ">="
84
- - !ruby/object:Gem::Version
85
- hash: 3
86
- segments:
87
- - 0
88
- version: "0"
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: sqlite3
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
89
70
  type: :development
90
- version_requirements: *id005
91
- name: genspec
92
- - !ruby/object:Gem::Dependency
93
71
  prerelease: false
94
- requirement: &id006 !ruby/object:Gem::Requirement
72
+ version_requirements: !ruby/object:Gem::Requirement
95
73
  none: false
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- hash: 3
100
- segments:
101
- - 0
102
- version: "0"
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: genspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
103
86
  type: :development
104
- version_requirements: *id006
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
105
95
  name: mocha
106
- description: ranked-model is a modern row sorting library built for Rails 3. It uses ARel aggressively and is better optimized than most other libraries.
107
- email:
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: ranked-model is a modern row sorting library built for Rails 3. It uses
111
+ ARel aggressively and is better optimized than most other libraries.
112
+ email:
108
113
  - matt.beale@madhatted.com
109
114
  executables: []
110
-
111
115
  extensions: []
112
-
113
116
  extra_rdoc_files: []
114
-
115
- files:
117
+ files:
116
118
  - .gitignore
117
119
  - .rspec
118
120
  - Gemfile
@@ -137,38 +139,35 @@ files:
137
139
  - tmp/.gitignore
138
140
  homepage: https://github.com/harvesthq/ranked-model
139
141
  licenses: []
140
-
141
142
  post_install_message:
142
143
  rdoc_options: []
143
-
144
- require_paths:
144
+ require_paths:
145
145
  - lib
146
- required_ruby_version: !ruby/object:Gem::Requirement
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
147
  none: false
148
- requirements:
149
- - - ">="
150
- - !ruby/object:Gem::Version
151
- hash: 3
152
- segments:
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ segments:
153
153
  - 0
154
- version: "0"
155
- required_rubygems_version: !ruby/object:Gem::Requirement
154
+ hash: -3628524900195041491
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
156
  none: false
157
- requirements:
158
- - - ">="
159
- - !ruby/object:Gem::Version
160
- hash: 3
161
- segments:
157
+ requirements:
158
+ - - ! '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ segments:
162
162
  - 0
163
- version: "0"
163
+ hash: -3628524900195041491
164
164
  requirements: []
165
-
166
165
  rubyforge_project:
167
- rubygems_version: 1.8.8
166
+ rubygems_version: 1.8.24
168
167
  signing_key:
169
168
  specification_version: 3
170
169
  summary: An acts_as_sortable replacement built for Rails 3
171
- test_files:
170
+ test_files:
172
171
  - spec/duck-model/duck_spec.rb
173
172
  - spec/duck-model/lots_of_ducks_spec.rb
174
173
  - spec/duck-model/wrong_ducks_spec.rb