list.rb
上传用户:netsea168
上传日期:2022-07-22
资源大小:4652k
文件大小:9k
源码类别:

Ajax

开发平台:

Others

  1. module ActiveRecord
  2.   module Acts #:nodoc:
  3.     module List #:nodoc:
  4.       def self.included(base)
  5.         base.extend(ClassMethods)
  6.       end
  7.       # This +acts_as+ extension provides the capabilities for sorting and reordering a number of objects in a list.
  8.       # The class that has this specified needs to have a +position+ column defined as an integer on
  9.       # the mapped database table.
  10.       #
  11.       # Todo list example:
  12.       #
  13.       #   class TodoList < ActiveRecord::Base
  14.       #     has_many :todo_items, :order => "position"
  15.       #   end
  16.       #
  17.       #   class TodoItem < ActiveRecord::Base
  18.       #     belongs_to :todo_list
  19.       #     acts_as_list :scope => :todo_list
  20.       #   end
  21.       #
  22.       #   todo_list.first.move_to_bottom
  23.       #   todo_list.last.move_higher
  24.       module ClassMethods
  25.         # Configuration options are:
  26.         #
  27.         # * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
  28.         # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt> 
  29.         #   (if it hasn't already been added) and use that as the foreign key restriction. It's also possible 
  30.         #   to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
  31.         #   Example: <tt>acts_as_list :scope => 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
  32.         def acts_as_list(options = {})
  33.           configuration = { :column => "position", :scope => "1 = 1" }
  34.           configuration.update(options) if options.is_a?(Hash)
  35.           configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/
  36.           if configuration[:scope].is_a?(Symbol)
  37.             scope_condition_method = %(
  38.               def scope_condition
  39.                 if #{configuration[:scope].to_s}.nil?
  40.                   "#{configuration[:scope].to_s} IS NULL"
  41.                 else
  42.                   "#{configuration[:scope].to_s} = #{#{configuration[:scope].to_s}}"
  43.                 end
  44.               end
  45.             )
  46.           else
  47.             scope_condition_method = "def scope_condition() "#{configuration[:scope]}" end"
  48.           end
  49.           class_eval <<-EOV
  50.             include ActiveRecord::Acts::List::InstanceMethods
  51.             def acts_as_list_class
  52.               ::#{self.name}
  53.             end
  54.             def position_column
  55.               '#{configuration[:column]}'
  56.             end
  57.             #{scope_condition_method}
  58.             before_destroy :remove_from_list
  59.             before_create  :add_to_list_bottom
  60.           EOV
  61.         end
  62.       end
  63.       # All the methods available to a record that has had <tt>acts_as_list</tt> specified. Each method works
  64.       # by assuming the object to be the item in the list, so <tt>chapter.move_lower</tt> would move that chapter
  65.       # lower in the list of all chapters. Likewise, <tt>chapter.first?</tt> would return +true+ if that chapter is
  66.       # the first in the list of all chapters.
  67.       module InstanceMethods
  68.         # Insert the item at the given position (defaults to the top position of 1).
  69.         def insert_at(position = 1)
  70.           insert_at_position(position)
  71.         end
  72.         # Swap positions with the next lower item, if one exists.
  73.         def move_lower
  74.           return unless lower_item
  75.           acts_as_list_class.transaction do
  76.             lower_item.decrement_position
  77.             increment_position
  78.           end
  79.         end
  80.         # Swap positions with the next higher item, if one exists.
  81.         def move_higher
  82.           return unless higher_item
  83.           acts_as_list_class.transaction do
  84.             higher_item.increment_position
  85.             decrement_position
  86.           end
  87.         end
  88.         # Move to the bottom of the list. If the item is already in the list, the items below it have their
  89.         # position adjusted accordingly.
  90.         def move_to_bottom
  91.           return unless in_list?
  92.           acts_as_list_class.transaction do
  93.             decrement_positions_on_lower_items
  94.             assume_bottom_position
  95.           end
  96.         end
  97.         # Move to the top of the list. If the item is already in the list, the items above it have their
  98.         # position adjusted accordingly.
  99.         def move_to_top
  100.           return unless in_list?
  101.           acts_as_list_class.transaction do
  102.             increment_positions_on_higher_items
  103.             assume_top_position
  104.           end
  105.         end
  106.         # Removes the item from the list.
  107.         def remove_from_list
  108.           if in_list?
  109.             decrement_positions_on_lower_items
  110.             update_attribute position_column, nil
  111.           end
  112.         end
  113.         # Increase the position of this item without adjusting the rest of the list.
  114.         def increment_position
  115.           return unless in_list?
  116.           update_attribute position_column, self.send(position_column).to_i + 1
  117.         end
  118.         # Decrease the position of this item without adjusting the rest of the list.
  119.         def decrement_position
  120.           return unless in_list?
  121.           update_attribute position_column, self.send(position_column).to_i - 1
  122.         end
  123.         # Return +true+ if this object is the first in the list.
  124.         def first?
  125.           return false unless in_list?
  126.           self.send(position_column) == 1
  127.         end
  128.         # Return +true+ if this object is the last in the list.
  129.         def last?
  130.           return false unless in_list?
  131.           self.send(position_column) == bottom_position_in_list
  132.         end
  133.         # Return the next higher item in the list.
  134.         def higher_item
  135.           return nil unless in_list?
  136.           acts_as_list_class.find(:first, :conditions =>
  137.             "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
  138.           )
  139.         end
  140.         # Return the next lower item in the list.
  141.         def lower_item
  142.           return nil unless in_list?
  143.           acts_as_list_class.find(:first, :conditions =>
  144.             "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
  145.           )
  146.         end
  147.         # Test if this record is in a list
  148.         def in_list?
  149.           !send(position_column).nil?
  150.         end
  151.         private
  152.           def add_to_list_top
  153.             increment_positions_on_all_items
  154.           end
  155.           def add_to_list_bottom
  156.             self[position_column] = bottom_position_in_list.to_i + 1
  157.           end
  158.           # Overwrite this method to define the scope of the list changes
  159.           def scope_condition() "1" end
  160.           # Returns the bottom position number in the list.
  161.           #   bottom_position_in_list    # => 2
  162.           def bottom_position_in_list(except = nil)
  163.             item = bottom_item(except)
  164.             item ? item.send(position_column) : 0
  165.           end
  166.           # Returns the bottom item
  167.           def bottom_item(except = nil)
  168.             conditions = scope_condition
  169.             conditions = "#{conditions} AND #{self.class.primary_key} != #{except.id}" if except
  170.             acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
  171.           end
  172.           # Forces item to assume the bottom position in the list.
  173.           def assume_bottom_position
  174.             update_attribute(position_column, bottom_position_in_list(self).to_i + 1)
  175.           end
  176.           # Forces item to assume the top position in the list.
  177.           def assume_top_position
  178.             update_attribute(position_column, 1)
  179.           end
  180.           # This has the effect of moving all the higher items up one.
  181.           def decrement_positions_on_higher_items(position)
  182.             acts_as_list_class.update_all(
  183.               "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
  184.             )
  185.           end
  186.           # This has the effect of moving all the lower items up one.
  187.           def decrement_positions_on_lower_items
  188.             return unless in_list?
  189.             acts_as_list_class.update_all(
  190.               "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
  191.             )
  192.           end
  193.           # This has the effect of moving all the higher items down one.
  194.           def increment_positions_on_higher_items
  195.             return unless in_list?
  196.             acts_as_list_class.update_all(
  197.               "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
  198.             )
  199.           end
  200.           # This has the effect of moving all the lower items down one.
  201.           def increment_positions_on_lower_items(position)
  202.             acts_as_list_class.update_all(
  203.               "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
  204.            )
  205.           end
  206.           # Increments position (<tt>position_column</tt>) of all items in the list.
  207.           def increment_positions_on_all_items
  208.             acts_as_list_class.update_all(
  209.               "#{position_column} = (#{position_column} + 1)",  "#{scope_condition}"
  210.             )
  211.           end
  212.           def insert_at_position(position)
  213.             remove_from_list
  214.             increment_positions_on_lower_items(position)
  215.             self.update_attribute(position_column, position)
  216.           end
  217.       end 
  218.     end
  219.   end
  220. end