aeonscope-acts_as_list 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = v1.0.0
2
+
3
+ * Forked the original {Acts as List}[http://github.com/rails/acts_as_list/tree/master] plugin code.
4
+ * Converted the plugin to a gem.
5
+ * Added the ability to accept order/position via the parameter hash during new record creation.
data/LICENSE.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining
2
+ a copy of this software and associated documentation files (the
3
+ "Software"), to deal in the Software without restriction, including
4
+ without limitation the rights to use, copy, modify, merge, publish,
5
+ distribute, sublicense, and/or sell copies of the Software, and to
6
+ permit persons to whom the Software is furnished to do so, subject to
7
+ the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be
10
+ included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,46 @@
1
+ = Overview
2
+
3
+ Provides capabilities for sorting and reordering a number of objects in a list. The class that has this specified needs to have a +position+ column defined as an integer on the mapped database table.
4
+
5
+ = License
6
+
7
+ Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license (see the LICENSE file).
8
+
9
+ = History
10
+
11
+ See the CHANGELOG file for more info.
12
+
13
+ = Requirements
14
+
15
+ * Rails 2.3.x or higher.
16
+
17
+ = Installation
18
+
19
+ Type the following from the command line to install:
20
+
21
+ * *UNIX*: sudo gem install aeonscope-acts_as_list
22
+ * *Windows*: gem install aeonscope-acts_as_list
23
+
24
+ Update your environment.rb file to include the new gem:
25
+
26
+ * config.gem "aeonscope-acts_as_list", :lib => "acts_as_list", :source => "http://gems.github.com"
27
+
28
+ = Usage
29
+
30
+ class TodoList < ActiveRecord::Base
31
+ has_many :todo_items, :order => "position"
32
+ end
33
+
34
+ class TodoItem < ActiveRecord::Base
35
+ belongs_to :todo_list
36
+ acts_as_list :scope => :todo_list
37
+ end
38
+
39
+ todo_list.first.move_to_bottom
40
+ todo_list.last.move_higher
41
+
42
+ = Contact/Feedback/Issues
43
+
44
+ * {Berserk Technologies}[http://www.berserktech.com] - Company web site.
45
+ * Aeonscope[http://www.aeonscope.net] - Personal web site.
46
+ * Twitter[http://www.twitter.com/Aeonscope] - Short bursts of insight and/or noise.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "acts_as_list"
8
+ gem.summary = "A fork of the original Rails plugin which allows models to be ordered."
9
+ gem.description = "Allows models to be ordered."
10
+ gem.authors = ["David Heinemeier Hansson", "Brooke Kuhlmann"]
11
+ gem.email = "aeonscope@gmail.com"
12
+ gem.homepage = "http://github.com/aeonscope/acts_as_list"
13
+ gem.required_ruby_version = ">= 1.8.6"
14
+ gem.add_dependency "rails", ">= 2.3.2"
15
+ gem.rdoc_options << "CHANGELOG.rdoc"
16
+ gem.files = FileList["[A-Z]*", "{bin,lib,generators,rails_generators,test,spec}/**/*"]
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION.yml')
40
+ config = YAML.load(File.read('VERSION.yml'))
41
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
42
+ else
43
+ version = ""
44
+ end
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "acts_as_list #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
51
+
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 0
4
+ :patch: 0
@@ -0,0 +1,11 @@
1
+ require File.join(File.dirname(__FILE__), "class_methods.rb")
2
+ require File.join(File.dirname(__FILE__), "instance_methods.rb")
3
+
4
+ module ActsAsList
5
+ def self.included base
6
+ base.extend ClassMethods
7
+ end
8
+ include InstanceMethods
9
+ end
10
+
11
+ ActiveRecord::Base.class_eval { include ActsAsList }
@@ -0,0 +1,65 @@
1
+ module ActsAsList
2
+ # This +acts_as+ extension provides the capabilities for sorting and reordering a number of objects in a list.
3
+ # The class that has this specified needs to have a +position+ column defined as an integer on
4
+ # the mapped database table.
5
+ #
6
+ # Todo list example:
7
+ #
8
+ # class TodoList < ActiveRecord::Base
9
+ # has_many :todo_items, :order => "position"
10
+ # end
11
+ #
12
+ # class TodoItem < ActiveRecord::Base
13
+ # belongs_to :todo_list
14
+ # acts_as_list :scope => :todo_list
15
+ # end
16
+ #
17
+ # todo_list.first.move_to_bottom
18
+ # todo_list.last.move_higher
19
+ module ClassMethods
20
+ # Configuration options are:
21
+ #
22
+ # * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
23
+ # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
24
+ # (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
25
+ # to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
26
+ # Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
27
+ def acts_as_list(options = {})
28
+ configuration = { :column => "position", :scope => "1 = 1" }
29
+ configuration.update(options) if options.is_a?(Hash)
30
+
31
+ configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
32
+
33
+ if configuration[:scope].is_a?(Symbol)
34
+ scope_condition_method = %(
35
+ def scope_condition
36
+ if #{configuration[:scope].to_s}.nil?
37
+ "#{configuration[:scope].to_s} IS NULL"
38
+ else
39
+ "#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}"
40
+ end
41
+ end
42
+ )
43
+ else
44
+ scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end"
45
+ end
46
+
47
+ class_eval <<-EOV
48
+ include ActsAsList::InstanceMethods
49
+
50
+ def acts_as_list_class
51
+ ::#{self.name}
52
+ end
53
+
54
+ def position_column
55
+ '#{configuration[:column]}'
56
+ end
57
+
58
+ #{scope_condition_method}
59
+
60
+ before_destroy :remove_from_list
61
+ before_create :add_to_list_bottom
62
+ EOV
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,184 @@
1
+ module ActsAsList
2
+ # All the methods available to a record that has had <tt>acts_as_list</tt> specified. Each method works
3
+ # by assuming the object to be the item in the list, so <tt>chapter.move_lower</tt> would move that chapter
4
+ # lower in the list of all chapters. Likewise, <tt>chapter.first?</tt> would return +true+ if that chapter is
5
+ # the first in the list of all chapters.
6
+ module InstanceMethods
7
+ # Insert the item at the given position (defaults to the top position of 1).
8
+ def insert_at(position = 1)
9
+ insert_at_position(position)
10
+ end
11
+
12
+ # Swap positions with the next lower item, if one exists.
13
+ def move_lower
14
+ return unless lower_item
15
+
16
+ acts_as_list_class.transaction do
17
+ lower_item.decrement_position
18
+ increment_position
19
+ end
20
+ end
21
+
22
+ # Swap positions with the next higher item, if one exists.
23
+ def move_higher
24
+ return unless higher_item
25
+
26
+ acts_as_list_class.transaction do
27
+ higher_item.increment_position
28
+ decrement_position
29
+ end
30
+ end
31
+
32
+ # Move to the bottom of the list. If the item is already in the list, the items below it have their
33
+ # position adjusted accordingly.
34
+ def move_to_bottom
35
+ return unless in_list?
36
+ acts_as_list_class.transaction do
37
+ decrement_positions_on_lower_items
38
+ assume_bottom_position
39
+ end
40
+ end
41
+
42
+ # Move to the top of the list. If the item is already in the list, the items above it have their
43
+ # position adjusted accordingly.
44
+ def move_to_top
45
+ return unless in_list?
46
+ acts_as_list_class.transaction do
47
+ increment_positions_on_higher_items
48
+ assume_top_position
49
+ end
50
+ end
51
+
52
+ # Removes the item from the list.
53
+ def remove_from_list
54
+ if in_list?
55
+ decrement_positions_on_lower_items
56
+ update_attribute position_column, nil
57
+ end
58
+ end
59
+
60
+ # Increase the position of this item without adjusting the rest of the list.
61
+ def increment_position
62
+ return unless in_list?
63
+ update_attribute position_column, self.send(position_column).to_i + 1
64
+ end
65
+
66
+ # Decrease the position of this item without adjusting the rest of the list.
67
+ def decrement_position
68
+ return unless in_list?
69
+ update_attribute position_column, self.send(position_column).to_i - 1
70
+ end
71
+
72
+ # Return +true+ if this object is the first in the list.
73
+ def first?
74
+ return false unless in_list?
75
+ self.send(position_column) == 1
76
+ end
77
+
78
+ # Return +true+ if this object is the last in the list.
79
+ def last?
80
+ return false unless in_list?
81
+ self.send(position_column) == bottom_position_in_list
82
+ end
83
+
84
+ # Return the next higher item in the list.
85
+ def higher_item
86
+ return nil unless in_list?
87
+ acts_as_list_class.find(:first, :conditions =>
88
+ "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
89
+ )
90
+ end
91
+
92
+ # Return the next lower item in the list.
93
+ def lower_item
94
+ return nil unless in_list?
95
+ acts_as_list_class.find(:first, :conditions =>
96
+ "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
97
+ )
98
+ end
99
+
100
+ # Test if this record is in a list
101
+ def in_list?
102
+ !send(position_column).nil?
103
+ end
104
+
105
+ private
106
+ def add_to_list_top
107
+ increment_positions_on_all_items
108
+ end
109
+
110
+ def add_to_list_bottom
111
+ self[position_column] ||= bottom_position_in_list.to_i + 1
112
+ end
113
+
114
+ # Overwrite this method to define the scope of the list changes
115
+ def scope_condition() "1" end
116
+
117
+ # Returns the bottom position number in the list.
118
+ # bottom_position_in_list # => 2
119
+ def bottom_position_in_list(except = nil)
120
+ item = bottom_item(except)
121
+ item ? item.send(position_column) : 0
122
+ end
123
+
124
+ # Returns the bottom item
125
+ def bottom_item(except = nil)
126
+ conditions = scope_condition
127
+ conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
128
+ acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
129
+ end
130
+
131
+ # Forces item to assume the bottom position in the list.
132
+ def assume_bottom_position
133
+ update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
134
+ end
135
+
136
+ # Forces item to assume the top position in the list.
137
+ def assume_top_position
138
+ update_attribute(position_column, 1)
139
+ end
140
+
141
+ # This has the effect of moving all the higher items up one.
142
+ def decrement_positions_on_higher_items(position)
143
+ acts_as_list_class.update_all(
144
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
145
+ )
146
+ end
147
+
148
+ # This has the effect of moving all the lower items up one.
149
+ def decrement_positions_on_lower_items
150
+ return unless in_list?
151
+ acts_as_list_class.update_all(
152
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
153
+ )
154
+ end
155
+
156
+ # This has the effect of moving all the higher items down one.
157
+ def increment_positions_on_higher_items
158
+ return unless in_list?
159
+ acts_as_list_class.update_all(
160
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
161
+ )
162
+ end
163
+
164
+ # This has the effect of moving all the lower items down one.
165
+ def increment_positions_on_lower_items(position)
166
+ acts_as_list_class.update_all(
167
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
168
+ )
169
+ end
170
+
171
+ # Increments position (<tt>position_column</tt>) of all items in the list.
172
+ def increment_positions_on_all_items
173
+ acts_as_list_class.update_all(
174
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
175
+ )
176
+ end
177
+
178
+ def insert_at_position(position)
179
+ remove_from_list
180
+ increment_positions_on_lower_items(position)
181
+ self.update_attribute(position_column, position)
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,6 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+
3
+ describe "ActsAsList" do
4
+ it "ToDo..." do
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'acts_as_list'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aeonscope-acts_as_list
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - David Heinemeier Hansson
8
+ - Brooke Kuhlmann
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-06-10 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rails
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 2.3.2
25
+ version:
26
+ description: Allows models to be ordered.
27
+ email: aeonscope@gmail.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - LICENSE.rdoc
34
+ - README.rdoc
35
+ files:
36
+ - CHANGELOG.rdoc
37
+ - LICENSE.rdoc
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION.yml
41
+ - lib/acts_as_list.rb
42
+ - lib/class_methods.rb
43
+ - lib/instance_methods.rb
44
+ - spec/acts_as_list_spec.rb
45
+ - spec/spec_helper.rb
46
+ has_rdoc: false
47
+ homepage: http://github.com/aeonscope/acts_as_list
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ - CHANGELOG.rdoc
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 1.8.6
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.2.0
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: A fork of the original Rails plugin which allows models to be ordered.
73
+ test_files:
74
+ - spec/acts_as_list_spec.rb
75
+ - spec/spec_helper.rb