module StewEucen::Acts::FertileForest::Table::Reconstructers

This module is for extending into derived class by ActiveRecord.
The caption contains “Instance Methods”, but it means “Class Methods” of each derived class.

Public Instance Methods

extinguish(base_obj) click to toggle source

Extinguish (remove top node and the descendant nodes). @param base_obj [Entity|Integer] Top node to extinguish. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 611
def extinguish(base_obj)
  prune(base_obj, SUBTREE_WITH_TOP_NODE)
end
graft(aim_obj, base_obj, kinship = -1) click to toggle source

Graft subtree nodes.

@param aim_obj [Entity|Integer] Top node of subtree to graft. @param base_obj [Entity|Integer] Base node to calc wedged queue. @param kinship [Boolean|Integer] Graft position from base_obj.

Integer: As child.
Boolean: As sibling.

@return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 28
def graft(aim_obj, base_obj, kinship = -1)
  transaction do
    nodes = ff_resolve_nodes([aim_obj, base_obj], true).values   # refresh
    aim_node = nodes[0]
    base_node = nodes[1]

    raise ActiveRecord::Rollback if aim_node.blank? || base_node.blank?

    # pick up node for wedged node to scoot over. (can be null)
    wedged_node = ff_get_wedged_node(base_node, kinship)

    is_sibling = ff_is_bool(kinship)
    depth_offset = base_node.ff_depth - aim_node.ff_depth + (is_sibling ? 0 : 1)

    return true if ff_fit_to_graft(aim_node, wedged_node, depth_offset)

    # return value
    ff_scoots_over(aim_node, wedged_node, depth_offset)
  end   # transaction end
end
move_by(node_obj, step) click to toggle source

Permute in siblings as “Move By”. @param node_obj [Entity|Integer] Moved node by Entity|id. @param step [Integer] Moving offset. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 437
def move_by(node_obj, step)
  ff_move_node(node_obj, step, true)   # true: move by
end
move_to(node_obj, nth = -1) click to toggle source

Permute in siblings as “Move To”. @param node_obj [Entity|Integer] Moved node by Entity|id. @param nth [Integer] Move rank in sibling. (-1:As last sibling) @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 426
def move_to(node_obj, nth = -1)
  ff_move_node(node_obj, nth, false)   # false: move to
end
normalize_depth(grove_id = nil) click to toggle source

Normalize ff_depth fields in ordered grove.

@param grove_id [Integer|nil] Grove ID to find. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 761
def normalize_depth(grove_id = nil)
  transaction do
    return false if has_grove? && grove_id.blank?

    feature_key = 'ff_is_fault'

    columns = [
      "@compare_depth + 1 < (@compare_depth := ff_depth) AS #{feature_key}",
    ]

    ffqq = arel_table[@_ff_queue]
    ffdd = arel_table[@_ff_depth]

    # exists subquery, can not work @compare_depth (avert to use coalesce())
    aim_query = ff_required_columns_scope(columns)
        .ff_usual_order_scope()
        .ff_usual_conditions_scope(grove_id)
        .having(feature_key)
        .limit(1000)

        # TODO: subtreeLimitSize

    ff_raw_query("SET @compare_depth = #{ROOT_DEPTH}")

    depth_offset_values = []
    aim_query.all.each do |node|
      prev_node = ff_get_previous_node(node)

      next if prev_node.blank?

      offset = node.ff_depth - prev_node.ff_depth - 1;
      top_queue = prev_node.ff_queue
      top_depth = prev_node.ff_depth
      top_grove = prev_node.ff_grove if has_grove?

      new_node = prev_node.clone

      depth_offets = [];
      (1 .. offset).each do |o|
        new_node.ff_depth = top_depth + o
        depth_offets << ff_get_boundary_queue(new_node)
      end

      grove_condition = has_grove? ? "#{@_ff_grove} = #{top_grove} AND" : '';

      depth_offets.each do |boundary_queue|
        boundary_condition = boundary_queue.blank? \
            ? ''
            : "AND ff_queue < #{boundary_queue}"

        depth_offset_values << "(CASE WHEN #{grove_condition} #{top_queue} < ff_queue #{boundary_condition} THEN 1 ELSE 0 END)"
      end
    end

    return false if depth_offset_values.blank?

    update_query = ff_usual_conditions_scope(grove_id)
    update_rows = update_query.update_all(
        "#{@_ff_depth} = #{@_ff_depth} - " + depth_offset_values.join('-')
    )

    # return value
    0 < update_rows.to_i
  end
