require 'mega/dynamod' module Og # Attach list/ordering methods to the enchanted class. module Orderable def self.append_dynamic_features(base, options) o = { :position => 'position', :type => Fixnum, } o.update(options) if options if o[:scope].is_a?(Symbol) && o[:scope].to_s !~ /_oid$/ o[:scope] = "#{o[:scope]}_oid".intern end position = o[:position] scope = o[:scope] if scope if scope.is_a?(Symbol) scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")} end cond = ':condition => ' + scope cond_and = ':condition => ' + scope + ' + " AND " +' else cond = ':condition => nil' cond_and = ':condition => ' end code = %{ property :#{position}, #{o[:type]} pre "add_to_bottom", :on => :og_insert pre "decrement_position_of_lower_items", :on => :og_delete def move_higher if higher = higher_item #{base}.transaction do higher.increment_position decrement_position end end end def move_lower if lower = lower_item #{base}.transaction do lower.decrement_position increment_position end end end def move_to_top #{base}.transaction do increment_position_of_higher_items set_top_position end end def move_to_bottom #{base}.transaction do decrement_position_of_lower_items set_bottom_position end end def move_to(dest_position) return if @#{position} == dest_position #{base}.transaction do if @#{position} < dest_position #{base}.update("#{position}=(#{position} - 1)", #{cond_and}"#{position} > \#\{@#{position}\} AND #{position} <= \#\{dest_position\}") else #{base}.update("#{position}=(#{position} + 1)", #{cond_and}"#{position} >= \#\{dest_position\} AND #{position} < \#\{@#{position}\} ") end @#{position} = dest_position update_property(:#{position}) end self end def add_to_top increment_position_of_all_items end def add_to_bottom @#{position} = bottom_position + 1 end def add_to end def higher_item #{base}.one(#{cond_and}"#{position}=\#\{@#{position} - 1\}") end alias_method :previous_item, :higher_item def lower_item #{base}.one(#{cond_and}"#{position}=\#\{@#{position} + 1\}") end alias_method :next_item, :lower_item def top_item end alias_method :first_item, :top_item def bottom_item #{base}.one(#{cond}, :order => "#{position} DESC", :limit => 1) end alias_method :last_item, :last_item def top? @#{position} == 1 end alias_method :first?, :top? def bottom? @#{position} == bottom_position end alias_method :last?, :bottom? def increment_position @#{position} += 1 update_property(:#{position}) end def decrement_position @#{position} -= 1 update_property(:#{position}) end def bottom_position item = bottom_item item ? item.#{position} : 0 end def set_top_position @#{position} = 1 update_property(:#{position}) end def set_bottom_position @#{position} = bottom_position + 1 update_property(:#{position}) end def increment_position_of_higher_items #{base}.update("#{position}=(#{position} + 1)", #{cond_and}"#{position} < \#\{@#{position}\}") end def increment_position_of_all_items #{base}.update("#{position}=(#{position} + 1)", #{cond}) end def decrement_position_of_lower_items #{base}.update("#{position}=(#{position} - 1)", #{cond_and}"#{position} > \#\{@#{position}\}") end } base.module_eval(code) end end end # * George Moschovitis