tree_support 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +2 -0
  6. data/README.org +471 -0
  7. data/Rakefile +6 -0
  8. data/examples/0100_simple.rb +22 -0
  9. data/examples/0110_embeded_node_class.rb +26 -0
  10. data/examples/0120_graphiz_output_image.rb +6 -0
  11. data/examples/0130_node_class_example.rb +98 -0
  12. data/examples/0140_active_record.rb +81 -0
  13. data/examples/0150_acts_as_tree.rb +101 -0
  14. data/examples/0160_acts_as_tree_and_list.rb +133 -0
  15. data/examples/0170_node_class.rb +28 -0
  16. data/examples/0180_replace_to_active_record_tree.rb +120 -0
  17. data/examples/0190_generate_ruby_code.rb +68 -0
  18. data/examples/0200_ar_tree_model.rb +82 -0
  19. data/examples/0201_safe_destroy_all.rb +55 -0
  20. data/examples/0210_take_drop.rb +40 -0
  21. data/examples/0220_it_will_not_be_strange_to_tojson.rb +27 -0
  22. data/examples/0230_list_to_tree.rb +68 -0
  23. data/examples/0240_memory_record.rb +34 -0
  24. data/examples/Gemfile +12 -0
  25. data/examples/Gemfile.lock +137 -0
  26. data/examples/demo.rb +126 -0
  27. data/images/drop.png +0 -0
  28. data/images/take.png +0 -0
  29. data/images/take_drop.png +0 -0
  30. data/images/tree.png +0 -0
  31. data/images/tree_color.png +0 -0
  32. data/images/tree_label.png +0 -0
  33. data/lib/tree_support/ar_tree_model.rb +74 -0
  34. data/lib/tree_support/graphviz_builder.rb +78 -0
  35. data/lib/tree_support/inspector.rb +116 -0
  36. data/lib/tree_support/node.rb +124 -0
  37. data/lib/tree_support/railtie.rb +9 -0
  38. data/lib/tree_support/tree_support.rb +28 -0
  39. data/lib/tree_support/treeable.rb +52 -0
  40. data/lib/tree_support/version.rb +3 -0
  41. data/lib/tree_support.rb +2 -0
  42. data/spec/ar_tree_model_spec.rb +82 -0
  43. data/spec/node_spec.rb +52 -0
  44. data/spec/spec_helper.rb +8 -0
  45. data/spec/tree_support_spec.rb +28 -0
  46. data/spec/treeable_spec.rb +59 -0
  47. data/tree_support.gemspec +30 -0
  48. metadata +196 -0
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "bundler/setup"
3
+ require "tree_support"
4
+ puts "--------------------------------------------------------------------------------"
5
+ puts TreeSupport.example.to_s_tree(drop: 3)
6
+ puts "--------------------------------------------------------------------------------"
7
+ puts TreeSupport.example.to_s_tree(take: 3)
8
+ puts "--------------------------------------------------------------------------------"
9
+ puts TreeSupport.example.to_s_tree(take: 3, drop: 1)
10
+ # >> --------------------------------------------------------------------------------
11
+ # >> Shake the sword
12
+ # >> Attack magic
13
+ # >> ├─Summoned Beast X
14
+ # >> └─Summoned Beast Y
15
+ # >> Repel sword in length
16
+ # >> Place a trap
17
+ # >> Shoot a bow and arrow
18
+ # >> Recovery magic
19
+ # >> Drink recovery medicine
20
+ # >> --------------------------------------------------------------------------------
21
+ # >> *root*
22
+ # >> ├─Battle
23
+ # >> │ ├─Attack
24
+ # >> │ └─Defense
25
+ # >> ├─Withdraw
26
+ # >> │ ├─To stop
27
+ # >> │ └─To escape
28
+ # >> └─Break
29
+ # >> ├─Stop
30
+ # >> └─Recover
31
+ # >> --------------------------------------------------------------------------------
32
+ # >> Battle
33
+ # >> ├─Attack
34
+ # >> └─Defense
35
+ # >> Withdraw
36
+ # >> ├─To stop
37
+ # >> └─To escape
38
+ # >> Break
39
+ # >> ├─Stop
40
+ # >> └─Recover
@@ -0,0 +1,27 @@
1
+ # It will not be strange to tojson
2
+ #
3
+ require "bundler/setup"
4
+
5
+ require "rails"
6
+ require "active_record"
7
+ require "tree_support"
8
+ require "byebug"
9
+
10
+ Class.new(Rails::Application){config.eager_load = false}.initialize! # In order to read Railtie
11
+
12
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
13
+ ActiveRecord::Migration.verbose = false
14
+
15
+ ActiveRecord::Schema.define do
16
+ create_table :nodes do |t|
17
+ t.belongs_to :parent
18
+ t.string :name
19
+ end
20
+ end
21
+
22
+ class Node < ActiveRecord::Base
23
+ ar_tree_model
24
+ end
25
+
26
+ Node.create!(name: "*root*")
27
+ Node.first.to_json # => "{\"id\":1,\"parent_id\":null,\"name\":\"*root*\"}"
@@ -0,0 +1,68 @@
1
+ require "bundler/setup"
2
+ require "tree_support"
3
+
4
+ records = [
5
+ {key: :a0, parent: nil},
6
+ {key: :a1, parent: :a0},
7
+ {key: :a2, parent: :a1},
8
+
9
+ {key: :b0, parent: nil},
10
+ {key: :b1, parent: :b0},
11
+ {key: :b2, parent: :b1},
12
+ ]
13
+
14
+ # ---------- Convert with function of library
15
+ if true
16
+ puts TreeSupport.records_to_tree(records).collect(&:to_s_tree)
17
+ puts TreeSupport.records_to_tree(records, root_key: :root).to_s_tree
18
+ end
19
+
20
+ # ---------- How to write by yourself
21
+ if true
22
+ source_hash = records.inject({}) { |a, e| a.merge(e[:key] => e) }
23
+ node_hash = records.inject({}) { |a, e| a.merge(e[:key] => TreeSupport::Node.new(e[:key])) }
24
+ node_hash.each_value { |node|
25
+ if parent = source_hash[node.key][:parent]
26
+ parent_node = node_hash[parent]
27
+ node.parent = parent_node
28
+ parent_node.children << node
29
+ end
30
+ }
31
+ roots = node_hash.each_value.find_all { |e| e.parent == nil }
32
+ puts roots.collect(&:to_s_tree)
33
+
34
+ # When you want to make one route
35
+ root = TreeSupport::Node.new(:root)
36
+ roots.each do |e|
37
+ e.parent = root
38
+ root.children << e
39
+ end
40
+ puts root.to_s_tree
41
+ end
42
+
43
+ # >> a0
44
+ # >> └─a1
45
+ # >> └─a2
46
+ # >> b0
47
+ # >> └─b1
48
+ # >> └─b2
49
+ # >> root
50
+ # >> ├─a0
51
+ # >> │ └─a1
52
+ # >> │ └─a2
53
+ # >> └─b0
54
+ # >> └─b1
55
+ # >> └─b2
56
+ # >> a0
57
+ # >> └─a1
58
+ # >> └─a2
59
+ # >> b0
60
+ # >> └─b1
61
+ # >> └─b2
62
+ # >> root
63
+ # >> ├─a0
64
+ # >> │ └─a1
65
+ # >> │ └─a2
66
+ # >> └─b0
67
+ # >> └─b1
68
+ # >> └─b2
@@ -0,0 +1,34 @@
1
+ # Example of combining with MemoryRecord
2
+ require "bundler/setup"
3
+ require "tree_support"
4
+ require "memory_record"
5
+
6
+ class Foo
7
+ include MemoryRecord
8
+ memory_record [
9
+ {key: :a, name: "A", parent: nil},
10
+ {key: :b, name: "B", parent: :a},
11
+ {key: :c, name: "C", parent: :b},
12
+ ]
13
+
14
+ # Any structure can be used as long as it can respond to parent and children
15
+ concerning :TreeMethods do
16
+ included do
17
+ include TreeSupport::Treeable
18
+ include TreeSupport::Stringify
19
+ end
20
+
21
+ def parent
22
+ self.class[super]
23
+ end
24
+
25
+ def children
26
+ self.class.find_all {|e| e.parent == self }
27
+ end
28
+ end
29
+ end
30
+
31
+ puts Foo.find_all(&:root?).collect(&:to_s_tree)
32
+ # >> A
33
+ # >> └─B
34
+ # >> └─C
data/examples/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "tree_support", :path => ".."
4
+ gem "memory_record"
5
+
6
+ gem "rails"
7
+ gem "activerecord"
8
+ gem "sqlite3"
9
+ gem "gviz"
10
+ gem "acts_as_tree"
11
+ gem "acts_as_list"
12
+ gem "byebug"
@@ -0,0 +1,137 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ tree_support (0.0.1)
5
+ activesupport
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actioncable (5.0.0)
11
+ actionpack (= 5.0.0)
12
+ nio4r (~> 1.2)
13
+ websocket-driver (~> 0.6.1)
14
+ actionmailer (5.0.0)
15
+ actionpack (= 5.0.0)
16
+ actionview (= 5.0.0)
17
+ activejob (= 5.0.0)
18
+ mail (~> 2.5, >= 2.5.4)
19
+ rails-dom-testing (~> 2.0)
20
+ actionpack (5.0.0)
21
+ actionview (= 5.0.0)
22
+ activesupport (= 5.0.0)
23
+ rack (~> 2.0)
24
+ rack-test (~> 0.6.3)
25
+ rails-dom-testing (~> 2.0)
26
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
27
+ actionview (5.0.0)
28
+ activesupport (= 5.0.0)
29
+ builder (~> 3.1)
30
+ erubis (~> 2.7.0)
31
+ rails-dom-testing (~> 2.0)
32
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
33
+ activejob (5.0.0)
34
+ activesupport (= 5.0.0)
35
+ globalid (>= 0.3.6)
36
+ activemodel (5.0.0)
37
+ activesupport (= 5.0.0)
38
+ activerecord (5.0.0)
39
+ activemodel (= 5.0.0)
40
+ activesupport (= 5.0.0)
41
+ arel (~> 7.0)
42
+ activesupport (5.0.0)
43
+ concurrent-ruby (~> 1.0, >= 1.0.2)
44
+ i18n (~> 0.7)
45
+ minitest (~> 5.1)
46
+ tzinfo (~> 1.1)
47
+ acts_as_list (0.7.6)
48
+ activerecord (>= 3.0)
49
+ acts_as_tree (2.4.0)
50
+ activerecord (>= 3.0.0)
51
+ arel (7.1.0)
52
+ builder (3.2.2)
53
+ byebug (9.0.5)
54
+ concurrent-ruby (1.0.2)
55
+ erubis (2.7.0)
56
+ globalid (0.3.6)
57
+ activesupport (>= 4.1.0)
58
+ gviz (0.3.5)
59
+ thor
60
+ i18n (0.7.0)
61
+ loofah (2.0.3)
62
+ nokogiri (>= 1.5.9)
63
+ mail (2.6.4)
64
+ mime-types (>= 1.16, < 4)
65
+ memory_record (0.0.6)
66
+ activemodel
67
+ activesupport
68
+ method_source (0.8.2)
69
+ mime-types (3.1)
70
+ mime-types-data (~> 3.2015)
71
+ mime-types-data (3.2016.0521)
72
+ mini_portile2 (2.1.0)
73
+ minitest (5.9.0)
74
+ nio4r (1.2.1)
75
+ nokogiri (1.6.8)
76
+ mini_portile2 (~> 2.1.0)
77
+ pkg-config (~> 1.1.7)
78
+ pkg-config (1.1.7)
79
+ rack (2.0.1)
80
+ rack-test (0.6.3)
81
+ rack (>= 1.0)
82
+ rails (5.0.0)
83
+ actioncable (= 5.0.0)
84
+ actionmailer (= 5.0.0)
85
+ actionpack (= 5.0.0)
86
+ actionview (= 5.0.0)
87
+ activejob (= 5.0.0)
88
+ activemodel (= 5.0.0)
89
+ activerecord (= 5.0.0)
90
+ activesupport (= 5.0.0)
91
+ bundler (>= 1.3.0, < 2.0)
92
+ railties (= 5.0.0)
93
+ sprockets-rails (>= 2.0.0)
94
+ rails-dom-testing (2.0.1)
95
+ activesupport (>= 4.2.0, < 6.0)
96
+ nokogiri (~> 1.6.0)
97
+ rails-html-sanitizer (1.0.3)
98
+ loofah (~> 2.0)
99
+ railties (5.0.0)
100
+ actionpack (= 5.0.0)
101
+ activesupport (= 5.0.0)
102
+ method_source
103
+ rake (>= 0.8.7)
104
+ thor (>= 0.18.1, < 2.0)
105
+ rake (11.2.2)
106
+ sprockets (3.7.0)
107
+ concurrent-ruby (~> 1.0)
108
+ rack (> 1, < 3)
109
+ sprockets-rails (3.1.1)
110
+ actionpack (>= 4.0)
111
+ activesupport (>= 4.0)
112
+ sprockets (>= 3.0.0)
113
+ sqlite3 (1.3.11)
114
+ thor (0.19.1)
115
+ thread_safe (0.3.5)
116
+ tzinfo (1.2.2)
117
+ thread_safe (~> 0.1)
118
+ websocket-driver (0.6.4)
119
+ websocket-extensions (>= 0.1.0)
120
+ websocket-extensions (0.1.2)
121
+
122
+ PLATFORMS
123
+ ruby
124
+
125
+ DEPENDENCIES
126
+ activerecord
127
+ acts_as_list
128
+ acts_as_tree
129
+ byebug
130
+ gviz
131
+ memory_record
132
+ rails
133
+ sqlite3
134
+ tree_support!
135
+
136
+ BUNDLED WITH
137
+ 1.16.0.pre.3
data/examples/demo.rb ADDED
@@ -0,0 +1,126 @@
1
+ # output ../images/*
2
+ require "bundler/setup"
3
+ require "tree_support"
4
+
5
+ root = TreeSupport.example
6
+
7
+ p root.each_node.collect.with_index {|n, i| [n.name, i]}
8
+ puts root.to_s_tree
9
+ puts TreeSupport.tree(root)
10
+ puts TreeSupport.tree(root, drop: 1)
11
+ puts TreeSupport.tree(root, take: 3)
12
+ puts TreeSupport.tree(root, take: 3, drop: 1)
13
+ puts TreeSupport.tree(root) {|node, _locals| node.object_id}
14
+ # TreeSupport.graph_open(root)
15
+
16
+ TreeSupport.graphviz(root) {|node|
17
+ if node.name.include?("Attack")
18
+ {fillcolor: "lightblue", style: "filled"}
19
+ elsif node.name.include?("Recover")
20
+ {fillcolor: "lightpink", style: "filled"}
21
+ end
22
+ }.output("../images/tree_color.png")
23
+
24
+ TreeSupport.graphviz(root) {|node|
25
+ {label: node.name.chars.first}
26
+ }.output("../images/tree_label.png")
27
+
28
+ TreeSupport.graphviz(root).output("../images/tree.png")
29
+ TreeSupport.graphviz(root, take: 3).output("../images/take.png")
30
+ TreeSupport.graphviz(root, drop: 1).output("../images/drop.png")
31
+ TreeSupport.graphviz(root, take: 3, drop: 1).output("../images/take_drop.png")
32
+ # >> [["*root*", 0], ["Battle", 1], ["Attack", 2], ["Shake the sword", 3], ["Attack magic", 4], ["Summoned Beast X", 5], ["Summoned Beast Y", 6], ["Repel sword in length", 7], ["Defense", 8], ["Withdraw", 9], ["To stop", 10], ["Place a trap", 11], ["Shoot a bow and arrow", 12], ["To escape", 13], ["Break", 14], ["Stop", 15], ["Recover", 16], ["Recovery magic", 17], ["Drink recovery medicine", 18]]
33
+ # >> *root*
34
+ # >> ├─Battle
35
+ # >> │ ├─Attack
36
+ # >> │ │ ├─Shake the sword
37
+ # >> │ │ ├─Attack magic
38
+ # >> │ │ │ ├─Summoned Beast X
39
+ # >> │ │ │ └─Summoned Beast Y
40
+ # >> │ │ └─Repel sword in length
41
+ # >> │ └─Defense
42
+ # >> ├─Withdraw
43
+ # >> │ ├─To stop
44
+ # >> │ │ ├─Place a trap
45
+ # >> │ │ └─Shoot a bow and arrow
46
+ # >> │ └─To escape
47
+ # >> └─Break
48
+ # >> ├─Stop
49
+ # >> └─Recover
50
+ # >> ├─Recovery magic
51
+ # >> └─Drink recovery medicine
52
+ # >> *root*
53
+ # >> ├─Battle
54
+ # >> │ ├─Attack
55
+ # >> │ │ ├─Shake the sword
56
+ # >> │ │ ├─Attack magic
57
+ # >> │ │ │ ├─Summoned Beast X
58
+ # >> │ │ │ └─Summoned Beast Y
59
+ # >> │ │ └─Repel sword in length
60
+ # >> │ └─Defense
61
+ # >> ├─Withdraw
62
+ # >> │ ├─To stop
63
+ # >> │ │ ├─Place a trap
64
+ # >> │ │ └─Shoot a bow and arrow
65
+ # >> │ └─To escape
66
+ # >> └─Break
67
+ # >> ├─Stop
68
+ # >> └─Recover
69
+ # >> ├─Recovery magic
70
+ # >> └─Drink recovery medicine
71
+ # >> Battle
72
+ # >> ├─Attack
73
+ # >> │ ├─Shake the sword
74
+ # >> │ ├─Attack magic
75
+ # >> │ │ ├─Summoned Beast X
76
+ # >> │ │ └─Summoned Beast Y
77
+ # >> │ └─Repel sword in length
78
+ # >> └─Defense
79
+ # >> Withdraw
80
+ # >> ├─To stop
81
+ # >> │ ├─Place a trap
82
+ # >> │ └─Shoot a bow and arrow
83
+ # >> └─To escape
84
+ # >> Break
85
+ # >> ├─Stop
86
+ # >> └─Recover
87
+ # >> ├─Recovery magic
88
+ # >> └─Drink recovery medicine
89
+ # >> *root*
90
+ # >> ├─Battle
91
+ # >> │ ├─Attack
92
+ # >> │ └─Defense
93
+ # >> ├─Withdraw
94
+ # >> │ ├─To stop
95
+ # >> │ └─To escape
96
+ # >> └─Break
97
+ # >> ├─Stop
98
+ # >> └─Recover
99
+ # >> Battle
100
+ # >> ├─Attack
101
+ # >> └─Defense
102
+ # >> Withdraw
103
+ # >> ├─To stop
104
+ # >> └─To escape
105
+ # >> Break
106
+ # >> ├─Stop
107
+ # >> └─Recover
108
+ # >> 70279213508120
109
+ # >> ├─70279213507760
110
+ # >> │ ├─70279213507500
111
+ # >> │ │ ├─70279213507240
112
+ # >> │ │ ├─70279213507000
113
+ # >> │ │ │ ├─70279213506800
114
+ # >> │ │ │ └─70279213506660
115
+ # >> │ │ └─70279213507200
116
+ # >> │ └─70279213509960
117
+ # >> ├─70279213498160
118
+ # >> │ ├─70279213497920
119
+ # >> │ │ ├─70279213497740
120
+ # >> │ │ └─70279213497600
121
+ # >> │ └─70279213497380
122
+ # >> └─70279213497180
123
+ # >> ├─70279213496960
124
+ # >> └─70279213496720
125
+ # >> ├─70279213496480
126
+ # >> └─70279213496320
data/images/drop.png ADDED
Binary file
data/images/take.png ADDED
Binary file
Binary file
data/images/tree.png ADDED
Binary file
Binary file
Binary file
@@ -0,0 +1,74 @@
1
+ # acts_as_tree replacement
2
+ #
3
+ # class Node < ActiveRecord::Base
4
+ # ar_tree_model # default
5
+ # ar_tree_model scope: -> { order(:name) } # Designate the scope yourself
6
+ # ar_tree_model scope: -> { order(:id).where(active: true) } # You can also specify where
7
+ # end
8
+ #
9
+ require "active_support/concern"
10
+
11
+ module TreeSupport
12
+ module ArTreeModel
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ end
17
+
18
+ class_methods do
19
+ def ar_tree_model(options = {})
20
+ return if ar_tree_model_defined?
21
+
22
+ class_attribute :ar_tree_model_configuration
23
+ self.ar_tree_model_configuration = {
24
+ scope: -> { order(:id) },
25
+ }.merge(options)
26
+
27
+ if block_given?
28
+ yield ar_tree_model_configuration
29
+ end
30
+
31
+ include SingletonMethods
32
+ end
33
+
34
+ def ar_tree_model_defined?
35
+ ancestors.include?(SingletonMethods)
36
+ end
37
+ end
38
+
39
+ concern :SingletonMethods do
40
+ include Treeable
41
+ include Stringify
42
+
43
+ included do
44
+ scope :tree_default_scope, ar_tree_model_configuration[:scope]
45
+
46
+ belongs_to :parent, -> { tree_default_scope }, class_name: name, foreign_key: :parent_id, required: false
47
+ has_many :children, -> { tree_default_scope }, class_name: name, foreign_key: :parent_id, dependent: :destroy, inverse_of: :parent
48
+ scope :roots, -> { tree_default_scope.where(parent_id: nil) }
49
+ end
50
+
51
+ class_methods do
52
+ def ar_tree_model?
53
+ ar_tree_model_defined? # && columns_hash.has_key?(:id)
54
+ end
55
+
56
+ def root
57
+ roots.first
58
+ end
59
+
60
+ # In combination with acts_as_list accident in destroy_all, we have to erase from the leaves in order
61
+ def safe_destroy_all
62
+ roots.collect(&:destroy)
63
+ end
64
+
65
+ def destroy_all(*args)
66
+ if respond_to?(:acts_as_list)
67
+ ActiveSupport::Deprecation.warn("When you use acts_as_list destroy_all do not get id failed to accidentally delete_all Please use safe_destroy_all to erase from the end")
68
+ end
69
+ super
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,78 @@
1
+ require "gviz"
2
+
3
+ # for GvizEx#output
4
+ require "pathname"
5
+ require "delegate"
6
+ require "fileutils"
7
+
8
+ require "active_support/core_ext/module/delegation"
9
+
10
+ module TreeSupport
11
+ def self.graphviz(*args, &block)
12
+ GraphvizBuilder.build(*args, &block)
13
+ end
14
+
15
+ def self.graph_open(*args, &block)
16
+ graphviz(*args, &block).output("_output.png")
17
+ `open _output.png`
18
+ end
19
+
20
+ class GraphvizBuilder
21
+ # For Gviz#save only for some reason
22
+ class GvizEx < SimpleDelegator
23
+ def output(filename)
24
+ filename = Pathname(filename).expand_path
25
+ FileUtils.makedirs(filename.dirname)
26
+ save("#{filename.dirname}/#{filename.basename(".*")}", filename.extname.delete("."))
27
+ end
28
+
29
+ # alias to_dot to_s can not
30
+ def to_dot
31
+ to_s
32
+ end
33
+ end
34
+
35
+ def self.build(object, *args, &block)
36
+ new(*args, &block).build(object)
37
+ end
38
+
39
+ def initialize(**options, &block)
40
+ @options = {
41
+ take: 256,
42
+ drop: 0,
43
+ }.merge(options)
44
+ @block = block
45
+ end
46
+
47
+ def build(object)
48
+ GvizEx.new(Gviz.new).tap do |gv|
49
+ gv.global(rankdir: "LR", concentrate: "true")
50
+ visit(gv, object)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def visit(gv, object, depth = 0)
57
+ if depth < @options[:take]
58
+ if @options[:drop] <= depth
59
+ attrs = {}
60
+ if @block
61
+ attrs = @block.call(object) || {}
62
+ end
63
+ attrs[:label] ||= TreeSupport.node_name(object)
64
+ gv.node(node_code(object), attrs)
65
+ if depth.next < @options[:take]
66
+ gv.route node_code(object) => object.children.collect {|node| node_code(node) }
67
+ end
68
+ end
69
+ object.children.each {|e| visit(gv, e, depth.next) }
70
+ end
71
+ end
72
+
73
+ def node_code(object)
74
+ # I do not want to be a symbol because it is not subject to GC, but because I get angry with Gviz it is a symbolic symbol
75
+ :"n#{object.object_id}"
76
+ end
77
+ end
78
+ end