end
normalize_queue(node_obj = nil, boundary_node_obj = nil) click to toggle source

Normalize ff_queue fields in ordered grove. @param node_obj [Entity|Integer] Start node. @param boundary_node_obj [Entity|Integer] Boundary node. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 634
def normalize_queue(node_obj = nil, boundary_node_obj = nil)
  transaction do
    # nodes can be nil
    aim_node = ff_resolve_nodes(node_obj)
    aim_boundary_node = ff_resolve_nodes(boundary_node_obj)

    aim_top_queue      = aim_node         .blank? ? nil : aim_node         .ff_queue
    aim_boundary_queue = aim_boundary_node.blank? ? nil : aim_boundary_node.ff_queue

    res = ff_evenize(aim_node.ff_grove, aim_top_queue, aim_boundary_queue, 0)  # 0: no appmend node

    # return value
    if res.present?
      res[EVENIZE_AFFECTED_ROWS_KEY]
    else
      false
    end
  end
end
permute(*args) click to toggle source

Reorder sibling nodes.

@param args [mixed] Sibling nodes to permute. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 298
def permute(*args)
  node_args = args.flatten

  transaction do
    sibling_nodes = ff_resolve_nodes(node_args, true)  # refresh

    # if node is only one, nothing to do.
    raise ActiveRecord::Rollback if sibling_nodes.length < 2

    # Aer they siblings?
    current_orderd_child_nodes = siblings?(sibling_nodes.values)

    raise ActiveRecord::Rollback if current_orderd_child_nodes.blank?

    # if they are siblings, yield to permute.

    # create array new orderd nodes.
    new_orderd_sibling_nodes = []
    new_orderd_ids = sibling_nodes.keys

    current_orderd_child_nodes.each_pair do |the_id, the_node|
      if sibling_nodes.has_key?(the_id)
        picked_id = new_orderd_ids.shift
        new_orderd_sibling_nodes << current_orderd_child_nodes[picked_id]
      else
        new_orderd_sibling_nodes << node
      end
    end

    # get sorted nodes of all siblings by queue.
    # TODO: need or not need?
    current_queue_orderd_nodes = ff_queue_sorted_nodes(current_orderd_child_nodes.values)

    # calc each siblingNode information.

    # get tail node
    tail_node = current_queue_orderd_nodes.last
    aim_grove = tail_node.ff_grove

    # get total boundary queue (can be null)
    siblings_boundary_queue = ff_get_boundary_queue(tail_node)
    total_tail_queue = siblings_boundary_queue.blank? \
        ? ff_get_max_queue(aim_grove, 0)
        : siblings_boundary_queue - 1

    # set by current order.
    node_attr_infos = {}
    current_queue_orderd_nodes.each do |node|
      node_attr_infos[node.id] = {}
    end

    last_node_index = current_queue_orderd_nodes.length - 1

    node_id_hash = {}
    current_queue_orderd_nodes.each.with_index do |the_node, i|
      is_last = i == last_node_index
      the_id = the_node.id
      node_attr_infos[the_id][:is_last] = is_last

      the_tail_queue = (is_last \
          ? total_tail_queue
          : (current_queue_orderd_nodes[i + 1].ff_queue - 1)
      )
      node_attr_infos[the_id][:tail_queue] = the_tail_queue

      # calc queue-width each sibling
      node_attr_infos[the_id][:queue_width] = the_tail_queue - the_node.ff_queue + 1

      # must use &$xxxx, because do not clone node instance.
      node_id_hash[the_id] = the_node
    end

    # get shifted range of queues
    range_queue_head = current_queue_orderd_nodes.first.ff_queue

    # calc moving queue span for each node.
    has_changed = false
    reduce_queue = range_queue_head   # default value of new queue.
    new_orderd_sibling_nodes.each do |the_node|
      update_id = the_node.id
      off = reduce_queue - node_id_hash[update_id].ff_queue

      node_attr_infos[update_id][:ff_offset] = off
      has_changed = true if off != 0

      reduce_queue += node_attr_infos[update_id][:queue_width]
    end

    # no move, no update.
    return false unless has_changed

    # create case for update by original order of queue.
    when_hash = ['CASE']
    current_queue_orderd_nodes.each do |node|
      orign_id = node.id
      aim_info = node_attr_infos[orign_id]

      off = aim_info[:ff_offset]
      if aim_info[:is_last]
        when_hash << "ELSE #{off}"
      else
        when_hash << "WHEN ff_queue <= #{aim_info[:tail_queue]} THEN #{off}"
      end
    end
    when_hash <<= 'END'

    case_string = when_hash.join(' ')

    # execute to update all
    # lteq(total_tail_queue) is for max_queue
    ffqq = arel_table[@_ff_queue]
    res = ff_usual_conditions_scope(aim_grove)
        .ff_usual_order_scope()
        .where(ffqq.gteq(range_queue_head))
        .where(ffqq.lteq(total_tail_queue))
        .update_all("ff_queue = ff_queue + (#{case_string})")

    res
  end     # transaction end
