tree_support 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/README.org +471 -0
- data/Rakefile +6 -0
- data/examples/0100_simple.rb +22 -0
- data/examples/0110_embeded_node_class.rb +26 -0
- data/examples/0120_graphiz_output_image.rb +6 -0
- data/examples/0130_node_class_example.rb +98 -0
- data/examples/0140_active_record.rb +81 -0
- data/examples/0150_acts_as_tree.rb +101 -0
- data/examples/0160_acts_as_tree_and_list.rb +133 -0
- data/examples/0170_node_class.rb +28 -0
- data/examples/0180_replace_to_active_record_tree.rb +120 -0
- data/examples/0190_generate_ruby_code.rb +68 -0
- data/examples/0200_ar_tree_model.rb +82 -0
- data/examples/0201_safe_destroy_all.rb +55 -0
- data/examples/0210_take_drop.rb +40 -0
- data/examples/0220_it_will_not_be_strange_to_tojson.rb +27 -0
- data/examples/0230_list_to_tree.rb +68 -0
- data/examples/0240_memory_record.rb +34 -0
- data/examples/Gemfile +12 -0
- data/examples/Gemfile.lock +137 -0
- data/examples/demo.rb +126 -0
- data/images/drop.png +0 -0
- data/images/take.png +0 -0
- data/images/take_drop.png +0 -0
- data/images/tree.png +0 -0
- data/images/tree_color.png +0 -0
- data/images/tree_label.png +0 -0
- data/lib/tree_support/ar_tree_model.rb +74 -0
- data/lib/tree_support/graphviz_builder.rb +78 -0
- data/lib/tree_support/inspector.rb +116 -0
- data/lib/tree_support/node.rb +124 -0
- data/lib/tree_support/railtie.rb +9 -0
- data/lib/tree_support/tree_support.rb +28 -0
- data/lib/tree_support/treeable.rb +52 -0
- data/lib/tree_support/version.rb +3 -0
- data/lib/tree_support.rb +2 -0
- data/spec/ar_tree_model_spec.rb +82 -0
- data/spec/node_spec.rb +52 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/tree_support_spec.rb +28 -0
- data/spec/treeable_spec.rb +59 -0
- data/tree_support.gemspec +30 -0
- metadata +196 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 35b3da8eb68307c23ab1a39ffe096c55bb826f27
|
4
|
+
data.tar.gz: 64366be7a67841d0f0c0fbc893e46835bf417f12
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 405374ce91c625deb5ad2f7eb3a1e3a0d9d518be315c960f32d92e858a0afe12224dcfbb0b4eb3951a40fde02f0973652d90b108c356881572c74a3be6ef2d58
|
7
|
+
data.tar.gz: cf0608091c08aaea0f5e099d4c1b439db05a2f8a2a4c3e3ed62efdb1f3444a22df0d798795213e2904417c960ed9cdda3e188fa1811ac5e7efd57b1fea774f07
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.org
ADDED
@@ -0,0 +1,471 @@
|
|
1
|
+
* Tree structure visualization function library
|
2
|
+
|
3
|
+
[[https://travis-ci.org/akicho8/tree_support.png]]
|
4
|
+
|
5
|
+
** How to use in one line
|
6
|
+
|
7
|
+
Just pass the object that has the parent, children method to TreeSupport.tree
|
8
|
+
|
9
|
+
#+BEGIN_SRC ruby
|
10
|
+
require "tree_support"
|
11
|
+
puts TreeSupport.tree(TreeSupport.example)
|
12
|
+
# >> *root*
|
13
|
+
# >> ├─Battle
|
14
|
+
# >> │ ├─Attack
|
15
|
+
# >> │ │ ├─Shake the sword
|
16
|
+
# >> │ │ ├─Attack magic
|
17
|
+
# >> │ │ │ ├─Summoned Beast X
|
18
|
+
# >> │ │ │ └─Summoned Beast Y
|
19
|
+
# >> │ │ └─Repel sword in length
|
20
|
+
# >> │ └─Defense
|
21
|
+
# >> ├─Withdraw
|
22
|
+
# >> │ ├─To stop
|
23
|
+
# >> │ │ ├─Place a trap
|
24
|
+
# >> │ │ └─Shoot a bow and arrow
|
25
|
+
# >> │ └─To escape
|
26
|
+
# >> └─Break
|
27
|
+
# >> ├─Stop
|
28
|
+
# >> └─Recover
|
29
|
+
# >> ├─Recovery magic
|
30
|
+
# >> └─Drink recovery medicine
|
31
|
+
#+END_SRC
|
32
|
+
|
33
|
+
** Detailed usage
|
34
|
+
|
35
|
+
*** Prepare a node class like this
|
36
|
+
|
37
|
+
#+BEGIN_SRC ruby
|
38
|
+
class Node
|
39
|
+
attr_accessor :name, :parent, :children
|
40
|
+
|
41
|
+
def initialize(name = nil, &block)
|
42
|
+
@name = name
|
43
|
+
@children = []
|
44
|
+
if block_given?
|
45
|
+
instance_eval(&block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def add(*args, &block)
|
50
|
+
tap do
|
51
|
+
children << self.class.new(*args, &block).tap { |v| v.parent = self }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
#+END_SRC
|
56
|
+
|
57
|
+
*** Create a tree
|
58
|
+
|
59
|
+
#+BEGIN_SRC ruby
|
60
|
+
root = Node.new("*root*") do
|
61
|
+
add "Battle" do
|
62
|
+
add "Attack" do
|
63
|
+
add "Shake the sword"
|
64
|
+
add "Attack magic" do
|
65
|
+
add "Summoned Beast X"
|
66
|
+
add "Summoned Beast Y"
|
67
|
+
end
|
68
|
+
add "Repel sword in length"
|
69
|
+
end
|
70
|
+
add "Defense"
|
71
|
+
end
|
72
|
+
add "Withdraw" do
|
73
|
+
add "To stop" do
|
74
|
+
add "Place a trap"
|
75
|
+
add "Shoot a bow and arrow"
|
76
|
+
end
|
77
|
+
add "To escape"
|
78
|
+
end
|
79
|
+
add "Break" do
|
80
|
+
add "Stop"
|
81
|
+
add "Recover" do
|
82
|
+
add "Recovery magic"
|
83
|
+
add "Drink recovery medicine"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
#+END_SRC
|
88
|
+
|
89
|
+
*** Visualization
|
90
|
+
|
91
|
+
#+BEGIN_SRC ruby
|
92
|
+
puts TreeSupport.tree(root)
|
93
|
+
# >> *root*
|
94
|
+
# >> ├─Battle
|
95
|
+
# >> │ ├─Attack
|
96
|
+
# >> │ │ ├─Shake the sword
|
97
|
+
# >> │ │ ├─Attack magic
|
98
|
+
# >> │ │ │ ├─Summoned Beast X
|
99
|
+
# >> │ │ │ └─Summoned Beast Y
|
100
|
+
# >> │ │ └─Repel sword in length
|
101
|
+
# >> │ └─Defense
|
102
|
+
# >> ├─Withdraw
|
103
|
+
# >> │ ├─To stop
|
104
|
+
# >> │ │ ├─Place a trap
|
105
|
+
# >> │ │ └─Shoot a bow and arrow
|
106
|
+
# >> │ └─To escape
|
107
|
+
# >> └─Break
|
108
|
+
# >> ├─Stop
|
109
|
+
# >> └─Recover
|
110
|
+
# >> ├─Recovery magic
|
111
|
+
# >> └─Drink recovery medicine
|
112
|
+
#+END_SRC
|
113
|
+
|
114
|
+
*** Troublesome writing TreeSupport.tree
|
115
|
+
|
116
|
+
Include TreeSupport::Stringify
|
117
|
+
|
118
|
+
#+BEGIN_SRC ruby
|
119
|
+
Node.include(TreeSupport::Stringify)
|
120
|
+
puts root.to_s_tree
|
121
|
+
# >> *root*
|
122
|
+
# >> ├─Battle
|
123
|
+
# >> │ ├─Attack
|
124
|
+
# >> │ │ ├─Shake the sword
|
125
|
+
# >> │ │ ├─Attack magic
|
126
|
+
# >> │ │ │ ├─Summoned Beast X
|
127
|
+
# >> │ │ │ └─Summoned Beast Y
|
128
|
+
# >> │ │ └─Repel sword in length
|
129
|
+
# >> │ └─Defense
|
130
|
+
# >> ├─Withdraw
|
131
|
+
# >> │ ├─To stop
|
132
|
+
# >> │ │ ├─Place a trap
|
133
|
+
# >> │ │ └─Shoot a bow and arrow
|
134
|
+
# >> │ └─To escape
|
135
|
+
# >> └─Break
|
136
|
+
# >> ├─Stop
|
137
|
+
# >> └─Recover
|
138
|
+
# >> ├─Recovery magic
|
139
|
+
# >> └─Drink recovery medicine
|
140
|
+
#+END_SRC
|
141
|
+
|
142
|
+
*** How do I change the label of a node?
|
143
|
+
|
144
|
+
We look for =to_s_tree_name=, =name=, =subject=, =title=, =to_s= defined by =TreeSupport.name_methods= in that order, so we define the method by considering the priority
|
145
|
+
|
146
|
+
*** How do I change labels without defining methods?
|
147
|
+
|
148
|
+
Add a block to tree
|
149
|
+
|
150
|
+
#+BEGIN_SRC ruby
|
151
|
+
puts TreeSupport.tree(root) { |node| node.object_id }
|
152
|
+
# >> 70308514816100
|
153
|
+
# >> ├─70308514815920
|
154
|
+
# >> │ ├─70308514815780
|
155
|
+
# >> │ │ ├─70308514815680
|
156
|
+
# >> │ │ ├─70308514815580
|
157
|
+
# >> │ │ │ ├─70308514815480
|
158
|
+
# >> │ │ │ └─70308514815420
|
159
|
+
# >> │ │ └─70308514815360
|
160
|
+
# >> │ └─70308514815300
|
161
|
+
# >> ├─70308514815220
|
162
|
+
# >> │ ├─70308514815080
|
163
|
+
# >> │ │ ├─70308514814980
|
164
|
+
# >> │ │ └─70308514814920
|
165
|
+
# >> │ └─70308514814860
|
166
|
+
# >> └─70308514814780
|
167
|
+
# >> ├─70308514814680
|
168
|
+
# >> └─70308514814580
|
169
|
+
# >> ├─70308514814480
|
170
|
+
# >> └─70308514814420
|
171
|
+
#+END_SRC
|
172
|
+
|
173
|
+
*** How to use methods that are common in tree structure?
|
174
|
+
|
175
|
+
The following methods become available in include of =TreeSupport::Treeable=
|
176
|
+
|
177
|
+
- each
|
178
|
+
- each_node
|
179
|
+
- descendants
|
180
|
+
- self_and_descendants
|
181
|
+
- ancestors
|
182
|
+
- root
|
183
|
+
- siblings
|
184
|
+
- self_and_siblings
|
185
|
+
- root?
|
186
|
+
- leaf?
|
187
|
+
|
188
|
+
*** How to convert to Gviz object?
|
189
|
+
|
190
|
+
#+BEGIN_SRC ruby
|
191
|
+
gv = TreeSupport.graphviz(root)
|
192
|
+
#+END_SRC
|
193
|
+
|
194
|
+
*** How to image it?
|
195
|
+
|
196
|
+
#+BEGIN_SRC ruby
|
197
|
+
gv.output("tree.png")
|
198
|
+
#+END_SRC
|
199
|
+
|
200
|
+
[[https://raw.github.com/akicho8/tree_support/master/images/tree.png]]
|
201
|
+
|
202
|
+
*** How do I change the color of a particular node?
|
203
|
+
|
204
|
+
Return the graphviz attribute as a hash in TreeSupport.graphviz block
|
205
|
+
|
206
|
+
#+BEGIN_SRC ruby
|
207
|
+
gv = TreeSupport.graphviz(root) do |node|
|
208
|
+
if node.name.include?("Attack")
|
209
|
+
{fillcolor: "lightblue", style: "filled"}
|
210
|
+
elsif node.name.include?("Recover")
|
211
|
+
{fillcolor: "lightpink", style: "filled"}
|
212
|
+
end
|
213
|
+
end
|
214
|
+
gv.output("tree_color.png")
|
215
|
+
#+END_SRC
|
216
|
+
|
217
|
+
[[https://raw.github.com/akicho8/tree_support/master/images/tree_color.png]]
|
218
|
+
|
219
|
+
*** How do I change the label of a particular node?
|
220
|
+
|
221
|
+
As with the above method, it returns a hash containing the label value
|
222
|
+
|
223
|
+
#+BEGIN_SRC ruby
|
224
|
+
gv = TreeSupport.graphviz(root) do |node|
|
225
|
+
{label: node.name.chars.first}
|
226
|
+
end
|
227
|
+
gv.output("tree_label.png")
|
228
|
+
#+END_SRC
|
229
|
+
|
230
|
+
[[https://raw.github.com/akicho8/tree_support/master/images/tree_label.png]]
|
231
|
+
|
232
|
+
*** How can I check the dot format of Graphviz?
|
233
|
+
|
234
|
+
#+BEGIN_SRC ruby
|
235
|
+
puts gv.to_dot
|
236
|
+
# >> digraph n70146110700700 {
|
237
|
+
# >> graph [charset = "UTF-8", rankdir = "LR"];
|
238
|
+
# >> n70146110700700 [label = "*root*"];
|
239
|
+
# >> n70146110700700 -> {n70146110698600; n70146110691220; n70146110689500;};
|
240
|
+
# >> n70146110698600 [label = "Battle"];
|
241
|
+
# >> n70146110698600 -> {n70146110698320; n70146110691720;};
|
242
|
+
# >> n70146110698320 [label = "Attack"];
|
243
|
+
# >> n70146110698320 -> {n70146110697900; n70146110697240; n70146110692060;};
|
244
|
+
# >> n70146110697900 [label = "Shake the sword"];
|
245
|
+
# >> n70146110697240 [label = "Attack magic"];
|
246
|
+
# >> n70146110697240 -> {n70146110695080; n70146110694480;};
|
247
|
+
# >> n70146110695080 [label = "Summoned Beast X"];
|
248
|
+
# >> n70146110694480 [label = "Summoned Beast Y"];
|
249
|
+
# >> n70146110692060 [label = "Repel sword in length"];
|
250
|
+
# >> n70146110691720 [label = "Defense"];
|
251
|
+
# >> n70146110691220 [label = "Withdraw"];
|
252
|
+
# >> n70146110691220 -> {n70146110690400; n70146110689620;};
|
253
|
+
# >> n70146110690400 [label = "To stop"];
|
254
|
+
# >> n70146110690400 -> {n70146110690220; n70146110689820;};
|
255
|
+
# >> n70146110690220 [label = "Place a trap"];
|
256
|
+
# >> n70146110689820 [label = "Shoot a bow and arrow"];
|
257
|
+
# >> n70146110689620 [label = "To escape"];
|
258
|
+
# >> n70146110689500 [label = "Break"];
|
259
|
+
# >> n70146110689500 -> {n70146110688500; n70146110687660;};
|
260
|
+
# >> n70146110688500 [label = "Stop"];
|
261
|
+
# >> n70146110687660 [label = "Recover"];
|
262
|
+
# >> n70146110687660 -> {n70146110686920; n70146110686220;};
|
263
|
+
# >> n70146110686920 [label = "Recovery magic"];
|
264
|
+
# >> n70146110686220 [label = "Drink recovery medicine"];
|
265
|
+
# >> }
|
266
|
+
#+END_SRC
|
267
|
+
|
268
|
+
*** How can I check the image conversion immediately when debugging?
|
269
|
+
|
270
|
+
#+BEGIN_SRC ruby
|
271
|
+
TreeSupport.graph_open(root)
|
272
|
+
#+END_SRC
|
273
|
+
|
274
|
+
Equivalent to the next shortcut
|
275
|
+
|
276
|
+
#+BEGIN_SRC ruby
|
277
|
+
TreeSupport.graphviz(root).output("_output.png")
|
278
|
+
`open _output.png`
|
279
|
+
#+END_SRC
|
280
|
+
|
281
|
+
*** Troublesome making node classes yourself
|
282
|
+
|
283
|
+
You can use =TreeSupport::Node= as it is.
|
284
|
+
|
285
|
+
#+BEGIN_SRC ruby
|
286
|
+
TreeSupport::Node.new("*root*") do
|
287
|
+
add "Battle" do
|
288
|
+
add "Attack" do
|
289
|
+
add "Shake the sword"
|
290
|
+
add "Attack magic" do
|
291
|
+
add "Summoned Beast X"
|
292
|
+
add "Summoned Beast Y"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
#+END_SRC
|
298
|
+
|
299
|
+
*** Troublesome making trees
|
300
|
+
|
301
|
+
#+BEGIN_SRC ruby
|
302
|
+
TreeSupport.example
|
303
|
+
#+END_SRC
|
304
|
+
|
305
|
+
There is a simple sample tree
|
306
|
+
|
307
|
+
*** How to trace leaves?
|
308
|
+
|
309
|
+
If you include =TreeSupport::Treeable= you can use each_node
|
310
|
+
|
311
|
+
#+BEGIN_SRC ruby
|
312
|
+
root = TreeSupport.example
|
313
|
+
root.each_node.with_index { |n, i| p [i, n.name] }
|
314
|
+
# >> [0, "*root*"]
|
315
|
+
# >> [1, "Battle"]
|
316
|
+
# >> [2, "Attack"]
|
317
|
+
# >> [3, "Shake the sword"]
|
318
|
+
# >> [4, "Attack magic"]
|
319
|
+
# >> [5, "Summoned Beast X"]
|
320
|
+
# >> [6, "Summoned Beast Y"]
|
321
|
+
# >> [7, "Repel sword in length"]
|
322
|
+
# >> [8, "Defense"]
|
323
|
+
# >> [9, "Withdraw"]
|
324
|
+
# >> [10, "To stop"]
|
325
|
+
# >> [11, "Place a trap"]
|
326
|
+
# >> [12, "Shoot a bow and arrow"]
|
327
|
+
# >> [13, "To escape"]
|
328
|
+
# >> [14, "Break"]
|
329
|
+
# >> [15, "Stop"]
|
330
|
+
# >> [16, "Recover"]
|
331
|
+
# >> [17, "Recovery magic"]
|
332
|
+
# >> [18, "Drink recovery medicine"]
|
333
|
+
#+END_SRC
|
334
|
+
|
335
|
+
*** I do not want to display the root
|
336
|
+
|
337
|
+
#+BEGIN_SRC ruby
|
338
|
+
puts TreeSupport.tree(root, drop: 1)
|
339
|
+
# >> Battle
|
340
|
+
# >> ├─Attack
|
341
|
+
# >> │ ├─Shake the sword
|
342
|
+
# >> │ ├─Attack magic
|
343
|
+
# >> │ │ ├─Summoned Beast X
|
344
|
+
# >> │ │ └─Summoned Beast Y
|
345
|
+
# >> │ └─Repel sword in length
|
346
|
+
# >> └─Defense
|
347
|
+
# >> Withdraw
|
348
|
+
# >> ├─To stop
|
349
|
+
# >> │ ├─Place a trap
|
350
|
+
# >> │ └─Shoot a bow and arrow
|
351
|
+
# >> └─To escape
|
352
|
+
# >> Break
|
353
|
+
# >> ├─Stop
|
354
|
+
# >> └─Recover
|
355
|
+
# >> ├─Recovery magic
|
356
|
+
# >> └─Drink recovery medicine
|
357
|
+
#+END_SRC
|
358
|
+
|
359
|
+
*** Since the trees are too big, it is enough up to the depth 3
|
360
|
+
|
361
|
+
#+BEGIN_SRC ruby
|
362
|
+
puts TreeSupport.tree(root, take: 3)
|
363
|
+
# >> *root*
|
364
|
+
# >> ├─Battle
|
365
|
+
# >> │ ├─Attack
|
366
|
+
# >> │ └─Defense
|
367
|
+
# >> ├─Withdraw
|
368
|
+
# >> │ ├─To stop
|
369
|
+
# >> │ └─To escape
|
370
|
+
# >> └─Break
|
371
|
+
# >> ├─Stop
|
372
|
+
# >> └─Recover
|
373
|
+
#+END_SRC
|
374
|
+
|
375
|
+
*** When you combine both
|
376
|
+
|
377
|
+
#+BEGIN_SRC ruby
|
378
|
+
puts TreeSupport.tree(root, take: 3, drop: 1)
|
379
|
+
# >> Battle
|
380
|
+
# >> ├─Attack
|
381
|
+
# >> └─Defense
|
382
|
+
# >> Withdraw
|
383
|
+
# >> ├─To stop
|
384
|
+
# >> └─To escape
|
385
|
+
# >> Break
|
386
|
+
# >> ├─Stop
|
387
|
+
# >> └─Recover
|
388
|
+
#+END_SRC
|
389
|
+
|
390
|
+
*** Image version also has similar options
|
391
|
+
|
392
|
+
#+BEGIN_SRC ruby
|
393
|
+
gv = TreeSupport.graphviz(root, drop: 1)
|
394
|
+
gv.output("drop.png")
|
395
|
+
#+END_SRC
|
396
|
+
|
397
|
+
[[https://raw.github.com/akicho8/tree_support/master/images/drop.png]]
|
398
|
+
|
399
|
+
#+BEGIN_SRC ruby
|
400
|
+
gv = TreeSupport.graphviz(root, take: 3)
|
401
|
+
gv.output("take.png")
|
402
|
+
#+END_SRC
|
403
|
+
|
404
|
+
[[https://raw.github.com/akicho8/tree_support/master/images/take.png]]
|
405
|
+
|
406
|
+
#+BEGIN_SRC ruby
|
407
|
+
gv = TreeSupport.graphviz(root, take: 3, drop: 1)
|
408
|
+
gv.output("take_drop.png")
|
409
|
+
#+END_SRC
|
410
|
+
|
411
|
+
[[https://raw.github.com/akicho8/tree_support/master/images/take_drop.png]]
|
412
|
+
|
413
|
+
*** How to use acts_as_tree equivalent?
|
414
|
+
|
415
|
+
Migration
|
416
|
+
|
417
|
+
#+BEGIN_SRC ruby
|
418
|
+
create_table :nodes do |t|
|
419
|
+
t.belongs_to :parent
|
420
|
+
end
|
421
|
+
#+END_SRC
|
422
|
+
|
423
|
+
Model
|
424
|
+
|
425
|
+
#+BEGIN_SRC ruby
|
426
|
+
class Node < ActiveRecord::Base
|
427
|
+
ar_tree_model
|
428
|
+
end
|
429
|
+
#+END_SRC
|
430
|
+
|
431
|
+
Difference from https://github.com/amerine/acts_as_tree
|
432
|
+
|
433
|
+
- simple
|
434
|
+
- Safely delete all safe_destroy_all (accident with destroy_all in combination with acts_as_list)
|
435
|
+
- Node.roots is defined by scope
|
436
|
+
- Arguments are different. =:order => :id= if you want to do it =scope: -> { order(:id) }=. By doing this you can also pass the where condition.
|
437
|
+
|
438
|
+
*** How do I correspond to memory_record gem?
|
439
|
+
|
440
|
+
Just as with ordinary classes, we need parent and children methods
|
441
|
+
|
442
|
+
#+BEGIN_SRC ruby
|
443
|
+
class Foo
|
444
|
+
include MemoryRecord
|
445
|
+
static_record [
|
446
|
+
{key: :a, parent: nil},
|
447
|
+
{key: :b, parent: :a},
|
448
|
+
{key: :c, parent: :b},
|
449
|
+
]
|
450
|
+
|
451
|
+
include TreeSupport::Treeable
|
452
|
+
include TreeSupport::Stringify
|
453
|
+
|
454
|
+
def parent
|
455
|
+
self.class[super]
|
456
|
+
end
|
457
|
+
|
458
|
+
def children
|
459
|
+
self.class.find_all { |e| e.parent == self }
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
puts Foo.find_all(&:root?).collect(&:to_s_tree)
|
464
|
+
# >> A
|
465
|
+
# >> └─B
|
466
|
+
# >> └─C
|
467
|
+
#+END_SRC
|
468
|
+
|
469
|
+
** With concern
|
470
|
+
|
471
|
+
- Since Gviz extends the standard class, concerns about future interference when combined with Rails (Active Support) etc.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "tree_support"
|
3
|
+
puts TreeSupport.tree(TreeSupport.example)
|
4
|
+
# >> *root*
|
5
|
+
# >> ├─Battle
|
6
|
+
# >> │ ├─Attack
|
7
|
+
# >> │ │ ├─Shake the sword
|
8
|
+
# >> │ │ ├─Attack magic
|
9
|
+
# >> │ │ │ ├─Summoned Beast X
|
10
|
+
# >> │ │ │ └─Summoned Beast Y
|
11
|
+
# >> │ │ └─Repel sword in length
|
12
|
+
# >> │ └─Defense
|
13
|
+
# >> ├─Withdraw
|
14
|
+
# >> │ ├─To stop
|
15
|
+
# >> │ │ ├─Place a trap
|
16
|
+
# >> │ │ └─Shoot a bow and arrow
|
17
|
+
# >> │ └─To escape
|
18
|
+
# >> └─Break
|
19
|
+
# >> ├─Stop
|
20
|
+
# >> └─Recover
|
21
|
+
# >> ├─Recovery magic
|
22
|
+
# >> └─Drink recovery medicine
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#
|
2
|
+
# Built-in TreeSupport::Node can be used generically when you want to express a little tree structure
|
3
|
+
#
|
4
|
+
require "bundler/setup"
|
5
|
+
require "tree_support"
|
6
|
+
|
7
|
+
node = TreeSupport::Node.new("foo")
|
8
|
+
node.name # => "foo"
|
9
|
+
|
10
|
+
node = TreeSupport::Node.new(:foo)
|
11
|
+
node.key # => :foo
|
12
|
+
|
13
|
+
node = TreeSupport::Node.new(a: 1, b: 2)
|
14
|
+
node[:a] # => 1
|
15
|
+
node.to_h # => {:a=>1, :b=>2}
|
16
|
+
|
17
|
+
tree = TreeSupport::Node.new(:root) do
|
18
|
+
add :a do
|
19
|
+
add :b do
|
20
|
+
add :c
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
tree.each_node.find { |e| e.key == :root }.key # => :root
|
26
|
+
tree.each_node.find { |e| e.key == :b }.key # => :b
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Examples of homebrew nodes
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tree_support"
|
5
|
+
|
6
|
+
class Node
|
7
|
+
attr_accessor :name, :parent, :children
|
8
|
+
|
9
|
+
def initialize(name = nil, &block)
|
10
|
+
@name = name
|
11
|
+
@children = []
|
12
|
+
if block_given?
|
13
|
+
instance_eval(&block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# 木を簡単につくるため
|
18
|
+
def add(*args, &block)
|
19
|
+
tap do
|
20
|
+
children << self.class.new(*args, &block).tap do |v|
|
21
|
+
v.parent = self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
root = Node.new("*root*") do
|
28
|
+
add "Battle" do
|
29
|
+
add "Attack" do
|
30
|
+
add "Shake the sword"
|
31
|
+
add "Attack magic" do
|
32
|
+
add "Summoned Beast X"
|
33
|
+
add "Summoned Beast Y"
|
34
|
+
end
|
35
|
+
add "Repel sword in length"
|
36
|
+
end
|
37
|
+
add "Defense"
|
38
|
+
end
|
39
|
+
add "Withdraw" do
|
40
|
+
add "To stop" do
|
41
|
+
add "Place a trap"
|
42
|
+
add "Shoot a bow and arrow"
|
43
|
+
end
|
44
|
+
add "To escape"
|
45
|
+
end
|
46
|
+
add "Break" do
|
47
|
+
add "Stop"
|
48
|
+
add "Recover" do
|
49
|
+
add "Recovery magic"
|
50
|
+
add "Drink recovery medicine"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# TreeSupport.tree に渡すオブジェクトは は parent.children と name に応答できさえすればいい
|
56
|
+
puts TreeSupport.tree(root)
|
57
|
+
|
58
|
+
# オブジェクトに文字列化するメソッドを入れるには?
|
59
|
+
Node.include(TreeSupport::Stringify)
|
60
|
+
puts root.to_s_tree
|
61
|
+
# >> *root*
|
62
|
+
# >> ├─Battle
|
63
|
+
# >> │ ├─Attack
|
64
|
+
# >> │ │ ├─Shake the sword
|
65
|
+
# >> │ │ ├─Attack magic
|
66
|
+
# >> │ │ │ ├─Summoned Beast X
|
67
|
+
# >> │ │ │ └─Summoned Beast Y
|
68
|
+
# >> │ │ └─Repel sword in length
|
69
|
+
# >> │ └─Defense
|
70
|
+
# >> ├─Withdraw
|
71
|
+
# >> │ ├─To stop
|
72
|
+
# >> │ │ ├─Place a trap
|
73
|
+
# >> │ │ └─Shoot a bow and arrow
|
74
|
+
# >> │ └─To escape
|
75
|
+
# >> └─Break
|
76
|
+
# >> ├─Stop
|
77
|
+
# >> └─Recover
|
78
|
+
# >> ├─Recovery magic
|
79
|
+
# >> └─Drink recovery medicine
|
80
|
+
# >> *root*
|
81
|
+
# >> ├─Battle
|
82
|
+
# >> │ ├─Attack
|
83
|
+
# >> │ │ ├─Shake the sword
|
84
|
+
# >> │ │ ├─Attack magic
|
85
|
+
# >> │ │ │ ├─Summoned Beast X
|
86
|
+
# >> │ │ │ └─Summoned Beast Y
|
87
|
+
# >> │ │ └─Repel sword in length
|
88
|
+
# >> │ └─Defense
|
89
|
+
# >> ├─Withdraw
|
90
|
+
# >> │ ├─To stop
|
91
|
+
# >> │ │ ├─Place a trap
|
92
|
+
# >> │ │ └─Shoot a bow and arrow
|
93
|
+
# >> │ └─To escape
|
94
|
+
# >> └─Break
|
95
|
+
# >> ├─Stop
|
96
|
+
# >> └─Recover
|
97
|
+
# >> ├─Recovery magic
|
98
|
+
# >> └─Drink recovery medicine
|