NaixSpirit
12/12/2016 - 7:50 AM

ChainedList

ChainedList

# a project have many steps

class Step
  module ChainedList
    extend ActiveSupport::Concern
    included do
      # DNode
      field :prior_step_id, type: BSON::ObjectId
      field :next_step_id,  type: BSON::ObjectId
    end

    # Usage: next_step_without_database
    # use Enumerable#find
    def next_step(list)
      return unless has_next_step?
      list.find { |i| i.id.to_s == next_step_id.to_s }
    end

    def update_chain_data_before_destroy
      if is_top_step? && next_step
        n = next_step
        project.update_attribute(:first_step_id, n.id)
        n.update_attribute(:prior_step_id, nil)
      elsif prior_step
        p = prior_step
        p.update_attribute(:next_step_id, next_step_id)
        if next_step
          n = next_step
          n.update_attribute(:prior_step_id, p.id)
        end
      end
    end

    # Usage: next_step or next_step_with_database
    def next_step_with_database
      return unless has_next_step?
      Step.find(next_step_id.to_s)
    end
    alias_method_chain :next_step, :database

    # Usage: prior_step_without_database
    # use Enumerable#find
    def prior_step(list)
      return unless has_prior_step?
      list.find { |i| i.id.to_s == prior_step_id.to_s }
    end

    # Usage: prior_step or prior_step_with_database
    def prior_step_with_database
      return unless has_prior_step?
      Step.find(prior_step_id.to_s)
    end
    alias_method_chain :prior_step, :database

    def has_next_step?
      !!next_step_id
    end

    def has_prior_step?
      !!prior_step_id
    end

    def is_top_step?
      project.first_step_id.to_s == id.to_s
    end

    def is_bottom_step?
      !has_next_step?
    end

    class_methods do
      def update_chain_data_after_create(project, target_id, step)
        if target = find(target_id.to_s)
          step.project = project
          step.prior_step_id = target_id
          if next_step = target.next_step
            step.next_step_id = next_step.id
            next_step.update_attribute(:prior_step_id, step.id)
          end
          target.update_attribute(:next_step_id, step.id)
        else
          step.project = project
          if first_step = project.first_step # if first_step is exist?
            step.next_step_id = first_step.id
            first_step.update_attribute(:prior_step_id, step.id)
          end
          project.update_attribute(:first_step_id, step.id)
        end
        step.save && step.reload
        step
      end
    end
  end
end