end
pollard(base_obj) click to toggle source

Pollard (remove the descendant nodes). @param base_obj [Entity|Integer] Top node to pollard. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 621
def pollard(base_obj)
  prune(base_obj, SUBTREE_WITHOUT_TOP_NODE)
end
prune(base_obj, with_top = false) click to toggle source

Prune subtree nodes. @param base_obj [Entity|Integer] Top node to prune. @param with_top [Boolean] Include base node in return query. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 573
def prune(base_obj, with_top = false)
  transaction do
    aim_node = ff_resolve_nodes(base_obj, true)       # refresh
    raise ActiveRecord::Rollback if aim_node.blank?   # nil as dubious

    aim_queue = aim_node.ff_queue
    aim_depth = aim_node.ff_depth
    aim_grove = aim_node.ff_grove

    # boundry queue (can be nil)
    aim_boundary_queue = ff_get_boundary_queue(aim_node)

    # for soft delete
    # can not use subquery for same table in UPDATE
    # Mysql2::Error: You can't specify target table 'categories' for update in FROM clause:
    prune_query = ff_subtree_scope(aim_node, with_top, false)

    # soft delete
    if has_soft_delete?
      delete_key = @_ff_soft_delete
      res = prune_query.update_all("#{delete_key} = #{ff_options[:delete_value]}")
    elsif enable_grove_delete?
      grove_key = @_ff_grove
      res = prune_query.update_all("#{grove_key} = #{grove_key} * -1")
    else
      res = prune_query.delete_all
    end

    res
  end   # tansaction end
end
remove(node_obj) click to toggle source

Remove the node and shift depth of descendant nodes.

soft delete
 (1) soft delete
 (2) grove delete
 (3) normal delete

@param node_obj [Entity|Integer] Node to remove. @return [Boolean] true: Success. @return [Boolean] false: Failure.

# File lib/fertile_forest/modules/reconstructers.rb, line 503
def remove(node_obj)
  transaction do
    remove_node = ff_resolve_nodes(node_obj, true)       # refresh
    raise ActiveRecord::Rollback if remove_node.blank?   # nil as dubious

    aim_queue = remove_node.ff_queue
    aim_depth = remove_node.ff_depth
    aim_grove = remove_node.ff_grove

    # get range of descendants for shifting these depth
    if aim_depth == ROOT_DEPTH
      offset_depth = 1
    else
      parent_node = genitor(remove_node)
      raise ActiveRecord::Rollback if parent_node.blank?

      offset_depth = aim_depth - parent_node.ff_depth
    end

    # for soft delete
    # can not use subquery for same table in UPDATE
    # Mysql2::Error: You can't specify target table 'categories' for update in FROM clause:
    remove_query = ff_subtree_scope(remove_node, true, false)

    ffdd = arel_table[@_ff_depth]

    update_columns = []
    depth_value = ff_create_case_expression(
      @_ff_queue,
      [[aim_queue, 0]],               # when then
      "ff_depth - #{offset_depth}"    # else value
    )
    update_columns << "ff_depth = (#{depth_value})"

    if has_soft_delete? || enable_grove_delete?
      if has_soft_delete?
        delete_value = ff_create_case_expression(
          @_ff_queue,
          [[aim_queue, ff_options[:delete_value]]],     # when then
          @_ff_soft_delete    # else value
        )
        update_columns << "#{@_ff_soft_delete} = (#{delete_value})"
      else
        delete_value = ff_create_case_expression(
          @_ff_queue,
          [[aim_queue, -1]],    # when then
          1                     # else value
        )
        update_columns << "#{@_ff_grove} = #{@_ff_grove} * (#{delete_value})"
      end

      res = remove_query.update_all(update_columns.join(', '))

    # hard delete
    else
      update_res = remove_query.update_all(update_columns.join(', '))
      res = delete_all(@_id => remove_node.id)
    end

    res
  end   # tansaction end
