ranked-model-rails2 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.
@@ -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