ranked-model-rails2 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 279b6761e2dd5ecf3adff73676890df2d2a16ad5
4
+ data.tar.gz: 296a49802a22f29242ff84159f77c20bdc809064
5
+ SHA512:
6
+ metadata.gz: 33bc41b5ac0209bd59d9220b489622a93663abd11156eeda31ccb3705b54f433f33924f1e86a28046d5024e90475fb51ef555913418052b0d284a45d9ed96ab7
7
+ data.tar.gz: 428bde2434df014565a085c1abcb1e6020059eb546f91fdc027c58a52ce0bcbe77d017f98a54d231799c437f307337aa709bd5383998aef91f77d2897c093fdd
@@ -0,0 +1,6 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ *.un~
5
+
6
+ Gemfile.lock
@@ -0,0 +1 @@
1
+ JRUBY_OPTS=--1.8
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
@@ -0,0 +1 @@
1
+ ranked-model
@@ -0,0 +1 @@
1
+ jruby-1.7.11
@@ -0,0 +1,82 @@
1
+ language: ruby
2
+ before_script:
3
+ - mysql -e 'create database ranked_model_test;'
4
+ - psql -c 'create database ranked_model_test;' -U postgres
5
+ rvm:
6
+ - "1.9.2"
7
+ - "1.9.3"
8
+ - "2.0.0"
9
+ - "2.1.0"
10
+ - "jruby-19mode"
11
+ - "rbx"
12
+ env:
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"
22
+ - "ACTIVERECORD_VERSION=3.1.12"
23
+ - "ACTIVERECORD_VERSION=3.1.12 DB=mysql_travis"
24
+ - "ACTIVERECORD_VERSION=3.1.12 DB=postgresql_travis"
25
+ - "ACTIVERECORD_VERSION=master"
26
+ - "ACTIVERECORD_VERSION=master DB=mysql_travis"
27
+ - "ACTIVERECORD_VERSION=master DB=postgresql_travis"
28
+ matrix:
29
+ exclude:
30
+ - env: "ACTIVERECORD_VERSION=4.1.0.beta1"
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"
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"
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"
72
+ allow_failures:
73
+ # Master may or may not pass
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 ADDED
@@ -0,0 +1,32 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ranked-model.gemspec
4
+ gemspec
5
+
6
+ ar_version = ENV["ACTIVERECORD_VERSION"] || "default"
7
+
8
+ ar_gem_version = case ar_version
9
+ when "master"
10
+ gem "activerecord", {:github => "rails/rails"}
11
+ when "default"
12
+ # Allow the gemspec to specify
13
+ else
14
+ gem "activerecord", "~> #{ar_version}"
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/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Iridesco LLC (support@harvestapp.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ task :default => :spec
@@ -0,0 +1,205 @@
1
+ **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.
2
+
3
+ [![Build Status](https://travis-ci.org/mixonic/ranked-model.png)](https://travis-ci.org/mixonic/ranked-model)
4
+
5
+ Installation
6
+ ------------
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
+
12
+ To install ranked-model, just add it to your `Gemfile`:
13
+
14
+ ``` ruby
15
+ gem 'ranked-model'
16
+
17
+ # Or pin ranked-model to git
18
+ # gem 'ranked-model',
19
+ # :git => 'git@github.com:mixonic/ranked-model.git'
20
+ ```
21
+
22
+ Then use `bundle install` to update your `Gemfile.lock`.
23
+
24
+ Simple Use
25
+ ----------
26
+
27
+ Use of ranked-model is straight ahead. Get some ducks:
28
+
29
+ ``` ruby
30
+ class Duck < ActiveRecord::Base
31
+ end
32
+ ```
33
+
34
+ Put your ducks in a row:
35
+
36
+ ``` ruby
37
+ class Duck < ActiveRecord::Base
38
+
39
+ include RankedModel
40
+ ranks :row_order
41
+
42
+ end
43
+ ```
44
+
45
+ This simple example assumes an integer column called `row_order`. To order Ducks by this order:
46
+
47
+ ``` ruby
48
+ Duck.rank(:row_order).all
49
+ ```
50
+
51
+ The ranking integers stored in the `row_order` column will be big and spaced apart. When you
52
+ implement a sorting UI, just update the resource by appending the column name with `_position` and indicating the desired position:
53
+
54
+ ``` ruby
55
+ @duck.update_attribute :row_order_position, 0 # or 1, 2, 37. :first, :last, :up and :down are also valid
56
+ ```
57
+
58
+ Position numbers begin at zero. A position number greater than the number of records acts the
59
+ same as :last. :up and :down move the record up/down the ladder by one step.
60
+
61
+ So using a normal json controller where `@duck.attributes = params[:duck]; @duck.save`, JS can
62
+ look pretty elegant:
63
+
64
+ ``` javascript
65
+ $.ajax({
66
+ type: 'PUT',
67
+ url: '/ducks',
68
+ dataType: 'json',
69
+ data: { duck: { row_order_position: 0 } }, // or whatever your new position is
70
+ });
71
+ ```
72
+
73
+ Complex Use
74
+ -----------
75
+
76
+ The `ranks` method takes serveral arguments:
77
+
78
+ ``` ruby
79
+ class Duck < ActiveRecord::Base
80
+
81
+ include RankedModel
82
+
83
+ ranks :row_order, # Name this ranker, used with rank()
84
+ :column => :sort_order # Override the default column, which defaults to the name
85
+
86
+ belongs_to :pond
87
+ ranks :swimming_order,
88
+ :with_same => :pond_id # Ducks belong_to Ponds, make the ranker scoped to one pond
89
+
90
+ scope :walking, where(:walking => true )
91
+ ranks :walking_order,
92
+ :scope => :walking # Narrow this ranker to a scope
93
+
94
+ end
95
+ ```
96
+
97
+ When you make a query, add the rank:
98
+
99
+ ``` ruby
100
+ Duck.rank(:row_order)
101
+
102
+ Pond.first.ducks.rank(:swimming_order)
103
+
104
+ Duck.walking.rank(:walking)
105
+ ```
106
+
107
+ Single Table Inheritance (STI)
108
+ ------------------------------
109
+
110
+ ranked-model scopes your records' positions based on the class name of the object. If you have
111
+ a STI `type` column set in your model, ranked-model will reference that class for positioning.
112
+
113
+ Consider this example:
114
+
115
+ ``` ruby
116
+ class Vehicle < ActiveRecord::Base
117
+ ranks :row_order
118
+ end
119
+
120
+ class Car < Vehicle
121
+ end
122
+
123
+ class Truck < Vehicle
124
+ end
125
+
126
+ car = Car.create!
127
+ truck = Truck.create!
128
+
129
+ car.row_order
130
+ => 0
131
+ truck.row_order
132
+ => 0
133
+ ```
134
+
135
+ In this example, the `row_order` for both `car` and `truck` will be set to `0` because they have
136
+ different class names (`Car` and `Truck`, respectively).
137
+
138
+ If you would like for both `car` and `truck` to be ranked together based on the base `Vehicle`
139
+ class instead, use the `class_name` option:
140
+
141
+ ``` ruby
142
+ class Vehicle < ActiveRecord::Base
143
+ ranks :row_order, class_name: 'Vehicle'
144
+ end
145
+
146
+ class Car < Vehicle
147
+ end
148
+
149
+ class Truck < Vehicle
150
+ end
151
+
152
+ car = Car.create!
153
+ truck = Truck.create!
154
+
155
+ car.row_order
156
+ => 0
157
+ truck.row_order
158
+ => 4194304
159
+ ```
160
+
161
+ Internals
162
+ ---------
163
+
164
+ This library is written using ARel from the ground-up. This leaves the code much cleaner
165
+ than many implementations. ranked-model is also optimized to write to the database as little
166
+ as possible: ranks are stored as a number between -8388607 and 8388607 (the MEDIUMINT range in MySQL).
167
+ When an item is given a new position, it assigns itself a rank number between two neighbors.
168
+ This allows several movements of items before no digits are available between two neighbors. When
169
+ this occurs, ranked-model will try to shift other records out of the way. If items can't be easily
170
+ shifted anymore, it will rebalance the distribution of rank numbers across all members
171
+ of the ranked group.
172
+
173
+ Contributing
174
+ ------------
175
+
176
+ Fork, clone, write a test, write some code, commit, push, send a pull request. Github FTW!
177
+
178
+ The specs can be run with sqlite, postgres, and mysql:
179
+
180
+ ```
181
+ DB=postgres bundle exec rake
182
+ ```
183
+
184
+ Is no DB is specified, the tests run against sqlite.
185
+
186
+ RankedModel is mostly the handiwork of Matthew Beale:
187
+
188
+ * [madhatted.com](http://madhatted.com) is where I blog. Also [@mixonic](http://twitter.com/mixonic).
189
+
190
+ A hearty thanks to these contributors:
191
+
192
+ * [Harvest](http://getharvest.com) where this Gem started. They are great, great folks.
193
+ * [yabawock](https://github.com/yabawock)
194
+ * [AndrewRadev](https://github.com/AndrewRadev)
195
+ * [adheerajkumar](https://github.com/adheerajkumar)
196
+ * [mikeycgto](https://github.com/mikeycgto)
197
+ * [robotex82](https://github.com/robotex82)
198
+ * [rociiu](https://github.com/rociiu)
199
+ * [codepodu](https://github.com/codepodu)
200
+ * [kakra](https://github.com/kakra)
201
+ * [metalon](https://github.com/metalon)
202
+ * [jamesalmond](https://github.com/jamesalmond)
203
+ * [jguyon](https://github.com/jguyon)
204
+ * [pehrlich](https://github.com/pehrlich)
205
+ * [petergoldstein](https://github.com/petergoldstein)
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__)+'/ranked-model/ranker'
2
+ require File.dirname(__FILE__)+'/ranked-model/railtie' if defined?(Rails::Railtie)
3
+
4
+ require "active_record" unless defined?(ActiveRecord::Base)
5
+ require "fake_arel"
6
+
7
+ module RankedModel
8
+
9
+ # Signed MEDIUMINT in MySQL
10
+ #
11
+ MAX_RANK_VALUE = 8388607
12
+ MIN_RANK_VALUE = -8388607
13
+
14
+ def self.included base
15
+
16
+ base.class_eval do
17
+ class_attribute :rankers
18
+
19
+ extend RankedModel::ClassMethods
20
+
21
+ before_save :handle_ranking
22
+
23
+ scope :rank, lambda { |name|
24
+ order ranker(name.to_sym).column
25
+ }
26
+ end
27
+
28
+ end
29
+
30
+ private
31
+
32
+ def handle_ranking
33
+ self.class.rankers.each do |ranker|
34
+ ranker.with(self).handle_ranking
35
+ end
36
+ end
37
+
38
+ module ClassMethods
39
+
40
+ def q(column_name)
41
+ connection.quote_column_name(column_name)
42
+ end
43
+
44
+ def ranker name
45
+ rankers.find do |ranker|
46
+ ranker.name == name
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def ranks *args
53
+ self.rankers ||= []
54
+ ranker = RankedModel::Ranker.new(*args)
55
+ self.rankers << ranker
56
+ attr_reader "#{ranker.name}_position"
57
+ define_method "#{ranker.name}_position=" do |position|
58
+ if position.present?
59
+ send "#{ranker.column}_will_change!"
60
+ instance_variable_set "@#{ranker.name}_position", position
61
+ end
62
+ end
63
+
64
+ public "#{ranker.name}_position", "#{ranker.name}_position="
65
+ end
66
+
67
+ end
68
+
69
+ end