reconn 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 13aa9f0063ed8270baa802db3b86f308b2aa4066
4
- data.tar.gz: 1b7df1c4764ce1a90b64aab61036819d6e6d67c3
3
+ metadata.gz: 98a521e90dd569d2e385655e695c2b20b573c72a
4
+ data.tar.gz: 68dea3d08b90bca2e6790d675970783886bab1d3
5
5
  SHA512:
6
- metadata.gz: 97c1778b30eed241da1babbd81be892ffc32dadc4dfc451864bacc26ecc9dc600c841e8c7752e6b1ba229bd707c3cb58a52ebbf3aeb92c29e419f8ef6d8d3943
7
- data.tar.gz: 78d10bae27845cd07162bb3470753c575811cba9bbb3a51b559bac7a9f8911de147d17c376538118fd075ceb5c8531fdaa6292a70b6738658665629c6f1c782b
6
+ metadata.gz: 50359a392132d7e1bfff84fef8afc3fa4bd0dd8c8504a54dd4d7263630c6e38c0b90f0560142325417e18b4eb43511b2e300a4a6bddef5fc3788e085d5c20bc1
7
+ data.tar.gz: b2de99d4e108c48ecf5f0acc30f1080fd1639dbcf5cd74eb2a49f36ab70346436688c7d57a4c0d06d335bbed63b467b341ad82d86ae4a6cba3c38c3591690b99
data/bin/reconn CHANGED
@@ -10,4 +10,4 @@ STDERR.sync = true
10
10
  my_path = File.expand_path(File.dirname(__FILE__))
11
11
  require_all Dir.glob(my_path + "/../lib/reconn/**/*.rb")
12
12
 
13
- View.new.show
13
+ Reconn::View.new.show
@@ -2,10 +2,8 @@
2
2
 
3
3
  require 'ruby_parser'
4
4
  require 'sexp_processor'
5
- require_relative 'util/project_scanner'
6
- require_relative 'analyzer/project_elements/class.rb'
7
- require_relative 'analyzer/project_elements/method.rb'
8
5
 
6
+ module Reconn
9
7
  module Analyzer
10
8
  class Analyzer < MethodBasedSexpProcessor
11
9
 
@@ -43,9 +41,10 @@ module Analyzer
43
41
  klass.complexity = count_complexity_in_class(klass)
44
42
  end
45
43
  prune_dependencies
44
+ find_external_dependencies(paths)
46
45
 
47
46
  # Deletes empty classes
48
- @classes.delete(Class.new(:none))
47
+ #@classes.delete(Class.new(:none))
49
48
 
50
49
  @smells = find_code_smells
51
50
 
@@ -58,7 +57,7 @@ module Analyzer
58
57
  def process_class(exp)
59
58
  exp.shift
60
59
  in_klass(exp.shift) do
61
- @current_class = Class.new(klass_name)
60
+ @current_class = Class.new(klass_name, [@current_path.to_s])
62
61
  @classes << @current_class
63
62
  process_until_empty exp
64
63
  end
@@ -68,10 +67,10 @@ module Analyzer
68
67
  end
69
68
 
70
69
  def process_defn(exp)
71
- exp.shift
70
+ is_singleton = exp.shift.to_s == "defn" ? false : true
72
71
  method_name = exp.shift.to_s
73
72
  lines = count_lines_in_method(method_name)
74
- @current_method = Method.new(method_name, @current_class.name, lines)
73
+ @current_method = Method.new(method_name, @current_path.to_s, @current_class.name, lines, is_singleton)
75
74
  exp.shift
76
75
  process_until_empty exp
77
76
 
@@ -163,8 +162,15 @@ module Analyzer
163
162
  @classes.each do |klass|
164
163
  klass.dependencies = klass.dependencies.map do |dep|
165
164
  dep_split = dep.split('::')
165
+ if class_names.include?(dep)
166
+ next dep
167
+ end
166
168
  klass_split = klass.name.split('::')
167
- klass_split.pop(dep_split.size)
169
+ if klass_split.size != dep_split.size
170
+ klass_split.pop(dep_split.size)
171
+ else
172
+ klass_split.pop(dep_split.size - 1)
173
+ end
168
174
  (klass_split + dep_split).join('::')
169
175
  end
