acts_as_associate_tree 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []