aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2004-12-08 15:39:22 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2004-12-08 15:39:22 +0000
commit2d483af0d7c3d17f5b22b702bc93c95a9e7d99da (patch)
treeb07d24824a9230461dfe39ed7b662c22a16684d4 /activerecord/lib
parentffd93a84ff345192db71704572bada99e63f161b (diff)
downloadrails-2d483af0d7c3d17f5b22b702bc93c95a9e7d99da.tar.gz
rails-2d483af0d7c3d17f5b22b702bc93c95a9e7d99da.tar.bz2
rails-2d483af0d7c3d17f5b22b702bc93c95a9e7d99da.zip
Unofficially added ListMixin
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@82 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/mixins/list_mixin.rb154
1 files changed, 154 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/mixins/list_mixin.rb b/activerecord/lib/active_record/mixins/list_mixin.rb
new file mode 100644
index 0000000000..9f964f11af
--- /dev/null
+++ b/activerecord/lib/active_record/mixins/list_mixin.rb
@@ -0,0 +1,154 @@
+module ActiveRecord
+ # This mixin provides the capabilities for sorting and reordering a number of objects in list.
+ # The class that has this mixin included needs to have a "position" column defined as an integer on
+ # the mapped database table. Further more, you need to implement the <tt>scope_condition</tt> that
+ # defines how to separate one list from another.
+ #
+ # Todo list example:
+ #
+ # class TodoList < ActiveRecord::Base
+ # has_many :todo_items, :order => "position"
+ # end
+ #
+ # class TodoItem < ActiveRecord::Base
+ # include ListMixin
+ # belongs_to :todo_list
+ #
+ # private
+ # def scope_condition
+ # "todo_list_id = #{todo_list_id}"
+ # end
+ # end
+ #
+ # todo_list.first.move_to_bottom
+ # todo_list.last.move_higher
+ #
+ module ListMixin
+ def self.append_features(base)
+ super
+ base.class_eval do
+ before_destroy :remove_from_list
+ after_create :add_to_list_bottom
+ end
+ end
+
+ # Moving around on the list
+
+ def move_lower
+ return unless lower_item
+ lower_item.decrement_position
+ increment_position
+ end
+
+ def move_higher
+ return unless higher_item
+ higher_item.increment_position
+ decrement_position
+ end
+
+ def move_to_bottom
+ decrement_positions_on_lower_items
+ assume_bottom_position
+ end
+
+ def move_to_top
+ increment_positions_on_higher_items
+ assume_top_position
+ end
+
+
+ # Entering or existing the list
+
+ def add_to_list_top
+ increment_positions_on_all_items
+ end
+
+ def add_to_list_bottom
+ assume_bottom_position
+ end
+
+ def remove_from_list
+ decrement_positions_on_lower_items
+ end
+
+
+ # Changing the position
+
+ def increment_position
+ self.position = position.to_i + 1
+ save
+ end
+
+ def decrement_position
+ self.position = position.to_i - 1
+ save
+ end
+
+
+ # Querying the position
+
+ def first?
+ self.position == 1
+ end
+
+ def last?
+ self.position == bottom_position_in_list
+ end
+
+ private
+ # Overwrite this method to define the scope of the list changes
+ def scope_condition; end
+
+ def higher_item
+ self.class.find_first(
+ "#{scope_condition} AND position = #{(position.to_i - 1).to_s}"
+ )
+ end
+
+ def lower_item
+ self.class.find_first(
+ "#{scope_condition} AND position = #{(position.to_i + 1).to_s}"
+ )
+ end
+
+ def bottom_position_in_list
+ item = bottom_item
+ item ? item.position : 0
+ end
+
+ def bottom_item
+ self.class.find_first(
+ "#{scope_condition} ",
+ "position DESC"
+ )
+ end
+
+ def assume_bottom_position
+ self.position = bottom_position_in_list.to_i + 1
+ save
+ end
+
+ def assume_top_position
+ self.position = 1
+ save
+ end
+
+ def decrement_positions_on_lower_items
+ self.class.update_all(
+ "position = (position - 1)", "#{scope_condition} AND position > #{position.to_i}"
+ )
+ end
+
+ def increment_positions_on_higher_items
+ self.class.update_all(
+ "position = (position + 1)", "#{scope_condition} AND position < #{position.to_i}"
+ )
+ end
+
+ def increment_positions_on_all_items
+ self.class.update_all(
+ "position = (position + 1)", "#{scope_condition}"
+ )
+ end
+ end
+end \ No newline at end of file