closure_tree 2.0.0 → 3.0.0
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.md +58 -25
- data/lib/closure_tree/acts_as_tree.rb +56 -30
- data/lib/closure_tree/version.rb +1 -1
- data/spec/db/schema.rb +16 -0
- data/spec/label_spec.rb +67 -0
- data/spec/support/models.rb +18 -2
- data/spec/tag_spec.rb +17 -7
- metadata +12 -9
data/README.md
CHANGED
@@ -105,12 +105,22 @@ Then:
|
|
105
105
|
We can do all the node creation and add_child calls from the prior section with one method call:
|
106
106
|
|
107
107
|
```ruby
|
108
|
-
child = Tag.find_or_create_by_path("grandparent", "parent", "child")
|
108
|
+
child = Tag.find_or_create_by_path(["grandparent", "parent", "child"])
|
109
109
|
```
|
110
110
|
|
111
|
-
You can ```find``` as well as ```find_or_create``` by "ancestry paths".
|
111
|
+
You can ```find``` as well as ```find_or_create``` by "ancestry paths".
|
112
|
+
Ancestry paths may be built using any column in your model. The default
|
113
|
+
column is ```name```, which can be changed with the :name_column option
|
114
|
+
provided to ```acts_as_tree```.
|
112
115
|
|
113
|
-
Note that
|
116
|
+
Note that any other AR fields can be set with the second, optional ```attributes``` argument.
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
child = Tag.find_or_create_by_path(%w{home chuck Photos"}, {:tag_type => "File"})
|
120
|
+
```
|
121
|
+
This will pass the attribute hash of ```{:name => "home", :tag_type => "File"}``` to
|
122
|
+
```Tag.find_or_create_by_name``` if the root directory doesn't exist (and
|
123
|
+
```{:name => "chuck", :tag_type => "File"}``` if the second-level tag doesn't exist, and so on).
|
114
124
|
|
115
125
|
### Available options
|
116
126
|
<a id="options" />
|
@@ -129,35 +139,58 @@ When you include ```acts_as_tree``` in your model, you can provide a hash to ove
|
|
129
139
|
|
130
140
|
### Class methods
|
131
141
|
|
132
|
-
* ```
|
133
|
-
* ```
|
134
|
-
* ```
|
142
|
+
* ```Tag.root``` returns an arbitrary root node
|
143
|
+
* ```Tag.roots``` returns all root nodes
|
144
|
+
* ```Tag.leaves``` returns all leaf nodes
|
135
145
|
|
136
146
|
### Instance methods
|
137
147
|
|
138
|
-
* ```
|
139
|
-
* ```
|
140
|
-
* ```
|
141
|
-
* ```
|
142
|
-
* ```
|
143
|
-
* ```
|
144
|
-
* ```
|
145
|
-
* ```
|
146
|
-
* ```
|
147
|
-
* ```
|
148
|
-
* ```
|
149
|
-
* ```
|
150
|
-
* ```
|
151
|
-
* ```
|
152
|
-
* ```
|
153
|
-
|
154
|
-
##
|
155
|
-
|
156
|
-
|
148
|
+
* ```tag.root``` returns the root for this node
|
149
|
+
* ```tag.root?``` returns true if this is a root node
|
150
|
+
* ```tag.child?``` returns true if this is a child node. It has a parent.
|
151
|
+
* ```tag.leaf?``` returns true if this is a leaf node. It has no children.
|
152
|
+
* ```tag.leaves``` returns an array of all the nodes in self_and_descendants that are leaves.
|
153
|
+
* ```tag.level``` returns the level, or "generation", for this node in the tree. A root node == 0.
|
154
|
+
* ```tag.parent``` returns the node's immediate parent. Root nodes will return nil.
|
155
|
+
* ```tag.children``` returns an array of immediate children (just those nodes whose parent is the current node).
|
156
|
+
* ```tag.ancestors``` returns an array of [ parent, grandparent, great grandparent, ... ]. Note that the size of this array will always equal ```tag.level```.
|
157
|
+
* ```tag.self_and_ancestors``` returns an array of self, parent, grandparent, great grandparent, etc.
|
158
|
+
* ```tag.siblings``` returns an array of brothers and sisters (all at that level), excluding self.
|
159
|
+
* ```tag.self_and_siblings``` returns an array of brothers and sisters (all at that level), including self.
|
160
|
+
* ```tag.descendants``` returns an array of all children, childrens' children, etc., excluding self.
|
161
|
+
* ```tag.self_and_descendants``` returns an array of all children, childrens' children, etc., including self.
|
162
|
+
* ```tag.destroy``` will destroy a node and do <em>something</em> to its children, which is determined by the ```:dependent``` option passed to ```acts_as_tree```.
|
163
|
+
|
164
|
+
## Polymorphic hierarchies
|
165
|
+
|
166
|
+
Polymorphic models are supported:
|
167
|
+
|
168
|
+
1. Create a db migration that adds a String ```type``` column to your model
|
169
|
+
2. Subclass the model class. You only need to add acts_as_tree to your base class.
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
class Tag < ActiveRecord::Base
|
173
|
+
acts_as_tree
|
174
|
+
end
|
175
|
+
class WhenTag < Tag ; end
|
176
|
+
class WhereTag < Tag ; end
|
177
|
+
class WhatTag < Tag ; end
|
178
|
+
```
|
179
|
+
|
180
|
+
## Change log
|
181
|
+
|
182
|
+
### 2.0.0
|
157
183
|
|
158
184
|
* Had to increment the major version, as rebuild! will need to be called by prior consumers to support the new ```leaves``` class and instance methods.
|
159
185
|
* Tag deletion is supported now along with ```:dependent => :destroy``` and ```:dependent => :delete_all```
|
160
186
|
* Switched from default rails plugin directory structure to rspec
|
187
|
+
* Support for running specs under different database engines: ```export DB ; for DB in sqlite3 mysql postgresql ; do rake ; done```
|
188
|
+
|
189
|
+
### 3.0.0
|
190
|
+
|
191
|
+
* Support for polymorphic trees
|
192
|
+
* ```find_by_path``` and ```find_or_create_by_path``` signatures changed to support constructor attributes
|
193
|
+
* tested against Rails 3.1.3
|
161
194
|
|
162
195
|
## Thanks to
|
163
196
|
|
@@ -10,6 +10,8 @@ module ClosureTree
|
|
10
10
|
:name_column => 'name'
|
11
11
|
}.merge(options)
|
12
12
|
|
13
|
+
raise IllegalArgumentException, "name_column can't be 'path'" if closure_tree_options[:name_column] == 'path'
|
14
|
+
|
13
15
|
include ClosureTree::Columns
|
14
16
|
extend ClosureTree::Columns
|
15
17
|
|
@@ -19,8 +21,8 @@ module ClosureTree
|
|
19
21
|
self.hierarchy_class = Object.const_set hierarchy_class_name, Class.new(ActiveRecord::Base)
|
20
22
|
|
21
23
|
self.hierarchy_class.class_eval <<-RUBY
|
22
|
-
belongs_to :ancestor, :class_name => "#{
|
23
|
-
belongs_to :descendant, :class_name => "#{
|
24
|
+
belongs_to :ancestor, :class_name => "#{ct_class.to_s}"
|
25
|
+
belongs_to :descendant, :class_name => "#{ct_class.to_s}"
|
24
26
|
RUBY
|
25
27
|
|
26
28
|
include ClosureTree::Model
|
@@ -30,23 +32,23 @@ module ClosureTree
|
|
30
32
|
after_save :acts_as_tree_after_save
|
31
33
|
|
32
34
|
belongs_to :parent,
|
33
|
-
:class_name =>
|
35
|
+
:class_name => ct_class.to_s,
|
34
36
|
:foreign_key => parent_column_name
|
35
37
|
|
36
38
|
has_many :children,
|
37
|
-
:class_name =>
|
39
|
+
:class_name => ct_class.to_s,
|
38
40
|
:foreign_key => parent_column_name,
|
39
41
|
:dependent => closure_tree_options[:dependent]
|
40
42
|
|
41
43
|
has_and_belongs_to_many :self_and_ancestors,
|
42
|
-
:class_name =>
|
44
|
+
:class_name => ct_class.to_s,
|
43
45
|
:join_table => hierarchy_table_name,
|
44
46
|
:foreign_key => "descendant_id",
|
45
47
|
:association_foreign_key => "ancestor_id",
|
46
48
|
:order => "generations asc"
|
47
49
|
|
48
50
|
has_and_belongs_to_many :self_and_descendants,
|
49
|
-
:class_name =>
|
51
|
+
:class_name => ct_class.to_s,
|
50
52
|
:join_table => hierarchy_table_name,
|
51
53
|
:foreign_key => "ancestor_id",
|
52
54
|
:association_foreign_key => "descendant_id",
|
@@ -124,21 +126,39 @@ module ClosureTree
|
|
124
126
|
without_self(self_and_siblings)
|
125
127
|
end
|
126
128
|
|
127
|
-
#
|
129
|
+
# Alias for appending to the children collection.
|
130
|
+
# You can also add directly to the children collection, if you'd prefer.
|
128
131
|
def add_child(child_node)
|
129
132
|
children << child_node
|
130
133
|
child_node
|
131
134
|
end
|
132
135
|
|
133
136
|
# Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+.
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
+
def find_by_path(path)
|
138
|
+
path = [path] unless path.is_a? Enumerable
|
139
|
+
node = self
|
140
|
+
while (!path.empty? && node)
|
141
|
+
node = node.children.send("find_by_#{name_column}", path.shift)
|
142
|
+
end
|
143
|
+
node
|
137
144
|
end
|
138
145
|
|
139
146
|
# Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+
|
140
|
-
def find_or_create_by_path(
|
141
|
-
|
147
|
+
def find_or_create_by_path(path, attributes = {})
|
148
|
+
path = [path] unless path.is_a? Enumerable
|
149
|
+
node = self
|
150
|
+
attrs = {}
|
151
|
+
attrs[:type] = self.type if ct_subclass? && ct_has_type?
|
152
|
+
path.each do |name|
|
153
|
+
attrs[name_sym] = name
|
154
|
+
child = node.children.where(attrs).first
|
155
|
+
unless child
|
156
|
+
child = self.class.new(attributes.merge attrs)
|
157
|
+
node.children << child
|
158
|
+
end
|
159
|
+
node = child
|
160
|
+
end
|
161
|
+
node
|
142
162
|
end
|
143
163
|
|
144
164
|
protected
|
@@ -195,16 +215,6 @@ module ClosureTree
|
|
195
215
|
SQL
|
196
216
|
end
|
197
217
|
|
198
|
-
def foc_by_path(method_prefix, *path)
|
199
|
-
path = path.flatten
|
200
|
-
return self if path.empty?
|
201
|
-
node = self
|
202
|
-
while (!path.empty? && node)
|
203
|
-
node = node.children.send("#{method_prefix}_by_#{name_column}", path.shift)
|
204
|
-
end
|
205
|
-
node
|
206
|
-
end
|
207
|
-
|
208
218
|
def without_self(scope)
|
209
219
|
scope.where(["#{quoted_table_name}.#{self.class.primary_key} != ?", self])
|
210
220
|
end
|
@@ -230,17 +240,21 @@ module ClosureTree
|
|
230
240
|
end
|
231
241
|
|
232
242
|
# Find the node whose +ancestry_path+ is +path+
|
233
|
-
def find_by_path(
|
234
|
-
|
235
|
-
|
236
|
-
r.nil? ? nil : r.find_by_path(*path)
|
243
|
+
def find_by_path(path)
|
244
|
+
root = roots.send("find_by_#{name_column}", path.shift)
|
245
|
+
root.try(:find_by_path, path)
|
237
246
|
end
|
238
247
|
|
239
248
|
# Find or create nodes such that the +ancestry_path+ is +path+
|
240
|
-
def find_or_create_by_path(
|
241
|
-
|
242
|
-
|
243
|
-
|
249
|
+
def find_or_create_by_path(path, attributes = {})
|
250
|
+
name = path.shift
|
251
|
+
# shenanigans because find_or_create can't infer we want the same class as this:
|
252
|
+
# Note that roots will already be constrained to this subclass (in the case of polymorphism):
|
253
|
+
root = roots.send("find_by_#{name_column}", name)
|
254
|
+
if root.nil?
|
255
|
+
root = create!(attributes.merge(name_sym => name))
|
256
|
+
end
|
257
|
+
root.find_or_create_by_path(path, attributes)
|
244
258
|
end
|
245
259
|
end
|
246
260
|
end
|
@@ -289,6 +303,18 @@ module ClosureTree
|
|
289
303
|
(self.is_a?(Class) ? self : self.class)
|
290
304
|
end
|
291
305
|
|
306
|
+
def ct_subclass?
|
307
|
+
ct_class != ct_class.base_class
|
308
|
+
end
|
309
|
+
|
310
|
+
def ct_attribute_names
|
311
|
+
@ct_attr_names ||= ct_class.new.attributes.keys - ct_class.protected_attributes.to_a
|
312
|
+
end
|
313
|
+
|
314
|
+
def ct_has_type?
|
315
|
+
ct_attribute_names.include? 'type'
|
316
|
+
end
|
317
|
+
|
292
318
|
def ct_table_name
|
293
319
|
ct_class.table_name
|
294
320
|
end
|
data/lib/closure_tree/version.rb
CHANGED
data/spec/db/schema.rb
CHANGED
@@ -37,4 +37,20 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
37
37
|
|
38
38
|
add_index :referral_hierarchies, [:ancestor_id, :descendant_id], :unique => true
|
39
39
|
add_index :referral_hierarchies, [:descendant_id]
|
40
|
+
|
41
|
+
create_table "labels", :force => true do |t|
|
42
|
+
t.string "name"
|
43
|
+
t.string "type"
|
44
|
+
t.integer "parent_id"
|
45
|
+
end
|
46
|
+
|
47
|
+
create_table "label_hierarchies", :id => false, :force => true do |t|
|
48
|
+
t.integer "ancestor_id", :null => false
|
49
|
+
t.integer "descendant_id", :null => false
|
50
|
+
t.integer "generations", :null => false
|
51
|
+
end
|
52
|
+
|
53
|
+
add_index :label_hierarchies, [:ancestor_id, :descendant_id], :unique => true
|
54
|
+
add_index :label_hierarchies, [:descendant_id]
|
55
|
+
|
40
56
|
end
|
data/spec/label_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
def nuke_db
|
4
|
+
Label.delete_all
|
5
|
+
LabelHierarchy.delete_all
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Label do
|
9
|
+
context "Base Label class" do
|
10
|
+
it "should find or create by path" do
|
11
|
+
# class method:
|
12
|
+
c = Label.find_or_create_by_path(%w{grandparent parent child})
|
13
|
+
c.ancestry_path.should == %w{grandparent parent child}
|
14
|
+
c.name.should == "child"
|
15
|
+
c.parent.name.should == "parent"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
context "DateLabel" do
|
19
|
+
|
20
|
+
it "should find or create by path" do
|
21
|
+
date = DateLabel.find_or_create_by_path(%w{2011 November 23})
|
22
|
+
date.ancestry_path.should == %w{2011 November 23}
|
23
|
+
date.parent
|
24
|
+
date.self_and_ancestors.each { |ea| ea.class.should == DateLabel }
|
25
|
+
date.name.should == "23"
|
26
|
+
date.parent.name.should == "November"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "DirectoryLabel" do
|
31
|
+
it "should find or create by path" do
|
32
|
+
dir = DirectoryLabel.find_or_create_by_path(%w{grandparent parent child})
|
33
|
+
dir.ancestry_path.should == %w{grandparent parent child}
|
34
|
+
dir.name.should == "child"
|
35
|
+
dir.parent.name.should == "parent"
|
36
|
+
dir.parent.parent.name.should == "grandparent"
|
37
|
+
dir.root.name.should == "grandparent"
|
38
|
+
dir.id.should_not == Label.find_or_create_by_path(%w{grandparent parent child})
|
39
|
+
dir.self_and_ancestors.each { |ea| ea.class.should == DirectoryLabel }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "Mixed class tree" do
|
44
|
+
it "should support mixed type ancestors" do
|
45
|
+
[Label, DateLabel, DirectoryLabel, EventLabel].permutation do |classes|
|
46
|
+
nuke_db
|
47
|
+
classes.each{|c|c.all.should(be_empty, "class #{c} wasn't cleaned out") }
|
48
|
+
names = ('A'..'Z').to_a.first(classes.size)
|
49
|
+
instances = classes.collect { |clazz| clazz.new(:name => names.shift) }
|
50
|
+
a = instances.first
|
51
|
+
a.save!
|
52
|
+
a.name.should == "A"
|
53
|
+
instances[1..-1].each_with_index do |ea, idx|
|
54
|
+
instances[idx].children << ea
|
55
|
+
end
|
56
|
+
roots = classes.first.roots
|
57
|
+
i = instances.shift
|
58
|
+
roots.should =~ [i]
|
59
|
+
while (!instances.empty?) do
|
60
|
+
child = instances.shift
|
61
|
+
i.children.should =~ [child]
|
62
|
+
i = child
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/support/models.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
class Tag < ActiveRecord::Base
|
2
2
|
acts_as_tree :dependent => :destroy
|
3
|
-
before_destroy :
|
3
|
+
before_destroy :add_destroyed_tag
|
4
4
|
|
5
5
|
def to_s
|
6
6
|
name
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
9
|
+
def add_destroyed_tag
|
10
10
|
# Proof for the tests that the destroy rather than the delete method was called:
|
11
11
|
DestroyedTag.create(:name => name)
|
12
12
|
end
|
@@ -24,3 +24,19 @@ class User < ActiveRecord::Base
|
|
24
24
|
email
|
25
25
|
end
|
26
26
|
end
|
27
|
+
|
28
|
+
class Label < ActiveRecord::Base
|
29
|
+
acts_as_tree
|
30
|
+
def to_s
|
31
|
+
"#{self.class}: #{name}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class EventLabel < Label
|
36
|
+
end
|
37
|
+
|
38
|
+
class DateLabel < Label
|
39
|
+
end
|
40
|
+
|
41
|
+
class DirectoryLabel < Label
|
42
|
+
end
|
data/spec/tag_spec.rb
CHANGED
@@ -2,14 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "empty db" do
|
4
4
|
|
5
|
-
def
|
5
|
+
def nuke_db
|
6
6
|
Tag.delete_all
|
7
7
|
TagHierarchy.delete_all
|
8
8
|
DestroyedTag.delete_all
|
9
9
|
end
|
10
10
|
|
11
11
|
before :each do
|
12
|
-
|
12
|
+
nuke_db
|
13
13
|
end
|
14
14
|
|
15
15
|
context "empty db" do
|
@@ -63,7 +63,7 @@ describe "empty db" do
|
|
63
63
|
Tag.all.should be_empty
|
64
64
|
Tag.roots.should be_empty
|
65
65
|
Tag.leaves.should be_empty
|
66
|
-
DestroyedTag.all.collect{|t|t.name}.should =~ %w{root mid leaf}
|
66
|
+
DestroyedTag.all.collect { |t| t.name }.should =~ %w{root mid leaf}
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
@@ -200,10 +200,10 @@ describe Tag do
|
|
200
200
|
it "should cascade delete all children" do
|
201
201
|
b2 = tags(:b2)
|
202
202
|
entities = b2.self_and_descendants.to_a
|
203
|
-
names = b2.self_and_descendants.collect{|t|t.name}
|
203
|
+
names = b2.self_and_descendants.collect { |t| t.name }
|
204
204
|
b2.destroy
|
205
|
-
entities.each{|e| Tag.find_by_id(e.id).should be_nil }
|
206
|
-
DestroyedTag.all.collect{|t|t.name}.should =~ names
|
205
|
+
entities.each { |e| Tag.find_by_id(e.id).should be_nil }
|
206
|
+
DestroyedTag.all.collect { |t| t.name }.should =~ names
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
@@ -257,9 +257,19 @@ describe Tag do
|
|
257
257
|
tags(:parent).find_by_path(%w{child larvae}).should be_nil
|
258
258
|
end
|
259
259
|
|
260
|
+
it "should return nil for missing nodes" do
|
261
|
+
Tag.find_by_path(%w{missing}).should be_nil
|
262
|
+
Tag.find_by_path(%w{grandparent missing}).should be_nil
|
263
|
+
Tag.find_by_path(%w{grandparent parent missing}).should be_nil
|
264
|
+
Tag.find_by_path(%w{grandparent parent missing child}).should be_nil
|
265
|
+
end
|
266
|
+
|
260
267
|
it "should find or create by path" do
|
261
268
|
# class method:
|
262
|
-
Tag.find_or_create_by_path(%w{grandparent
|
269
|
+
grandparent = Tag.find_or_create_by_path(%w{grandparent})
|
270
|
+
grandparent.should == tags(:grandparent)
|
271
|
+
child = Tag.find_or_create_by_path(%w{grandparent parent child})
|
272
|
+
child.should == tags(:child)
|
263
273
|
Tag.find_or_create_by_path(%w{events anniversary}).ancestry_path.should == %w{events anniversary}
|
264
274
|
a = Tag.find_or_create_by_path(%w{a})
|
265
275
|
a.ancestry_path.should == %w{a}
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: closure_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
-
-
|
7
|
+
- 3
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
version:
|
10
|
+
version: 3.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matthew McEachen
|
@@ -15,10 +15,12 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-27 00:00:00 -08:00
|
19
|
+
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
22
24
|
none: false
|
23
25
|
requirements:
|
24
26
|
- - ">="
|
@@ -29,10 +31,9 @@ dependencies:
|
|
29
31
|
- 0
|
30
32
|
- 0
|
31
33
|
version: 3.0.0
|
32
|
-
requirement: *id001
|
33
34
|
type: :runtime
|
34
|
-
prerelease: false
|
35
35
|
name: activerecord
|
36
|
+
version_requirements: *id001
|
36
37
|
description: " A mostly-API-compatible replacement for the acts_as_tree and awesome_nested_set gems,\n but with much better mutation performance thanks to the Closure Tree storage algorithm\n"
|
37
38
|
email:
|
38
39
|
- matthew-github@mceachen.org
|
@@ -53,10 +54,12 @@ files:
|
|
53
54
|
- spec/db/database.yml
|
54
55
|
- spec/db/schema.rb
|
55
56
|
- spec/fixtures/tags.yml
|
57
|
+
- spec/label_spec.rb
|
56
58
|
- spec/spec_helper.rb
|
57
59
|
- spec/support/models.rb
|
58
60
|
- spec/tag_spec.rb
|
59
61
|
- spec/user_spec.rb
|
62
|
+
has_rdoc: true
|
60
63
|
homepage: http://matthew.mceachen.us/closure_tree
|
61
64
|
licenses: []
|
62
65
|
|
@@ -86,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
89
|
requirements: []
|
87
90
|
|
88
91
|
rubyforge_project:
|
89
|
-
rubygems_version: 1.
|
92
|
+
rubygems_version: 1.6.2
|
90
93
|
signing_key:
|
91
94
|
specification_version: 3
|
92
95
|
summary: Hierarchies for ActiveRecord models using a Closure Tree storage algorithm
|
@@ -94,8 +97,8 @@ test_files:
|
|
94
97
|
- spec/db/database.yml
|
95
98
|
- spec/db/schema.rb
|
96
99
|
- spec/fixtures/tags.yml
|
100
|
+
- spec/label_spec.rb
|
97
101
|
- spec/spec_helper.rb
|
98
102
|
- spec/support/models.rb
|
99
103
|
- spec/tag_spec.rb
|
100
104
|
- spec/user_spec.rb
|
101
|
-
has_rdoc:
|