kwala 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/README +62 -0
  2. data/bin/kwala +39 -0
  3. data/lib/kwala.rb +59 -0
  4. data/lib/kwala/actions/code_change.rb +128 -0
  5. data/lib/kwala/actions/code_coverage.rb +303 -0
  6. data/lib/kwala/actions/code_duplication.rb +222 -0
  7. data/lib/kwala/actions/code_formatting.rb +358 -0
  8. data/lib/kwala/actions/cycle_detection.rb +118 -0
  9. data/lib/kwala/actions/cyclomatic_complexity.rb +159 -0
  10. data/lib/kwala/actions/dead_code.rb +68 -0
  11. data/lib/kwala/actions/executable_files_check.rb +111 -0
  12. data/lib/kwala/actions/flagged_comments.rb +128 -0
  13. data/lib/kwala/actions/gem_plugin.rb +31 -0
  14. data/lib/kwala/actions/loc_count.rb +153 -0
  15. data/lib/kwala/actions/multi_ruby_unit_test.rb +62 -0
  16. data/lib/kwala/actions/outside_links.rb +85 -0
  17. data/lib/kwala/actions/rails_migrate.rb +62 -0
  18. data/lib/kwala/actions/strange_requires.rb +106 -0
  19. data/lib/kwala/actions/syntax_check.rb +130 -0
  20. data/lib/kwala/actions/unit_test.rb +368 -0
  21. data/lib/kwala/build_action.rb +88 -0
  22. data/lib/kwala/build_context.rb +49 -0
  23. data/lib/kwala/command_line_runner.rb +184 -0
  24. data/lib/kwala/ext/demos.jar +0 -0
  25. data/lib/kwala/ext/pmd.jar +0 -0
  26. data/lib/kwala/ext/prefuse.jar +0 -0
  27. data/lib/kwala/extensions.rb +36 -0
  28. data/lib/kwala/lib/code_analyzer.rb +678 -0
  29. data/lib/kwala/lib/cycle_detector.rb +793 -0
  30. data/lib/kwala/lib/strange_requires_detector.rb +145 -0
  31. data/lib/kwala/notification.rb +45 -0
  32. data/lib/kwala/notifications/email.rb +54 -0
  33. data/lib/kwala/notifications/rss.rb +151 -0
  34. data/lib/kwala/project_builder_utils.rb +178 -0
  35. data/lib/kwala/templates/build_template.html +33 -0
  36. data/lib/kwala/templates/code_change_summary.html +27 -0
  37. data/lib/kwala/templates/code_coverage_detailed.html +25 -0
  38. data/lib/kwala/templates/code_coverage_summary.html +30 -0
  39. data/lib/kwala/templates/code_duplication_detailed.html +45 -0
  40. data/lib/kwala/templates/code_duplication_summary.html +9 -0
  41. data/lib/kwala/templates/code_formatting_detailed.html +27 -0
  42. data/lib/kwala/templates/code_formatting_summary.html +11 -0
  43. data/lib/kwala/templates/cycle_detection_detailed.html +20 -0
  44. data/lib/kwala/templates/cycle_detection_summary.html +7 -0
  45. data/lib/kwala/templates/cyclomatic_complexity_summary.html +27 -0
  46. data/lib/kwala/templates/executable_files_check_detailed.html +31 -0
  47. data/lib/kwala/templates/executable_files_check_summary.html +20 -0
  48. data/lib/kwala/templates/flagged_comments_detailed.html +22 -0
  49. data/lib/kwala/templates/flagged_comments_summary.html +10 -0
  50. data/lib/kwala/templates/loc_count_detailed.html +24 -0
  51. data/lib/kwala/templates/loc_count_summary.html +14 -0
  52. data/lib/kwala/templates/mdd/1.png +0 -0
  53. data/lib/kwala/templates/mdd/2.png +0 -0
  54. data/lib/kwala/templates/mdd/3.png +0 -0
  55. data/lib/kwala/templates/mdd/4.png +0 -0
  56. data/lib/kwala/templates/mdd/5.png +0 -0
  57. data/lib/kwala/templates/outside_links_summary.html +4 -0
  58. data/lib/kwala/templates/strange_requires_detailed.html +23 -0
  59. data/lib/kwala/templates/strange_requires_summary.html +13 -0
  60. data/lib/kwala/templates/style.css +95 -0
  61. data/lib/kwala/templates/syntax_check_detailed.html +21 -0
  62. data/lib/kwala/templates/syntax_check_summary.html +7 -0
  63. data/lib/kwala/templates/unit_test_detailed.html +66 -0
  64. data/lib/kwala/templates/unit_test_summary.html +70 -0
  65. data/test/tc_build_action.rb +32 -0
  66. data/test/tc_templates.rb +24 -0
  67. metadata +118 -0
