class CollectiveIdea::Acts::NestedSet::Move
:nodoc:
:nodoc:
:nodoc:
def case_condition_for_direction(column_name)
def case_condition_for_direction(column_name) column = send(column_name) "#{column} = CASE " + "WHEN #{column} BETWEEN :a AND :b " + "THEN #{column} + :d - :b " + "WHEN #{column} BETWEEN :c AND :d " + "THEN #{column} + :a - :c " + "ELSE #{column} END, " end
def case_condition_for_parent
def case_condition_for_parent "#{quoted_parent_column_name} = CASE " + "WHEN #{quoted_primary_column_name} = :primary_id THEN :new_parent_id " + "ELSE #{quoted_parent_column_name} END" end
def conditions(a, b, c, d)
def conditions(a, b, c, d) _conditions = case_condition_for_direction(:quoted_left_column_name) + case_condition_for_direction(:quoted_right_column_name) + case_condition_for_parent # We want the record to be 'touched' if it timestamps. if @instance.respond_to?(:updated_at) _conditions << ", updated_at = :timestamp" end [ _conditions, { :a => a, :b => b, :c => c, :d => d, :primary_id => instance.primary_id, :new_parent_id => new_parent_id, :timestamp => Time.now.utc } ] end
def get_boundaries
def get_boundaries if (bound = target_bound) > right bound -= 1 other_bound = right + 1 else other_bound = left - 1 end [bound, other_bound] end
def initialize(target, position, instance)
def initialize(target, position, instance) @target = target @position = position @instance = instance end
def lock_nodes_between!(left_bound, right_bound)
def lock_nodes_between!(left_bound, right_bound) # select the rows in the model between a and d, and apply a lock instance_base_class.default_scoped.nested_set_scope. right_of(left_bound).left_of_right_side(right_bound). select(primary_column_name). lock(true) end
def move
def move prevent_impossible_move bound, other_bound = get_boundaries # there would be no change return if bound == right || bound == left # we have defined the boundaries of two non-overlapping intervals, # so sorting puts both the intervals and their boundaries in order a, b, c, d = [left, right, bound, other_bound].sort lock_nodes_between! a, d nested_set_scope_without_default_scope.where(where_statement(a, d)).update_all( conditions(a, b, c, d) ) end
def new_parent_id
def new_parent_id case position when :child then target.primary_id when :root then nil else target[parent_column_name] end end
def prevent_impossible_move
def prevent_impossible_move if !root && !instance.move_possible?(target) error_msg = "Impossible move, target node (#{target.class.name},ID: #{target.id}) cannot be inside moved tree (#{instance.class.name},ID: #{instance.id})." raise ImpossibleMove, error_msg end end
def root
def root position == :root end
def target_bound
def target_bound case position when :child then right(target) when :left then left(target) when :right then right(target) + 1 when :root then nested_set_scope_without_default_scope.pluck(right_column_name).max + 1 else raise ActiveRecord::ActiveRecordError, "Position should be :child, :left, :right or :root ('#{position}' received)." end end
def where_statement(left_bound, right_bound)
def where_statement(left_bound, right_bound) instance_arel_table[left_column_name].between(left_bound..right_bound). or(instance_arel_table[right_column_name].between(left_bound..right_bound)) end