Module: PathTree

Includes:
Patterns
Defined in:
lib/path_tree.rb,
lib/ruby_19_patterns.rb,
lib/path_tree/version.rb

Overview

This module implements a tree structure by using a convention of converting a name into a path. Paths created by normalizing a name attribute and then separating levels with periods with the lowest level coming last.

In order to use this module, the model must respond to the first and all methods like ActiveRecord, have support for after_destroy and after_save callbacks, validates_* macros and include attributes for name, node_path, path, and parent_path.

Defined Under Namespace

Modules: ClassMethods, Patterns

Constant Summary collapse

VERSION =
'1.0.12'.freeze

Constants included from Patterns

Patterns::LOWER_AE_PATTERN, Patterns::LOWER_A_PATTERN, Patterns::LOWER_C_PATTERN, Patterns::LOWER_E_PATTERN, Patterns::LOWER_I_PATTERN, Patterns::LOWER_N_PATTERN, Patterns::LOWER_O_PATTERN, Patterns::LOWER_U_PATTERN, Patterns::LOWER_Y_PATTERN, Patterns::SS_PATTERN, Patterns::UPPER_AE_PATTERN, Patterns::UPPER_A_PATTERN, Patterns::UPPER_C_PATTERN, Patterns::UPPER_D_PATTERN, Patterns::UPPER_E_PATTERN, Patterns::UPPER_I_PATTERN, Patterns::UPPER_N_PATTERN, Patterns::UPPER_O_PATTERN, Patterns::UPPER_U_PATTERN, Patterns::UPPER_Y_PATTERN

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/path_tree.rb', line 16

def self.included (base)
  base.extend(ClassMethods)
  
  base.validates_uniqueness_of :path
  base.validates_uniqueness_of :node_path, :scope => :parent_path
  base.validates_presence_of :name, :node_path, :path
  
  base.after_save do |record|
    if record.path_changed? and !record.path_was.nil?
      record.children.each do |child|
        child.update_attributes(:parent_path => record.path)
      end
    end
    record.instance_variable_set(:@children, nil)
  end
  
  base.after_destroy do |record|
    record.children.each do |child|
      child.update_attributes(:parent_path => record.parent_path)
    end
  end
end

Instance Method Details

#ancestorsObject

Get all ancestors of this node with the root node first.



219
220
221
222
223
224
225
226
227
# File 'lib/path_tree.rb', line 219

def ancestors
  ancestor_paths = expanded_paths
  ancestor_paths.pop
  if ancestor_paths.empty?
    []
  else
    self.class.base_class.all(:conditions => {:path => ancestor_paths}).sort{|a,b| a.path.length <=> b.path.length}
  end
end

#childrenObject

Get all nodes that are direct children of this node.



199
200
201
202
203
204
205
206
# File 'lib/path_tree.rb', line 199

def children
  unless @children
    childrens_path = new_record? ? path : path_was
    @children = self.class.base_class.all(:conditions => {:parent_path => childrens_path})
    @children.each{|c| c.parent = self}
  end
  @children
end

#descendantsObject

Get all descendant of this node.



214
215
216
# File 'lib/path_tree.rb', line 214

def descendants
  self.class.base_class.path_like(path)
end

#expanded_pathsObject

Returns an array containing the paths of this node and those of all its ancestors.



230
231
232
# File 'lib/path_tree.rb', line 230

def expanded_paths
  self.class.expanded_paths(path)
end

#full_name(options = {}) ⇒ Object

Get the full name of a node including the names of all it’s parent nodes. Specify the separator string to use between values with :separator (defaults to “ > ”). You can also specify the context for the full name by specifying a path in :context. This will only render the names up to and not including that part of the tree.



143
144
145
146
147
148
149
# File 'lib/path_tree.rb', line 143

def full_name (options = {})
  separator = options[:separator] || " > "
  n = ""
  n << parent.full_name(options) if parent_path and parent_path != options[:context]
  n << separator unless n.blank?
  n << name
end

#name=(value) ⇒ Object



184
185
186
187
188
189
190
# File 'lib/path_tree.rb', line 184

def name= (value)
  unless value == name
    self[:name] = value
    self.node_path = value if node_path.blank?
  end
  value
end

#node_path=(value) ⇒ Object



192
193
194
195
196
# File 'lib/path_tree.rb', line 192

def node_path= (value)
  pathified = self.class.pathify(value)
  self[:node_path] = pathified
  recalculate_path
end

#parentObject

Get the parent node.



156
157
158
159
160
161
162
163
164
165
# File 'lib/path_tree.rb', line 156

def parent
  unless instance_variable_defined?(:@parent)
    if path.index(path_delimiter)
      @parent = self.class.base_class.first(:conditions => {:path => parent_path})
    else
      @parent = nil
    end
  end
  @parent
end

#parent=(node) ⇒ Object

Set the parent node.



168
169
170
171
172
# File 'lib/path_tree.rb', line 168

def parent= (node)
  node_path = node.path if node
  self.parent_path = node_path unless parent_path == node_path
  @parent = node
end

#parent_path=(value) ⇒ Object

Set the parent path



175
176
177
178
179
180
181
182
# File 'lib/path_tree.rb', line 175

def parent_path= (value)
  unless value == parent_path
    self[:parent_path] = value
    recalculate_path
    remove_instance_variable(:@parent) if instance_variable_defined?(:@parent)
  end
  value
end

#path_delimiterObject



151
152
153
# File 'lib/path_tree.rb', line 151

def path_delimiter
  self.class.path_delimiter
end

#siblingsObject

Get all nodes that share the same parent as this node.



209
210
211
# File 'lib/path_tree.rb', line 209

def siblings
  self.class.base_class.all(:conditions => {:parent_path => parent_path}).reject{|node| node == self}
end