From 2d483af0d7c3d17f5b22b702bc93c95a9e7d99da Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 8 Dec 2004 15:39:22 +0000 Subject: Unofficially added ListMixin git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@82 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../lib/active_record/mixins/list_mixin.rb | 154 +++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 activerecord/lib/active_record/mixins/list_mixin.rb 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 scope_condition 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 -- cgit v1.2.3