acts_as_dag 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/acts_as_dag/acts_as_dag.rb +26 -19
- data/spec/acts_as_dag_spec.rb +48 -22
- data/spec/spec_helper.rb +1 -1
- metadata +12 -16
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3d531050423c7ca9be51544e54596e0310b96b2c
|
4
|
+
data.tar.gz: a0ec98ace714701dec05881502ffa834646d9fab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c618e157390becaede12d0065b237945852cb3d64f5e4400933c10748e64a32bb80b1ecddc7245545b8dacea0c52d996ad74206440001c371f114ec821c55e64
|
7
|
+
data.tar.gz: 59fb1e46422ff35ec92c13b3877be868ce044ce4c50011b3a55f992877bd4965c109076937eb92e5effbf9717803145f872a99d2495e4659c21773c2c19ea66a
|
@@ -75,7 +75,7 @@ module ActsAsDAG
|
|
75
75
|
after_create :initialize_descendants
|
76
76
|
|
77
77
|
extend ActsAsDAG::ClassMethods
|
78
|
-
include ActsAsDAG::InstanceMethods
|
78
|
+
include ActsAsDAG::InstanceMethods
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -88,7 +88,7 @@ module ActsAsDAG
|
|
88
88
|
# Can pass a list of categories and only those will be reorganized
|
89
89
|
def reorganize(categories_to_reorganize = self.all)
|
90
90
|
return if categories_to_reorganize.empty?
|
91
|
-
|
91
|
+
|
92
92
|
reset_hierarchy(categories_to_reorganize)
|
93
93
|
|
94
94
|
word_count_groups = categories_to_reorganize.group_by{|category| ActsAsDAG::HelperMethods.word_count(category)}.sort
|
@@ -101,16 +101,16 @@ module ActsAsDAG
|
|
101
101
|
|
102
102
|
# Try drop each category into each root
|
103
103
|
categories.sort_by(&:name).each do |category|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
104
|
+
ActiveRecord::Base.benchmark "Analyze #{category.name}" do
|
105
|
+
suitable_parent = false
|
106
|
+
roots_categories.each do |root|
|
107
|
+
suitable_parent = true if ActsAsDAG::HelperMethods.plinko(root, category)
|
108
|
+
end
|
109
|
+
unless suitable_parent
|
110
|
+
ActiveRecord::Base.logger.info { "Plinko couldn't find a suitable parent for #{category.name}" }
|
111
|
+
categories_with_no_parents << category
|
112
|
+
end
|
112
113
|
end
|
113
|
-
puts "took #{Time.now - start} to analyze #{category.name}"
|
114
114
|
end
|
115
115
|
|
116
116
|
# Add all categories from this group without suitable parents to the roots
|
@@ -136,7 +136,7 @@ module ActsAsDAG
|
|
136
136
|
category.send :initialize_links
|
137
137
|
category.send :initialize_descendants
|
138
138
|
end
|
139
|
-
end
|
139
|
+
end
|
140
140
|
end
|
141
141
|
|
142
142
|
module InstanceMethods
|
@@ -145,6 +145,13 @@ module ActsAsDAG
|
|
145
145
|
self.class.roots.exists? self
|
146
146
|
end
|
147
147
|
|
148
|
+
def make_root
|
149
|
+
ancestor_links.delete_all
|
150
|
+
parent_links.delete_all
|
151
|
+
send :initialize_links
|
152
|
+
send :initialize_descendants
|
153
|
+
end
|
154
|
+
|
148
155
|
# Adds a category as a parent of this category (self)
|
149
156
|
def add_parent(parent)
|
150
157
|
ActsAsDAG::HelperMethods.link(parent, self)
|
@@ -207,7 +214,7 @@ module ActsAsDAG
|
|
207
214
|
def self.plinko(current, other)
|
208
215
|
# ActiveRecord::Base.logger.info { "Plinkoing '#{other.name}' into '#{current.name}'..." }
|
209
216
|
if should_descend_from?(current, other)
|
210
|
-
# Find the descendants of the current category that +other+ should descend from
|
217
|
+
# Find the descendants of the current category that +other+ should descend from
|
211
218
|
descendants_other_should_descend_from = current.descendants.select{|descendant| should_descend_from?(descendant, other) }
|
212
219
|
# Of those, find the categories with the most number of matching words and make +other+ their child
|
213
220
|
# We find all suitable candidates to provide support for categories whose names are permutations of each other
|
@@ -219,7 +226,7 @@ module ActsAsDAG
|
|
219
226
|
other.add_parent(new_parent)
|
220
227
|
|
221
228
|
# We've just affected the associations in ways we can not possibly imagine, so let's clear the association cache
|
222
|
-
current.clear_association_cache
|
229
|
+
current.clear_association_cache
|
223
230
|
end
|
224
231
|
return true
|
225
232
|
end
|
@@ -235,8 +242,8 @@ module ActsAsDAG
|
|
235
242
|
unless plinko(current, category)
|
236
243
|
end
|
237
244
|
end
|
238
|
-
end
|
239
|
-
end
|
245
|
+
end
|
246
|
+
end
|
240
247
|
|
241
248
|
# Returns the portion of this category's name that is not present in any of it's parents
|
242
249
|
def self.unique_name_portion(current)
|
@@ -331,7 +338,7 @@ module ActsAsDAG
|
|
331
338
|
# A F
|
332
339
|
# / \ /
|
333
340
|
# B C
|
334
|
-
# |
|
341
|
+
# |
|
335
342
|
# | D
|
336
343
|
# \ /
|
337
344
|
# E
|
@@ -350,7 +357,7 @@ module ActsAsDAG
|
|
350
357
|
|
351
358
|
# Create a descendant link to iteself, then iterate through all children
|
352
359
|
# We add this node to the ancestor array we received
|
353
|
-
# Then we create a descendant link between it and all nodes in the array we were passed (nodes traversed between it and all its ancestors affected by the unlinking).
|
360
|
+
# Then we create a descendant link between it and all nodes in the array we were passed (nodes traversed between it and all its ancestors affected by the unlinking).
|
354
361
|
# Then iterate to all children of the current node passing the ancestor array along
|
355
362
|
def self.rebuild_descendant_links(current, ancestors = [])
|
356
363
|
indent = Array.new(ancestors.size, " ").join
|
@@ -392,4 +399,4 @@ module ActsAsDAG
|
|
392
399
|
|
393
400
|
validates_presence_of :ancestor_id, :descendant_id
|
394
401
|
end
|
395
|
-
end
|
402
|
+
end
|
data/spec/acts_as_dag_spec.rb
CHANGED
@@ -5,7 +5,7 @@ describe 'acts_as_dag' do
|
|
5
5
|
before(:each) do
|
6
6
|
@klass.destroy_all # Because we're using sqlite3 and it doesn't support transactional specs (afaik)
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
describe "and" do
|
10
10
|
before(:each) do
|
11
11
|
@grandpa = @klass.create(:name => 'grandpa')
|
@@ -81,7 +81,7 @@ describe 'acts_as_dag' do
|
|
81
81
|
|
82
82
|
@grandpa.descendants.should == [@grandpa, @dad, @child]
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
it "should be able to test descent" do
|
86
86
|
@dad.add_child(@child)
|
87
87
|
@grandpa.add_child(@dad)
|
@@ -95,13 +95,40 @@ describe 'acts_as_dag' do
|
|
95
95
|
it "should be a root node immediately after saving" do
|
96
96
|
@grandpa.parents.should be_empty
|
97
97
|
@grandpa.root?.should be_true
|
98
|
-
end
|
98
|
+
end
|
99
99
|
|
100
100
|
it "should be a child if it has a parent" do
|
101
101
|
@grandpa.add_child(@dad)
|
102
102
|
@grandpa.add_child(@mom)
|
103
103
|
@klass.children.order(:id).should == [@dad, @mom]
|
104
|
-
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when a record hierarchy exists" do
|
108
|
+
before(:each) do
|
109
|
+
@grandma = @klass.create(:name => 'grandma')
|
110
|
+
@mom = @klass.create(:name => 'mom')
|
111
|
+
@brother = @klass.create(:name => 'brother')
|
112
|
+
|
113
|
+
@grandma.add_child(@mom)
|
114
|
+
@mom.add_child(@brother)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "destroying a record should delete the associated hierarchy-tracking records " do
|
118
|
+
@mom.destroy
|
119
|
+
@mom.descendant_links.should be_empty
|
120
|
+
@mom.ancestor_links.should be_empty
|
121
|
+
@mom.parent_links.should be_empty
|
122
|
+
@mom.child_links.should be_empty
|
123
|
+
end
|
124
|
+
|
125
|
+
it "make_root should make the record a root, but maintain it's children" do
|
126
|
+
@mom.make_root
|
127
|
+
|
128
|
+
@mom.should be_root
|
129
|
+
@mom.parents.should be_empty
|
130
|
+
@mom.children.should be_present
|
131
|
+
end
|
105
132
|
end
|
106
133
|
|
107
134
|
describe "reorganization" do
|
@@ -110,7 +137,7 @@ describe 'acts_as_dag' do
|
|
110
137
|
@totem_pole = @klass.create(:name => "totem pole")
|
111
138
|
@big_totem_pole = @klass.create(:name => "big totem pole")
|
112
139
|
@big_model_totem_pole = @klass.create(:name => "big model totem pole")
|
113
|
-
@big_red_model_totem_pole = @klass.create(:name => "big red model totem pole")
|
140
|
+
@big_red_model_totem_pole = @klass.create(:name => "big red model totem pole")
|
114
141
|
end
|
115
142
|
|
116
143
|
it "should reinitialize links and descendants after resetting the hierarchy" do
|
@@ -121,18 +148,18 @@ describe 'acts_as_dag' do
|
|
121
148
|
@big_totem_pole.descendants.should == [@big_totem_pole]
|
122
149
|
end
|
123
150
|
|
124
|
-
it "should be able to determine whether one category is an ancestor of the other by inspecting the name" do
|
151
|
+
it "should be able to determine whether one category is an ancestor of the other by inspecting the name" do
|
125
152
|
ActsAsDAG::HelperMethods.should_descend_from?(@totem_pole, @big_totem_pole).should be_true
|
126
153
|
ActsAsDAG::HelperMethods.should_descend_from?(@big_totem_pole, @totem_pole).should be_false
|
127
154
|
end
|
128
155
|
|
129
|
-
it "should be able to determine the number of matching words in two categories names" do
|
156
|
+
it "should be able to determine the number of matching words in two categories names" do
|
130
157
|
ActsAsDAG::HelperMethods.matching_word_count(@totem_pole, @big_totem_pole).should == 2
|
131
158
|
end
|
132
159
|
|
133
160
|
it "should arrange the categories correctly when not passed any arguments" do
|
134
161
|
@klass.reorganize
|
135
|
-
|
162
|
+
|
136
163
|
@totem.children.should == [@totem_pole]
|
137
164
|
@totem_pole.children.should == [@big_totem_pole]
|
138
165
|
@big_totem_pole.children.should == [@big_model_totem_pole]
|
@@ -141,7 +168,7 @@ describe 'acts_as_dag' do
|
|
141
168
|
|
142
169
|
it "should arrange the categories correctly when passed a set of nodes to reorganize" do
|
143
170
|
@klass.reorganize [@totem, @totem_pole, @big_totem_pole, @big_model_totem_pole, @big_red_model_totem_pole]
|
144
|
-
|
171
|
+
|
145
172
|
@totem.reload.children.should == [@totem_pole]
|
146
173
|
@totem_pole.reload.children.should == [@big_totem_pole]
|
147
174
|
@big_totem_pole.reload.children.should == [@big_model_totem_pole]
|
@@ -158,7 +185,7 @@ describe 'acts_as_dag' do
|
|
158
185
|
@big_totem_pole.children.should == [@big_model_totem_pole]
|
159
186
|
@big_model_totem_pole.reload.children.should == [@big_red_model_totem_pole]
|
160
187
|
end
|
161
|
-
|
188
|
+
|
162
189
|
it "should still work when there are categories that are permutations of each other" do
|
163
190
|
@big_totem_pole_model = @klass.create(:name => "big totem pole model")
|
164
191
|
|
@@ -169,7 +196,7 @@ describe 'acts_as_dag' do
|
|
169
196
|
(@big_totem_pole.children - [@big_model_totem_pole, @big_totem_pole_model]).should == []
|
170
197
|
@big_model_totem_pole.reload.children.should == [@big_red_model_totem_pole]
|
171
198
|
@big_totem_pole_model.reload.children.should == [@big_red_model_totem_pole]
|
172
|
-
end
|
199
|
+
end
|
173
200
|
|
174
201
|
describe "when there is a single long inheritance chain" do
|
175
202
|
before(:each) do
|
@@ -195,14 +222,14 @@ describe 'acts_as_dag' do
|
|
195
222
|
end
|
196
223
|
|
197
224
|
it "should return multiple instances of descendants before breaking the old link" do
|
198
|
-
@totem.descendants.sort_by(&:id).should == [@totem, @totem_pole, @big_totem_pole, @big_model_totem_pole, @big_model_totem_pole, @big_red_model_totem_pole, @big_red_model_totem_pole].sort_by(&:id)
|
225
|
+
@totem.descendants.sort_by(&:id).should == [@totem, @totem_pole, @big_totem_pole, @big_model_totem_pole, @big_model_totem_pole, @big_red_model_totem_pole, @big_red_model_totem_pole].sort_by(&:id)
|
199
226
|
end
|
200
227
|
|
201
228
|
it "should return the correct inheritance chain after breaking the old link" do
|
202
229
|
@totem_pole.remove_child(@big_model_totem_pole)
|
203
230
|
|
204
|
-
@totem_pole.children.sort_by(&:id).should == [@big_totem_pole].sort_by(&:id)
|
205
|
-
@totem.descendants.sort_by(&:id).should == [@totem, @totem_pole, @big_totem_pole, @big_model_totem_pole, @big_red_model_totem_pole].sort_by(&:id)
|
231
|
+
@totem_pole.children.sort_by(&:id).should == [@big_totem_pole].sort_by(&:id)
|
232
|
+
@totem.descendants.sort_by(&:id).should == [@totem, @totem_pole, @big_totem_pole, @big_model_totem_pole, @big_red_model_totem_pole].sort_by(&:id)
|
206
233
|
end
|
207
234
|
|
208
235
|
it "should return the correct inheritance chain after breaking the old link when there is are two ancestor root nodes" do
|
@@ -215,7 +242,7 @@ describe 'acts_as_dag' do
|
|
215
242
|
@totem.descendants.sort_by(&:id).should == [@totem, @totem_pole, @big_totem_pole, @big_model_totem_pole, @big_red_model_totem_pole].sort_by(&:id)
|
216
243
|
end
|
217
244
|
end
|
218
|
-
end
|
245
|
+
end
|
219
246
|
end
|
220
247
|
|
221
248
|
describe "and two paths of the same length exist to the same node" do
|
@@ -229,14 +256,14 @@ describe 'acts_as_dag' do
|
|
229
256
|
@grandpa.add_child(@dad)
|
230
257
|
@dad.add_child(@child)
|
231
258
|
@child.add_parent(@mom)
|
232
|
-
@mom.add_parent(@grandpa)
|
259
|
+
@mom.add_parent(@grandpa)
|
233
260
|
end
|
234
261
|
|
235
262
|
it "descendants should not return multiple instances of a child" do
|
236
263
|
@grandpa.descendants.sort_by(&:id).should == [@grandpa, @dad, @mom, @child].sort_by(&:id)
|
237
|
-
end
|
264
|
+
end
|
238
265
|
|
239
|
-
describe "and a link between parent and ancestor is removed" do
|
266
|
+
describe "and a link between parent and ancestor is removed" do
|
240
267
|
before(:each) do
|
241
268
|
# the incest is undone!
|
242
269
|
@dad.remove_parent(@grandpa)
|
@@ -253,7 +280,7 @@ describe 'acts_as_dag' do
|
|
253
280
|
@mom.descendants.sort_by(&:id).should == [@mom, @child].sort_by(&:id)
|
254
281
|
@dad.descendants.sort_by(&:id).should == [@dad, @child].sort_by(&:id)
|
255
282
|
@grandpa.descendants.sort_by(&:id).should == [@grandpa, @mom, @child].sort_by(&:id)
|
256
|
-
end
|
283
|
+
end
|
257
284
|
end
|
258
285
|
end
|
259
286
|
end
|
@@ -269,7 +296,6 @@ describe 'acts_as_dag' do
|
|
269
296
|
describe "models with unified link tables" do
|
270
297
|
before(:each) do
|
271
298
|
@klass = UnifiedLinkModel
|
272
|
-
@klass.logger = Logger.new(STDOUT)
|
273
299
|
end
|
274
300
|
|
275
301
|
it_should_behave_like "DAG Model"
|
@@ -280,5 +306,5 @@ describe 'acts_as_dag' do
|
|
280
306
|
record.parent_links.first.category_type.should == @klass.name
|
281
307
|
record.descendant_links.first.category_type.should == @klass.name
|
282
308
|
end
|
283
|
-
end
|
284
|
-
end
|
309
|
+
end
|
310
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_dag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
5
|
-
prerelease:
|
4
|
+
version: 1.2.3
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Nicholas Jakobsen
|
@@ -10,22 +9,20 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 2014-06-05 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: activerecord
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
|
-
- - ~>
|
18
|
+
- - "~>"
|
21
19
|
- !ruby/object:Gem::Version
|
22
20
|
version: '4.0'
|
23
21
|
type: :runtime
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
|
-
- - ~>
|
25
|
+
- - "~>"
|
29
26
|
- !ruby/object:Gem::Version
|
30
27
|
version: '4.0'
|
31
28
|
description:
|
@@ -34,34 +31,33 @@ executables: []
|
|
34
31
|
extensions: []
|
35
32
|
extra_rdoc_files: []
|
36
33
|
files:
|
37
|
-
-
|
34
|
+
- LICENSE
|
35
|
+
- README.rdoc
|
38
36
|
- lib/acts_as_dag.rb
|
37
|
+
- lib/acts_as_dag/acts_as_dag.rb
|
39
38
|
- spec/acts_as_dag_spec.rb
|
40
39
|
- spec/spec_helper.rb
|
41
|
-
- LICENSE
|
42
|
-
- README.rdoc
|
43
40
|
homepage: http://github.com/rrn/acts_as_dag
|
44
41
|
licenses: []
|
42
|
+
metadata: {}
|
45
43
|
post_install_message:
|
46
44
|
rdoc_options: []
|
47
45
|
require_paths:
|
48
46
|
- lib
|
49
47
|
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
48
|
requirements:
|
52
|
-
- -
|
49
|
+
- - ">="
|
53
50
|
- !ruby/object:Gem::Version
|
54
51
|
version: '0'
|
55
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
-
none: false
|
57
53
|
requirements:
|
58
|
-
- -
|
54
|
+
- - ">="
|
59
55
|
- !ruby/object:Gem::Version
|
60
56
|
version: '0'
|
61
57
|
requirements: []
|
62
58
|
rubyforge_project:
|
63
|
-
rubygems_version:
|
59
|
+
rubygems_version: 2.2.2
|
64
60
|
signing_key:
|
65
|
-
specification_version:
|
61
|
+
specification_version: 4
|
66
62
|
summary: Adds directed acyclic graph functionality to ActiveRecord.
|
67
63
|
test_files: []
|