ar-orderable 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format d
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rails', '>= 3.0.0'
4
+
5
+ group :development do
6
+ gem 'sqlite3', '~> 1.3.4'
7
+ gem 'rspec', '~> 2.6.0'
8
+ gem 'ruby-debug19', '~> 0.11.6'
9
+ gem "bundler", "~> 1.0.0"
10
+ gem "jeweler", "~> 1.6.4"
11
+ gem "rcov", ">= 0"
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,110 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.9)
6
+ actionpack (= 3.0.9)
7
+ mail (~> 2.2.19)
8
+ actionpack (3.0.9)
9
+ activemodel (= 3.0.9)
10
+ activesupport (= 3.0.9)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.5.0)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.14)
16
+ rack-test (~> 0.5.7)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.9)
19
+ activesupport (= 3.0.9)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.5.0)
22
+ activerecord (3.0.9)
23
+ activemodel (= 3.0.9)
24
+ activesupport (= 3.0.9)
25
+ arel (~> 2.0.10)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.9)
28
+ activemodel (= 3.0.9)
29
+ activesupport (= 3.0.9)
30
+ activesupport (3.0.9)
31
+ archive-tar-minitar (0.5.2)
32
+ arel (2.0.10)
33
+ builder (2.1.2)
34
+ columnize (0.3.4)
35
+ diff-lcs (1.1.2)
36
+ erubis (2.6.6)
37
+ abstract (>= 1.0.0)
38
+ git (1.2.5)
39
+ i18n (0.5.0)
40
+ jeweler (1.6.4)
41
+ bundler (~> 1.0)
42
+ git (>= 1.2.5)
43
+ rake
44
+ linecache19 (0.5.12)
45
+ ruby_core_source (>= 0.1.4)
46
+ mail (2.2.19)
47
+ activesupport (>= 2.3.6)
48
+ i18n (>= 0.4.0)
49
+ mime-types (~> 1.16)
50
+ treetop (~> 1.4.8)
51
+ mime-types (1.16)
52
+ polyglot (0.3.2)
53
+ rack (1.2.3)
54
+ rack-mount (0.6.14)
55
+ rack (>= 1.0.0)
56
+ rack-test (0.5.7)
57
+ rack (>= 1.0)
58
+ rails (3.0.9)
59
+ actionmailer (= 3.0.9)
60
+ actionpack (= 3.0.9)
61
+ activerecord (= 3.0.9)
62
+ activeresource (= 3.0.9)
63
+ activesupport (= 3.0.9)
64
+ bundler (~> 1.0)
65
+ railties (= 3.0.9)
66
+ railties (3.0.9)
67
+ actionpack (= 3.0.9)
68
+ activesupport (= 3.0.9)
69
+ rake (>= 0.8.7)
70
+ rdoc (~> 3.4)
71
+ thor (~> 0.14.4)
72
+ rake (0.9.2)
73
+ rcov (0.9.9)
74
+ rdoc (3.9.1)
75
+ rspec (2.6.0)
76
+ rspec-core (~> 2.6.0)
77
+ rspec-expectations (~> 2.6.0)
78
+ rspec-mocks (~> 2.6.0)
79
+ rspec-core (2.6.4)
80
+ rspec-expectations (2.6.0)
81
+ diff-lcs (~> 1.1.2)
82
+ rspec-mocks (2.6.0)
83
+ ruby-debug-base19 (0.11.25)
84
+ columnize (>= 0.3.1)
85
+ linecache19 (>= 0.5.11)
86
+ ruby_core_source (>= 0.1.4)
87
+ ruby-debug19 (0.11.6)
88
+ columnize (>= 0.3.1)
89
+ linecache19 (>= 0.5.11)
90
+ ruby-debug-base19 (>= 0.11.19)
91
+ ruby_core_source (0.1.5)
92
+ archive-tar-minitar (>= 0.5.2)
93
+ sqlite3 (1.3.4)
94
+ thor (0.14.6)
95
+ treetop (1.4.10)
96
+ polyglot
97
+ polyglot (>= 0.3.1)
98
+ tzinfo (0.3.29)
99
+
100
+ PLATFORMS
101
+ ruby
102
+
103
+ DEPENDENCIES
104
+ bundler (~> 1.0.0)
105
+ jeweler (~> 1.6.4)
106
+ rails (>= 3.0.0)
107
+ rcov
108
+ rspec (~> 2.6.0)
109
+ ruby-debug19 (~> 0.11.6)
110
+ sqlite3 (~> 1.3.4)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 IT House, Gatis Tomsons
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Gatis Tomsons
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ ## Activerecord::Orderable
2
+
3
+ Rails 3 plugin for simple ordering.
4
+
5
+ ### Install
6
+
7
+ Insert into your `Gemfile`:
8
+
9
+ gem 'ar-orderable'
10
+
11
+ then `bundle install`
12
+
13
+ ### Setup
14
+
15
+ 1. Add order field, like "order_nr" as integer
16
+ 2. In model add line "acts_as_orderable" and if needed add :column => "my_orderfield_name".
17
+ 3. If your table already has some rows of data then use the 'order_unordered' after adding new column:
18
+
19
+ Example migration:
20
+
21
+ add_column :categories, :order_nr, :integer
22
+ Category.order_unordered # remove this for new table
23
+ add_index :categories, :order_nr
24
+
25
+ ### Examples
26
+
27
+ To reorder items use the `move_to(<integer>)`, `move_up` and `move_down` methods, for example:
28
+
29
+ item.move_to 3 # moved to 3rd position
30
+ item.move_up # moved to 2rd position
31
+ item.move_down # moved to 3d position
32
+
33
+ To skip model callbacks and just update order information you can specify `:skip_callbacks => true` option:
34
+
35
+ # in your model
36
+ acts_as_orderable :skip_callbacks => true
37
+
38
+ # or whenever you call one of the ordering methods
39
+ item.move_to 3, :skip_callbacks => true
40
+ item.move_up :skip_callbacks => true
41
+ item.move_down :skip_callbacks => true
42
+
43
+ ### Tests
44
+
45
+ rspec spec # all examples should be green
46
+
47
+ Copyright (c) 2009 IT House, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "ar-orderable"
18
+ gem.homepage = "http://github.com/ithouse/ar-orderable"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Rails 3 plugin for simple ordering.}
21
+ gem.description = %Q{You can order AR records and skip callbacks}
22
+ gem.email = "gatis@ithouse.cc"
23
+ gem.authors = ["Gatis Tomsons","IT House"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'spec'
31
+ test.pattern = 'spec/**/_spec*.rb'
32
+ test.verbose = true
33
+ test.rcov_opts << '--exclude "gems/*"'
34
+ end
35
+
36
+ require 'rdoc/task'
37
+ Rake::RDocTask.new do |rdoc|
38
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
39
+
40
+ rdoc.rdoc_dir = 'rdoc'
41
+ rdoc.title = "ar-orderable #{version}"
42
+ rdoc.rdoc_files.include('README*')
43
+ rdoc.rdoc_files.include('lib/**/*.rb')
44
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,74 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ar-orderable}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Gatis Tomsons", "IT House"]
12
+ s.date = %q{2011-08-10}
13
+ s.description = %q{You can order AR records and skip callbacks}
14
+ s.email = %q{gatis@ithouse.cc}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "MIT-LICENSE",
26
+ "README.md",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "ar-orderable.gemspec",
30
+ "init.rb",
31
+ "lib/ar-orderable.rb",
32
+ "lib/ar_orderable.rb",
33
+ "spec/ar-orderable_spec.rb",
34
+ "spec/ar_orderable_spec.rb",
35
+ "spec/spec.opts",
36
+ "spec/spec_helper.rb"
37
+ ]
38
+ s.homepage = %q{http://github.com/ithouse/ar-orderable}
39
+ s.licenses = ["MIT"]
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.6.2}
42
+ s.summary = %q{Rails 3 plugin for simple ordering.}
43
+
44
+ if s.respond_to? :specification_version then
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<rails>, [">= 3.0.0"])
49
+ s.add_development_dependency(%q<sqlite3>, ["~> 1.3.4"])
50
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
51
+ s.add_development_dependency(%q<ruby-debug19>, ["~> 0.11.6"])
52
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
54
+ s.add_development_dependency(%q<rcov>, [">= 0"])
55
+ else
56
+ s.add_dependency(%q<rails>, [">= 3.0.0"])
57
+ s.add_dependency(%q<sqlite3>, ["~> 1.3.4"])
58
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
59
+ s.add_dependency(%q<ruby-debug19>, ["~> 0.11.6"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
62
+ s.add_dependency(%q<rcov>, [">= 0"])
63
+ end
64
+ else
65
+ s.add_dependency(%q<rails>, [">= 3.0.0"])
66
+ s.add_dependency(%q<sqlite3>, ["~> 1.3.4"])
67
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
68
+ s.add_dependency(%q<ruby-debug19>, ["~> 0.11.6"])
69
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
70
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
71
+ s.add_dependency(%q<rcov>, [">= 0"])
72
+ end
73
+ end
74
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'ar-orderable'
2
+ ActiveRecord::Base.send(:include, ActiveRecord::Orderable)
@@ -0,0 +1,126 @@
1
+ module ActiveRecord # :nodoc:
2
+ module Orderable # :nodoc:
3
+ def self.included(base) # :nodoc:
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ attr_accessor :orderable_scope, :orderable_column, :skip_callbacks_for_orderable
9
+
10
+ # @:column [string] column name
11
+ # acts_as_orderable :column => "order_nr"
12
+ def acts_as_orderable options = {}
13
+ self.orderable_column = (options[:column] || "order_nr").to_s
14
+ self.skip_callbacks_for_orderable = options[:skip_callbacks]
15
+ if self.columns_hash.keys.include? self.orderable_column
16
+ self.orderable_scope = options[:scope]
17
+ self.before_save :pre_save_ordering
18
+ self.before_destroy :pre_destroy_ordering
19
+ self.default_scope :order => self.orderable_column
20
+ #self.validates_uniqueness_of self.orderable_column, :scope => @orderable_scope
21
+ include ActiveRecord::Orderable::InstanceMethods
22
+ else
23
+ msg = "[IMPORTANT] ActiveRecord::Orderable plugin: class #{self} has missing column '#{self.orderable_column}'"
24
+ puts msg if Rails.env == "development"
25
+ RAILS_DEFAULT_LOGGER.error msg
26
+ end
27
+ end
28
+
29
+ # updates all unordered items puts them into the end of list
30
+ def order_unordered
31
+ self.reset_column_information # because before this usual 'add_column' is executed and the new column isn't fetched yet
32
+ self.group(self.orderable_scope).each do |obj|
33
+ unordered_conditions = "#{self.orderable_column} IS NULL OR #{self.table_name}.#{self.orderable_column} = 0"
34
+ ordered_conditions = "#{self.orderable_column} IS NOT NULL AND #{self.table_name}.#{self.orderable_column} != 0"
35
+ order_nr = obj.all_orderable.order(self.orderable_column).last[self.orderable_column] || 0
36
+ obj.all_orderable.where(unordered_conditions).each do |item|
37
+ order_nr += 1
38
+ raw_orderable_update(item.id, order_nr)
39
+ end
40
+ end
41
+ end
42
+
43
+ def raw_orderable_update id, nr
44
+ self.connection.execute("update #{self.table_name} set #{self.orderable_column} = #{nr.to_i} where #{self.table_name}.id = #{id.to_i};")
45
+ end
46
+ end
47
+
48
+ module InstanceMethods
49
+
50
+ # Moves Item to given position
51
+ def move_to nr, options = {}
52
+ if options[:skip_callbacks].nil? ? self.class.skip_callbacks_for_orderable : options[:skip_callbacks]
53
+ self[self.class.orderable_column] = nr
54
+ self.send(:pre_save_ordering)
55
+ self.class.raw_orderable_update(self.id, nr)
56
+ else
57
+ self.update_attribute(self.class.orderable_column, nr)
58
+ end
59
+ end
60
+
61
+ def move_up options = {}
62
+ move_to(self[self.class.orderable_column] - 1, options) if self[self.class.orderable_column]
63
+ end
64
+
65
+ def move_down options = {}
66
+ move_to(self[self.class.orderable_column] + 1, options) if self[self.class.orderable_column]
67
+ end
68
+
69
+ def all_orderable
70
+ if self.class.orderable_scope
71
+ self.class.where(:"#{self.class.orderable_scope}" => self[self.class.orderable_scope])
72
+ else
73
+ self.class.scoped
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def pre_save_ordering
80
+ self[self.class.orderable_column] = 0 if self[self.class.orderable_column].nil?
81
+ if self.id
82
+ if self[self.class.orderable_column] == 0
83
+ self[self.class.orderable_column] = 1
84
+ end
85
+ if self[self.class.orderable_column] > self.all_orderable.count
86
+ self[self.class.orderable_column] = self[self.class.orderable_column] -1
87
+ end
88
+ else
89
+ self[self.class.orderable_column] = self.all_orderable.count + 1 if self[self.class.orderable_column] == 0
90
+ end
91
+ self.all_orderable.where("#{self.class.table_name}.id != ?",self.id || 0).each do |item|
92
+ item[self.class.orderable_column] = 0 if item[self.class.orderable_column].nil?
93
+ if self.id
94
+ if item[self.class.orderable_column] > (self.send("#{self.class.orderable_column}_was") || 0 )
95
+ if item[self.class.orderable_column] <= self[self.class.orderable_column]
96
+ item[self.class.orderable_column] -= 1
97
+ end
98
+ else
99
+ if item[self.class.orderable_column] >= self[self.class.orderable_column]
100
+ item[self.class.orderable_column] += 1
101
+ end
102
+ end
103
+
104
+ if item[self.class.orderable_column] != item.send("#{self.class.orderable_column}_was")
105
+ self.class.raw_orderable_update(item.id, item[self.class.orderable_column])
106
+ end
107
+ else
108
+ if item[self.class.orderable_column] >= self[self.class.orderable_column]
109
+ item[self.class.orderable_column] += 1
110
+ self.class.raw_orderable_update(item.id, item[self.class.orderable_column])
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ def pre_destroy_ordering
117
+ self.all_orderable.each do |item|
118
+ if item[self.class.orderable_column].to_i > self[self.class.orderable_column].to_i
119
+ item[self.class.orderable_column] -= 1
120
+ self.class.raw_orderable_update(item.id, item[self.class.orderable_column])
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,126 @@
1
+ module ActiveRecord # :nodoc:
2
+ module Orderable # :nodoc:
3
+ def self.included(base) # :nodoc:
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ attr_accessor :orderable_scope, :orderable_column, :skip_callbacks_for_orderable
9
+
10
+ # @:column [string] column name
11
+ # acts_as_orderable :column => "order_nr"
12
+ def acts_as_orderable options = {}
13
+ self.orderable_column = (options[:column] || "order_nr").to_s
14
+ self.skip_callbacks_for_orderable = options[:skip_callbacks]
15
+ if self.columns_hash.keys.include? self.orderable_column
16
+ self.orderable_scope = options[:scope]
17
+ self.before_save :pre_save_ordering
18
+ self.before_destroy :pre_destroy_ordering
19
+ self.default_scope :order => self.orderable_column
20
+ #self.validates_uniqueness_of self.orderable_column, :scope => @orderable_scope
21
+ include ActiveRecord::Orderable::InstanceMethods
22
+ else
23
+ msg = "[IMPORTANT] ActiveRecord::Orderable plugin: class #{self} has missing column '#{self.orderable_column}'"
24
+ puts msg if Rails.env == "development"
25
+ RAILS_DEFAULT_LOGGER.error msg
26
+ end
27
+ end
28
+
29
+ # updates all unordered items puts them into the end of list
30
+ def order_unordered
31
+ self.reset_column_information # because before this usual 'add_column' is executed and the new column isn't fetched yet
32
+ self.group(self.orderable_scope).each do |obj|
33
+ unordered_conditions = "#{self.orderable_column} IS NULL OR #{self.table_name}.#{self.orderable_column} = 0"
34
+ ordered_conditions = "#{self.orderable_column} IS NOT NULL AND #{self.table_name}.#{self.orderable_column} != 0"
35
+ order_nr = obj.all_orderable.order(self.orderable_column).last[self.orderable_column] || 0
36
+ obj.all_orderable.where(unordered_conditions).each do |item|
37
+ order_nr += 1
38
+ raw_orderable_update(item.id, order_nr)
39
+ end
40
+ end
41
+ end
42
+
43
+ def raw_orderable_update id, nr
44
+ self.connection.execute("update #{self.table_name} set #{self.orderable_column} = #{nr.to_i} where #{self.table_name}.id = #{id.to_i};")
45
+ end
46
+ end
47
+
48
+ module InstanceMethods
49
+
50
+ # Moves Item to given position
51
+ def move_to nr, options = {}
52
+ if options[:skip_callbacks].nil? ? self.class.skip_callbacks_for_orderable : options[:skip_callbacks]
53
+ self[self.class.orderable_column] = nr
54
+ self.send(:pre_save_ordering)
55
+ self.class.raw_orderable_update(self.id, nr)
56
+ else
57
+ self.update_attribute(self.class.orderable_column, nr)
58
+ end
59
+ end
60
+
61
+ def move_up options = {}
62
+ move_to(self[self.class.orderable_column] - 1, options) if self[self.class.orderable_column]
63
+ end
64
+
65
+ def move_down options = {}
66
+ move_to(self[self.class.orderable_column] + 1, options) if self[self.class.orderable_column]
67
+ end
68
+
69
+ def all_orderable
70
+ if self.class.orderable_scope
71
+ self.class.where(:"#{self.class.orderable_scope}" => self[self.class.orderable_scope])
72
+ else
73
+ self.class.scoped
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def pre_save_ordering
80
+ self[self.class.orderable_column] = 0 if self[self.class.orderable_column].nil?
81
+ if self.id
82
+ if self[self.class.orderable_column] == 0
83
+ self[self.class.orderable_column] = 1
84
+ end
85
+ if self[self.class.orderable_column] > self.all_orderable.count
86
+ self[self.class.orderable_column] = self[self.class.orderable_column] -1
87
+ end
88
+ else
89
+ self[self.class.orderable_column] = self.all_orderable.count + 1 if self[self.class.orderable_column] == 0
90
+ end
91
+ self.all_orderable.where("#{self.class.table_name}.id != ?",self.id || 0).each do |item|
92
+ item[self.class.orderable_column] = 0 if item[self.class.orderable_column].nil?
93
+ if self.id
94
+ if item[self.class.orderable_column] > (self.send("#{self.class.orderable_column}_was") || 0 )
95
+ if item[self.class.orderable_column] <= self[self.class.orderable_column]
96
+ item[self.class.orderable_column] -= 1
97
+ end
98
+ else
99
+ if item[self.class.orderable_column] >= self[self.class.orderable_column]
100
+ item[self.class.orderable_column] += 1
101
+ end
102
+ end
103
+
104
+ if item[self.class.orderable_column] != item.send("#{self.class.orderable_column}_was")
105
+ self.class.raw_orderable_update(item.id, item[self.class.orderable_column])
106
+ end
107
+ else
108
+ if item[self.class.orderable_column] >= self[self.class.orderable_column]
109
+ item[self.class.orderable_column] += 1
110
+ self.class.raw_orderable_update(item.id, item[self.class.orderable_column])
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ def pre_destroy_ordering
117
+ self.all_orderable.each do |item|
118
+ if item[self.class.orderable_column].to_i > self[self.class.orderable_column].to_i
119
+ item[self.class.orderable_column] -= 1
120
+ self.class.raw_orderable_update(item.id, item[self.class.orderable_column])
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,130 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe ActiveRecord::Orderable do
4
+ it "should order simple categories correctly" do
5
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
6
+ Category.count.should == 5
7
+ Category.first.order_nr.should == 1
8
+ Category.last.order_nr.should == 5
9
+ c = Category.first
10
+ c.update_attribute(:order_nr, 4)
11
+ Category.find_by_name("Cat 1").order_nr.should == 4
12
+ Category.first.name.should == "Cat 2"
13
+ c = Category.first
14
+ Category.find_by_name("Cat 4").move_to 1
15
+ Category.find_by_name("Cat 4").order_nr.should == 1
16
+ Category.find(c.id).order_nr.should == 2
17
+ end
18
+
19
+ it "should order categories correct by scope" do
20
+ cat_types = [
21
+ CatType.create(:name => "Type 1"),
22
+ CatType.create(:name => "Type 2")
23
+ ]
24
+
25
+ 5.times{|i| Category.create(:name => "Cat #{i+1}", :cat_type => cat_types.first)}
26
+ 5.times{|i| Category.create(:name => "Cat #{i+6}", :cat_type => cat_types.last)}
27
+
28
+ Category.find_by_name("Cat 9").move_to 1
29
+ Category.find_by_name("Cat 9").order_nr.should == 1
30
+ Category.find_by_name("Cat 6").order_nr.should == 2
31
+ Category.find_by_name("Cat 7").order_nr.should == 3
32
+ Category.find_by_name("Cat 8").order_nr.should == 4
33
+ Category.find_by_name("Cat 10").order_nr.should == 5
34
+
35
+ Category.find_by_name("Cat 7").move_to 5
36
+
37
+ Category.find_by_name("Cat 9").order_nr.should == 1
38
+ Category.find_by_name("Cat 6").order_nr.should == 2
39
+ Category.find_by_name("Cat 8").order_nr.should == 3
40
+ Category.find_by_name("Cat 10").order_nr.should == 4
41
+ Category.find_by_name("Cat 7").order_nr.should == 5
42
+
43
+ Category.find_by_name("Cat 7").move_to 4
44
+
45
+ Category.find_by_name("Cat 9").order_nr.should == 1
46
+ Category.find_by_name("Cat 6").order_nr.should == 2
47
+ Category.find_by_name("Cat 8").order_nr.should == 3
48
+ Category.find_by_name("Cat 7").order_nr.should == 4
49
+ Category.find_by_name("Cat 10").order_nr.should == 5
50
+
51
+ Category.find_by_name("Cat 9").move_to 5
52
+
53
+ Category.find_by_name("Cat 6").order_nr.should == 1
54
+ Category.find_by_name("Cat 8").order_nr.should == 2
55
+ Category.find_by_name("Cat 7").order_nr.should == 3
56
+ Category.find_by_name("Cat 10").order_nr.should == 4
57
+ Category.find_by_name("Cat 9").order_nr.should == 5
58
+
59
+ Category.find_by_name("Cat 10").move_to 2
60
+
61
+ Category.find_by_name("Cat 6").order_nr.should == 1
62
+ Category.find_by_name("Cat 10").order_nr.should == 2
63
+ Category.find_by_name("Cat 8").order_nr.should == 3
64
+ Category.find_by_name("Cat 7").order_nr.should == 4
65
+ Category.find_by_name("Cat 9").order_nr.should == 5
66
+ end
67
+
68
+ it "should create unordered list and order it with order_unordered method" do
69
+ 1.upto(4){|i| Category.create(:name => "Cat #{i}")}
70
+ cat = Category.create(:name => "Cat 0")
71
+ Category.update_all("order_nr = NULL")
72
+ Category.order_unordered
73
+ cat.order_nr.should_not == 1
74
+ cat.move_to 1
75
+ cat.reload
76
+ cat.order_nr.should == 1
77
+ Category.find_by_name("Cat 4").order_nr.should == 5
78
+ end
79
+
80
+ it "should create scoped unordered list and order it with order_unordered method" do
81
+ cat_types = [
82
+ CatType.create(:name => "Type 1"),
83
+ CatType.create(:name => "Type 2")
84
+ ]
85
+ 5.times{|i| Category.create(:name => "Cat #{i+1}", :cat_type => cat_types.first)}
86
+ 5.times{|i| Category.create(:name => "Cat #{i+6}", :cat_type => cat_types.last)}
87
+ Category.update_all("order_nr = NULL")
88
+ Category.all.map(&:order_nr).should == [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
89
+ Category.order_unordered
90
+ Category.all.map(&:order_nr).should == [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
91
+ end
92
+
93
+ it "should move_up" do
94
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
95
+ c = Category.find_by_name("Cat 2")
96
+ c.order_nr.should == 2
97
+ c.move_up
98
+ c.reload
99
+ c.order_nr.should == 1
100
+ c.move_up
101
+ c.reload
102
+ c.order_nr.should == 1
103
+ end
104
+
105
+ it "should move_down" do
106
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
107
+ c = Category.find_by_name("Cat 2")
108
+ c.order_nr.should == 2
109
+ c.move_down
110
+ c.reload
111
+ c.order_nr.should == 3
112
+ c.move_to(5)
113
+ c.reload
114
+ c.order_nr.should == 5
115
+ c.move_down
116
+ c.reload
117
+ c.order_nr.should == 5
118
+ end
119
+
120
+ it "should skip_callbacks" do
121
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
122
+ c = Category.find_by_name("Cat 2")
123
+ c.background_task.should be_nil
124
+ c.move_down
125
+ c.background_task.should == :done
126
+ c.background_task = nil
127
+ c.move_up(:skip_callbacks => true)
128
+ c.background_task.should be_nil
129
+ end
130
+ end
@@ -0,0 +1,130 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe ActiveRecord::Orderable do
4
+ it "should order simple categories correctly" do
5
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
6
+ Category.count.should == 5
7
+ Category.first.order_nr.should == 1
8
+ Category.last.order_nr.should == 5
9
+ c = Category.first
10
+ c.update_attribute(:order_nr, 4)
11
+ Category.find_by_name("Cat 1").order_nr.should == 4
12
+ Category.first.name.should == "Cat 2"
13
+ c = Category.first
14
+ Category.find_by_name("Cat 4").move_to 1
15
+ Category.find_by_name("Cat 4").order_nr.should == 1
16
+ Category.find(c.id).order_nr.should == 2
17
+ end
18
+
19
+ it "should order categories correct by scope" do
20
+ cat_types = [
21
+ CatType.create(:name => "Type 1"),
22
+ CatType.create(:name => "Type 2")
23
+ ]
24
+
25
+ 5.times{|i| Category.create(:name => "Cat #{i+1}", :cat_type => cat_types.first)}
26
+ 5.times{|i| Category.create(:name => "Cat #{i+6}", :cat_type => cat_types.last)}
27
+
28
+ Category.find_by_name("Cat 9").move_to 1
29
+ Category.find_by_name("Cat 9").order_nr.should == 1
30
+ Category.find_by_name("Cat 6").order_nr.should == 2
31
+ Category.find_by_name("Cat 7").order_nr.should == 3
32
+ Category.find_by_name("Cat 8").order_nr.should == 4
33
+ Category.find_by_name("Cat 10").order_nr.should == 5
34
+
35
+ Category.find_by_name("Cat 7").move_to 5
36
+
37
+ Category.find_by_name("Cat 9").order_nr.should == 1
38
+ Category.find_by_name("Cat 6").order_nr.should == 2
39
+ Category.find_by_name("Cat 8").order_nr.should == 3
40
+ Category.find_by_name("Cat 10").order_nr.should == 4
41
+ Category.find_by_name("Cat 7").order_nr.should == 5
42
+
43
+ Category.find_by_name("Cat 7").move_to 4
44
+
45
+ Category.find_by_name("Cat 9").order_nr.should == 1
46
+ Category.find_by_name("Cat 6").order_nr.should == 2
47
+ Category.find_by_name("Cat 8").order_nr.should == 3
48
+ Category.find_by_name("Cat 7").order_nr.should == 4
49
+ Category.find_by_name("Cat 10").order_nr.should == 5
50
+
51
+ Category.find_by_name("Cat 9").move_to 5
52
+
53
+ Category.find_by_name("Cat 6").order_nr.should == 1
54
+ Category.find_by_name("Cat 8").order_nr.should == 2
55
+ Category.find_by_name("Cat 7").order_nr.should == 3
56
+ Category.find_by_name("Cat 10").order_nr.should == 4
57
+ Category.find_by_name("Cat 9").order_nr.should == 5
58
+
59
+ Category.find_by_name("Cat 10").move_to 2
60
+
61
+ Category.find_by_name("Cat 6").order_nr.should == 1
62
+ Category.find_by_name("Cat 10").order_nr.should == 2
63
+ Category.find_by_name("Cat 8").order_nr.should == 3
64
+ Category.find_by_name("Cat 7").order_nr.should == 4
65
+ Category.find_by_name("Cat 9").order_nr.should == 5
66
+ end
67
+
68
+ it "should create unordered list and order it with order_unordered method" do
69
+ 1.upto(4){|i| Category.create(:name => "Cat #{i}")}
70
+ cat = Category.create(:name => "Cat 0")
71
+ Category.update_all("order_nr = NULL")
72
+ Category.order_unordered
73
+ cat.order_nr.should_not == 1
74
+ cat.move_to 1
75
+ cat.reload
76
+ cat.order_nr.should == 1
77
+ Category.find_by_name("Cat 4").order_nr.should == 5
78
+ end
79
+
80
+ it "should create scoped unordered list and order it with order_unordered method" do
81
+ cat_types = [
82
+ CatType.create(:name => "Type 1"),
83
+ CatType.create(:name => "Type 2")
84
+ ]
85
+ 5.times{|i| Category.create(:name => "Cat #{i+1}", :cat_type => cat_types.first)}
86
+ 5.times{|i| Category.create(:name => "Cat #{i+6}", :cat_type => cat_types.last)}
87
+ Category.update_all("order_nr = NULL")
88
+ Category.all.map(&:order_nr).should == [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
89
+ Category.order_unordered
90
+ Category.all.map(&:order_nr).should == [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]
91
+ end
92
+
93
+ it "should move_up" do
94
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
95
+ c = Category.find_by_name("Cat 2")
96
+ c.order_nr.should == 2
97
+ c.move_up
98
+ c.reload
99
+ c.order_nr.should == 1
100
+ c.move_up
101
+ c.reload
102
+ c.order_nr.should == 1
103
+ end
104
+
105
+ it "should move_down" do
106
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
107
+ c = Category.find_by_name("Cat 2")
108
+ c.order_nr.should == 2
109
+ c.move_down
110
+ c.reload
111
+ c.order_nr.should == 3
112
+ c.move_to(5)
113
+ c.reload
114
+ c.order_nr.should == 5
115
+ c.move_down
116
+ c.reload
117
+ c.order_nr.should == 5
118
+ end
119
+
120
+ it "should skip_callbacks" do
121
+ 5.times{|i| Category.create(:name => "Cat #{i+1}")}
122
+ c = Category.find_by_name("Cat 2")
123
+ c.background_task.should be_nil
124
+ c.move_down
125
+ c.background_task.should == :done
126
+ c.background_task = nil
127
+ c.move_up(:skip_callbacks => true)
128
+ c.background_task.should be_nil
129
+ end
130
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --reverse
3
+ --backtrace
@@ -0,0 +1,47 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require 'rails'
5
+ require 'active_record'
6
+ require 'rspec'
7
+ require 'logger'
8
+ require 'ruby-debug'
9
+
10
+ require File.dirname(__FILE__)+'/../init.rb'
11
+ ActiveRecord::Base.logger = Logger.new(File.open("#{File.dirname(__FILE__)}/database.log", 'w+'))
12
+ ActiveRecord::Base.establish_connection({ :database => ":memory:", :adapter => 'sqlite3', :timeout => 500 })
13
+
14
+ ActiveRecord::Schema.define do
15
+ create_table :categories, :force => true do |t|
16
+ t.string :name
17
+ t.integer :cat_type_id
18
+ t.integer :order_nr
19
+ end
20
+ create_table :cat_types, :force => true do |t|
21
+ t.string :name
22
+ end
23
+ end
24
+
25
+ class CatType < ActiveRecord::Base
26
+ has_many :categories, :dependent => :destroy
27
+ end
28
+
29
+ class Category < ActiveRecord::Base
30
+ belongs_to :cat_type
31
+ acts_as_orderable :scope => :cat_type_id
32
+ after_save :do_background_task
33
+ attr_accessor :background_task
34
+
35
+ private
36
+
37
+ def do_background_task
38
+ self.background_task = :done
39
+ end
40
+ end
41
+
42
+ RSpec.configure do |config|
43
+ config.before(:each) do
44
+ Category.destroy_all
45
+ CatType.destroy_all
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ar-orderable
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gatis Tomsons
9
+ - IT House
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-08-10 00:00:00.000000000 +03:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rails
18
+ requirement: &89325120 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: 3.0.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *89325120
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: &89324880 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 1.3.4
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *89324880
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ requirement: &89324640 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.6.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *89324640
49
+ - !ruby/object:Gem::Dependency
50
+ name: ruby-debug19
51
+ requirement: &89324400 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 0.11.6
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *89324400
60
+ - !ruby/object:Gem::Dependency
61
+ name: bundler
62
+ requirement: &89324160 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 1.0.0
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *89324160
71
+ - !ruby/object:Gem::Dependency
72
+ name: jeweler
73
+ requirement: &89323920 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: 1.6.4
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *89323920
82
+ - !ruby/object:Gem::Dependency
83
+ name: rcov
84
+ requirement: &89323680 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *89323680
93
+ description: You can order AR records and skip callbacks
94
+ email: gatis@ithouse.cc
95
+ executables: []
96
+ extensions: []
97
+ extra_rdoc_files:
98
+ - LICENSE.txt
99
+ - README.md
100
+ files:
101
+ - .document
102
+ - .rspec
103
+ - Gemfile
104
+ - Gemfile.lock
105
+ - LICENSE.txt
106
+ - MIT-LICENSE
107
+ - README.md
108
+ - Rakefile
109
+ - VERSION
110
+ - ar-orderable.gemspec
111
+ - init.rb
112
+ - lib/ar-orderable.rb
113
+ - lib/ar_orderable.rb
114
+ - spec/ar-orderable_spec.rb
115
+ - spec/ar_orderable_spec.rb
116
+ - spec/spec.opts
117
+ - spec/spec_helper.rb
118
+ has_rdoc: true
119
+ homepage: http://github.com/ithouse/ar-orderable
120
+ licenses:
121
+ - MIT
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ segments:
133
+ - 0
134
+ hash: 660910161
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 1.6.2
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Rails 3 plugin for simple ordering.
147
+ test_files: []