ordered_tree 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,169 @@
1
+ module OrderedTree
2
+ module InstanceMethods
3
+ module Tree
4
+ ## Tree Read Methods
5
+
6
+ # returns the top node in the object's tree
7
+ #
8
+ # return is cached, unless nil
9
+ # use root(true) to force a reload
10
+ def root(reload = false)
11
+ reload = true if !@root
12
+ reload ? find_root : @root
13
+ end
14
+
15
+ # returns an array of ancestors, starting from parent until root.
16
+ # return is cached
17
+ # use ancestors(true) to force a reload
18
+ def ancestors(reload = false)
19
+ reload = true if !@ancestors
20
+ reload ? find_ancestors : @ancestors
21
+ end
22
+
23
+ # returns object's parent in the tree
24
+ # auto-loads itself on first access
25
+ # instead of returning "<parent_node not loaded yet>"
26
+ #
27
+ # return is cached, unless nil
28
+ # use parent(true) to force a reload
29
+ def parent(reload=false)
30
+ reload = true if !@parent
31
+ reload ? parent_node(true) : @parent
32
+ end
33
+
34
+ # returns an array of the object's immediate children
35
+ # auto-loads itself on first access
36
+ # instead of returning "<child_nodes not loaded yet>"
37
+ #
38
+ # return is cached
39
+ # use children(true) to force a reload
40
+ def children(reload=false)
41
+ reload = true if !@children
42
+ reload ? child_nodes(true) : @children
43
+ end
44
+
45
+ # returns an array of the object's descendants
46
+ #
47
+ # return is cached
48
+ # use descendants(true) to force a reload
49
+ def descendants(reload = false)
50
+ @descendants = nil if reload
51
+ reload = true if !@descendants
52
+ reload ? find_descendants(self) : @descendants
53
+ end
54
+
55
+ ## Tree Update Methods
56
+
57
+ # shifts a node to another parent, optionally specifying it's position
58
+ # (descendants will follow along)
59
+ #
60
+ # shift_to()
61
+ # defaults to the bottom of the "roots" list
62
+ #
63
+ # shift_to(nil, new_sibling)
64
+ # will move the item to "roots",
65
+ # and position the item above new_sibling
66
+ #
67
+ # shift_to(new_parent)
68
+ # will move the item to the new parent,
69
+ # and position at the bottom of the parent's list
70
+ #
71
+ # shift_to(new_parent, new_sibling)
72
+ # will move the item to the new parent,
73
+ # and position the item above new_sibling
74
+ #
75
+ def shift_to(new_parent = nil, new_sibling = nil)
76
+ if new_parent
77
+ ok = new_parent.children(true) << self
78
+ else
79
+ ok = orphan
80
+ end
81
+ if ok && new_sibling
82
+ ok = move_above(new_sibling) if self_and_siblings(true).include?(new_sibling)
83
+ end
84
+ return ok
85
+ end
86
+
87
+ # orphans the node (sends it to the roots list)
88
+ # (descendants follow)
89
+ def orphan
90
+ self[foreign_key_column] = 0
91
+ self.save
92
+ end
93
+
94
+ # orphans the node's children
95
+ # sends all immediate children to the 'roots' list
96
+ def orphan_children
97
+ self.class.transaction do
98
+ children(true).each{|child| child.orphan}
99
+ end
100
+ end
101
+
102
+ # hands children off to parent
103
+ # if no parent, children will be orphaned
104
+ def parent_adopts_children
105
+ if parent(true)
106
+ self.class.transaction do
107
+ children(true).each{|child| parent.children << child}
108
+ end
109
+ else
110
+ orphan_children
111
+ end
112
+ end
113
+
114
+ # sends self and immediate children to the roots list
115
+ def orphan_self_and_children
116
+ self.class.transaction do
117
+ orphan_children
118
+ orphan
119
+ end
120
+ end
121
+
122
+ # hands children off to parent (if possible), then orphans itself
123
+ def orphan_self_and_parent_adopts_children
124
+ self.class.transaction do
125
+ parent_adopts_children
126
+ orphan
127
+ end
128
+ end
129
+ end
130
+
131
+ protected
132
+
133
+ def check_parentage #:nodoc:
134
+ if !self_and_siblings(true).include?(self)
135
+ if self.parent == self
136
+ errors.add(:base, "cannot be a parent to itself.")
137
+ elsif (self.parent && self.descendants(true).include?(self.parent))
138
+ errors.add(:base, "is an ancestor of the new parent.")
139
+ end
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def find_root
146
+ node = self
147
+ node = node.parent while node.parent(true)
148
+ node
149
+ end
150
+
151
+ def find_ancestors
152
+ node, nodes = self, []
153
+ nodes << node = node.parent while node.parent(true)
154
+ nodes
155
+ end
156
+
157
+ # recursive method
158
+ def find_descendants(node)
159
+ @descendants ||= []
160
+ node.children(true).each do |child|
161
+ @descendants << child
162
+ find_descendants(child)
163
+ end
164
+ @descendants
165
+ end
166
+
167
+ end
168
+ end
169
+
@@ -0,0 +1,13 @@
1
+ require 'ordered_tree/instance_methods/tree'
2
+ require 'ordered_tree/instance_methods/list'
3
+ require 'ordered_tree/instance_methods/destroy'
4
+ require 'ordered_tree/instance_methods/misc'
5
+
6
+ module OrderedTree
7
+ module InstanceMethods
8
+ include OrderedTree::InstanceMethods::Tree
9
+ include OrderedTree::InstanceMethods::List
10
+ include OrderedTree::InstanceMethods::Destroy
11
+ include OrderedTree::InstanceMethods::Misc
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ require 'ordered_tree/class_methods'
2
+ require 'ordered_tree/instance_methods'
3
+
4
+ module OrderedTree #:nodoc:
5
+ # Configuration:
6
+ #
7
+ # class Person < ActiveRecord::Base
8
+ # ordered_tree :foreign_key => :parent_id,
9
+ # :order => :position
10
+ # end
11
+ #
12
+ # class CreatePeople < ActiveRecord::Migration
13
+ # def self.up
14
+ # create_table :people do |t|
15
+ # t.column :parent_id ,:integer ,:null => false ,:default => 0
16
+ # t.column :position ,:integer
17
+ # end
18
+ # add_index(:people, :parent_id)
19
+ # end
20
+ # end
21
+
22
+ def ordered_tree(options = {})
23
+ cattr_accessor :ordered_tree_config
24
+ self.ordered_tree_config ||= {}
25
+ self.ordered_tree_config[:foreign_key] ||= :parent_id
26
+ self.ordered_tree_config[:order] ||= :position
27
+ self.ordered_tree_config.update(options) if options.is_a?(Hash)
28
+ include OrderedTree::ClassMethods
29
+ include OrderedTree::InstanceMethods
30
+ end #ordered_tree
31
+ end #module OrderedTree
32
+
33
+ ActiveRecord::Base.extend OrderedTree
@@ -0,0 +1,85 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ordered_tree}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ramon Tayag"]
12
+ s.date = %q{2011-06-03}
13
+ s.description = %q{Uses parent_id and position to create an ordered tree.}
14
+ s.email = %q{ramon@tayag.net}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc",
18
+ "README.textile"
19
+ ]
20
+ s.files = [
21
+ ".rvmrc",
22
+ "CHANGELOG",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "Guardfile",
26
+ "LICENSE.txt",
27
+ "README.rdoc",
28
+ "README.textile",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "lib/ordered_tree.rb",
32
+ "lib/ordered_tree/class_methods.rb",
33
+ "lib/ordered_tree/instance_methods.rb",
34
+ "lib/ordered_tree/instance_methods/destroy.rb",
35
+ "lib/ordered_tree/instance_methods/list.rb",
36
+ "lib/ordered_tree/instance_methods/misc.rb",
37
+ "lib/ordered_tree/instance_methods/tree.rb",
38
+ "ordered_tree.gemspec",
39
+ "spec/fixtures/person.rb",
40
+ "spec/ordered_tree_spec.rb",
41
+ "spec/spec_helper.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/ramontayag/ordered_tree}
44
+ s.licenses = ["MIT"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.6.2}
47
+ s.summary = %q{Gem version of Wizard's ActsAsTree}
48
+
49
+ if s.respond_to? :specification_version then
50
+ s.specification_version = 3
51
+
52
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
53
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0"])
54
+ s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
55
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
56
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.2"])
57
+ s.add_development_dependency(%q<rcov>, [">= 0"])
58
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
59
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
60
+ s.add_development_dependency(%q<libnotify>, [">= 0"])
61
+ s.add_development_dependency(%q<rb-inotify>, [">= 0"])
62
+ else
63
+ s.add_dependency(%q<activerecord>, [">= 3.0.0"])
64
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
65
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
67
+ s.add_dependency(%q<rcov>, [">= 0"])
68
+ s.add_dependency(%q<sqlite3>, [">= 0"])
69
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
70
+ s.add_dependency(%q<libnotify>, [">= 0"])
71
+ s.add_dependency(%q<rb-inotify>, [">= 0"])
72
+ end
73
+ else
74
+ s.add_dependency(%q<activerecord>, [">= 3.0.0"])
75
+ s.add_dependency(%q<rspec>, ["~> 2.6.0"])
76
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
77
+ s.add_dependency(%q<jeweler>, ["~> 1.6.2"])
78
+ s.add_dependency(%q<rcov>, [">= 0"])
79
+ s.add_dependency(%q<sqlite3>, [">= 0"])
80
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
81
+ s.add_dependency(%q<libnotify>, [">= 0"])
82
+ s.add_dependency(%q<rb-inotify>, [">= 0"])
83
+ end
84
+ end
85
+
@@ -0,0 +1,3 @@
1
+ class Person < ActiveRecord::Base
2
+ ordered_tree
3
+ end