acts_as_dag 1.2.2 → 1.2.3

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.
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
- start = Time.now
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
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
@@ -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
@@ -10,7 +10,7 @@ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":me
10
10
  ActiveRecord::Schema.define(:version => 0) do
11
11
 
12
12
  # MODEL TABLES
13
-
13
+
14
14
  create_table :separate_link_models, :force => true do |t|
15
15
  t.string :name
16
16
  end
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.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: 2013-08-15 00:00:00.000000000 Z
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
- - lib/acts_as_dag/acts_as_dag.rb
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: 1.8.25
59
+ rubygems_version: 2.2.2
64
60
  signing_key:
65
- specification_version: 3
61
+ specification_version: 4
66
62
  summary: Adds directed acyclic graph functionality to ActiveRecord.
67
63
  test_files: []