require 'mega/dynamod' require 'glue/aspects' module Glue # Attach list/ordering methods to the enchanted class. module Orderable include Glue::Aspects dynamic_feature do |opt| opt_position = opt[:position] || 'position' opt_type = opt[:type] || Fixnum # provide a user defined condition. opt_condition = opt[:condition] # provide a condition based on a key field (?) opt_scope = opt[:scope] # clean scope field. if opt_scope if opt_scope.to_s !~ /_oid$/ opt_scope = "#{opt_scope}_oid".to_sym else opt_scope = opt_scope.to_sym end end define_method :orderable_position do opt_position end define_method :orderable_type do opt_type end define_method :orderable_scope do opt_scope end define_method :orderable_condition do scope = orderable_scope if scope scope_value = send(scope) scope = scope_value ? "#{scope} = #{scope_value}" : "#{scope} IS NULL" end return [ opt_condition, scope ].compact end # How to check if property is already defined? unless method_defined?( opt_position ) define_method( opt_position ) do @position ; end define_method( "#{opt_position}=" ) do |x| @position = x ; end property opt_position, opt_type end # Use position for general reference. attr_accessor :position end #dynamic_feature before "add_to_bottom", :on => :og_insert before "decrement_position_of_lower_items", :on => :og_delete # Move higher. def move_higher if higher = higher_item self.class.transaction do higher.increment_position decrement_position end end end # Move lower. def move_lower if lower = lower_item self.class.transaction do lower.decrement_position increment_position end end end # Move to the top. def move_to_top self.class.transaction do increment_position_of_higher_items set_top_position end end # Move to the bottom. def move_to_bottom self.class.transaction do decrement_position_of_lower_items set_bottom_position end end # Move to a specific position. def move_to(dest_position) return if @position == dest_position pos = orderable_position con = orderable_condition self.class.transaction do if @position < dest_position adj = "#{pos} = #{pos} - 1" con = con + [ "#{pos} > #{@position}", "#{pos} <= #{dest_position}" ] else adj = "#{pos} = #{pos} + 1" con = con + [ "#{pos} < #{@position}", "#{pos} >= #{dest_position}" ] end self.class.update( adj, :condition => con.join(' AND ') ) @position = dest_position update_property(:"#{pos}") 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 # TODO end def higher_item pos = orderable_position con = orderable_condition + [ "#{pos} = #{@position - 1}" ] self.class.one( :condition => con.join(' AND ') ) end alias_method :previous_item, :higher_item def lower_item pos = orderable_position con = orderable_condition + [ "#{pos} = #{@position + 1}" ] self.class.one( :condition => con.join(' AND ') ) end alias_method :next_item, :lower_item def top_item # TODO end alias_method :first_item, :top_item def bottom_item pos = orderable_position con = orderable_condition con = con.empty? ? nil : con.join(' AND ') self.class.one(:condition => con, :order => "#{pos} 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(:"#{orderable_position}") end def decrement_position @position -= 1 update_property(:"#{orderable_position}") end def bottom_position item = bottom_item item ? item.send(orderable_position) : 0 end def set_top_position @position = 1 update_property(:"#{orderable_position}") end def set_bottom_position @position = bottom_position + 1 update_property(:"#{orderable_position}") end def increment_position_of_higher_items pos = orderable_position con = orderable_condition + [ "#{pos} < #{@position}" ] self.class.update( "#{pos}=(#{pos} + 1)", :condition => con.join(' AND ') ) end def increment_position_of_all_items pos = orderable_position con = orderable_condition con = con.empty? ? nil : con.join(' AND ') self.class.update("#{pos}=(#{pos} + 1)", :condition => con ) end def decrement_position_of_lower_items pos = orderable_position con = orderable_condition + [ "#{pos} > #{@position}" ] self.class.update( "#{pos}=(#{pos} - 1)", :condition => con.join(' AND ') ) end end end # * George Moschovitis # * Thomas Sawyer