acts_as_associate_tree 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/acts_as_associate_tree.rb +193 -0
- data/lib/acts_as_associate_tree/node_set.rb +107 -0
- data/lib/acts_as_associate_tree/set_tree.rb +47 -0
- metadata +47 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
##
|
2
|
+
# Module
|
3
|
+
# ActsAsAssociateTree
|
4
|
+
#
|
5
|
+
# Used for generating tree-like data
|
6
|
+
#
|
7
|
+
# Interface: generate_tree_nodes
|
8
|
+
#
|
9
|
+
# Suppose you have a class like this
|
10
|
+
#
|
11
|
+
# class Foo
|
12
|
+
# attr_reader :i
|
13
|
+
# def initialize(i)
|
14
|
+
# @i = i
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# def items
|
18
|
+
# foo_items = []
|
19
|
+
# i.times do |i|
|
20
|
+
# foo_items.push Foo.new("Item" + i.to_s)
|
21
|
+
# end
|
22
|
+
# foo_items
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class << self
|
26
|
+
# def all
|
27
|
+
# all_foos = []
|
28
|
+
# 2.times do |i|
|
29
|
+
# all_foos.push Foo.new(i + 1)
|
30
|
+
# end
|
31
|
+
# all_foos
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# generate_tree_nodes do
|
37
|
+
# nodes { Foo.all }
|
38
|
+
# node_attrs { { :text => i } } # invoke this block by the instance_eval
|
39
|
+
# # method of each element in nodes
|
40
|
+
# children {
|
41
|
+
# nodes { items }
|
42
|
+
# node_attrs { { :text => i } } # using nodes#element.instance_eval to run this block
|
43
|
+
# }
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# it will return a array like bellow:
|
47
|
+
#
|
48
|
+
# [{
|
49
|
+
# :text=>1,
|
50
|
+
# :children=>[{
|
51
|
+
# :text=>"Item0",
|
52
|
+
# :leaf=>true
|
53
|
+
# }]
|
54
|
+
# }, {
|
55
|
+
# :text=>2,
|
56
|
+
# :children=>[{
|
57
|
+
# :text=>"Item0",
|
58
|
+
# :leaf=>true
|
59
|
+
# }, {
|
60
|
+
# :text=>"Item1",
|
61
|
+
# :leaf=>true
|
62
|
+
# }]
|
63
|
+
# }]
|
64
|
+
#
|
65
|
+
# include this module in your controller or model or any of your classes
|
66
|
+
# used to generating tree-like datas, you will get a instance function
|
67
|
+
# *generate_tree_nodes*, then you could use it this way.
|
68
|
+
#
|
69
|
+
# class Parent < ActiveRecord::Base
|
70
|
+
# has_many :children
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# class Child < ActiveRecord::Base
|
74
|
+
# belongs_to :parent
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# 其数据如下:
|
78
|
+
#
|
79
|
+
# # Parent.all
|
80
|
+
# # +-------+---------+
|
81
|
+
# # | id | name |
|
82
|
+
# # +-------+---------+
|
83
|
+
# # | 1 | xx1 |
|
84
|
+
# # | 2 | xx2 |
|
85
|
+
# # | 3 | xx3 |
|
86
|
+
# # | 4 | xx4 |
|
87
|
+
# # | 5 | xx5 |
|
88
|
+
# # +-------+---------+
|
89
|
+
#
|
90
|
+
# # Child.all
|
91
|
+
# # +-------+--------------+--------+
|
92
|
+
# # | id | parent_id | name |
|
93
|
+
# # +-------+--------------+--------+
|
94
|
+
# # | 1 | 1 | xx0 |
|
95
|
+
# # | 2 | 1 | xx1 |
|
96
|
+
# # | 3 | 1 | xx2 |
|
97
|
+
# # | 4 | 2 | xx0 |
|
98
|
+
# # | 5 | 2 | xx1 |
|
99
|
+
# # +-------+--------------+--------+
|
100
|
+
#
|
101
|
+
# class ParentController < ApplicationController
|
102
|
+
# include ActsAsAssociateTree
|
103
|
+
#
|
104
|
+
# # GET /parent/index_tree.json
|
105
|
+
# def index_tree
|
106
|
+
# special_tag = 'special_tag'
|
107
|
+
# render :josn => generate_tree_nodes {
|
108
|
+
# # 一个树可以有不同的结点集
|
109
|
+
# add_set {
|
110
|
+
# nodes { Parent.all } # 结点的数据,block的执行结果需返回一个数组
|
111
|
+
# node_attrs { { :name => name } } # 结点属性,block执行结果需返回一个Hash实例,block 中可以执行任何一个 Parent 实例能执行的方法
|
112
|
+
# children {
|
113
|
+
# nodes { children } # 在子结点中可以执行任何一个 Parent 实例能执行的方法
|
114
|
+
# node_attrs { { :name => name, :special => id == 1 ? special_tag : 'nothing' } } # 每一个 block 中都能访问闭包变量,如 special_tag
|
115
|
+
# }
|
116
|
+
# # 可以添加多个子节点
|
117
|
+
# children {
|
118
|
+
# nodes { [1, 2, 3, 4, 5] }
|
119
|
+
# node_attrs { { :name => self } }
|
120
|
+
# }
|
121
|
+
# }
|
122
|
+
# # 其他结点集
|
123
|
+
# add_set {
|
124
|
+
# set_attrs { { :text => 'i am a tree nodes set' } } # 可以添加结点集描述
|
125
|
+
# nodes { %w(act_as_associate_tree is so powerful) }
|
126
|
+
# node_attrs { { :text => self } }
|
127
|
+
# }
|
128
|
+
# }
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# *index_tree* 方法会返回如下形式的数据
|
133
|
+
#
|
134
|
+
# [{:name=>"xx1",
|
135
|
+
# :children=>
|
136
|
+
# [{:name=>"xx0", :special=>"special_tag", :leaf=>true},
|
137
|
+
# {:name=>"xx1", :special=>"nothing", :leaf=>true},
|
138
|
+
# {:name=>"xx2", :special=>"nothing", :leaf=>true},
|
139
|
+
# {:name=>1, :leaf=>true},
|
140
|
+
# {:name=>2, :leaf=>true},
|
141
|
+
# {:name=>3, :leaf=>true},
|
142
|
+
# {:name=>4, :leaf=>true},
|
143
|
+
# {:name=>5, :leaf=>true}]},
|
144
|
+
# {:name=>"xx2",
|
145
|
+
# :children=>
|
146
|
+
# [{:name=>"xx0", :special=>"nothing", :leaf=>true},
|
147
|
+
# {:name=>"xx1", :special=>"nothing", :leaf=>true},
|
148
|
+
# {:name=>1, :leaf=>true},
|
149
|
+
# {:name=>2, :leaf=>true},
|
150
|
+
# {:name=>3, :leaf=>true},
|
151
|
+
# {:name=>4, :leaf=>true},
|
152
|
+
# {:name=>5, :leaf=>true}]},
|
153
|
+
# {:name=>"xx3",
|
154
|
+
# :children=>
|
155
|
+
# [{:name=>1, :leaf=>true},
|
156
|
+
# {:name=>2, :leaf=>true},
|
157
|
+
# {:name=>3, :leaf=>true},
|
158
|
+
# {:name=>4, :leaf=>true},
|
159
|
+
# {:name=>5, :leaf=>true}]},
|
160
|
+
# {:name=>"xx4",
|
161
|
+
# :children=>
|
162
|
+
# [{:name=>1, :leaf=>true},
|
163
|
+
# {:name=>2, :leaf=>true},
|
164
|
+
# {:name=>3, :leaf=>true},
|
165
|
+
# {:name=>4, :leaf=>true},
|
166
|
+
# {:name=>5, :leaf=>true}]},
|
167
|
+
# {:name=>"xx5",
|
168
|
+
# :children=>
|
169
|
+
# [{:name=>1, :leaf=>true},
|
170
|
+
# {:name=>2, :leaf=>true},
|
171
|
+
# {:name=>3, :leaf=>true},
|
172
|
+
# {:name=>4, :leaf=>true},
|
173
|
+
# {:name=>5, :leaf=>true}]},
|
174
|
+
# {:text=>"i am a tree nodes set",
|
175
|
+
# :children=>
|
176
|
+
# [{:text=>"act_as_associate_tree", :leaf=>true},
|
177
|
+
# {:text=>"is", :leaf=>true},
|
178
|
+
# {:text=>"so", :leaf=>true},
|
179
|
+
# {:text=>"powerful", :leaf=>true}]}]
|
180
|
+
#
|
181
|
+
require_relative 'acts_as_associate_tree/node_set.rb'
|
182
|
+
require_relative 'acts_as_associate_tree/set_tree.rb'
|
183
|
+
|
184
|
+
module ActsAsAssociateTree
|
185
|
+
class ConfigurationError < Exception; end
|
186
|
+
|
187
|
+
##
|
188
|
+
# act_as_associate_tree 的统一接口,传入结点构造器后,产生结点
|
189
|
+
def generate_tree_nodes &block
|
190
|
+
(SetTree.new.instance_eval &block).expand
|
191
|
+
end
|
192
|
+
module_function :generate_tree_nodes
|
193
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module ActsAsAssociateTree
|
2
|
+
class NodeSet
|
3
|
+
attr_reader :children_sets, :nodes_block, :node_attrs_block, :set_block, :set_attrs_block
|
4
|
+
attr_reader :set_symbol, :expandable, :mother_tree, :parent_set # for 'symbol' feature
|
5
|
+
def initialize(mother_tree, parent_set = nil, &block)
|
6
|
+
@children_sets = []
|
7
|
+
@set_symbol = ''
|
8
|
+
@mother_tree = mother_tree
|
9
|
+
@expandable = true
|
10
|
+
@parent_set = parent_set
|
11
|
+
|
12
|
+
self.instance_eval &block
|
13
|
+
end
|
14
|
+
|
15
|
+
# with symbol setting, you would be able to generate only a part of a tree instead of a whole one
|
16
|
+
def symbol(sym)
|
17
|
+
raise ConfigurationError.new('symbol Must be a SYMBOL') unless sym.kind_of? Symbol
|
18
|
+
@set_symbol = sym
|
19
|
+
return if mother_tree.expanding_symbol == :all
|
20
|
+
@expandable = false
|
21
|
+
enable_expanding if mother_tree.expanding_symbol == @set_symbol
|
22
|
+
end
|
23
|
+
|
24
|
+
# resymbolize
|
25
|
+
# def resymbol
|
26
|
+
# symbol set_symbol
|
27
|
+
# end
|
28
|
+
|
29
|
+
def enable_expanding
|
30
|
+
@expandable = true
|
31
|
+
parent_set.enable_expanding unless parent_set.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# 树集的属性
|
36
|
+
# block 的执行结果需返回一个 hash
|
37
|
+
def set_attrs(&block)
|
38
|
+
@set_attrs_block = block
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# 节点集合
|
43
|
+
def nodes(&block)
|
44
|
+
@nodes_block = block
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# 节点属性
|
49
|
+
# block 的执行结果需返回一个 hash
|
50
|
+
def node_attrs(&block)
|
51
|
+
@node_attrs_block = block
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# 子节点属性
|
56
|
+
def children(&block)
|
57
|
+
@children_sets << NodeSet.new(mother_tree, self, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def expandable?
|
61
|
+
expandable
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# 展开树集
|
66
|
+
def expand(parent_scope = Object)
|
67
|
+
return [] unless expandable?
|
68
|
+
nodes = parent_scope.instance_eval &nodes_block
|
69
|
+
# Expand child set of each node
|
70
|
+
expanded_set = nodes.map do |node|
|
71
|
+
node_attrs = generate_node_attrs(node)
|
72
|
+
expand_node_children_set(node, node_attrs)
|
73
|
+
end
|
74
|
+
# Wrap a set around the expaned set
|
75
|
+
expanded_set = set_up_set_attributes(parent_scope, expanded_set) unless set_attrs_block.nil?
|
76
|
+
|
77
|
+
expanded_set
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def generate_node_attrs(node)
|
82
|
+
attrs = node.instance_eval &node_attrs_block
|
83
|
+
raise ConfigurationError.new("Node attributes isn't a HASH INSTANCE") unless attrs.kind_of?(Hash)
|
84
|
+
attrs
|
85
|
+
end
|
86
|
+
|
87
|
+
def expand_node_children_set(node, node_attrs)
|
88
|
+
node_attrs[:children] = []
|
89
|
+
children_sets.each do |children_set|
|
90
|
+
node_attrs[:children] += children_set.expand(node)
|
91
|
+
end
|
92
|
+
if node_attrs[:children].empty?
|
93
|
+
node_attrs.delete(:children)
|
94
|
+
node_attrs[:leaf] = true
|
95
|
+
end
|
96
|
+
|
97
|
+
node_attrs
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_up_set_attributes(parent_scope, expanded_set)
|
101
|
+
set_attrs = parent_scope.instance_eval &set_attrs_block
|
102
|
+
raise ConfigurationError.new("Set attributes isn't a HASH INSTANCE") unless set_attrs.kind_of?(Hash)
|
103
|
+
set_attrs[:children] = expanded_set
|
104
|
+
[set_attrs]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ActsAsAssociateTree
|
2
|
+
class SetTree
|
3
|
+
attr_reader :root
|
4
|
+
def initialize
|
5
|
+
@node_sets = []
|
6
|
+
@expanding_symbol = :all
|
7
|
+
end
|
8
|
+
|
9
|
+
##
|
10
|
+
# 添加根节点,如果不需要根节点,可以不添加,节点类型需为一个哈希
|
11
|
+
def enroot(root)
|
12
|
+
raise ConfigurationError.new("Root configuration should be a HASH INSTANCE") unless root.kind_of?(Hash)
|
13
|
+
@root = root
|
14
|
+
end
|
15
|
+
|
16
|
+
# expanding_symbol = :all || :set_symbol
|
17
|
+
def expanding_symbol(type = nil)
|
18
|
+
return @expanding_symbol if type.nil?
|
19
|
+
raise ConfigurationError.new('expanding_symbol Must be a SYMBOL') unless type.kind_of? Symbol
|
20
|
+
@expanding_symbol = type
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# 添加一个新的节点集
|
25
|
+
def add_set(&set_block)
|
26
|
+
@node_sets << NodeSet.new(self, &set_block)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# 展开所有的节点集
|
32
|
+
def expand
|
33
|
+
# Expand all node sets
|
34
|
+
expanded_node_sets = []
|
35
|
+
@node_sets.each do |node_set|
|
36
|
+
expanded_node_sets += node_set.expand
|
37
|
+
end
|
38
|
+
# set up root attributes
|
39
|
+
unless @root.nil?
|
40
|
+
@root[:children] = expanded_node_sets
|
41
|
+
expanded_node_sets = @root
|
42
|
+
end
|
43
|
+
|
44
|
+
expanded_node_sets
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_associate_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Justin_lu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-13 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: organize a tree data
|
15
|
+
email: gdjyluxiaoyong@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/acts_as_associate_tree.rb
|
21
|
+
- lib/acts_as_associate_tree/node_set.rb
|
22
|
+
- lib/acts_as_associate_tree/set_tree.rb
|
23
|
+
homepage: http://rubygems.org/gems/acts_as_associate_tree
|
24
|
+
licenses: []
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 1.8.24
|
44
|
+
signing_key:
|
45
|
+
specification_version: 3
|
46
|
+
summary: tree data
|
47
|
+
test_files: []
|