kwala 0.9.0

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.
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