closure_tree 1.0.0.beta1 → 1.0.0.beta2
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.
- data/README.rdoc +13 -1
- data/lib/closure_tree/acts_as_tree.rb +61 -1
- data/lib/closure_tree/version.rb +1 -1
- data/test/dummy/config/database.yml +6 -0
- data/test/dummy/test/unit/tag_test.rb +23 -0
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -34,7 +34,7 @@ Note that closure_tree is being developed for Rails 3.1.0.rc1
|
|
34
34
|
|
35
35
|
class CreateTagHierarchy < ActiveRecord::Migration
|
36
36
|
def change
|
37
|
-
create_table :tags_hierarchy do |t|
|
37
|
+
create_table :tags_hierarchy, :id => false do |t|
|
38
38
|
t.integer :ancestor_id, :null => false # ID of the parent/grandparent/great-grandparent/... tag
|
39
39
|
t.integer :descendant_id, :null => false # ID of the target tag
|
40
40
|
t.integer :generations, :null => false # Number of generations between the ancestor and the descendant. Parent/child = 1, for example.
|
@@ -79,6 +79,18 @@ Then:
|
|
79
79
|
puts grandparent.self_and_descendants.collect{ |t| t.name }.join(" > ")
|
80
80
|
"grandparent > parent > child"
|
81
81
|
|
82
|
+
child.ancestry_names
|
83
|
+
["grandparent", "parent", "child"]
|
84
|
+
|
85
|
+
=== <code>find_or_create_by_path</code>
|
86
|
+
|
87
|
+
If your model has a single string column that can build a given node (like a "name" or a "title"),
|
88
|
+
you can use that to find_or_create:
|
89
|
+
|
90
|
+
Tag.find_or_create_by_path %w{one two three}
|
91
|
+
|
92
|
+
You can customize the column name with the :name_column option in <code>acts_as_tree</code>.
|
93
|
+
|
82
94
|
== Accessing Data
|
83
95
|
|
84
96
|
=== Class methods
|
@@ -6,7 +6,8 @@ module ClosureTree #:nodoc:
|
|
6
6
|
self.closure_tree_options = {
|
7
7
|
:parent_column_name => 'parent_id',
|
8
8
|
:dependent => :delete_all, # or :destroy
|
9
|
-
:hierarchy_table_suffix => '_hierarchies'
|
9
|
+
:hierarchy_table_suffix => '_hierarchies',
|
10
|
+
:name_column => 'name'
|
10
11
|
}.merge(options)
|
11
12
|
|
12
13
|
include ClosureTree::Columns
|
@@ -90,6 +91,12 @@ module ClosureTree #:nodoc:
|
|
90
91
|
[self].concat ancestors.to_a
|
91
92
|
end
|
92
93
|
|
94
|
+
# Returns an array, root first, of self_and_ancestors' +name_column+ values
|
95
|
+
# (so child.ancestry_names == +%w{grandparent parent child}+
|
96
|
+
def ancestry_names
|
97
|
+
self_and_ancestors.reverse.collect { |n| n.send name_column.to_sym }
|
98
|
+
end
|
99
|
+
|
93
100
|
def self_and_descendants
|
94
101
|
[self].concat descendants.to_a
|
95
102
|
end
|
@@ -121,8 +128,26 @@ module ClosureTree #:nodoc:
|
|
121
128
|
new_parent.add_child self
|
122
129
|
end
|
123
130
|
|
131
|
+
# Find a child node whose +ancestry_names+ minus self.ancestry_names is +path+
|
132
|
+
def find_by_path path
|
133
|
+
_find_or_create_by_path path, "find_by_#{name_column}".to_sym
|
134
|
+
end
|
135
|
+
|
136
|
+
# Find a child node whose +ancestry_names+ minus self.ancestry_names is +path+
|
137
|
+
def find_or_create_by_path path
|
138
|
+
_find_or_create_by_path path, "find_or_create_by_#{name_column}".to_sym
|
139
|
+
end
|
140
|
+
|
124
141
|
protected
|
125
142
|
|
143
|
+
def _find_or_create_by_path path, methodname
|
144
|
+
node = self
|
145
|
+
while (name = path.shift and node)
|
146
|
+
node = node.children.send(methodname, name)
|
147
|
+
end
|
148
|
+
node
|
149
|
+
end
|
150
|
+
|
126
151
|
def without_self(scope)
|
127
152
|
scope.where(["#{quoted_table_name}.#{self.class.primary_key} != ?", self])
|
128
153
|
end
|
@@ -145,6 +170,29 @@ module ClosureTree #:nodoc:
|
|
145
170
|
nil
|
146
171
|
end
|
147
172
|
|
173
|
+
# Find the node whose +ancestry_names+ is +path+
|
174
|
+
def find_by_path path
|
175
|
+
self.where(name_sym => path.last).each do |n|
|
176
|
+
return n if path == n.ancestry_names
|
177
|
+
end
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
|
181
|
+
# Find or create nodes such that the +ancestry_names+ is +path+
|
182
|
+
def find_or_create_by_path path
|
183
|
+
# short-circuit if we can:
|
184
|
+
n = find_by_path path
|
185
|
+
return n if n
|
186
|
+
|
187
|
+
name = path.shift
|
188
|
+
node = roots.where(name_sym => name).first
|
189
|
+
node = create!(name_sym => name) unless node
|
190
|
+
while (name = path.shift)
|
191
|
+
node = node.children.send("find_or_create_by_#{name_column}".to_sym, name)
|
192
|
+
end
|
193
|
+
node
|
194
|
+
end
|
195
|
+
|
148
196
|
private
|
149
197
|
def rebuild_node_and_children node
|
150
198
|
node.parent.add_child node if node.parent
|
@@ -162,6 +210,18 @@ module ClosureTree #:nodoc:
|
|
162
210
|
closure_tree_options[:parent_column_name]
|
163
211
|
end
|
164
212
|
|
213
|
+
def has_name?
|
214
|
+
ct_class.new.attributes.include? closure_tree_options[:name_column]
|
215
|
+
end
|
216
|
+
|
217
|
+
def name_column
|
218
|
+
closure_tree_options[:name_column]
|
219
|
+
end
|
220
|
+
|
221
|
+
def name_sym
|
222
|
+
name_column.to_sym
|
223
|
+
end
|
224
|
+
|
165
225
|
def hierarchy_table_name
|
166
226
|
ct_table_name + closure_tree_options[:hierarchy_table_suffix]
|
167
227
|
end
|
data/lib/closure_tree/version.rb
CHANGED
@@ -56,6 +56,29 @@ class TagTest < ActiveSupport::TestCase
|
|
56
56
|
assert_equal [tags(:child), tags(:parent), tags(:grandparent)], tags(:child).self_and_ancestors
|
57
57
|
end
|
58
58
|
|
59
|
+
def test_ancestry_path
|
60
|
+
assert_equal %w{grandparent parent child}, tags(:child).ancestry_names
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_find_by_path
|
64
|
+
# class method:
|
65
|
+
assert_equal tags(:child), Tag.find_by_path(%w{grandparent parent child})
|
66
|
+
assert_equal tags(:child), Tag.find_or_create_by_path(%w{grandparent parent child})
|
67
|
+
# instance method:
|
68
|
+
assert_equal tags(:child), tags(:parent).find_by_path(%w{child})
|
69
|
+
assert_equal tags(:child), tags(:grandparent).find_by_path(%w{parent child})
|
70
|
+
assert_nil tags(:parent).find_by_path(%w{child larvae})
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_find_or_create_by_path
|
74
|
+
# class method:
|
75
|
+
assert_equal %w{events anniversary}, Tag.find_or_create_by_path(%w{events anniversary}).ancestry_names
|
76
|
+
a = Tag.find_or_create_by_path(%w{a})
|
77
|
+
assert_equal %w{a}, a.ancestry_names
|
78
|
+
# instance method:
|
79
|
+
assert_equal %w{a b c}, a.find_or_create_by_path(%w{b c}).ancestry_names
|
80
|
+
end
|
81
|
+
|
59
82
|
def test_descendants
|
60
83
|
assert_equal [tags(:child)], tags(:parent).descendants
|
61
84
|
assert_equal [tags(:parent), tags(:child)], tags(:parent).self_and_descendants
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: closure_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: 6
|
5
|
-
version: 1.0.0.
|
5
|
+
version: 1.0.0.beta2
|
6
6
|
platform: ruby
|
7
7
|
authors: []
|
8
8
|
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-05-
|
13
|
+
date: 2011-05-25 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -73,7 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
73
|
requirements:
|
74
74
|
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
hash:
|
76
|
+
hash: -1571970483886197148
|
77
77
|
segments:
|
78
78
|
- 0
|
79
79
|
version: "0"
|