170
176
  klass.dependencies = klass.dependencies.uniq.keep_if {|dep| dep != klass.name && class_names.include?(dep)}
@@ -184,14 +190,32 @@ module Analyzer
184
190
  end
185
191
  end
186
192
 
193
+ def find_external_dependencies(paths)
194
+ @classes.each do |klass|
195
+ external_deps = []
196
+ klass.filepaths.each do |path|
197
+ File.foreach(path) do |line|
198
+ line.strip!
199
+ if line =~ /^require .*$/
200
+ dep = line.split(" ")[1].gsub(/([\"\'])/, "")
201
+ external_deps << dep if !paths.find {|p| p.to_s =~ /.*#{dep}.*/}
202
+ else
203
+ next
204
+ end
205
+ end
206
+ end
207
+ klass.external_deps = external_deps
208
+ end
209
+ end
210
+
187
211
  def find_code_smells
188
212
  code_smells = []
189
213
  @methods.each do |method|
190
214
  if method.lines > MAX_METHOD_LENGTH
191
- code_smells << CodeSmell.new(:too_big_method, method.class_name, method.name)
215
+ code_smells << CodeSmell.new(:too_big_method, method)
192
216
  end
193
217
  if method.complexity > MAX_COMPLEXITY
194
- code_smells << CodeSmell.new(:too_complex_method, method.class_name, method.name)
218
+ code_smells << CodeSmell.new(:too_complex_method, method)
195
219
  end
196
220
  end
197
221
  code_smells
@@ -199,3 +223,4 @@ module Analyzer
199
223
 
200
224
  end
201
225
  end
226
+ end
@@ -1,16 +1,16 @@
1
- module Analyzer
2
- class CodeSmell
3
- attr_reader :type, :class_name, :method_name
1
+ module Reconn
2
+ module Analyzer
3
+ class CodeSmell
4
+ attr_reader :type, :method
4
5
 
5
- def initialize(type, class_name = :none, method_name)
6
- @type = type
7
- @class_name = class_name
8
- @method_name = method_name
9
- end
6
+ def initialize(type, method)
7
+ @type = type
8
+ @method = method
9
+ end
10
10
 
11
- def to_s
12
- "Smell: #{@type.to_s} in Class: #{@class_name.to_s} "\
13
- "Method: #{@method_name.to_s}"
11
+ def to_s
12
+ "Smell: #{@type.to_s} in #{@method.to_s} in file: #{method.filepath}"
13
+ end
14
14
  end
15
15
  end
16
16
  end
@@ -1,8 +1,9 @@
1
+ module Reconn
1
2
  module Analyzer
2
3
  # Represents a class in the project
3
4
  class Class
4
- attr_reader :name, :methods
5
- attr_accessor :lines, :complexity, :dependencies
5
+ attr_reader :name, :methods, :filepaths
6
+ attr_accessor :lines, :complexity, :dependencies, :external_deps
6
7
 
7
8
  include Comparable
8
9
 
@@ -10,8 +11,9 @@ module Analyzer
10
11
  name == other.name
11
12
  end
12
13
 
13
- def initialize(name)
14
+ def initialize(name, filepaths = [])
14
15
  @name = name
16
+ @filepaths = filepaths
15
17
  @dependencies = []
16
18
  @methods = []
17
19
  @lines = 0
@@ -37,6 +39,7 @@ module Analyzer
37
39
  end
38
40
  end
39
41
  @dependencies += other.dependencies
42
+ @filepaths += other.filepaths
40
43
  self
41
44
  end
42
45
 
@@ -46,3 +49,4 @@ module Analyzer
46
49
 
47
50
  end
48
51
  end
52
+ end
@@ -1,7 +1,8 @@
1
+ module Reconn
1
2
  module Analyzer
2
3
  # Represents a method in the project
3
4
  class Method
4
- attr_reader :name, :class_name, :lines, :complexity
5
+ attr_reader :name, :class_name, :lines, :complexity, :filepath
5
6
 
6
7
  include Comparable
7
8
 
@@ -9,20 +10,27 @@ module Analyzer
9
10
  name == other.name && class_name == other.class_name
10
11
  end
11
12
 
12
- def initialize(name, class_name = :none, lines = 0)
13
+ def initialize(name, filepath = "", class_name = :none, lines = 0, is_singleton = false)
13
14
  @name = name
15
+ @filepath = filepath
14
16
  @class_name = class_name
15
17
  @lines = lines
16
18
  @complexity = 1
19
+ @is_singleton = is_singleton
17
20
  end
18
21
 
19
22
  def to_s
20
- name.to_s
23
+ class_name.to_s + (is_singleton? ? "::" : "#") + name.to_s
21
24
  end
22
25
 
23
26
  def incr_complexity
24
27
  @complexity += 1
25
28
  end
26
29
 
30
+ def is_singleton?
31
+ @is_singleton
32
+ end
33
+
27
34
  end
28
35
  end
36
+ end
@@ -1,28 +1,30 @@
1
1
  require 'find'
2
2
 
3
- class ProjectScanner
4
- # Scans the given directory and all its subdirectories for ruby files
5
- #
6
- # @param proj_path [String] path to the project directory
7
- # @return [Array<String>] paths to the ruby files
8
- # @raise [InvalidPathException] if it can't open the directory
9
- def self.scan(proj_path)
10
- paths = []
11
- begin
12
- Find.find(proj_path) do |path|
13
- if FileTest.directory?(path)
14
- if File.basename(path)[0] == '.'
15
- Find.prune
3
+ module Reconn
4
+ class ProjectScanner
5
+ # Scans the given directory and all its subdirectories for ruby files
6
+ #
7
+ # @param proj_path [String] path to the project directory
8
+ # @return [Array<String>] paths to the ruby files
9
+ # @raise [InvalidPathException] if it can't open the directory
10
+ def self.scan(proj_path)
11
+ paths = []
12
+ begin
13
+ Find.find(proj_path) do |path|
14
+ if FileTest.directory?(path)
15
+ if File.basename(path)[0] == '.'
16
+ Find.prune
17
+ end
18
+ end
19
+ if File.extname(path) == '.rb'
20
+ paths << path
16
21
  end
17
22
  end
18
- if File.extname(path) == '.rb'
19
- paths << path
20
- end
23
+ rescue
24
+ raise InvalidPathException, "Can't open the directory"
21
25
  end
22
- rescue
23
- raise InvalidPathException, "Can't open the directory"
24
- end
25
26
 
26
- paths
27
+ paths
28
+ end
27
29
  end
28
30
  end
@@ -1,3 +1,3 @@
1
1
  module Reconn
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,6 +1,7 @@
1
1
  require_relative '../analyzer.rb'
2
2
  require_relative '../visualizer.rb'
3
3
 
4
+ module Reconn
4
5
  class View
5
6
 
6
7
  include GladeGUI
@@ -195,8 +196,8 @@ class View
195
196
  end
196
197
 
197
198
  def build_method_diag_view
198
- diagrams = [ {title: "Lines of code", parameter: :lines} ]
199
- diagrams << {title: "Cyclomatic complexity", parameter: :complexity}
199
+ diagrams = [ {title: "Lines of code", parameter: :lines, treshold: Analyzer::Analyzer::MAX_METHOD_LENGTH} ]
200
+ diagrams << {title: "Cyclomatic complexity", parameter: :complexity, treshold: Analyzer::Analyzer::MAX_COMPLEXITY}
200
201
  build_diag_view(@methods, diagrams)
201
202
  end
202
203
 
@@ -209,7 +210,7 @@ class View
209
210
  title = diag[:title]
210
211
 
211
212
  pie_chart = build_pie_chart(title, data)
212
- bar_chart = build_bar_chart(title, data.first(10))
213
+ bar_chart = build_bar_chart(title, data.first(10), diag[:treshold])
213
214
 
214
215
  container = Gtk::VBox.new(false, 4)
215
216
  container = container.pack_end(pie_chart)
@@ -227,8 +228,8 @@ class View
227
228
  chart_to_image(binary_chart)
228
229
  end
229
230
 
230
- def build_bar_chart(title, data)
231
- binary_chart = Visualizer.make_bar_chart(title, data)
231
+ def build_bar_chart(title, data, treshold)
232
+ binary_chart = Visualizer.make_bar_chart(title, data, treshold)
232
233
  chart_to_image(binary_chart)
233
234
  end
234
235
 
@@ -275,3 +276,4 @@ class View
275
276
  :build_method_stats_view, :build_if_smell_view,
276
277
  :build_method_smell_view, :build_text_view
277
278
  end
279
+ end
@@ -1,44 +1,165 @@
1
1
  require 'gruff'
2
2
  require 'ruby-graphviz'
3
3
 
4
- class Visualizer
5
- def self.make_pie_chart(title, data, items_number)
6
- chart = Gruff::Pie.new
7
- chart.title = title.to_s
8
- data = Array.new(data)
9
- data.first(items_number).each do |item|
10
- chart.data(item[:label], item[:value])
11
- data.delete(item)
4
+ module Reconn
5
+ class Visualizer
6
+ def self.make_pie_chart(title, data, items_number)
7
+ chart = Gruff::Pie.new
8
+ chart.title = title.to_s
9
+ data = Array.new(data)
10
+ data.first(items_number).each do |item|
11
+ chart.data(item[:label], item[:value])
12
+ data.delete(item)
13
+ end
14
+
15
+ chart.data("other", data.map {|itm| itm[:value]}.inject(:+))
16
+
17
+ chart.to_blob
12
18
  end
13
19
 
14
- chart.data("other", data.map {|itm| itm[:value]}.inject(:+))
20
+ def self.make_bar_chart(title, data, additional_line_value = nil)
21
+ chart = Gruff::Bar.new
22
+ chart.minimum_value = 0
23
+ chart.maximum_value = data.first[:value]
24
+ chart.additional_line_values = [additional_line_value] unless additional_line_value.nil?
25
+ chart.title = title.to_s
26
+ data.each do |item|
27
+ chart.data(item[:label], item[:value])
28
+ end
29
+
30
+ chart.to_blob
31
+ end
15
32
 
16
- chart.to_blob
33
+ def self.make_dependency_diagram(classes)
34
+ diagram = GraphViz.new(:G, :type => :digraph)
35
+ nodes = classes.map { |c| diagram.add_nodes(c.name) }
36
+ external_nodes = classes.map {|c| c.external_deps}.inject(:+).uniq.map {|n| diagram.add_nodes(n)}
37
+ classes.each do |klass|
38
+ classes.each do |other_klass|
39
+ if !klass.dependencies.index {|d| d == other_klass.name }.nil? || klass.dependencies.find_all {|d| other_klass.name =~ /^.*{0,}::#{d}$/}.size == 1
40
+ node, other_node = [klass, other_klass].map {|k| nodes.find {|n| n[:label].to_s.gsub('"', '') == k.name}}
41
+ diagram.add_edges(node, other_node)
42
+ end
43
+ end
44
+ external_nodes.each {|n| n.set {|_n| _n.color = "blue"}}
45
+ external_nodes.each do |ext_node|
46
+ if klass.external_deps.include?(ext_node[:label].to_s.gsub('"', ''))
47
+ node = nodes.find {|n| n[:label].to_s.gsub('"', '') == klass.name}
48
+ diagram.add_edges(node, ext_node)
49
+ end
50
+ end
51
+ end
52
+ diagram.output(:png => String)
53
+ end
17
54
  end
55
+ end
56
+
57
+ #Monkeypatching additional line drawing
58
+ module Gruff
59
+ class Bar
60
+ def draw
61
+ @center_labels_over_point = (@labels.keys.length > @column_count ? true : false)
62
+ super
63
+ return unless @has_data
18
64
 
19
- def self.make_bar_chart(title, data)
20
- chart = Gruff::Bar.new
21
- chart.minimum_value = 0
22
- chart.maximum_value = data.first[:value]
23
- chart.title = title.to_s
24
- data.each do |item|
25
- chart.data(item[:label], item[:value])
65
+ draw_bars
66
+ draw_additional_line
26
67
  end
68
+ def draw_bars
69
+ # Setup spacing.
70
+ #
71
+ # Columns sit side-by-side.
72
+ @bar_spacing ||= @spacing_factor # space between the bars
73
+ @bar_width = @graph_width / (@column_count * @data.length).to_f
74
+ padding = (@bar_width * (1 - @bar_spacing)) / 2
27
75
 
28
- chart.to_blob
29
- end
76
+ @d = @d.stroke_opacity 0.0
77
+
78
+ # Setup the BarConversion Object
79
+ conversion = Gruff::BarConversion.new()
80
+ conversion.graph_height = @graph_height
81
+ conversion.graph_top = @graph_top
30
82
 
31
- def self.make_dependency_diagram(classes)
32
- diagram = GraphViz.new(:G, :type => :digraph)
33
- nodes = classes.map { |c| diagram.add_nodes(c.name) }
34
- classes.each do |klass|
35
- classes.each do |other_klass|
36
- if !klass.dependencies.index {|d| d == other_klass.name }.nil?
37
- node, other_node = [klass, other_klass].map {|k| nodes.find {|n| n[:label].to_s.gsub('"', '') == k.name}}
38
- diagram.add_edges(node, other_node)
83
+ # Set up the right mode [1,2,3] see BarConversion for further explanation
84
+ if @minimum_value >= 0 then
85
+ # all bars go from zero to positiv
86
+ conversion.mode = 1
87
+ else
88
+ # all bars go from 0 to negativ
89
+ if @maximum_value <= 0 then
90
+ conversion.mode = 2
91
+ else
92
+ # bars either go from zero to negativ or to positiv
93
+ conversion.mode = 3
94
+ conversion.spread = @spread
95
+ conversion.minimum_value = @minimum_value
96
+ conversion.zero = -@minimum_value/@spread
39
97
  end
40
98
  end
99
+
100
+ # iterate over all normalised data
101
+ @norm_data.each_with_index do |data_row, row_index|
102
+
103
+ data_row[DATA_VALUES_INDEX].each_with_index do |data_point, point_index|
104
+ # Use incremented x and scaled y
105
+ # x
106
+ left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index))) + padding
107
+ right_x = left_x + @bar_width * @bar_spacing
108
+ # y
109
+ conv = []
110
+ conversion.get_left_y_right_y_scaled( data_point, conv )
111
+
112
+ # create new bar
113
+ @d = @d.fill data_row[DATA_COLOR_INDEX]
114
+ @d = @d.rectangle(left_x, conv[0], right_x, conv[1])
115
+
116
+ # Calculate center based on bar_width and current row
117
+ label_center = @graph_left +
118
+ (@data.length * @bar_width * point_index) +
119
+ (@data.length * @bar_width / 2.0)
120
+
121
+ # Subtract half a bar width to center left if requested
122
+ draw_label(label_center - (@center_labels_over_point ? @bar_width / 2.0 : 0.0), point_index)
123
+ if @show_labels_for_bar_values
124
+ val = (@label_formatting || '%.2f') % @norm_data[row_index][3][point_index]
125
+ draw_value_label(left_x + (right_x - left_x)/2, conv[0]-30, val.commify, true)
126
+ end
127
+ end
128
+ end
129
+
130
+ # Draw the last label if requested
131
+ draw_label(@graph_right, @column_count) if @center_labels_over_point
132
+
133
+ draw_additional_line
134
+ @d.draw(@base_image)
135
+ end
136
+ # Draws additional horizontal line
137
+ def draw_additional_line
138
+ @additional_line_colors << '#f61100'
139
+ @d = @d.stroke_opacity 100.0
140
+ i = 0
141
+ @additional_line_values.each do |value|
142
+ @increment_scaled = @graph_height.to_f / (@maximum_value.to_f / value)
143
+
144
+ y = @graph_top + @graph_height - @increment_scaled
145
+
146
+ @d = @d.stroke(@additional_line_colors[i])
147
+ @d = @d.line(@graph_left, y, @graph_right, y)
148
+
149
+
150
+ @d.fill = @additional_line_colors[i]
151
+ @d.font = @font if @font
152
+ @d.stroke('transparent')
153
+ @d.pointsize = scale_fontsize(@marker_font_size)
154
+ @d.gravity = EastGravity
155
+ @d = @d.annotate_scaled( @base_image,
156
+ @graph_right - LABEL_MARGIN, 1.0,
157
+ 0.0, y - (@marker_font_size/2.0),
158
+ label(value, value), @scale)
159
+ i += 1
160
+ end
161
+
162
+ @d = @d.stroke_antialias true
41
163
  end
42
- diagram.output(:png => String)
43
164
  end
44
165
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reconn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Czarnecki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-28 00:00:00.000000000 Z
11
+ date: 2015-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: vrlib