end

Protected Instance Methods

ff_can_evenize(grove_id, aim_queue, aim_boundary_queue, add_count) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 711
def ff_can_evenize(grove_id, aim_queue, aim_boundary_queue, add_count)
  # get count of node for UPDATE
  ffqq = arel_table[@_ff_queue]
  aim_query = ff_usual_conditions_scope(grove_id)

  aim_query.where!(ffqq.gteq(aim_queue))        if aim_queue.present?
  aim_query.where!(ffqq.lt(aim_boundary_queue)) if aim_boundary_queue.present?

  evenizing_count = aim_query.count

  head_queue = aim_queue || 0

  # can nomalize?
  divid_number = evenizing_count + add_count
  return nil if divid_number < 1

  # get boundary queue for calc
  if aim_boundary_queue.present?
    aim_queue_span = aim_boundary_queue - head_queue
  else
    max_queue = ff_get_last_queue(grove_id, 0)     # 0 for nil
    request_queue_span = QUEUE_DEFAULT_INTERVAL * (add_count + 1)

    if QUEUE_MAX_VALUE - max_queue < request_queue_span
      aim_queue_span = QUEUE_MAX_VALUE - head_queue + 1
    else
      aim_queue_span = max_queue - head_queue + request_queue_span
    end
  end

  queue_interval = aim_queue_span / divid_number

  return nil if queue_interval < 1

  # return value
  {
    queue_interval: queue_interval,
    node_count:     evenizing_count,
  }
end
ff_can_graft_by_node(aim_node, aim_boundary_node, wedged_node) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 248
def ff_can_graft_by_node(aim_node, aim_boundary_node, wedged_node)
  # When no wedged node, it means that last queue.
  # In the case, can shift always
  # TODO: should think with boundary node is null
  return true if wedged_node.blank?

  # If grove is different, can not shift.
  if has_grove? && aim_node.ff_grove != wedged_node.ff_grove
    # TODO: set error restructure.defferentGroves'
    return false
  end

  #
  # If wedged queue between the shifting subtree, can not shift.
  # head < wedged < boundary
  #
  # can be "head == wedged". It is OK for shifting.
  # because it means "depth-shifting".
  #
  # 2015/04/30
  # float type aborted to use, because float has arithmetic error.
  #
  wedged_queue = wedged_node.ff_queue

  # can be "head == wedged". It is OK for shifting.
  # because it means "depth-shifting".
  return true if wedged_queue <= aim_node.ff_queue

  # In this case, must use boundary queue.
  if aim_boundary_node.blank?
    # TODO: set error restructure.graftIntoOwn
    return false
  end

  # It is safe.
  return true if aim_boundary_node.ff_queue < wedged_queue

  # TODO: set error restructure.graftIntoOwn'
  return false
end
ff_evenize( grove_id, aim_queue, aim_boundary_queue, add_count, rear_justified = false ) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 656
def ff_evenize(
  grove_id,
  aim_queue,
  aim_boundary_queue,
  add_count,
  rear_justified = false
)
  return nil if has_grove? && grove_id.to_i <= 0

  # can evenize?
  # 1.0 <= (boundaryQueue - headQueue) / (updatedNodeCount + appendNodeCount)
  can_evenize = ff_can_evenize(grove_id, aim_queue, aim_boundary_queue, add_count)
  return nil if can_evenize.blank?

  queue_interval = can_evenize[:queue_interval]
  node_count     = can_evenize[:node_count]

  # execute to slide

  # calc defaut value of new queues
  # add_count can be 0
  head_queue = aim_queue || 0
  if rear_justified
    start_queue = head_queue + queue_interval * (add_count - 1)
  else
    start_queue = head_queue - queue_interval
  end

  #
  # excute update query
  #

  # exists subquery, can not work @compare_depth (avert to use coalesce())
  # Use both query "SET @ffqq = xxx" and "COALESCE(@ffqq, xxx)",
  # because connection is cut between SET and UPDATE
  update_conditions = ff_create_usual_conditions_hash(grove_id)
    update_conditions['ff_queue >='] = aim_queue          if aim_queue.present?
    update_conditions['ff_queue <' ] = aim_boundary_queue if aim_boundary_queue.present?

  update_rows = ff_update_all_in_order(
    {ff_queue: "@forest_queue := @forest_queue + #{queue_interval}"},
    update_conditions,
    ff_usual_order_array(),
    "SET @forest_queue = #{start_queue}"
  )

  return nil if update_rows.to_i <= 0

  # return value
  {
    SPROUT_VACANT_QUEUE_KEY   => head_queue + (rear_justified ? 0 : queue_interval * node_count),
    EVENIZE_AFFECTED_ROWS_KEY => update_rows,
  }
