Module: CollectiveIdea::Acts::NestedSet::Base::SingletonMethods

Defined in:
lib/nested_set/base.rb

Overview

This acts provides Nested Set functionality. Nested Set is a smart way to implement an ordered tree, with the added feature that you can select the children and all of their descendants with a single query. The drawback is that insertion or move need some complex sql queries. But everything is done here by this module!

Nested sets are appropriate each time you want either an orderd tree (menus, commercial categories) or an efficient way of querying big trees (threaded posts).

API

Methods names are aligned with acts_as_tree as much as possible to make replacment from one by another easier.

item.children.create(:name => "child1")

Instance Method Summary collapse

Instance Method Details

#acts_as_nested_set(options = {}) ⇒ Object

Configuration options are:

  • :primary_key_column - specifies the column name to use for keeping the position integer (default: id)

  • :parent_column - specifies the column name to use for keeping the position integer (default: parent_id)

  • :left_column - column name for left boundry data, default “lft”

  • :right_column - column name for right boundry data, default “rgt”

  • :depth_column - column name for level cache data, default “depth”

  • :scope - restricts what is to be considered a list. Given a symbol, it’ll attach “_id” (if it hasn’t been already) and use that as the foreign key restriction. You can also pass an array to scope by multiple attributes. Example: acts_as_nested_set :scope => [:notable_id, :notable_type]

  • :dependent - behavior for cascading destroy. If set to :destroy, all the child objects are destroyed alongside this object by calling their destroy method. If set to :delete_all (default), all the child objects are deleted without calling their destroy method.

See CollectiveIdea::Acts::NestedSet::ClassMethods for a list of class methods and CollectiveIdea::Acts::NestedSet::InstanceMethods for a list of instance methods added to acts_as_nested_set models



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/nested_set/base.rb', line 45

def acts_as_nested_set(options = {})
  options = {
    :primary_key_column => self.primary_key,
    :parent_column => 'parent_id',
    :left_column => 'lft',
    :right_column => 'rgt',
    :depth_column => 'depth',
    :dependent => :delete_all
  }.merge(options)

  if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
    options[:scope] = "#{options[:scope]}_id".intern
  end

  class_attribute :acts_as_nested_set_options
  self.acts_as_nested_set_options = options

  unless self.is_a?(ClassMethods)
    include Comparable
    include Columns
    include InstanceMethods

    include Depth
    include Descendants

    extend Columns
    extend ClassMethods

    belongs_to :parent, :class_name => self.base_class.to_s,
      :foreign_key => parent_column_name
    has_many :children, :class_name => self.base_class.to_s,
      :foreign_key => parent_column_name, :order => quoted_left_column_name

    attr_accessor :skip_before_destroy

    # no bulk assignment
    if accessible_attributes.blank?
      attr_protected  left_column_name.intern, right_column_name.intern
    end

    before_create  :set_default_left_and_right
    before_save    :store_new_parent
    after_save     :move_to_new_parent
    before_destroy :destroy_descendants

    # no assignment to structure fields
    [left_column_name, right_column_name].each do |column|
      module_eval <<-"end_eval", __FILE__, __LINE__
        def #{column}=(x)
          raise ActiveRecord::ActiveRecordError, "Unauthorized assignment to #{column}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
        end
      end_eval
    end

    scope :roots, lambda {
      where(parent_column_name => nil).order(quoted_left_column_name)
    }
    scope :leaves, lambda {
      where("#{quoted_right_column_name} - #{quoted_left_column_name} = 1").
      order(quoted_left_column_name)
    }
    scope :nodes, lambda {
      where("(#{quoted_right_column_name} - #{quoted_left_column_name} - 1) / 2 != 0").
      order(quoted_left_column_name)
    }
    scope :with_depth, proc {|level| where(:"#{depth_column_name}" => level).order(quoted_left_column_name) }

    define_callbacks :move, :terminator => "result == false"
  end
end