reconn 0.1.0 → 1.0.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.
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