treevisitor 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/LICENSE.txt +20 -0
  2. data/README.rdoc +42 -0
  3. data/Rakefile +9 -0
  4. data/VERSION.yml +5 -0
  5. data/bin/tree.rb +9 -0
  6. data/examples/find_files.rb +17 -0
  7. data/examples/print_files.rb +15 -0
  8. data/lib/tree_visitor.rb +2 -0
  9. data/lib/treevisitor.rb +31 -0
  10. data/lib/treevisitor/abs_node.rb +146 -0
  11. data/lib/treevisitor/cli/cli_tree.rb +98 -0
  12. data/lib/treevisitor/dir_processor.rb +46 -0
  13. data/lib/treevisitor/dir_tree_walker.rb +164 -0
  14. data/lib/treevisitor/leaf_node.rb +33 -0
  15. data/lib/treevisitor/tree_node.rb +247 -0
  16. data/lib/treevisitor/tree_node_visitor.rb +29 -0
  17. data/lib/treevisitor/visitors/block_tree_node_visitor.rb +21 -0
  18. data/lib/treevisitor/visitors/build_dir_tree_visitor.rb +53 -0
  19. data/lib/treevisitor/visitors/callback_tree_node_visitor.rb +43 -0
  20. data/lib/treevisitor/visitors/callback_tree_node_visitor2.rb +61 -0
  21. data/lib/treevisitor/visitors/clone_tree_node_visitor.rb +39 -0
  22. data/lib/treevisitor/visitors/depth_tree_node_visitor.rb +27 -0
  23. data/lib/treevisitor/visitors/flat_print_tree_node_visitors.rb +20 -0
  24. data/lib/treevisitor/visitors/print_dir_tree_visitor.rb +21 -0
  25. data/lib/treevisitor/visitors/print_tree_node_visitor.rb +40 -0
  26. data/lib/treevisitor_cli.rb +7 -0
  27. data/spec/fixtures/test_dir/.dir_with_dot/dummy.txt +0 -0
  28. data/spec/fixtures/test_dir/dir.1/dir.1.2/file.1.2.1 +0 -0
  29. data/spec/fixtures/test_dir/dir.1/file.1.1 +0 -0
  30. data/spec/fixtures/test_dir/dir.2/file.2.1 +0 -0
  31. data/spec/spec_helper.rb +29 -0
  32. data/spec/treevisitor/cli/cli_tree_spec.rb +58 -0
  33. data/spec/treevisitor/dir_processor_spec.rb +15 -0
  34. data/spec/treevisitor/dir_tree_walker_spec.rb +33 -0
  35. data/spec/treevisitor/tree_node_dsl_spec.rb +153 -0
  36. data/spec/treevisitor/tree_node_spec.rb +153 -0
  37. data/spec/treevisitor/tree_node_visitor_spec.rb +70 -0
  38. data/tasks/jeweler.rake +55 -0
  39. data/tasks/rspec.rake +34 -0
  40. data/tasks/rubyforge.rake +28 -0
  41. data/tasks/yard.rake +36 -0
  42. metadata +192 -0
