acts_as_associate_tree 0.0.1
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.
- 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: []
|