tree_support 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,81 @@
|
|
1
|
+
# Visualization of tree structure using only ActiveRecord
|
2
|
+
#
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tree_support"
|
5
|
+
require "active_record"
|
6
|
+
require "pp"
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
9
|
+
ActiveRecord::Migration.verbose = false
|
10
|
+
|
11
|
+
ActiveRecord::Schema.define do
|
12
|
+
create_table :nodes do |t|
|
13
|
+
t.belongs_to :parent
|
14
|
+
t.string :name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Node < ActiveRecord::Base
|
19
|
+
belongs_to :parent, class_name: name, foreign_key: "parent_id"
|
20
|
+
has_many :children, -> { order(:id) }, class_name: name, foreign_key: "parent_id"
|
21
|
+
|
22
|
+
def add(name, &block)
|
23
|
+
tap do
|
24
|
+
child = children.create!(name: name)
|
25
|
+
if block_given?
|
26
|
+
child.instance_eval(&block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
root = Node.create!(name: "*root*").tap do |n|
|
33
|
+
n.instance_eval do
|
34
|
+
add "Battle" do
|
35
|
+
add "Attack" do
|
36
|
+
add "Shake the sword"
|
37
|
+
add "Attack magic" do
|
38
|
+
add "Summoned Beast X"
|
39
|
+
add "Summoned Beast Y"
|
40
|
+
end
|
41
|
+
add "Repel sword in length"
|
42
|
+
end
|
43
|
+
add "Defense"
|
44
|
+
end
|
45
|
+
add "Withdraw" do
|
46
|
+
add "To stop" do
|
47
|
+
add "Place a trap"
|
48
|
+
add "Shoot a bow and arrow"
|
49
|
+
end
|
50
|
+
add "To escape"
|
51
|
+
end
|
52
|
+
add "Break" do
|
53
|
+
add "Stop"
|
54
|
+
add "Recover" do
|
55
|
+
add "Recovery magic"
|
56
|
+
add "Drink recovery medicine"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
puts TreeSupport.tree(root)
|
63
|
+
# >> *root*
|
64
|
+
# >> ├─Battle
|
65
|
+
# >> │ ├─Attack
|
66
|
+
# >> │ │ ├─Shake the sword
|
67
|
+
# >> │ │ ├─Attack magic
|
68
|
+
# >> │ │ │ ├─Summoned Beast X
|
69
|
+
# >> │ │ │ └─Summoned Beast Y
|
70
|
+
# >> │ │ └─Repel sword in length
|
71
|
+
# >> │ └─Defense
|
72
|
+
# >> ├─Withdraw
|
73
|
+
# >> │ ├─To stop
|
74
|
+
# >> │ │ ├─Place a trap
|
75
|
+
# >> │ │ └─Shoot a bow and arrow
|
76
|
+
# >> │ └─To escape
|
77
|
+
# >> └─Break
|
78
|
+
# >> ├─Stop
|
79
|
+
# >> └─Recover
|
80
|
+
# >> ├─Recovery magic
|
81
|
+
# >> └─Drink recovery medicine
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# Example using acts_as_tree
|
2
|
+
#
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tree_support"
|
5
|
+
require "active_record"
|
6
|
+
|
7
|
+
begin
|
8
|
+
require "acts_as_tree"
|
9
|
+
ActiveRecord::Base.include(ActsAsTree)
|
10
|
+
end
|
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
|
+
acts_as_tree order: "name"
|
24
|
+
|
25
|
+
def add(name, &block)
|
26
|
+
tap do
|
27
|
+
child = children.create!(name: name)
|
28
|
+
if block_given?
|
29
|
+
child.instance_eval(&block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
_root = Node.create!(name: "*root*").tap do |n|
|
36
|
+
n.instance_eval do
|
37
|
+
add "Battle" do
|
38
|
+
add "Attack" do
|
39
|
+
add "Shake the sword"
|
40
|
+
add "Attack magic" do
|
41
|
+
add "Summoned Beast X"
|
42
|
+
add "Summoned Beast Y"
|
43
|
+
end
|
44
|
+
add "Repel sword in length"
|
45
|
+
end
|
46
|
+
add "Defense"
|
47
|
+
end
|
48
|
+
add "Withdraw" do
|
49
|
+
add "To stop" do
|
50
|
+
add "Place a trap"
|
51
|
+
add "Shoot a bow and arrow"
|
52
|
+
end
|
53
|
+
add "To escape"
|
54
|
+
end
|
55
|
+
add "Break" do
|
56
|
+
add "Stop"
|
57
|
+
add "Recover" do
|
58
|
+
add "Recovery magic"
|
59
|
+
add "Drink recovery medicine"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Node.extend(ActsAsTree::TreeView)
|
66
|
+
Node.tree_view(:name)
|
67
|
+
|
68
|
+
# puts TreeSupport.tree(root)
|
69
|
+
# ~> from -:8:in `<main>'
|
70
|
+
# ~> from -:8:in `require'
|
71
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree.rb:322:in `<top (required)>'
|
72
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree.rb:322:in `require'
|
73
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree/active_record/acts/tree.rb:1:in `<top (required)>'
|
74
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree/active_record/acts/tree.rb:1:in `require'
|
75
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activesupport-5.0.0/lib/active_support/core_ext/hash/transform_values.rb:11: warning: method redefined; discarding old transform_values
|
76
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activesupport-5.0.0/lib/active_support/core_ext/hash/transform_values.rb:23: warning: method redefined; discarding old transform_values!
|
77
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
78
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
79
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
80
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
81
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/arel-7.1.0/lib/arel/nodes/casted.rb:14: warning: instance variable @class not initialized
|
82
|
+
# >> root
|
83
|
+
# >> |_ *root*
|
84
|
+
# >> | |_ Battle
|
85
|
+
# >> | |_ Attack
|
86
|
+
# >> | |_ Attack magic
|
87
|
+
# >> | |_ Summoned Beast X
|
88
|
+
# >> | |_ Summoned Beast Y
|
89
|
+
# >> | |_ Repel sword in length
|
90
|
+
# >> | |_ Shake the sword
|
91
|
+
# >> | |_ Defense
|
92
|
+
# >> | |_ Break
|
93
|
+
# >> | |_ Recover
|
94
|
+
# >> | |_ Drink recovery medicine
|
95
|
+
# >> | |_ Recovery magic
|
96
|
+
# >> | |_ Stop
|
97
|
+
# >> | |_ Withdraw
|
98
|
+
# >> | |_ To escape
|
99
|
+
# >> | |_ To stop
|
100
|
+
# >> | |_ Place a trap
|
101
|
+
# >> | |_ Shoot a bow and arrow
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# Example of holding sibling's order in acts_as_list with acts_as_tree
|
2
|
+
#
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tree_support"
|
5
|
+
require "active_record"
|
6
|
+
|
7
|
+
begin
|
8
|
+
require "acts_as_tree"
|
9
|
+
ActiveRecord::Base.include(ActsAsTree)
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require "acts_as_list"
|
14
|
+
ActiveRecord::Base.include(ActiveRecord::Acts::List)
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
18
|
+
ActiveRecord::Migration.verbose = false
|
19
|
+
|
20
|
+
ActiveRecord::Schema.define do
|
21
|
+
create_table :nodes do |t|
|
22
|
+
t.belongs_to :parent
|
23
|
+
t.string :name
|
24
|
+
t.integer :position
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Node < ActiveRecord::Base
|
29
|
+
acts_as_tree order: "position"
|
30
|
+
acts_as_list scope: :parent
|
31
|
+
|
32
|
+
def add(name, &block)
|
33
|
+
tap do
|
34
|
+
child = children.create!(name: name)
|
35
|
+
if block_given?
|
36
|
+
child.instance_eval(&block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
root = Node.create!(name: "*root*").tap do |n|
|
43
|
+
n.instance_eval do
|
44
|
+
add "Battle" do
|
45
|
+
add "Attack" do
|
46
|
+
add "Shake the sword"
|
47
|
+
add "Attack magic" do
|
48
|
+
add "Summoned Beast X"
|
49
|
+
add "Summoned Beast Y"
|
50
|
+
end
|
51
|
+
add "Repel sword in length"
|
52
|
+
end
|
53
|
+
add "Defense"
|
54
|
+
end
|
55
|
+
add "Withdraw" do
|
56
|
+
add "To stop" do
|
57
|
+
add "Place a trap"
|
58
|
+
add "Shoot a bow and arrow"
|
59
|
+
end
|
60
|
+
add "To escape"
|
61
|
+
end
|
62
|
+
add "Break" do
|
63
|
+
add "Stop"
|
64
|
+
add "Recover" do
|
65
|
+
add "Recovery magic"
|
66
|
+
add "Drink recovery medicine"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Node.extend(ArTreeModel::TreeView)
|
73
|
+
# Node.tree_view(:name)
|
74
|
+
|
75
|
+
puts TreeSupport.tree(root) {|e| "#{e.name}(#{e.position})"}
|
76
|
+
|
77
|
+
# acts_as_tree + acts_as_list は destroy_all で事故る
|
78
|
+
Node.destroy_all rescue $! # => #<ActiveRecord::RecordNotFound: Couldn't find Node with 'id'=2>
|
79
|
+
# ~> from -:8:in `<main>'
|
80
|
+
# ~> from -:8:in `require'
|
81
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree.rb:322:in `<top (required)>'
|
82
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree.rb:322:in `require'
|
83
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree/active_record/acts/tree.rb:1:in `<top (required)>'
|
84
|
+
# ~> from /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/acts_as_tree-2.4.0/lib/acts_as_tree/active_record/acts/tree.rb:1:in `require'
|
85
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activesupport-5.0.0/lib/active_support/core_ext/hash/transform_values.rb:11: warning: method redefined; discarding old transform_values
|
86
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activesupport-5.0.0/lib/active_support/core_ext/hash/transform_values.rb:23: warning: method redefined; discarding old transform_values!
|
87
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
88
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
89
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
90
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/concurrent-ruby-1.0.2/lib/concurrent/map.rb:230: warning: constant ::Fixnum is deprecated
|
91
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/arel-7.1.0/lib/arel/nodes/casted.rb:14: warning: instance variable @class not initialized
|
92
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
93
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
94
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
95
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
96
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
97
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
98
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
99
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
100
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
101
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
102
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
103
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
104
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
105
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
106
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
107
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
108
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
109
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
110
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.0.0/lib/active_record/sanitization.rb:163: warning: too many arguments for format string
|
111
|
+
# ~> /usr/local/var/rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/arel-7.1.0/lib/arel/nodes/casted.rb:14: warning: instance variable @class not initialized
|
112
|
+
# ~> !XMP1512801308_58182_9031![1] => ActiveRecord::RecordNotFound #<ActiveRecord::RecordNotFound: Couldn't find Node with 'id'=2>
|
113
|
+
# ~> root
|
114
|
+
# ~> _xmp_1512801308_58182_55846
|
115
|
+
# >> *root*(1)
|
116
|
+
# >> ├─Battle(1)
|
117
|
+
# >> │ ├─Attack(1)
|
118
|
+
# >> │ │ ├─Shake the sword(1)
|
119
|
+
# >> │ │ ├─Attack magic(2)
|
120
|
+
# >> │ │ │ ├─Summoned Beast X(1)
|
121
|
+
# >> │ │ │ └─Summoned Beast Y(2)
|
122
|
+
# >> │ │ └─Repel sword in length(3)
|
123
|
+
# >> │ └─Defense(2)
|
124
|
+
# >> ├─Withdraw(2)
|
125
|
+
# >> │ ├─To stop(1)
|
126
|
+
# >> │ │ ├─Place a trap(1)
|
127
|
+
# >> │ │ └─Shoot a bow and arrow(2)
|
128
|
+
# >> │ └─To escape(2)
|
129
|
+
# >> └─Break(3)
|
130
|
+
# >> ├─Stop(1)
|
131
|
+
# >> └─Recover(2)
|
132
|
+
# >> ├─Recovery magic(1)
|
133
|
+
# >> └─Drink recovery medicine(2)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# each_node can be used if Treeable module is included
|
2
|
+
#
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tree_support"
|
5
|
+
|
6
|
+
root = TreeSupport.example
|
7
|
+
root.class.ancestors # => [TreeSupport::Node, TreeSupport::Stringify, TreeSupport::Treeable, Object, Kernel, BasicObject]
|
8
|
+
root.each_node.with_index { |n, i| p [i, n.name] }
|
9
|
+
|
10
|
+
# >> [0, "*root*"]
|
11
|
+
# >> [1, "Battle"]
|
12
|
+
# >> [2, "Attack"]
|
13
|
+
# >> [3, "Shake the sword"]
|
14
|
+
# >> [4, "Attack magic"]
|
15
|
+
# >> [5, "Summoned Beast X"]
|
16
|
+
# >> [6, "Summoned Beast Y"]
|
17
|
+
# >> [7, "Repel sword in length"]
|
18
|
+
# >> [8, "Defense"]
|
19
|
+
# >> [9, "Withdraw"]
|
20
|
+
# >> [10, "To stop"]
|
21
|
+
# >> [11, "Place a trap"]
|
22
|
+
# >> [12, "Shoot a bow and arrow"]
|
23
|
+
# >> [13, "To escape"]
|
24
|
+
# >> [14, "Break"]
|
25
|
+
# >> [15, "Stop"]
|
26
|
+
# >> [16, "Recover"]
|
27
|
+
# >> [17, "Recovery magic"]
|
28
|
+
# >> [18, "Drink recovery medicine"]
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Idiom replacing TreeSupport::Node's tree with ActiveRecord's tree
|
4
|
+
#
|
5
|
+
require "bundler/setup"
|
6
|
+
require "tree_support"
|
7
|
+
require "active_record"
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
10
|
+
ActiveRecord::Migration.verbose = false
|
11
|
+
|
12
|
+
ActiveRecord::Schema.define do
|
13
|
+
create_table :nodes do |t|
|
14
|
+
t.belongs_to :parent
|
15
|
+
t.string :name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Node < ActiveRecord::Base
|
20
|
+
belongs_to :parent, class_name: name, foreign_key: :parent_id
|
21
|
+
has_many :children, class_name: name, foreign_key: :parent_id
|
22
|
+
end
|
23
|
+
|
24
|
+
# METHOD 1. It is simple but unique key equivalent becomes essential
|
25
|
+
Node.destroy_all
|
26
|
+
TreeSupport.example.each_node do |node|
|
27
|
+
obj = Node.find_or_initialize_by(name: node.name) # I'm doing find_or to update
|
28
|
+
if node.parent
|
29
|
+
obj.parent = Node.find_by(name: node.parent.name) # It is difficult to draw here without a unique key
|
30
|
+
else
|
31
|
+
obj.parent = nil
|
32
|
+
end
|
33
|
+
obj.save!
|
34
|
+
end
|
35
|
+
root = Node.find_by(name: TreeSupport.example.name)
|
36
|
+
puts TreeSupport.tree(root)
|
37
|
+
|
38
|
+
# METHOD 2. Improve method 1 by making sure that parents are always present when children are made
|
39
|
+
Node.destroy_all
|
40
|
+
stock = {}
|
41
|
+
TreeSupport.example.each_node do |node|
|
42
|
+
obj = Node.find_or_initialize_by(name: node.name)
|
43
|
+
if node.parent
|
44
|
+
obj.parent = stock[node.parent]
|
45
|
+
else
|
46
|
+
obj.parent = nil
|
47
|
+
end
|
48
|
+
obj.save!
|
49
|
+
stock[node] = obj
|
50
|
+
end
|
51
|
+
root = Node.find_by(name: TreeSupport.example.name)
|
52
|
+
puts TreeSupport.tree(root)
|
53
|
+
|
54
|
+
# Method 3. Unnecessary to use a unique key when recursive. However, it is a bit that create! Is two places. Update is difficult (?)
|
55
|
+
Node.destroy_all
|
56
|
+
def create_recursion(root, node)
|
57
|
+
sub = root.children.create!(name: node.name)
|
58
|
+
node.children.each {|node| create_recursion(sub, node) }
|
59
|
+
end
|
60
|
+
root = Node.create!(name: TreeSupport.example.name)
|
61
|
+
TreeSupport.example.children.each {|node| create_recursion(root, node)}
|
62
|
+
puts TreeSupport.tree(root)
|
63
|
+
|
64
|
+
# >> *root*
|
65
|
+
# >> ├─Battle
|
66
|
+
# >> │ ├─Attack
|
67
|
+
# >> │ │ ├─Shake the sword
|
68
|
+
# >> │ │ ├─Attack magic
|
69
|
+
# >> │ │ │ ├─Summoned Beast X
|
70
|
+
# >> │ │ │ └─Summoned Beast Y
|
71
|
+
# >> │ │ └─Repel sword in length
|
72
|
+
# >> │ └─Defense
|
73
|
+
# >> ├─Withdraw
|
74
|
+
# >> │ ├─To stop
|
75
|
+
# >> │ │ ├─Place a trap
|
76
|
+
# >> │ │ └─Shoot a bow and arrow
|
77
|
+
# >> │ └─To escape
|
78
|
+
# >> └─Break
|
79
|
+
# >> ├─Stop
|
80
|
+
# >> └─Recover
|
81
|
+
# >> ├─Recovery magic
|
82
|
+
# >> └─Drink recovery medicine
|
83
|
+
# >> *root*
|
84
|
+
# >> ├─Battle
|
85
|
+
# >> │ ├─Attack
|
86
|
+
# >> │ │ ├─Shake the sword
|
87
|
+
# >> │ │ ├─Attack magic
|
88
|
+
# >> │ │ │ ├─Summoned Beast X
|
89
|
+
# >> │ │ │ └─Summoned Beast Y
|
90
|
+
# >> │ │ └─Repel sword in length
|
91
|
+
# >> │ └─Defense
|
92
|
+
# >> ├─Withdraw
|
93
|
+
# >> │ ├─To stop
|
94
|
+
# >> │ │ ├─Place a trap
|
95
|
+
# >> │ │ └─Shoot a bow and arrow
|
96
|
+
# >> │ └─To escape
|
97
|
+
# >> └─Break
|
98
|
+
# >> ├─Stop
|
99
|
+
# >> └─Recover
|
100
|
+
# >> ├─Recovery magic
|
101
|
+
# >> └─Drink recovery medicine
|
102
|
+
# >> *root*
|
103
|
+
# >> ├─Battle
|
104
|
+
# >> │ ├─Attack
|
105
|
+
# >> │ │ ├─Shake the sword
|
106
|
+
# >> │ │ ├─Attack magic
|
107
|
+
# >> │ │ │ ├─Summoned Beast X
|
108
|
+
# >> │ │ │ └─Summoned Beast Y
|
109
|
+
# >> │ │ └─Repel sword in length
|
110
|
+
# >> │ └─Defense
|
111
|
+
# >> ├─Withdraw
|
112
|
+
# >> │ ├─To stop
|
113
|
+
# >> │ │ ├─Place a trap
|
114
|
+
# >> │ │ └─Shoot a bow and arrow
|
115
|
+
# >> │ └─To escape
|
116
|
+
# >> └─Break
|
117
|
+
# >> ├─Stop
|
118
|
+
# >> └─Recover
|
119
|
+
# >> ├─Recovery magic
|
120
|
+
# >> └─Drink recovery medicine
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Generating original code from tree of TreeSupport::Node Rough
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "tree_support"
|
5
|
+
|
6
|
+
def generate(node)
|
7
|
+
s = ""
|
8
|
+
if node.parent
|
9
|
+
s << "add #{node.name.inspect}"
|
10
|
+
else
|
11
|
+
s << "TreeSupport::Node.new(#{node.name.inspect})"
|
12
|
+
end
|
13
|
+
unless node.children.empty?
|
14
|
+
s << [" do\n", node.children.collect {|node| generate(node) }.join, "end"].join
|
15
|
+
end
|
16
|
+
s << "\n"
|
17
|
+
end
|
18
|
+
|
19
|
+
code = generate(TreeSupport.example)
|
20
|
+
puts code
|
21
|
+
|
22
|
+
puts TreeSupport.tree(eval(code))
|
23
|
+
# >> TreeSupport::Node.new("*root*") do
|
24
|
+
# >> add "Battle" do
|
25
|
+
# >> add "Attack" do
|
26
|
+
# >> add "Shake the sword"
|
27
|
+
# >> add "Attack magic" do
|
28
|
+
# >> add "Summoned Beast X"
|
29
|
+
# >> add "Summoned Beast Y"
|
30
|
+
# >> end
|
31
|
+
# >> add "Repel sword in length"
|
32
|
+
# >> end
|
33
|
+
# >> add "Defense"
|
34
|
+
# >> end
|
35
|
+
# >> add "Withdraw" do
|
36
|
+
# >> add "To stop" do
|
37
|
+
# >> add "Place a trap"
|
38
|
+
# >> add "Shoot a bow and arrow"
|
39
|
+
# >> end
|
40
|
+
# >> add "To escape"
|
41
|
+
# >> end
|
42
|
+
# >> add "Break" do
|
43
|
+
# >> add "Stop"
|
44
|
+
# >> add "Recover" do
|
45
|
+
# >> add "Recovery magic"
|
46
|
+
# >> add "Drink recovery medicine"
|
47
|
+
# >> end
|
48
|
+
# >> end
|
49
|
+
# >> end
|
50
|
+
# >> *root*
|
51
|
+
# >> ├─Battle
|
52
|
+
# >> │ ├─Attack
|
53
|
+
# >> │ │ ├─Shake the sword
|
54
|
+
# >> │ │ ├─Attack magic
|
55
|
+
# >> │ │ │ ├─Summoned Beast X
|
56
|
+
# >> │ │ │ └─Summoned Beast Y
|
57
|
+
# >> │ │ └─Repel sword in length
|
58
|
+
# >> │ └─Defense
|
59
|
+
# >> ├─Withdraw
|
60
|
+
# >> │ ├─To stop
|
61
|
+
# >> │ │ ├─Place a trap
|
62
|
+
# >> │ │ └─Shoot a bow and arrow
|
63
|
+
# >> │ └─To escape
|
64
|
+
# >> └─Break
|
65
|
+
# >> ├─Stop
|
66
|
+
# >> └─Recover
|
67
|
+
# >> ├─Recovery magic
|
68
|
+
# >> └─Drink recovery medicine
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# An example using TreeSupport::ArTreeModel of alternate library of acts_as_tree
|
4
|
+
#
|
5
|
+
require "bundler/setup"
|
6
|
+
|
7
|
+
require "rails"
|
8
|
+
require "active_record"
|
9
|
+
require "tree_support"
|
10
|
+
|
11
|
+
Class.new(Rails::Application) { config.eager_load = false }.initialize! # Activate ar_tree_model
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
14
|
+
ActiveRecord::Migration.verbose = false
|
15
|
+
|
16
|
+
ActiveRecord::Schema.define do
|
17
|
+
create_table :nodes do |t|
|
18
|
+
t.belongs_to :parent
|
19
|
+
t.string :name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Node < ActiveRecord::Base
|
24
|
+
ar_tree_model scope: -> { order(:name).where.not(name: "Break") }
|
25
|
+
|
26
|
+
def add(name, &block)
|
27
|
+
tap do
|
28
|
+
child = children.create!(name: name)
|
29
|
+
if block_given?
|
30
|
+
child.instance_eval(&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Node.create!(name: "*root*").tap do |n|
|
37
|
+
n.instance_eval do
|
38
|
+
add "Battle" do
|
39
|
+
add "Attack" do
|
40
|
+
add "Shake the sword"
|
41
|
+
add "Attack magic" do
|
42
|
+
add "Summoned Beast X"
|
43
|
+
add "Summoned Beast Y"
|
44
|
+
end
|
45
|
+
add "Repel sword in length"
|
46
|
+
end
|
47
|
+
add "Defense"
|
48
|
+
end
|
49
|
+
add "Withdraw" do
|
50
|
+
add "To stop" do
|
51
|
+
add "Place a trap"
|
52
|
+
add "Shoot a bow and arrow"
|
53
|
+
end
|
54
|
+
add "To escape"
|
55
|
+
end
|
56
|
+
add "Break" do
|
57
|
+
add "Stop"
|
58
|
+
add "Recover" do
|
59
|
+
add "Recovery magic"
|
60
|
+
add "Drink recovery medicine"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
puts Node.root.to_s_tree
|
67
|
+
Node.destroy_all # Because it does not use acts_as_list, it can be erased
|
68
|
+
|
69
|
+
# >> *root*
|
70
|
+
# >> ├─Battle
|
71
|
+
# >> │ ├─Attack
|
72
|
+
# >> │ │ ├─Attack magic
|
73
|
+
# >> │ │ │ ├─Summoned Beast X
|
74
|
+
# >> │ │ │ └─Summoned Beast Y
|
75
|
+
# >> │ │ ├─Repel sword in length
|
76
|
+
# >> │ │ └─Shake the sword
|
77
|
+
# >> │ └─Defense
|
78
|
+
# >> └─Withdraw
|
79
|
+
# >> ├─To escape
|
80
|
+
# >> └─To stop
|
81
|
+
# >> ├─Place a trap
|
82
|
+
# >> └─Shoot a bow and arrow
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# An example requiring safe_destroy_all (since acts_as_list is behind, you can roll it with destroy_all)
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
|
5
|
+
require "rails"
|
6
|
+
require "active_record"
|
7
|
+
require "tree_support"
|
8
|
+
|
9
|
+
begin
|
10
|
+
require "acts_as_list"
|
11
|
+
ActiveRecord::Base.include(ActiveRecord::Acts::List)
|
12
|
+
end
|
13
|
+
|
14
|
+
Class.new(Rails::Application) { config.eager_load = false }.initialize! # Activate ar_tree_model
|
15
|
+
|
16
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
17
|
+
ActiveRecord::Migration.verbose = false
|
18
|
+
|
19
|
+
ActiveRecord::Schema.define do
|
20
|
+
create_table :nodes do |t|
|
21
|
+
t.belongs_to :parent
|
22
|
+
t.string :name
|
23
|
+
t.integer :position
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Node < ActiveRecord::Base
|
28
|
+
ar_tree_model
|
29
|
+
acts_as_list scope: :parent
|
30
|
+
|
31
|
+
def add(name, &block)
|
32
|
+
tap do
|
33
|
+
child = children.create!(name: name)
|
34
|
+
if block_given?
|
35
|
+
child.instance_eval(&block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Node.create!(name: "*root*").tap do |n|
|
42
|
+
n.instance_eval do
|
43
|
+
add "Battle" do
|
44
|
+
add "Attack"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
puts Node.root.to_s_tree
|
50
|
+
Node.destroy_all rescue $! # => #<ActiveRecord::RecordNotFound: Couldn't find Node with 'id'=2>
|
51
|
+
Node.safe_destroy_all
|
52
|
+
Node.count # => 0
|
53
|
+
# >> *root*
|
54
|
+
# >> └─Battle
|
55
|
+
# >> └─Attack
|