end
ff_fit_to_graft(graft_node, wedged_node, depth_offset) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 79
def ff_fit_to_graft(graft_node, wedged_node, depth_offset)
  shift_queue = graft_node.ff_queue
  shift_grove = graft_node.ff_grove

  if has_grove? && shift_grove.blank?
    # TODO: set error
    return false
  end

  # exists subquery, can not work @compare_depth (avert to use coalesce())
  graft_query = ff_subtree_scope(graft_node, SUBTREE_WITH_TOP_NODE, false)

  # count grafting subtree span of queue
  shift_boundary_queue = ff_get_boundary_queue(graft_node)
  max_queue = ff_get_last_queue(shift_grove, 0) \
      if shift_boundary_queue.blank? || wedged_node.blank?

  if shift_boundary_queue.blank?
    shift_span = max_queue - shift_queue + 1
  else
    shift_span = shift_boundary_queue - shift_queue
  end

  if wedged_node.blank?
    wedged_space = QUEUE_MAX_VALUE - max_queue
  else
    prev_queue = ff_get_previous_queue(wedged_node)
    wedged_space = wedged_node.ff_queue - prev_queue - 1
  end

  # If fit to graft as it is, execute to graft.
  if shift_span <= wedged_space
    if wedged_node.blank?
      queue_offset = max_queue - shift_queue + 1
    else
      queue_offset = prev_queue - shift_queue + 1
    end

    return false if queue_offset == 0 && depth_offset == 0

    graft_query = graft_query

    update_columns = []
    update_columns << "ff_queue = ff_queue + #{queue_offset}" \
        if queue_offset != 0

    update_columns << "ff_depth = ff_depth + #{depth_offset}" \
        if depth_offset != 0

    return 0 < graft_query.update_all(update_columns.join(', '))
  end

  # try to fit to shift with evenizing.
  node_count = graft_query.count

  if node_count <= wedged_space
    return false if node_count < 1

    return false if wedged_node.blank? && depth_offset == 0

    # SET fields
    queue_interval = [QUEUE_DEFAULT_INTERVAL, wedged_space / node_count].min.to_i
    start_queue = (wedged_node.blank? ? max_queue : prev_queue) + 1 - queue_interval

    update_columns[:ff_queue] = "@forest_queue := @forest_queue + #{queue_interval}"
    update_columns[:ff_depth] = "ff_depth + #{depth_offset}" \
        if depth_offset != 0

    # WHERE conditions
    update_conditions = ff_create_usual_conditions_hash(shift_grove)
      update_conditions['ff_queue >='] = shift_queue          if shift_queue.present?
      update_conditions['ff_queue <' ] = shift_boundary_queue if shift_boundary_queue.present?

    # use raw query, because can not use ORDER BY in standard updateAll().
    update_rows = ff_update_all_in_order(
      update_columns,
      update_conditions,
      ff_usual_order_array(),
      "SET @forest_queue = #{start_queue}"
    )

    return 0 < update_rows.to_i
  end

  # can not fit to shift
  false
end
ff_get_wedged_node(base_node, kinship) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 51
def ff_get_wedged_node(base_node, kinship)
  is_sibling = ff_is_bool(kinship)

  if is_sibling
    ff_get_wedged_node_as_sibling(base_node, kinship)
  else
    ff_get_wedged_node_as_child(base_node, kinship)
  end
end
ff_get_wedged_node_as_child(base_node, nth) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 61
def ff_get_wedged_node_as_child(base_node, nth)
  # pickup wedged node by order of children
  if 0 <= nth
    nth_child = ff_nth_child(base_node, nth)
    return nth_child if nth_child.present?
  end

  ff_get_boundary_node(base_node)   # can be null
end
ff_get_wedged_node_as_sibling(base_node, after_sibling_node) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 71
def ff_get_wedged_node_as_sibling(base_node, after_sibling_node)
  if after_sibling_node
    ff_get_boundary_node(base_node)
  else
    base_node
  end
