ranked-model 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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