reconn 0.1.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.
@@ -0,0 +1,111 @@
1
+ # @author Mateusz Czarnecki <mateusz.czarnecki92@gmail.com>
2
+
3
+ require 'ruby_parser'
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
+
9
+ module Analyzer
10
+ class Analyzer < MethodBasedSexpProcessor
11
+ def initialize
12
+ super()
13
+ self.auto_shift_type = false
14
+ @classes = []
15
+ @methods = []
16
+ @smells = []
17
+ @current_class = Class.new(:none)
18
+ end
19
+
20
+ # Analyzes all the ruby files in the given directory and its subdirectories
21
+ # @param dir [String] path to the directory
22
+ # @return [Array(Array<Class>, Array<Method>, Array<CodeSmell>)] found classes,
23
+ # methods and code smells
24
+ def analyze(dir)
25
+ parser = RubyParser.new
26
+ paths = ProjectScanner.scan(dir)
27
+ paths.each do |path|
28
+ @current_path = path
29
+ ast = parser.process(File.binread(path), path)
30
+ process ast
31
+ @current_class = Class.new(:none)
32
+ end
33
+
34
+ @classes.each {|klass| klass.lines = count_lines_in_class(klass)}
35
+ prune_dependencies
36
+
37
+ return @classes, @methods, @smells
38
+ end
39
+
40
+ #########################################
41
+ # Process methods:
42
+
43
+ def process_class(exp)
44
+ exp.shift
45
+ class_name = exp.shift.to_s
46
+ @current_class = Class.new(class_name)
47
+ @classes << @current_class
48
+ process_until_empty exp
49
+ s()
50
+ end
51
+
52
+ def process_defn(exp)
53
+ exp.shift
54
+ method_name = exp.shift.to_s
55
+ lines = count_lines_in_method(method_name)
56
+ method = Method.new(method_name, @current_class.name, lines)
57
+ @methods << method
58
+ @current_class.add_method(method)
59
+ exp.shift
60
+ process_until_empty exp
61
+ s()
62
+ end
63
+
64
+ def process_const(exp)
65
+ exp.shift
66
+ name = exp.shift.to_s
67
+ is_class = !(Object.const_get(name) rescue nil).nil?
68
+ @current_class.add_dependency(name) if is_class
69
+
70
+ exp.shift
71
+ process_until_empty exp
72
+ s()
73
+ end
74
+
75
+ ########################################
76
+
77
+ #Counts lines of code in a method
78
+ #
79
+ #@param method_name [String] the name of the method
80
+ #@return [Integer] lines of code count
81
+ def count_lines_in_method(method_name)
82
+ flag = false
83
+ lines = []
84
+ File.foreach(@current_path) do |line|
85
+ break if line =~ /def/ && flag
86
+ lines << line if flag && line.strip != '' && line.strip[0] != '#'
87
+ flag = true if line =~ /def #{method_name}/
88
+ end
89
+
90
+ lines.size
91
+ end
92
+
93
+ #Counts lines of code in a class (sums LOC of methods)
94
+ #
95
+ #@param klass [Class] the class
96
+ #@return [Integer] lines of code count
97
+ def count_lines_in_class(klass)
98
+ lines = klass.methods.map {|method| method.lines}.inject(:+)
99
+ lines.nil? ? 0 : lines
100
+ end
101
+
102
+ #Deletes dependencies which are not classes within analyzed project
103
+ def prune_dependencies
104
+ class_names = @classes.map {|klass| klass.name}
105
+ @classes.each do |klass|
106
+ klass.dependencies = klass.dependencies.uniq.keep_if {|dep| class_names.include?(dep)}
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,189 @@
1
+ require_relative '../analyzer.rb'
2
+ require_relative '../visualizer.rb'
3
+
4
+ class View
5
+
6
+ include GladeGUI
7
+
8
+ def before_show()
9
+ dialog_response, dialog = show_file_chooser_dialog
10
+ if dialog_response == Gtk::Dialog::RESPONSE_ACCEPT
11
+ analyzer = Analyzer::Analyzer.new
12
+ @classes, @methods, @smells = analyzer.analyze(dialog.filename)
13
+
14
+ @general_stats_view = build_general_stats_view
15
+ @builder['dataviewport'].add(@general_stats_view)
16
+ end
17
+ dialog.destroy
18
+ end
19
+
20
+ #########################################
21
+ # On button clicked methods
22
+
23
+ def on_general_stats_button_clicked
24
+ clean_data_view
25
+
26
+ unless @general_stats_view
27
+ @general_stats_view = build_general_stats_view
28
+ end
29
+
30
+ @builder['dataviewport'].add(@general_stats_view)
31
+ end
32
+
33
+ def on_class_stats_button_clicked
34
+ clean_data_view
35
+
36
+ unless @class_stats_view
37
+ @class_stats_view = build_class_stats_view
38
+ end
39
+
40
+ @builder['dataviewport'].add(@class_stats_view)
41
+ end
42
+
43
+ def on_class_diag_button_clicked
44
+ clean_data_view
45
+
46
+ unless @class_diag_view
47
+ @class_diag_view = build_class_diag_view
48
+ end
49
+
50
+ @builder['dataviewport'].add(@class_diag_view)
51
+ end
52
+
53
+ def on_class_dep_button_clicked
54
+ clean_data_view
55
+
56
+ unless @class_dep_view
57
+ end
58
+
59
+ @builder['dataviewport'].add(Gtk::TextView.new)
60
+ end
61
+
62
+ def on_method_stats_button_clicked
63
+ clean_data_view
64
+
65
+ unless @method_stats_view
66
+ @method_stats_view = build_method_stats_view
67
+ end
68
+
69
+ @builder['dataviewport'].add(@method_stats_view)
70
+ end
71
+
72
+ def on_method_diag_button_clicked
73
+ clean_data_view
74
+
75
+ unless @method_diag_view
76
+ end
77
+
78
+ @builder['dataviewport'].add(Gtk::TextView.new)
79
+ end
80
+
81
+ def on_if_smell_button_clicked
82
+ clean_data_view
83
+
84
+ unless @if_smell_view
85
+ @if_smell_view = build_if_smell_view
86
+ end
87
+
88
+ @builder['dataviewport'].add(@if_smell_view)
89
+ end
90
+
91
+ def on_method_smell_button_clicked
92
+ clean_data_view
93
+
94
+ unless @method_smell_view
95
+ @method_smell_view = build_method_smell_view
96
+ end
97
+
98
+ @builder['dataviewport'].add(@method_smell_view)
99
+ end
100
+
101
+ ###########################################
102
+ # View builders
103
+
104
+ def build_general_stats_view
105
+ lines = 0
106
+ @methods.each do |method|
107
+ lines += method.lines
108
+ end
109
+
110
+ text_arr = ["Total number of classes: #{@classes.length} \n"]
111
+ text_arr << "Total number of methods: #{@methods.length} \n"
112
+ text_arr << "Total number of lines of code: #{lines}"
113
+ build_text_view(text_arr.join)
114
+ end
115
+
116
+ def build_class_stats_view
117
+ largest_class = @classes.sort.pop
118
+
119
+ class_methodnum = Hash.new(0)
120
+ @classes.each do |klass|
121
+ class_methodnum.store(klass.name, klass.methods.size)
122
+ end
123
+ class_methodnum = class_methodnum.sort_by {|name, methods| methods}
124
+
125
+ text_arr = ["Largest class: #{largest_class} \n"]
126
+ text_arr << "Number of lines: #{largest_class.lines} \n"
127
+ text_arr << "\n"
128
+ text_arr << "Class with largest number of methods: #{class_methodnum.last[0]} \n"
129
+ text_arr << "Number of methods: #{class_methodnum.last[1]} \n"
130
+ build_text_view(text_arr.join)
131
+ end
132
+
133
+ def build_method_stats_view
134
+ largest_method = @methods.sort.pop
135
+ text_arr = ["Largest method: #{largest_method} \n"]
136
+ text_arr << "Number of lines: #{largest_method.lines}"
137
+ build_text_view(text_arr.join)
138
+ end
139
+
140
+ def build_if_smell_view
141
+ text_arr = []
142
+ build_text_view(text_arr.join)
143
+ end
144
+
145
+ def build_method_smell_view
146
+ text_arr = []
147
+ build_text_view(text_arr.join)
148
+ end
149
+
150
+ def build_text_view(text)
151
+ stats_view = Gtk::TextView.new
152
+ stats_view.editable = false
153
+ stats_view.cursor_visible = false
154
+ stats_view.buffer.text = text
155
+ stats_view.show
156
+ stats_view
157
+ end
158
+
159
+ def build_class_diag_view
160
+ binary_chart = Visualizer.make_class_pie_chart(@classes)
161
+ loader = Gdk::PixbufLoader.new("png")
162
+ loader.last_write(binary_chart)
163
+ chart = loader.pixbuf
164
+ Gtk::Image.new(chart).show
165
+ end
166
+
167
+ ##########################################
168
+
169
+ def show_file_chooser_dialog
170
+ dialog = Gtk::FileChooserDialog.new("Open File",
171
+ nil,
172
+ Gtk::FileChooser::ACTION_SELECT_FOLDER,
173
+ nil,
174
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
175
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
176
+ dialog_response = dialog.run
177
+ return dialog_response, dialog
178
+ end
179
+
180
+ def clean_data_view
181
+ @builder['dataviewport'].each do |child|
182
+ @builder['dataviewport'].remove(child)
183
+ end
184
+ end
185
+
186
+ private :build_general_stats_view, :build_class_stats_view,
187
+ :build_method_stats_view, :build_if_smell_view,
188
+ :build_method_smell_view, :build_text_view
189
+ end
@@ -0,0 +1,111 @@
1
+ # @author Mateusz Czarnecki <mateusz.czarnecki92@gmail.com>
2
+
3
+ require 'ruby_parser'
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
+
9
+ module Analyzer
10
+ class Analyzer < MethodBasedSexpProcessor
11
+ def initialize
12
+ super()
13
+ self.auto_shift_type = false
14
+ @classes = []
15
+ @methods = []
16
+ @smells = []
17
+ @current_class = Class.new(:none)
18
+ end
19
+
20
+ # Analyzes all the ruby files in the given directory and its subdirectories
21
+ # @param dir [String] path to the directory
22
+ # @return [Array(Array<Class>, Array<Method>, Array<CodeSmell>)] found classes,
23
+ # methods and code smells
24
+ def analyze(dir)
25
+ parser = RubyParser.new
26
+ paths = ProjectScanner.scan(dir)
27
+ paths.each do |path|
28
+ @current_path = path
29
+ ast = parser.process(File.binread(path), path)
30
+ process ast
31
+ @current_class = Class.new(:none)
32
+ end
33
+
34
+ @classes.each {|klass| klass.lines = count_lines_in_class(klass)}
35
+ prune_dependencies
36
+
37
+ return @classes, @methods, @smells
38
+ end
39
+
40
+ #########################################
41
+ # Process methods:
42
+
43
+ def process_class(exp)
44
+ exp.shift
45
+ class_name = exp.shift.to_s
46
+ @current_class = Class.new(class_name)
47
+ @classes << @current_class
48
+ process_until_empty exp
49
+ s()
50
+ end
51
+
52
+ def process_defn(exp)
53
+ exp.shift
54
+ method_name = exp.shift.to_s
55
+ lines = count_lines_in_method(method_name)
56
+ method = Method.new(method_name, @current_class.name, lines)
57
+ @methods << method
58
+ @current_class.add_method(method)
59
+ exp.shift
60
+ process_until_empty exp
61
+ s()
62
+ end
63
+
64
+ def process_const(exp)
65
+ exp.shift
66
+ name = exp.shift.to_s
67
+ is_class = !(Object.const_get(name) rescue nil).nil?
68
+ @current_class.add_dependency(name) if is_class
69
+
70
+ exp.shift
71
+ process_until_empty exp
72
+ s()
73
+ end
74
+
75
+ ########################################
76
+
77
+ #Counts lines of code in a method
78
+ #
79
+ #@param method_name [String] the name of the method
80
+ #@return [Integer] lines of code count
81
+ def count_lines_in_method(method_name)
82
+ flag = false
83
+ lines = []
84
+ File.foreach(@current_path) do |line|
85
+ break if line =~ /def/ && flag
86
+ lines << line if flag && line.strip != '' && line.strip[0] != '#'
87
+ flag = true if line =~ /def #{method_name}/
88
+ end
89
+
90
+ lines.size
91
+ end
92
+
93
+ #Counts lines of code in a class (sums LOC of methods)
94
+ #
95
+ #@param klass [Class] the class
96
+ #@return [Integer] lines of code count
97
+ def count_lines_in_class(klass)
98
+ lines = klass.methods.map {|method| method.lines}.inject(:+)
99
+ lines.nil? ? 0 : lines
100
+ end
101
+
102
+ #Deletes dependencies which are not classes within analyzed project
103
+ def prune_dependencies
104
+ class_names = @classes.map {|klass| klass.name}
105
+ @classes.each do |klass|
106
+ klass.dependencies = klass.dependencies.uniq.keep_if {|dep| class_names.include?(dep)}
107
+ end
108
+ end
109
+
110
+ end
111
+ end
metadata ADDED
@@ -0,0 +1,226 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reconn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mateusz Czarnecki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: vrlib
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.16
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.16
27
+ - !ruby/object:Gem::Dependency
28
+ name: gtk2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: require_all
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.3.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby_parser
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.6.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 3.6.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: sexp_processor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 4.4.4
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: 4.4.4
83
+ - !ruby/object:Gem::Dependency
84
+ name: gruff
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.5.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.5.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: ruby-graphviz
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.2.1
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.2.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '1.5'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: '1.5'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description:
154
+ email:
155
+ - mateusz.czarnecki92@gmail.com
156
+ executables:
157
+ - reconn
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - .gitignore
162
+ - Gemfile
163
+ - LICENSE.txt
164
+ - README.md
165
+ - Rakefile
166
+ - bin/reconn
167
+ - lib/reconn.rb
168
+ - lib/reconn/analyzer.rb
169
+ - lib/reconn/analyzer/code_smell.rb
170
+ - lib/reconn/analyzer/project_elements/class.rb
171
+ - lib/reconn/analyzer/project_elements/method.rb
172
+ - lib/reconn/util/project_scanner.rb
173
+ - lib/reconn/version.rb
174
+ - lib/reconn/view/glade/View.glade
175
+ - lib/reconn/view/view.rb
176
+ - lib/reconn/visualizer.rb
177
+ - reconn.gemspec
178
+ - spec/reconn/analyzer/analyzer_spec.rb
179
+ - spec/reconn/analyzer/code_smells/code_smell_spec.rb
180
+ - spec/reconn/analyzer/code_smells/if_smell_spec.rb
181
+ - spec/reconn/analyzer/code_smells/too_big_method_smell_spec.rb
182
+ - spec/reconn/analyzer/project_elements/class_spec.rb
183
+ - spec/reconn/analyzer/project_elements/method_spec.rb
184
+ - spec/reconn/util/project_scanner_spec.rb
185
+ - spec/spec_helper.rb
186
+ - spec/test_projects/empty_project/not_rb_file
187
+ - spec/test_projects/multiple_files_project/analyzer.rb
188
+ - spec/test_projects/multiple_files_project/view.rb
189
+ - spec/test_projects/one_file_project/analyzer.rb
190
+ homepage: ''
191
+ licenses:
192
+ - MIT
193
+ metadata: {}
194
+ post_install_message:
195
+ rdoc_options: []
196
+ require_paths:
197
+ - lib
198
+ required_ruby_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - '>='
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ required_rubygems_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - '>='
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ requirements: []
209
+ rubyforge_project:
210
+ rubygems_version: 2.2.2
211
+ signing_key:
212
+ specification_version: 4
213
+ summary: A tool for analysis and visualization of projects written in Ruby
214
+ test_files:
215
+ - spec/reconn/analyzer/analyzer_spec.rb
216
+ - spec/reconn/analyzer/code_smells/code_smell_spec.rb
217
+ - spec/reconn/analyzer/code_smells/if_smell_spec.rb
218
+ - spec/reconn/analyzer/code_smells/too_big_method_smell_spec.rb
219
+ - spec/reconn/analyzer/project_elements/class_spec.rb
220
+ - spec/reconn/analyzer/project_elements/method_spec.rb
221
+ - spec/reconn/util/project_scanner_spec.rb
222
+ - spec/spec_helper.rb
223
+ - spec/test_projects/empty_project/not_rb_file
224
+ - spec/test_projects/multiple_files_project/analyzer.rb
225
+ - spec/test_projects/multiple_files_project/view.rb
226
+ - spec/test_projects/one_file_project/analyzer.rb