ar-orderable 1.0.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/.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: []