@@ -0,0 +1,793 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+ #!/usr/bin/env ruby -w
9
+
10
+ # Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
11
+ # All rights reserved.
12
+ #
13
+ # Redistribution and use in source and binary forms, with or without
14
+ # modification, are permitted provided that the following conditions are
15
+ # met:
16
+ #
17
+ #
18
+ # * Redistributions of source code must retain the above copyright
19
+ # notice, this list of conditions and the following disclaimer.
20
+ #
21
+ # * Redistributions in binary form must reproduce the above
22
+ # copyright notice, this list of conditions and the following
23
+ # disclaimer in the documentation and/or other materials provided
24
+ # with the distribution.
25
+ #
26
+ # * Neither the name of Ubit nor the names of its
27
+ # contributors may be used to endorse or promote products derived
28
+ # from this software without specific prior written permission.
29
+ #
30
+ #
31
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42
+ #
43
+ # == Author
44
+ # Zev Blut (zb@ubit.com)
45
+
46
+ require 'kwala'
47
+
48
+ class CycleDetector
49
+
50
+ attr_writer :print_strategy
51
+
52
+ def initialize
53
+ @graph = Hash.new
54
+ @print_strategy = STDOUTPrintStrategy.new
55
+ @filter = Proc.new do |files|
56
+ files.find_all do |f|
57
+ /tests\// !~ f && /extensions\// !~ f &&
58
+ /tools\// !~ f && /\/ms\// !~ f && /\/site\// !~f
59
+ end
60
+ end
61
+ end
62
+
63
+ def make_graph(dir, convert = nil)
64
+ files = ReqWalker.find_ruby_files(dir)
65
+
66
+ if convert
67
+ files = ReqWalker.convert_files_path_to(files, convert.from, convert.to)
68
+ end
69
+
70
+ files = @filter.call(files)
71
+
72
+ build_graph_from_edges(files)
73
+ reset_state
74
+ end
75
+
76
+ def find_cycles
77
+ cycles = Array.new
78
+ @graph.each do |req, node|
79
+
80
+ cycle = find_cycle(node, [])
81
+ if cycle
82
+ cycles<< cycle
83
+ end
84
+
85
+ reset_state
86
+ end
87
+ cycles
88
+ end
89
+
90
+ def size
91
+ @graph.size
92
+ end
93
+
94
+
95
+ def print_graph
96
+ reset_state
97
+ @print_strategy.print_graph(@graph)
98
+ reset_state
99
+ end
100
+
101
+ def print_cycles(cycles)
102
+ reset_state
103
+ @print_strategy.print_cycles(cycles)
104
+ reset_state
105
+ end
106
+
107
+ def print_unique_cycles(cycles)
108
+ reset_state
109
+ @print_strategy.print_cycles( unique_cycles(cycles) )
110
+ reset_state
111
+ end
112
+
113
+ ##############################################################################
114
+ protected
115
+
116
+ def build_graph_from_edges(edges)
117
+ edges.each do |f|
118
+ cf = ReqWalker.clean_file(f)
119
+ if (!@graph.key?(cf) || @graph[cf].state == Node::UNVISITED)
120
+ build_node(f, cf)
121
+ end
122
+ end
123
+ end
124
+
125
+ def build_node(file, clean_name)
126
+
127
+ if @graph.key?(clean_name)
128
+ node = @graph[clean_name]
129
+ else
130
+ node = Node.new( clean_name )
131
+ @graph[clean_name] = node
132
+ end
133
+
134
+ node.state = Node::INPROGRESS
135
+ edges = ReqWalker.get_requires(file)
136
+ edges = @filter.call(edges)
137
+ edges = edges.find_all { |f| ReqWalker.find_file(f) }
138
+ edges.each do |edg_file|
139
+ ce = ReqWalker.clean_file(edg_file)
140
+ if !@graph.key?(ce)
141
+ edge_node = Node.new(ce)
142
+ @graph[ce] = edge_node
143
+ else
144
+ edge_node = @graph[ce]
145
+ end
146
+
147
+ node.edges << edge_node
148
+ end
149
+
150
+ build_graph_from_edges(edges)
151
+ node.state = Node::DONE
152
+ end
153
+
154
+ def find_cycle(node, path = Array.new)
155
+ begin
156
+ path << node.name
157
+ node.state = Node::INPROGRESS
158
+
159
+ all_cycles = Array.new
160
+
161
+ node.edges.each do |edge|
162
+ npath = path.dup
163
+ case edge.state
164
+ when Node::INPROGRESS
165
+ npath << edge.name
166
+ return npath
167
+ when Node::DONE
168
+ nil
169
+ else
170
+ c = find_cycle(edge, npath)
171
+ if c
172
+ return c
173
+ end
174
+ end
175
+ end
176
+
177
+ node.state = Node::DONE
178
+ rescue SystemStackError => err
179
+ puts err.class
180
+ puts "<"
181
+ puts path
182
+ puts ">"
183
+ node.state = Node::DONE
184
+ nil
185
+ end
186
+ nil
187
+ end
188
+
189
+ def reset_state
190
+ @graph.each do |k, v|
191
+ v.reset
192
+ end
193
+ end
194
+
195
+ def unique_cycles(cycles)
196
+ uniq = Hash.new
197
+
198
+ cycles.each do |cycle|
199
+ uf = Hash.new
200
+ cycle.each_with_index do |p, i|
201
+ if uf.key?(p)
202
+ # only add new cycles
203
+ if !uniq.key?(p)
204
+ cyc = cycle[uf[p] .. -1]
205
+ uniq[p] = cyc
206
+ end
207
+ else
208
+ uf[p] = i
209
+ end
210
+ end
211
+ end
212
+
213
+ uniq.values
214
+ end
215
+
216
+ class Node
217
+ DONE = "DONE"
218
+ INPROGRESS = "INPROGRESS"
219
+ UNVISITED = "UNVISITED"
220
+
221
+ attr_accessor :name, :edges, :state
222
+
223
+ def initialize( name = self.object_id )
224
+ @name = name
225
+ @edges = Array.new
226
+ @state = UNVISITED
227
+ end
228
+
229
+ def reset
230
+ @state = UNVISITED
231
+ end
232
+ end
233
+
234
+ class STDOUTPrintStrategy
235
+
236
+ def print_graph(graph)
237
+ puts "Graph is #{ graph.size}"
238
+
239
+ graph.each do |k, v|
240
+ print_node(v)
241
+ end
242
+ end
243
+
244
+ def print_cycles(cycles)
245
+ if cycles.size > 0
246
+ puts "Directory has #{cycles.size} CYCLES !"
247
+ cycles.each do |path|
248
+ print_cycle_path(path)
249
+ end
250
+ end
251
+ end
252
+
253
+
254
+ ############################################################################
255
+ protected
256
+
257
+ def print_node(node)
258
+ if node.state == CycleDetector::Node::UNVISITED
259
+ node.state = CycleDetector::Node::INPROGRESS
260
+ puts " --- #{ node.name } --- (#{node.edges.size} edges)"
261
+ node.edges.each do |eg|
262
+ puts " -> #{ eg.name }"
263
+ end
264
+ node.state = CycleDetector::Node::DONE
265
+ end
266
+ end
267
+
268
+ def print_cycle_path(path)
269
+ puts ""
270
+ puts "#{path.first} <-> #{ path.last}"
271
+ puts " " + path.join("\n ")
272
+ puts ""
273
+ end
274
+
275
+ end
276
+
277
+ class DOTPrintStrategy
278
+ def initialize
279
+ @visited = Hash.new(false)
280
+ end
281
+
282
+ def print_graph(graph)
283
+ print_dot_wrapper do
284
+ print_sub_clusers(graph.keys)
285
+
286
+ graph.sort.each do |k, v|
287
+ print_node(v)
288
+ end
289
+ end
290
+ end
291
+
292
+ def print_cycles(cycles)
293
+ print_dot_wrapper do
294
+ if cycles.size > 0
295
+
296
+ cycles.each do |path|
297
+ print_cycle_path(path)
298
+ end
299
+
300
+ end
301
+ end
302
+ end
303
+
304
+ ############################################################################
305
+ protected
306
+
307
+ def print_dot_wrapper(&block)
308
+ @visited.clear
309
+ puts "digraph G {"
310
+ puts " center=1;"
311
+ puts " rankdir=LR;"
312
+ puts " ratio=compress;"
313
+ puts " size=\"20,20\";"
314
+
315
+ yield
316
+
317
+ puts "}"
318
+ @visited.clear
319
+ end
320
+
321
+ def print_node(node)
322
+ if node.state == CycleDetector::Node::UNVISITED
323
+ node.state = CycleDetector::Node::INPROGRESS
324
+ if !@visited.key?(node.name)
325
+ puts " \"#{ node.name }\";"
326
+ @visited[node.name] = true
327
+ end
328
+ node.edges.each do |edge|
329
+ puts " \"#{ node.name}\" -> \"#{ edge.name }\";"
330
+ end
331
+ node.state = CycleDetector::Node::DONE
332
+ end
333
+ end
334
+
335
+ def print_cycle_path(path)
336
+ # Group up the adjacent items into pairs
337
+ path[0..-2].zip(path[1..-1]) do |head, tail|
338
+ vkey = "#{head},#{tail}"
339
+ # Only print the unvisited nodes
340
+ if !@visited.key?(vkey)
341
+ puts " \"#{ head }\" -> \"#{ tail }\";"
342
+ @visited[vkey] = true
343
+ end
344
+ end
345
+ end
346
+
347
+ def print_sub_clusers(keys)
348
+ clusters = make_clusters(keys)
349
+ print_sub_clusters_and_members(clusters)
350
+ end
351
+
352
+ DEFAULTCLUSTER = ""
353
+
354
+ def print_sub_clusters_and_members(clusters, idx = "0", indent = 1)
355
+ padding = " " * (indent + 1)
356
+ opadding = " " * indent
357
+
358
+ clusters.sort.each do |cluster_name, members|
359
+ next if cluster_name == DEFAULTCLUSTER
360
+ puts "", opadding + "subgraph cluster#{ idx } { "
361
+ puts padding + "label = \"#{cluster_name}\";"
362
+
363
+ subs, mems = members.partition{ |m| m.is_a?(Hash) }
364
+
365
+ print_cluster_members(mems, padding)
366
+
367
+ tidx = idx + "_a"
368
+ subs.each do |member|
369
+ puts ""
370
+ print_sub_clusters_and_members(member, tidx , indent + 1)
371
+ tidx = tidx.succ
372
+ end
373
+
374
+
375
+ puts "", opadding + "}", ""
376
+ idx = idx.succ
377
+ end
378
+
379
+ idx
380
+ end
381
+
382
+ def print_cluster_members(mems, padding)
383
+ #group into 4s
384
+ groupings = Array.new
385
+ cgroup = nil
386
+
387
+ mems.each_with_index do |m, i|
388
+ if ( i % 4 == 0)
389
+ cgroup = Array.new
390
+ groupings<< cgroup
391
+ end
392
+ cgroup << m
393
+ end
394
+
395
+ groupings.each do |vals|
396
+ puts padding + "{ rank = same; "
397
+ puts padding + " \"" + vals.join("\"; \"") + "\";"
398
+ vals.each { |v| @visited[v] = true }
399
+
400
+ puts padding + "}", ""
401
+ end
402
+
403
+ end
404
+
405
+ def make_clusters(keys)
406
+ groups = Hash.new { |h,k| h[k] = Array.new }
407
+
408
+ keys.sort.each do |k|
409
+ if k.include?("/")
410
+ spname = k.split("/")
411
+ gname = spname[0..-2].join("/")
412
+ else
413
+ gname = DEFAULTCLUSTER
414
+ end
415
+ groups[gname] << k
416
+ end
417
+
418
+ sub_group_clusters(groups)
419
+ end
420
+
421
+ def sub_group_clusters(groups)
422
+
423
+
424
+ groups.keys.sort.each do |key|
425
+ if key.include?("/")
426
+ spname = key.split("/")
427
+ gname = spname[0..-2].join("/")
428
+
429
+ if groups.key?(gname)
430
+ groups[gname] << { key => groups[key] }
431
+ end
432
+ end
433
+ end
434
+
435
+ # now condense
436
+ groups.keys.sort.reverse.each do |key|
437
+
438
+ (groups.keys - [key]).reverse.sort.each do |gkey|
439
+ subs = groups[gkey].find_all { |v| v.kind_of?(Hash) }
440
+ if subs
441
+ sk = (subs.map { |k| k.keys }).flatten
442
+ if sk.include?(key)
443
+ groups.delete(key)
444
+ break
445
+ end
446
+ end
447
+ end
448
+
449
+ end
450
+
451
+
452
+ groups
453
+ end
454
+
455
+ end
456
+
457
+
458
+ class GraphMLPrintStrategy
459
+
460
+ def initialize
461
+ @visited = Hash.new(false)
462
+ @edges = []
463
+ @nodeid = Hash.new
464
+ @cid = 0
465
+ end
466
+
467
+ def print_graph(graph)
468
+ print_ml_wrapper do
469
+ #print_sub_clusers(graph.keys)
470
+
471
+ # Reorder the nodes based upon if it has edges or not.
472
+ # Prefuse, fails if the first nodes do not have any links to
473
+ # or from it.
474
+ noedge, edge = graph.sort.partition { |k,v| v.edges.empty? }
475
+
476
+ (edge + noedge).each do |k, v|
477
+ print_node(v)
478
+ end
479
+
480
+ print_edges
481
+ end
482
+ end
483
+
484
+ def print_cycles(cycles)
485
+ print_ml_wrapper do
486
+ if cycles.size > 0
487
+
488
+ cycles.each do |path|
489
+ puts "Nothing"
490
+ end
491
+
492
+ end
493
+ end
494
+ end
495
+
496
+ ############################################################################
497
+ protected
498
+
499
+ def print_ml_wrapper(&block)
500
+ @visited.clear
501
+
502
+ puts "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
503
+ puts "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\">"
504
+ puts "<graph edgedefault=\"directed\">"
505
+ puts "<!-- data schema -->"
506
+ puts "<key id=\"name\" for=\"node\" attr.name=\"name\" attr.type=\"string\"/>"
507
+
508
+ yield
509
+
510
+ puts "</graph>"
511
+ puts "</graphml>"
512
+
513
+ @visited.clear
514
+ @edges = []
515
+ @cid = 0
516
+ @nodeid.clear
517
+ end
518
+
519
+ def print_node(node)
520
+ if node.state == CycleDetector::Node::UNVISITED
521
+ node.state = CycleDetector::Node::INPROGRESS
522
+ if !@visited.key?(node.name)
523
+ @cid += 1
524
+ @nodeid[node.name] = @cid
525
+ puts "<node id=\"#{ @cid }\">"
526
+ puts " <data key=\"name\">#{ node.name }</data>"
527
+ puts "</node>"
528
+ @visited[node.name] = true
529
+ end
530
+ node.edges.each do |edge|
531
+ @edges << [node.name, edge.name ]
532
+ end
533
+ node.state = CycleDetector::Node::DONE
534
+ end
535
+ end
536
+
537
+ def print_edges
538
+ @edges.each do |src, tgt|
539
+ sid = @nodeid[src]
540
+ tid = @nodeid[tgt]
541
+ puts "<edge source=\"#{sid}\" target=\"#{tid}\"></edge>"
542
+ end
543
+ end
544
+
545
+ end
546
+
547
+
548
+ class DKWonMLPrintStrategy
549
+
550
+ def initialize
551
+ @visited = Hash.new(false)
552
+ @edges = []
553
+ @nodeid = Hash.new
554
+ @cid = 0
555
+ end
556
+
557
+ def print_graph(graph)
558
+ print_ml_wrapper do
559
+
560
+ noedge, edge = graph.sort.partition { |k,v| v.edges.empty? }
561
+
562
+ (edge + noedge).each do |k, v|
563
+ print_node(v)
564
+ end
565
+
566
+ end
567
+ end
568
+
569
+ def print_cycles(cycles)
570
+ print_ml_wrapper do
571
+ if cycles.size > 0
572
+
573
+ cycles.each do |path|
574
+ puts "Nothing"
575
+ end
576
+
577
+ end
578
+ end
579
+ end
580
+
581
+ ############################################################################
582
+ protected
583
+
584
+ def print_ml_wrapper(&block)
585
+ @visited.clear
586
+
587
+ puts "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
588
+ puts "<graph edgedefault=\"directed\">"
589
+ puts "<!-- DK data schema -->"
590
+ puts "<key id=\"name\" for=\"node\" attr.name=\"name\" attr.type=\"string\"/>"
591
+
592
+ yield
593
+
594
+ puts "</graph>"
595
+
596
+ @visited.clear
597
+ @edges = []
598
+ @cid = 0
599
+ @nodeid.clear
600
+ end
601
+
602
+ def print_node(node, indent = 0)
603
+ if node.state == CycleDetector::Node::UNVISITED
604
+ node.state = CycleDetector::Node::INPROGRESS
605
+ if !@visited.key?(node.name)
606
+ head = (' ' * indent ) + "<node name=\"#{ node.name.strip }\""
607
+ if node.edges.empty?
608
+ puts head + " />"
609
+ else
610
+ puts head + " >"
611
+ node.edges.each do |edge|
612
+ print_node(edge, indent + 1)
613
+ end
614
+ puts (' ' * indent ) + "</node>"
615
+ end
616
+ @visited[node.name] = true
617
+ end
618
+ node.state = CycleDetector::Node::DONE
619
+ elsif node.state == CycleDetector::Node::INPROGRESS
620
+ puts (' ' * indent ) + "<node name=\"#{ node.name.strip }\" />"
621
+ end
622
+ end
623
+
624
+ end
625
+
626
+ end
627
+
628
+
629
+
630
+
631
+ class ReqWalker
632
+ URL = "/"
633
+ REQUIRE_REG = /^\s*require\s*\(?\s*[\(\'\"]([\w\-\/\.]+)[\"\'\)]/
634
+
635
+ def self.get_requires(file)
636
+ reqs = Array.new
637
+ path = find_file(file)
638
+ return reqs if path.nil?
639
+
640
+ IO.readlines(path).each_with_index do |line, idx|
641
+ # Add this break to ignore requires at the end...
642
+ break if idx > 150
643
+ if r = get_require(line)
644
+ reqs<< r
645
+ end
646
+ end
647
+
648
+ reqs
649
+ end
650
+
651
+ def self.get_require(line)
652
+ if m = REQUIRE_REG.match(line)
653
+ return m[1].split(" ").first.gsub("\"","").gsub("'","")
654
+ else
655
+ return nil
656
+ end
657
+ end
658
+
659
+ def self.find_file(file)
660
+ return nil if file =~ /\.so$/
661
+
662
+ if file !~ /\.rb$/
663
+ nfile = file + ".rb"
664
+ else
665
+ nfile = file
666
+ end
667
+
668
+ [file, nfile].uniq.each do |chk|
669
+ if File.exists?(chk) && !File.directory?(chk)
670
+ return chk
671
+ end
672
+ end
673
+
674
+ filter_path($LOAD_PATH).each do |path|
675
+ #$LOAD_PATH.each do |path|
676
+ npath = path + "/" + nfile
677
+ if File.exists?(npath) && !File.directory?(npath)
678
+ return npath
679
+ end
680
+ end
681
+ nil
682
+ end
683
+
684
+ def self.filter_path(path)
685
+ #path.find_all { |p| p !~ /\/ruby\/1\.8/ } #|| p !~ /\/ruby\/site_ruby\/1\.8/ }
686
+ #["./"]
687
+ []
688
+ path
689
+ end
690
+
691
+ def self.clean_file(file)
692
+ # Get rid of any "./a.rb" files as they will make duplicates
693
+ if m = /^\.\/(.*)/.match(file)
694
+ file = m[1]
695
+ end
696
+
697
+ if m = /(.*)?(\.rb|\.so)$/.match(file)
698
+ m[1]
699
+ else
700
+ file
701
+ end
702
+ end
703
+
704
+ def self.clean_files(files)
705
+ nfiles = Array.new
706
+ files.each do |f|
707
+ nfiles<< clean_file(f)
708
+ end
709
+ nfiles
710
+ end
711
+
712
+ def self.find_ruby_files(dir)
713
+ ProjectBuilderUtils.find_ruby_files(dir)
714
+ end
715
+
716
+ def self.convert_files_path_to(files, from = "./", to = "")
717
+ files.map { |f| f.sub(from, to) }
718
+ end
719
+
720
+ end
721
+
722
+ class PathConvert
723
+ attr_accessor :from , :to
724
+
725
+ def initialize
726
+ @from = "./"
727
+ @to = ""
728
+ end
729
+
730
+ end
731
+
732
+ if __FILE__ == $0
733
+ require 'getoptlong'
734
+
735
+ def usage
736
+ exit 1
737
+ end
738
+
739
+ cd = CycleDetector.new
740
+ print_graph = false
741
+ dir = "./"
742
+ convert = nil
743
+
744
+ begin
745
+ options = GetoptLong.new(
746
+ ["-i","--dot", GetoptLong::NO_ARGUMENT],
747
+ ["-m","--graphml", GetoptLong::NO_ARGUMENT],
748
+ ["-k","--dkwonml", GetoptLong::NO_ARGUMENT],
749
+ ["-g","--graph", GetoptLong::NO_ARGUMENT],
750
+ ["-d","--dir", GetoptLong::REQUIRED_ARGUMENT],
751
+ ["-f","--from", GetoptLong::REQUIRED_ARGUMENT],
752
+ ["-t","--to", GetoptLong::REQUIRED_ARGUMENT]
753
+ )
754
+
755
+ options.each do |opt,arg|
756
+ case opt
757
+ when "-i"
758
+ cd.print_strategy = CycleDetector::DOTPrintStrategy.new
759
+ when "-m"
760
+ cd.print_strategy = CycleDetector::GraphMLPrintStrategy.new
761
+ when "-k"
762
+ cd.print_strategy = CycleDetector::DKWonMLPrintStrategy.new
763
+ when "-g"
764
+ print_graph = true
765
+ when "-d"
766
+ dir = arg
767
+ when "-f"
768
+ convert = PathConvert.new if convert.nil?
769
+ convert.from = arg
770
+ when "-t"
771
+ convert = PathConvert.new if convert.nil?
772
+ convert.to = arg
773
+ else
774
+ STDERR.puts "Unknown command #{ opt }"
775
+ usage
776
+ end
777
+ end
778
+ rescue => err
779
+ STDERR.puts "Error invalid command"
780
+ usage
781
+ end
782
+
783
+ cd.make_graph(dir, convert)
784
+
785
+ if print_graph
786
+ cd.print_graph
787
+ else
788
+ cycles = cd.find_cycles
789
+ #cd.print_cycles(cycles)
790
+ cd.print_unique_cycles(cycles)
791
+ end
792
+
793
+ end