aeonscope-acts_as_list 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/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