end
ff_move_node(node_obj, move_number, as_move_by = false) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 443
def ff_move_node(node_obj, move_number, as_move_by = false)
  aim_node = ff_resolve_nodes(node_obj)
  return false if aim_node.blank?

  siblings_query = siblings(aim_node, [@_id])
  return false if siblings_query.blank?
  sibling_nodes = siblings_query.all

  aim_id = aim_node.id

  move_number = move_number.to_i

  # get orderd rank from move_number
  if as_move_by
    return false if move_number == 0

    nth = nil
    sibling_nodes.each.with_index do |node, i|
      if node.id == aim_id
        nth = i
        break
      end
    end

    return false if nth.nil?

    nth = [0, nth + move_number].max
    nth = -1 if sibling_nodes.length <= nth
  else
    nth = move_number
  end

  nth = sibling_nodes.length - 1 if nth < 0

  return false if sibling_nodes.length <= nth

  return false if sibling_nodes[nth].id == aim_id

  new_orderd_nodes = []
  sibling_nodes.each do |node|
    new_orderd_nodes << node if node.id != aim_id
  end

  new_orderd_nodes.insert(nth, aim_node)

  permute(new_orderd_nodes)
end
ff_scoots_over(shift_node, wedged_node, depth_offset) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 167
def ff_scoots_over(shift_node, wedged_node, depth_offset)
  # find boundary node of shift node (can be null)
  aim_boundary_node = ff_get_boundary_node(shift_node)

  return false unless ff_can_graft_by_node(shift_node, aim_boundary_node, wedged_node)

  aim_grove = shift_node.ff_grove
  aim_queue = shift_node.ff_queue

  max_queue = ff_get_last_queue(aim_grove, 0) \
      if aim_boundary_node.blank? || wedged_node.blank?

  if aim_boundary_node.blank?
    aim_tail_queue = max_queue
  else
    aim_tail_queue = aim_boundary_node.ff_queue - 1
  end

  if wedged_node.blank?
    wedged_tail_queue = max_queue
  else
    wedged_tail_queue = wedged_node.ff_queue - 1
  end

  # moving direction progress/retrogress
  # when same queue, as retrogress. Therefore use "<=".
  is_retrogression = wedged_tail_queue <= aim_queue

  # moving distance
  move_offset = is_retrogression \
    ? wedged_tail_queue - aim_queue + 1
    : wedged_tail_queue - aim_tail_queue

  involved_offset = (aim_tail_queue - aim_queue + 1) * (is_retrogression ? 1 : -1)

  queue_offset_case = is_retrogression \
    ? ff_create_case_expression(
        nil,
        [["ff_queue < #{aim_queue}", involved_offset]],
        move_offset
      )
    : ff_create_case_expression(
        nil,
        [["ff_queue <= #{aim_tail_queue}", move_offset]],
        involved_offset
      )

  ffqq = arel_table[@_ff_queue]
  ffdd = arel_table[@_ff_depth]
  ffgg = arel_table[@_ff_grove]

  # WHERE conditions
  head_queue = is_retrogression ? wedged_tail_queue + 1 : aim_queue
  tail_queue = is_retrogression ? aim_tail_queue        : wedged_tail_queue
  scoots_over_query = ff_usual_conditions_scope(aim_grove)
    .where(ffqq.gteq(head_queue))
    .where(ffqq.lteq(tail_queue))

  # UPDATE SET columns
  # To set depth must be firstly, because it include condition of queue.
  # If to set queue firstly, depth condition is changed before set.
  update_columns = []
  if depth_offset != 0
    depth_offset_case = ff_create_case_expression(
      nil,
      [[
        [ffqq.gteq(aim_queue).to_sql, 'AND', ffqq.lteq(aim_tail_queue).to_sql].join(' '),
        depth_offset
      ]],
      #[["#{aim_queue} <= ff_queue AND ff_queue <= #{aim_tail_queue}", depth_offset]],
      0
    )
    update_columns << "ff_depth = ff_depth + (#{depth_offset_case})"
    #update_columns << "ff_depth = ff_depth + (#{depth_offset_case})"
  end
  update_columns << "ff_queue = ff_queue + (#{queue_offset_case})"

  # return value
  0 < scoots_over_query.update_all(update_columns.join(', '))
end
ff_usual_order_array(is_descent = false) click to toggle source
# File lib/fertile_forest/modules/reconstructers.rb, line 827
def ff_usual_order_array(is_descent = false)
  direction = is_descent ? 'DESC' : 'ASC'
  res = {}
  res[:ff_soft_delete] = direction if has_soft_delete?
  res[:ff_grove      ] = direction if has_grove?
  res[:ff_depth      ] = direction if is_descent
  res[:ff_queue      ] = direction

  res
end