验证节点在嵌套集中的移动

时间:2009-06-03 20:45:20

标签: ruby-on-rails validation plugins nested-sets

我正在创建一个Category模型并使用awesome_nested_set插件(acts_as_nested_set的替代品)来处理层次结构。使用awesome_nested_set,创建对象,然后保存,然后放置在集合中。同样,lftrgtparent_idattr_protected,因此无法直接写入。{/ p>

当我将节点放入我想要捕获的集合中时,我遇到了两种情况,因此我通知用户(可能还有更多我还没想过):

  1. 尝试将节点作为自己的子节点(self.id == self.parent_id
  2. 放置
  3. 尝试在自己的后代(self.descendants.include? self.parent_id == true
  4. 下移动节点

    在这两种情况下,移动都会失败,但awesome_nested_set只会引发ActiveRecord::ActiveRecordError例外,其中的消息不像我希望能够提供给用户那样具有描述性。

    awesome_nested_set有许多节点移动方法,所有方法都调用move_to(target, position)(其中position:root之一,:child,{{1}或:left:right是所有target的相关节点,但position)。该方法触发:root回调,但没有提供一种方法,我可以看到它在发生之前验证移动。为了验证移动,我需要访问回调未收到的目标和位置。

    有没有人知道在before_move中验证移动的方法(通过一种方法将目标和位置传递给另一个方法的awesome_nested_set回调),或另一个嵌套的set插件那会让我验证吗?我不想分叉或编写自己的插件。

1 个答案:

答案 0 :(得分:3)

以下是我提出的解决方案:

class Category < ActiveRecord::Base
  acts_as_nested_set :dependent => :destroy

  #=== Nested set methods ===

  def save_with_place_in_set(parent_id = nil)
    Category.transaction do
      return false if !save_without_place_in_set
      raise ActiveRecord::Rollback if !validate_move parent_id

      place_in_nested_set parent_id
      return true
    end

    return false
  end

  alias_method_chain :save, :place_in_set

  def validate_move(parent_id)
    raise ActiveRecord::RecordNotSaved, "record must be saved before moved into the nested set" if new_record?
    return true if parent_id.nil?

    parent_id = parent_id.to_i

    if self.id == parent_id
      @error = :cannot_be_child_of_self
    elsif !Category.all.map(&:id).include?(parent_id)
      @error = :given_parent_is_invalid
    elsif descendants.map(&:id).include? parent_id
      @error = :cannot_be_child_of_descendant
    end

    errors.add(:parent_id, @error) if @error
    return @error.nil?
  end

  def place_in_nested_set(parent_id)
    if parent_id.nil? || parent_id.blank?
      move_to_root
    else
      move_to_child_of parent_id
    end
    return true
  end
end

现在,在控制器中,我只需要说@category.save(parent_id),其中parent_idnil或父级的ID,然后处理验证,节点放置和保存在模型中。