@@ -0,0 +1,164 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TreeVisitor
3
+
4
+ #
5
+ # Visit a file system directory
6
+ #
7
+ class DirTreeWalker
8
+
9
+ #
10
+ # @param [String] dirname the root of the tree (top level directory)
11
+ #
12
+ def initialize( dirname )
13
+ @dirname = dirname
14
+ unless File.directory?( dirname )
15
+ raise "#{dirname} is not a directory!"
16
+ end
17
+
18
+ @visitor = nil
19
+
20
+ #
21
+ # pattern
22
+ #
23
+ @ignore_dir_patterns = []
24
+ @ignore_file_patterns = []
25
+
26
+ @match_file_patterns = []
27
+
28
+ #
29
+ # options
30
+ #
31
+ @visit_leaf = true
32
+ end
33
+
34
+ ##########################################################################
35
+ # Pattern
36
+
37
+
38
+ #
39
+ # Ignore a node (leaf/Tree) matching pattern
40
+ # @param [RegEx] pattern
41
+ #
42
+ def ignore(pattern)
43
+ @ignore_dir_patterns << pattern
44
+ @ignore_file_patterns << pattern
45
+ end
46
+
47
+ #
48
+ # Ignore a directory (Tree) matching pattern
49
+ # @param [RegEx] pattern
50
+ #
51
+ def ignore_dir( pattern )
52
+ @ignore_dir_patterns << pattern
53
+ end
54
+
55
+ #
56
+ # Ignore a file (Leaf) matching pattern
57
+ # @param [RegEx] pattern
58
+ #
59
+ def ignore_file( pattern )
60
+ @ignore_file_patterns << pattern
61
+ end
62
+
63
+ #
64
+ # Just the opposite of ignore
65
+ # directory/file matching pattern will be visited
66
+ #
67
+ # @param [RegEx] pattern
68
+ #
69
+ def match( pattern )
70
+ @match_file_patterns << pattern
71
+ end
72
+
73
+ ##########################################################################
74
+ # Options
75
+
76
+ #
77
+ # boh
78
+ #
79
+ attr_accessor :visit_leaf
80
+
81
+ ##########################################################################
82
+
83
+
84
+ #
85
+ # Test directory ignore pattern
86
+ #
87
+ # @param [String] directory name
88
+ # @return [boolean] if dirname match almost one pattern
89
+ #
90
+ def ignore_dir?( dirname )
91
+ _include?( @ignore_dir_patterns, File.basename( dirname ) )
92
+ end
93
+
94
+ #
95
+ # Test file ignore pattern
96
+ #
97
+ # @param [String] file name
98
+ # @return [boolean] if filename match almost one pattern
99
+ #
100
+ def ignore_file?( filename )
101
+ _include?( @ignore_file_patterns, File.basename( filename ) )
102
+ end
103
+
104
+ #
105
+ # Test common ignore pattern
106
+ #
107
+ # @param [String] file name
108
+ # @return [boolean] if filename match almost one pattern
109
+ #
110
+ def match?( filename )
111
+ return true if @match_file_patterns.empty?
112
+ _include?( @match_file_patterns, File.basename( filename ) )
113
+ end
114
+
115
+ #
116
+ # Run the visitor through the directory tree
117
+ #
118
+ # return the visitor
119
+ #
120
+ def run( tree_node_visitor )
121
+ @visitor = tree_node_visitor
122
+ process_directory( File.expand_path( @dirname ) )
123
+ tree_node_visitor
124
+ end
125
+
126
+ private
127
+
128
+ def _include?(patterns, basename)
129
+ # return false if the patters.empty?
130
+ patterns.find{ |pattern|
131
+ if pattern.respond_to?(:match) # or if pattern.kind_of? Regexp
132
+ pattern.match( basename )
133
+ else
134
+ basename == pattern
135
+ end
136
+ }
137
+ end
138
+
139
+ #
140
+ # recurse on other directories
141
+ #
142
+ def process_directory( dirname )
143
+ @visitor.enter_tree_node( dirname )
144
+ # return if ignore_dir?( dirname )
145
+
146
+ Dir.entries( dirname ).sort.each { |basename|
147
+ next if basename == "." or basename == ".." # ignore always "." and ".."
148
+ pathname = File.join( dirname, basename )
149
+
150
+ if File.directory?( pathname )
151
+ # directory
152
+ process_directory( pathname ) unless ignore_dir?( basename )
153
+ else
154
+ if @visit_leaf
155
+ if match?( basename ) && ! ignore_file?( basename )
156
+ @visitor.visit_leaf_node( pathname )
157
+ end
158
+ end
159
+ end
160
+ }
161
+ @visitor.exit_tree_node( dirname )
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TreeVisitor
3
+
4
+ #
5
+ # Represent a LeafNode
6
+ #
7
+ class LeafNode < AbsNode
8
+
9
+ #
10
+ # @param [Object] content of node
11
+ #
12
+ def initialize( content, parent = nil )
13
+ super( content )
14
+ parent.add_leaf(self) if parent
15
+ end
16
+
17
+ #
18
+ # @return false because a leaf_node cannot be a root
19
+ #
20
+ def root?
21
+ false
22
+ end
23
+
24
+ #
25
+ # @return [TreeNodeVisitor] the visitor
26
+ #
27
+ def accept( visitor )
28
+ visitor.visit_leaf_node( self )
29
+ visitor
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,247 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TreeVisitor
3
+ #
4
+ # TreeNode can contains other TreeNode (children)
5
+ # and can contains LeafNode (leaves)
6
+ #
7
+ # TreeNode @children -1---n-> TreeNode
8
+ # @leaves -1---n-> LeafNode
9
+ #
10
+ class TreeNode < AbsNode
11
+
12
+ class << self
13
+
14
+ def create(class1 = TreeNode, class2 = LeafNode, &block)
15
+ if class1.ancestors.include?(TreeNode) and class2.ancestors.include?(LeafNode)
16
+ @tree_node_class = class1
17
+ @leaf_node_class = class2
18
+ elsif class1.ancestors.include?(LeafNode) and class2 == LeafNode
19
+ @tree_node_class = self
20
+ @leaf_node_class = class1
21
+ end
22
+
23
+ if @tree_node_class.nil? || @leaf_node_class.nil?
24
+ raise "Must be specified class derived from TreeNode and LeafNode"
25
+ end
26
+
27
+ @scope_stack = []
28
+ class_eval(&block)
29
+ end
30
+
31
+ private
32
+
33
+ def node(*args, &block)
34
+ parent_node = @scope_stack.length > 0 ? @scope_stack[-1] : nil
35
+ args << parent_node
36
+ tree_node = @tree_node_class.new(*args)
37
+ @scope_stack.push tree_node
38
+ if block
39
+ if block.arity == 0 || block.arity == -1
40
+ class_eval(&block)
41
+ elsif block.arity == 1
42
+ new_block = Proc.new { block.call(tree_node) }
43
+ class_eval(&new_block)
44
+ else
45
+ raise "block take too much arguments #{block.arity}"
46
+ end
47
+ end
48
+ @scope_stack.pop
49
+ end
50
+
51
+ def leaf(*args, &block)
52
+ tree_node = @scope_stack[-1]
53
+ args << tree_node
54
+ leaf_node = @leaf_node_class.new(*args)
55
+ if block
56
+ if block.arity == 0 || block.arity == -1
57
+ class_eval(&block)
58
+ elsif block.arity == 1
59
+ new_block = Proc.new { block.call(leaf_node) }
60
+ class_eval(&new_block)
61
+ else
62
+ raise "block take too much arguments #{block.arity}"
63
+ end
64
+ end
65
+ leaf_node
66
+ end
67
+ end
68
+
69
+ attr_reader :leaves
70
+ attr_reader :children
71
+
72
+ #
73
+ # @param [Object] content of this node
74
+ #
75
+ def initialize(content, parent = nil)
76
+ @leaves = []
77
+ @children = []
78
+ super(content)
79
+ parent.add_child(self) if parent
80
+ end
81
+
82
+ #
83
+ # Test if is a root
84
+ #
85
+ def root?
86
+ @parent.nil?
87
+ end
88
+
89
+ #
90
+ # invalidate cached info
91
+ # invalidate propagates form parent to children and leaves
92
+ #
93
+ def invalidate
94
+ super
95
+ @children.each { |c| c.invalidate }
96
+ @leaves.each { |l| l.invalidate }
97
+ end
98
+
99
+ #
100
+ # @return [FixNum] total number of nodes
101
+ #
102
+ def nr_nodes
103
+ nr = @leaves.length + @children.length
104
+ @children.inject(nr) { |sum, c| sum + c.nr_nodes }
105
+ end
106
+
107
+ #
108
+ # @return [FixNum] total number of leaves
109
+ #
110
+ def nr_leaves
111
+ @leaves.length + @children.inject(0) { |sum, child| sum + child.nr_leaves }
112
+ end
113
+
114
+ #
115
+ # @return [FixNum] total number of children
116
+ #
117
+ def nr_children
118
+ @children.length + @children.inject(0) { |sum, child| sum + child.nr_children }
119
+ end
120
+
121
+ #
122
+ # Add a Leaf
123
+ # @param [LeafNode]
124
+ #
125
+ # @return self
126
+ #
127
+ def add_leaf(leaf)
128
+ return if leaf.parent == self
129
+ if not leaf.parent.nil?
130
+ leaf.remove_from_parent
131
+ end
132
+ leaf.parent = self
133
+ if @leaves.length > 0
134
+ @leaves.last.next = leaf
135
+ leaf.prev = @leaves.last
136
+ else
137
+ leaf.prev = nil
138
+ end
139
+ leaf.next = nil
140
+ leaf.invalidate
141
+ @leaves << leaf
142
+ self
143
+ end
144
+
145
+ #
146
+ # Add a Tree
147
+ # @param [LeafNode]
148
+ #
149
+ # @return self
150
+ #
151
+ def add_child(tree_node)
152
+ return if tree_node.parent == self
153
+ if not tree_node.parent.nil?
154
+ tree_node.remove_from_parent
155
+ else
156
+ tree_node.prefix_path = nil
157
+ end
158
+ tree_node.invalidate
159
+ tree_node.parent = self
160
+ if @children.length > 0
161
+ @children.last.next = tree_node
162
+ tree_node.prev = @children.last
163
+ else
164
+ tree_node.prev = nil
165
+ end
166
+ tree_node.next = nil
167
+ @children << tree_node
168
+ self
169
+ end
170
+
171
+ #
172
+ # Find a node down the hierarchy with content
173
+ # @param [Object] content of searched node
174
+ # @return [Object, nil] nil if no
175
+ #
176
+ def find(content = nil, &block)
177
+ if content and block_given?
178
+ raise "TreeNode::find - passed content AND block"
179
+ end
180
+
181
+ if content
182
+ block = proc { |c| c == content }
183
+ end
184
+ return self if block.call(self.content)
185
+
186
+ leaf = @leaves.find { |l| block.call(l.content) }
187
+ return leaf if leaf
188
+
189
+ @children.each do |child|
190
+ node = child.find &block
191
+ return node if node
192
+ end
193
+ nil
194
+ end
195
+
196
+ #
197
+ # return the visitor
198
+ #
199
+ def accept(visitor)
200
+ visitor.enter_tree_node(self)
201
+ @leaves.each { |leaf|
202
+ leaf.accept(visitor)
203
+ }
204
+ @children.each { |child|
205
+ child.accept(visitor)
206
+ }
207
+ visitor.exit_tree_node(self)
208
+ visitor
209
+ end
210
+
211
+ #
212
+ # Format the content of tree
213
+ #
214
+ def to_str(prefix= "")
215
+ str = ""
216
+
217
+ if root?
218
+ str << to_s << "\n"
219
+ else
220
+ str << prefix
221
+ if self.next
222
+ str << '|-- '
223
+ else
224
+ str << '`-- '
225
+ end
226
+ str << to_s << "\n"
227
+ prefix += self.next ? "| " : " "
228
+ end
229
+
230
+ @leaves.each do |leaf|
231
+ str << prefix
232
+ if !leaf.next.nil? or !@children.empty?
233
+ str << '|-- '
234
+ else
235
+ str << '`-- '
236
+ end
237
+ str << leaf.to_s << "\n"
238
+ end
239
+
240
+ @children.each do |child|
241
+ str << child.to_str(prefix)
242
+ end
243
+ str
244
+ end
245
+
246
+ end
247
+ end
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TreeVisitor
3
+ #
4
+ # Callback methods used to visit a tree
5
+ # Are empty so it is possible to define only a subset
6
+ #
7
+ class TreeNodeVisitor
8
+
9
+ #
10
+ # called on tree node at start of the visit i.e. we start to visit the subtree
11
+ #
12
+ def enter_tree_node( tree_node )
13
+ end
14
+
15
+ #
16
+ # called on tree node at end of the visit i.e. oll subtree are visited
17
+ #
18
+ def exit_tree_node( tree_node )
19
+ end
20
+
21
+ #
22
+ # called when visit leaf node
23
+ #
24
+ def visit_leaf_node( leaf_node )
25
+ end
26
+
27
+ end
28
+
29
+ end