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.
- data/README +62 -0
- data/bin/kwala +39 -0
- data/lib/kwala.rb +59 -0
- data/lib/kwala/actions/code_change.rb +128 -0
- data/lib/kwala/actions/code_coverage.rb +303 -0
- data/lib/kwala/actions/code_duplication.rb +222 -0
- data/lib/kwala/actions/code_formatting.rb +358 -0
- data/lib/kwala/actions/cycle_detection.rb +118 -0
- data/lib/kwala/actions/cyclomatic_complexity.rb +159 -0
- data/lib/kwala/actions/dead_code.rb +68 -0
- data/lib/kwala/actions/executable_files_check.rb +111 -0
- data/lib/kwala/actions/flagged_comments.rb +128 -0
- data/lib/kwala/actions/gem_plugin.rb +31 -0
- data/lib/kwala/actions/loc_count.rb +153 -0
- data/lib/kwala/actions/multi_ruby_unit_test.rb +62 -0
- data/lib/kwala/actions/outside_links.rb +85 -0
- data/lib/kwala/actions/rails_migrate.rb +62 -0
- data/lib/kwala/actions/strange_requires.rb +106 -0
- data/lib/kwala/actions/syntax_check.rb +130 -0
- data/lib/kwala/actions/unit_test.rb +368 -0
- data/lib/kwala/build_action.rb +88 -0
- data/lib/kwala/build_context.rb +49 -0
- data/lib/kwala/command_line_runner.rb +184 -0
- data/lib/kwala/ext/demos.jar +0 -0
- data/lib/kwala/ext/pmd.jar +0 -0
- data/lib/kwala/ext/prefuse.jar +0 -0
- data/lib/kwala/extensions.rb +36 -0
- data/lib/kwala/lib/code_analyzer.rb +678 -0
- data/lib/kwala/lib/cycle_detector.rb +793 -0
- data/lib/kwala/lib/strange_requires_detector.rb +145 -0
- data/lib/kwala/notification.rb +45 -0
- data/lib/kwala/notifications/email.rb +54 -0
- data/lib/kwala/notifications/rss.rb +151 -0
- data/lib/kwala/project_builder_utils.rb +178 -0
- data/lib/kwala/templates/build_template.html +33 -0
- data/lib/kwala/templates/code_change_summary.html +27 -0
- data/lib/kwala/templates/code_coverage_detailed.html +25 -0
- data/lib/kwala/templates/code_coverage_summary.html +30 -0
- data/lib/kwala/templates/code_duplication_detailed.html +45 -0
- data/lib/kwala/templates/code_duplication_summary.html +9 -0
- data/lib/kwala/templates/code_formatting_detailed.html +27 -0
- data/lib/kwala/templates/code_formatting_summary.html +11 -0
- data/lib/kwala/templates/cycle_detection_detailed.html +20 -0
- data/lib/kwala/templates/cycle_detection_summary.html +7 -0
- data/lib/kwala/templates/cyclomatic_complexity_summary.html +27 -0
- data/lib/kwala/templates/executable_files_check_detailed.html +31 -0
- data/lib/kwala/templates/executable_files_check_summary.html +20 -0
- data/lib/kwala/templates/flagged_comments_detailed.html +22 -0
- data/lib/kwala/templates/flagged_comments_summary.html +10 -0
- data/lib/kwala/templates/loc_count_detailed.html +24 -0
- data/lib/kwala/templates/loc_count_summary.html +14 -0
- data/lib/kwala/templates/mdd/1.png +0 -0
- data/lib/kwala/templates/mdd/2.png +0 -0
- data/lib/kwala/templates/mdd/3.png +0 -0
- data/lib/kwala/templates/mdd/4.png +0 -0
- data/lib/kwala/templates/mdd/5.png +0 -0
- data/lib/kwala/templates/outside_links_summary.html +4 -0
- data/lib/kwala/templates/strange_requires_detailed.html +23 -0
- data/lib/kwala/templates/strange_requires_summary.html +13 -0
- data/lib/kwala/templates/style.css +95 -0
- data/lib/kwala/templates/syntax_check_detailed.html +21 -0
- data/lib/kwala/templates/syntax_check_summary.html +7 -0
- data/lib/kwala/templates/unit_test_detailed.html +66 -0
- data/lib/kwala/templates/unit_test_summary.html +70 -0
- data/test/tc_build_action.rb +32 -0
- data/test/tc_templates.rb +24 -0
- metadata +118 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
class BuildAction
|
2
|
+
extend InheritanceTracker
|
3
|
+
|
4
|
+
def build_action(context)
|
5
|
+
raise "Must be implemented by subclass."
|
6
|
+
end
|
7
|
+
|
8
|
+
def summary_display(context)
|
9
|
+
raise "Must be implemented by subclass."
|
10
|
+
end
|
11
|
+
|
12
|
+
def detailed_display(context)
|
13
|
+
[nil, nil]
|
14
|
+
end
|
15
|
+
|
16
|
+
# a number from 1 - 10 that represents some type of rating that will help
|
17
|
+
# determine if the koala's expression.
|
18
|
+
# For now if nil, then not scorable. This might be better as an additional method
|
19
|
+
# called scoreable?
|
20
|
+
# Default is unscoreable
|
21
|
+
def score
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.command_line_action_name
|
26
|
+
self.name.gsub(/BuildAction|Action/, '').kwala_underscore
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.summary_template_file
|
30
|
+
"#{TEMPLATE_DIR}/#{command_line_action_name}_summary.html"
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.detailed_template_file
|
34
|
+
"#{TEMPLATE_DIR}/#{command_line_action_name}_detailed.html"
|
35
|
+
end
|
36
|
+
|
37
|
+
def BuildAction.create_action_from_command_line_name(name)
|
38
|
+
imp = BuildAction.get_implementors.find do |imp|
|
39
|
+
imp.command_line_action_name == name
|
40
|
+
end
|
41
|
+
|
42
|
+
if imp
|
43
|
+
imp.new
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def BuildAction.command_line_action_names
|
50
|
+
BuildAction.get_implementors.map do |imp|
|
51
|
+
imp.command_line_action_name
|
52
|
+
end.sort
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def time_out_readlines(io, timeout_period = 300)
|
59
|
+
res = nil
|
60
|
+
begin
|
61
|
+
timeout(timeout_period) do
|
62
|
+
res = io.readlines
|
63
|
+
end
|
64
|
+
rescue TimeoutError => err
|
65
|
+
res = ["IO readlines timed out."]
|
66
|
+
end
|
67
|
+
res
|
68
|
+
end
|
69
|
+
|
70
|
+
def summary_expand(template, context)
|
71
|
+
res = ProjectBuilderUtils.expand_template(template, context.amrita_data)
|
72
|
+
Amrita::SanitizedString.new(res)
|
73
|
+
end
|
74
|
+
|
75
|
+
# These are bad param names, need to make them better
|
76
|
+
# Extremely naive function to put the errors to some scale around 10.
|
77
|
+
def score_scaling(errors, bounds)
|
78
|
+
10 - ((errors.to_f / bounds.to_f) * 10).ceil
|
79
|
+
end
|
80
|
+
|
81
|
+
def notify(result, data = nil)
|
82
|
+
Notification.notify(self.class.command_line_action_name, result, data)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
class KwalaAbortBuildException < StandardError
|
88
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class BuildContext
|
2
|
+
attr_accessor :project_directory, :ruby_files, :test_files
|
3
|
+
attr_accessor :amrita_data, :ri_dir_1, :ri_dir_2, :pre_requires, :require
|
4
|
+
attr_accessor :output_directory
|
5
|
+
attr_accessor :vcs_num
|
6
|
+
attr_reader :project_name
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@project_directory = @project_name = @ri_dir_1 = @ri_dir_2 =
|
10
|
+
@output_directory = @require = nil
|
11
|
+
@ruby_files = Array.new
|
12
|
+
@test_files = Array.new
|
13
|
+
@pre_requires = nil
|
14
|
+
self.initialize_build_data
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def initialize_build_data()
|
19
|
+
@amrita_data = Hash.new
|
20
|
+
@amrita_data[:project_name] = self.project_name
|
21
|
+
@amrita_data[:build_date] = Time.now
|
22
|
+
end
|
23
|
+
|
24
|
+
def project_name=(project_name)
|
25
|
+
@project_name = project_name
|
26
|
+
@amrita_data[:project_name] = @project_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_complete
|
30
|
+
@amrita_data[:complete_time] = Time.now
|
31
|
+
@amrita_data[:total_build_time] = ( (@amrita_data[:complete_time] - @amrita_data[:build_date]) / 60.0 )
|
32
|
+
end
|
33
|
+
|
34
|
+
def vcs_info
|
35
|
+
if @vcs_info.nil?
|
36
|
+
# For now svn
|
37
|
+
cur = Dir.pwd
|
38
|
+
begin
|
39
|
+
Dir.chdir(self.project_directory)
|
40
|
+
@vcs_info = `svn log -r #{self.vcs_num} --verbose`
|
41
|
+
ensure
|
42
|
+
Dir.chdir(cur)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
@vcs_info
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
#! /usr/bin/ruby1.8
|
2
|
+
|
3
|
+
# Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are
|
8
|
+
# met:
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# * Redistributions of source code must retain the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer.
|
13
|
+
#
|
14
|
+
# * Redistributions in binary form must reproduce the above
|
15
|
+
# copyright notice, this list of conditions and the following
|
16
|
+
# disclaimer in the documentation and/or other materials provided
|
17
|
+
# with the distribution.
|
18
|
+
#
|
19
|
+
# * Neither the name of Ubit nor the names of its
|
20
|
+
# contributors may be used to endorse or promote products derived
|
21
|
+
# from this software without specific prior written permission.
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
25
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
26
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
27
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
28
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
29
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
30
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
31
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
32
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
33
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
34
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
35
|
+
#
|
36
|
+
# == Author
|
37
|
+
# Zev Blut (zb@ubit.com)
|
38
|
+
|
39
|
+
puts "Starting #{Time.now}"
|
40
|
+
|
41
|
+
require 'getoptlong'
|
42
|
+
require 'kwala'
|
43
|
+
|
44
|
+
def usage
|
45
|
+
puts "Usage: #{$0} [-d -p -a -y] -o"
|
46
|
+
puts " --project_name, -p (project name): The name of the project"
|
47
|
+
puts " --directory, -d (project base directory): The base directory of the project."
|
48
|
+
puts " if not set then current directory is assumed."
|
49
|
+
puts " --action, -a (action name): The name of an action to execute."
|
50
|
+
puts " if no action is set then all actions are assumed."
|
51
|
+
puts " --yaml, -y (yaml debug file name): The name of a file to save yaml debug information to."
|
52
|
+
puts " --output_dir, -o (output directory): The directory to output results to."
|
53
|
+
puts " --require,-r (file to require): A file to require for use when testing unit tests."
|
54
|
+
puts " --ri_cmp_1,-1 (old ri directory): The old ri output directory to compare against."
|
55
|
+
puts " --ri_cmp_2,-2 (new ri directory): The new ri output directory to compare against."
|
56
|
+
puts " --list_actions, -l: Lists the action names available."
|
57
|
+
puts " --notifications, -n: Activates notifications for action results."
|
58
|
+
puts " --vcs_num, -v: A string with the vcs version numbers to check"
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def list_actions
|
63
|
+
puts "Available actions are: "
|
64
|
+
BuildAction.command_line_action_names.each do |name|
|
65
|
+
puts " #{ name }"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
ProjectBuilderUtils.load_actions
|
70
|
+
|
71
|
+
actions = Array.new
|
72
|
+
context = BuildContext.new
|
73
|
+
|
74
|
+
context.project_directory = Dir.pwd
|
75
|
+
context.project_name = ""
|
76
|
+
debug_results = nil
|
77
|
+
begin
|
78
|
+
options = GetoptLong.new(
|
79
|
+
["-p", "--project_name", GetoptLong::REQUIRED_ARGUMENT],
|
80
|
+
["-d", "--dir", GetoptLong::REQUIRED_ARGUMENT],
|
81
|
+
["-a", "--action", GetoptLong::REQUIRED_ARGUMENT],
|
82
|
+
["-y", "--yaml", GetoptLong::REQUIRED_ARGUMENT],
|
83
|
+
["-r", "--require", GetoptLong::REQUIRED_ARGUMENT],
|
84
|
+
["-1", "--ri_cmp_1", GetoptLong::REQUIRED_ARGUMENT],
|
85
|
+
["-2", "--ri_cmp_2", GetoptLong::REQUIRED_ARGUMENT],
|
86
|
+
["-e", "--extensions", GetoptLong::REQUIRED_ARGUMENT],
|
87
|
+
["-o", "--output_dir", GetoptLong::REQUIRED_ARGUMENT],
|
88
|
+
["-l", "--list_actions", GetoptLong::NO_ARGUMENT],
|
89
|
+
["-n", "--notifications", GetoptLong::NO_ARGUMENT],
|
90
|
+
["-v", "--vcs_num", GetoptLong::REQUIRED_ARGUMENT] )
|
91
|
+
|
92
|
+
options.each do |opt, arg|
|
93
|
+
case opt
|
94
|
+
when "-p"
|
95
|
+
context.project_name = arg
|
96
|
+
when "-a"
|
97
|
+
actions << arg
|
98
|
+
when "-d"
|
99
|
+
context.project_directory = arg
|
100
|
+
when "-y"
|
101
|
+
debug_results = arg
|
102
|
+
when "-o"
|
103
|
+
context.output_directory = arg
|
104
|
+
when "-r"
|
105
|
+
context.pre_requires = arg
|
106
|
+
when "-1"
|
107
|
+
context.ri_dir_1 = ProjectBuilderUtils.clean_trailing_slash(arg)
|
108
|
+
when "-2"
|
109
|
+
context.ri_dir_2 = ProjectBuilderUtils.clean_trailing_slash(arg)
|
110
|
+
when "-e"
|
111
|
+
require arg
|
112
|
+
# required file should define get extensions
|
113
|
+
extensions = get_extensions
|
114
|
+
when "-l"
|
115
|
+
list_actions
|
116
|
+
exit 0
|
117
|
+
when "-n"
|
118
|
+
Notification.context = context
|
119
|
+
Notification.notifications
|
120
|
+
Notification.add_notification(EmailNotification.new)
|
121
|
+
Notification.add_notification(JabberNotification.new)
|
122
|
+
when "-v"
|
123
|
+
context.vcs_num = arg
|
124
|
+
else
|
125
|
+
STDERR.puts "Unknown argument #{opt}"
|
126
|
+
usage
|
127
|
+
end
|
128
|
+
end
|
129
|
+
rescue => err
|
130
|
+
STDERR.puts "Error: #{err}", err.backtrace
|
131
|
+
usage
|
132
|
+
end
|
133
|
+
|
134
|
+
usage if context.output_directory.nil?
|
135
|
+
|
136
|
+
# Set default actions if not passed
|
137
|
+
if actions.empty?
|
138
|
+
actions = %w(loc_count syntax_check unit_test flagged_comments)
|
139
|
+
end
|
140
|
+
|
141
|
+
if actions.include?("code_change")
|
142
|
+
usage if (context.ri_dir_1 && !context.ri_dir_2) || (!context.ri_dir_1 && context.ri_dir_2)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Set up necessary info before actions
|
146
|
+
|
147
|
+
ruby_files, test_files = ProjectBuilderUtils.get_ruby_and_unit_test_files(context.project_directory)
|
148
|
+
context.ruby_files = ruby_files
|
149
|
+
context.test_files = test_files
|
150
|
+
|
151
|
+
action_instances = Array.new
|
152
|
+
actions.each do |action|
|
153
|
+
action_obj = BuildAction.create_action_from_command_line_name(action)
|
154
|
+
if action_obj
|
155
|
+
action_instances << action_obj
|
156
|
+
else
|
157
|
+
STDERR.puts "Unknown action : #{action}"
|
158
|
+
list_actions
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Run the build stage
|
163
|
+
action_instances = ProjectBuilderUtils.build_actions(action_instances, context)
|
164
|
+
|
165
|
+
# Run the summary stage
|
166
|
+
summaries = ProjectBuilderUtils.build_summaries(action_instances, context)
|
167
|
+
|
168
|
+
if debug_results
|
169
|
+
#Check the syncro of templates
|
170
|
+
File.open(debug_results, "w") do |f|
|
171
|
+
f.puts context.amrita_data.to_yaml
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
ProjectBuilderUtils.make_detailed_reports(context, action_instances)
|
176
|
+
|
177
|
+
ProjectBuilderUtils.make_summary_report(context, summaries)
|
178
|
+
|
179
|
+
|
180
|
+
# Copy the stylesheet to the output directory
|
181
|
+
FileUtils.cp(TEMPLATE_DIR + "style.css", context.output_directory)
|
182
|
+
FileUtils.cp_r(TEMPLATE_DIR + "mdd", context.output_directory)
|
183
|
+
|
184
|
+
puts "Done #{Time.now}"
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Provides a method that gives instances of all of the includer's subclasses.
|
2
|
+
module InheritanceTracker
|
3
|
+
|
4
|
+
# Returns all subclasses of calling Class.
|
5
|
+
def get_implementors
|
6
|
+
imps = []
|
7
|
+
|
8
|
+
ObjectSpace.each_object(Class) do |klass|
|
9
|
+
if klass.ancestors.include?(self)
|
10
|
+
imps << klass
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# remove self from the list
|
15
|
+
imps - [self]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class DeepCopy
|
21
|
+
extend Marshal
|
22
|
+
|
23
|
+
def DeepCopy.deep_copy(obj)
|
24
|
+
load(dump(obj))
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class String
|
30
|
+
|
31
|
+
# Very Simple underscore implementation.
|
32
|
+
def kwala_underscore
|
33
|
+
self.gsub(/([A-Z])/, '_\1').downcase.gsub(/^_/, '')
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,678 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
# Copyright (c) 2006, Ubiquitous Business Technology (http://ubit.com)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are
|
8
|
+
# met:
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# * Redistributions of source code must retain the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer.
|
13
|
+
#
|
14
|
+
# * Redistributions in binary form must reproduce the above
|
15
|
+
# copyright notice, this list of conditions and the following
|
16
|
+
# disclaimer in the documentation and/or other materials provided
|
17
|
+
# with the distribution.
|
18
|
+
#
|
19
|
+
# * Neither the name of Ubit nor the names of its
|
20
|
+
# contributors may be used to endorse or promote products derived
|
21
|
+
# from this software without specific prior written permission.
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
25
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
26
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
27
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
28
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
29
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
30
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
31
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
32
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
33
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
34
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
35
|
+
#
|
36
|
+
# == Author
|
37
|
+
# Zev Blut (zb@ubit.com)
|
38
|
+
|
39
|
+
require 'irb/ruby-lex'
|
40
|
+
require 'pp'
|
41
|
+
|
42
|
+
# Add indent to token
|
43
|
+
module RubyToken
|
44
|
+
class Token
|
45
|
+
attr_accessor :indent
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class FileAnalyzer
|
50
|
+
include RubyToken
|
51
|
+
|
52
|
+
attr_reader :file , :requires , :classes , :modules , :constants ,
|
53
|
+
:methods , :g_vars , :i_vars , :c_vars ,
|
54
|
+
:symbols , :blocks , :yields , :raises , :rescues , :cases , :aliases ,
|
55
|
+
:undefs , :ensures , :evals , :big_begins , :big_ends , :exits , :defines ,
|
56
|
+
:retries , :redoes , :nexts , :breaks , :ensures , :assignments ,
|
57
|
+
:is_a_kind_ofs , :method_missing , :static_methods
|
58
|
+
|
59
|
+
def initialize(file)
|
60
|
+
@file = file
|
61
|
+
@requires = []
|
62
|
+
@classes = []
|
63
|
+
@modules = []
|
64
|
+
@constants = []
|
65
|
+
@methods = []
|
66
|
+
@g_vars = []
|
67
|
+
@i_vars = []
|
68
|
+
@c_vars = []
|
69
|
+
@symbols = []
|
70
|
+
@blocks = 0
|
71
|
+
@yields = 0
|
72
|
+
@raises = 0
|
73
|
+
@rescues = 0
|
74
|
+
@cases = 0
|
75
|
+
@aliases = []
|
76
|
+
@undefs = []
|
77
|
+
@ensures = 0
|
78
|
+
@evals = 0
|
79
|
+
@big_begins = 0
|
80
|
+
@big_ends = 0
|
81
|
+
@exits = 0
|
82
|
+
@defines = 0
|
83
|
+
@retries = 0
|
84
|
+
@redoes = 0
|
85
|
+
@nexts = 0
|
86
|
+
@breaks = 0
|
87
|
+
@ensures = 0
|
88
|
+
@assignments = 0
|
89
|
+
@is_a_kind_ofs = 0
|
90
|
+
@method_missing = 0
|
91
|
+
@static_methods = []
|
92
|
+
@tl_ref = Hash.new
|
93
|
+
end
|
94
|
+
|
95
|
+
def analyze
|
96
|
+
#puts "Lexing File: #{ @file }"
|
97
|
+
|
98
|
+
lexer = RubyLex.new
|
99
|
+
lexer.exception_on_syntax_error = false
|
100
|
+
lexer.set_input(File.new(@file,"r"))
|
101
|
+
|
102
|
+
tokens = Array.new
|
103
|
+
while (tok = lexer.token)
|
104
|
+
tok.indent = lexer.indent
|
105
|
+
tokens<< tok
|
106
|
+
@tl_ref[tok.object_id] = lexer.line_no
|
107
|
+
end
|
108
|
+
|
109
|
+
i = 0
|
110
|
+
while i < tokens.size
|
111
|
+
ni = check_token(tokens[i], tokens[i..-1])
|
112
|
+
i += ni
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
# types, rem = BaseType.create_types_from_tokens(tokens)
|
117
|
+
|
118
|
+
|
119
|
+
# puts "TYPES ----------------"
|
120
|
+
# pp types
|
121
|
+
# exit 1
|
122
|
+
|
123
|
+
self
|
124
|
+
rescue RubyLex::SyntaxError => synerr
|
125
|
+
$stderr.puts synerr
|
126
|
+
ensure
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
def summarize
|
131
|
+
puts "-- Summary for #{ @file } --",""
|
132
|
+
puts "Requires".ljust(15) + ": " + @requires.size.to_s
|
133
|
+
puts "Modules".ljust(15) + ": " + @modules.size.to_s
|
134
|
+
puts "Classes".ljust(15) + ": " + @classes.size.to_s
|
135
|
+
puts "Constants".ljust(15) + ": " + @constants.size.to_s
|
136
|
+
puts "Methods".ljust(15) + ": " + @methods.size.to_s
|
137
|
+
puts "Static Methods".ljust(15) + ": " + @static_methods.size.to_s
|
138
|
+
puts "G Vars".ljust(15) + ": " + @g_vars.size.to_s
|
139
|
+
puts "C Vars".ljust(15) + ": " + @c_vars.size.to_s
|
140
|
+
puts "I Vars".ljust(15) + ": " + @i_vars.size.to_s
|
141
|
+
puts "Symbols".ljust(15) + ": " + @symbols.size.to_s
|
142
|
+
puts "Blocks".ljust(15) + ": " + @blocks.to_s
|
143
|
+
puts "Yields".ljust(15) + ": " + @yields.to_s
|
144
|
+
puts "Raises".ljust(15) + ": " + @raises.to_s
|
145
|
+
puts "Rescues".ljust(15) + ": " + @rescues.to_s
|
146
|
+
puts "Retries".ljust(15) + ": " + @retries.to_s
|
147
|
+
puts "Redoes".ljust(15) + ": " + @redoes.to_s
|
148
|
+
puts "Ensures".ljust(15) + ": " + @ensures.to_s
|
149
|
+
puts "Undefs".ljust(15) + ": " + @undefs.size.to_s
|
150
|
+
puts "Evals".ljust(15) + ": " + @evals.to_s
|
151
|
+
puts "Aliases".ljust(15) + ": " + @aliases.size.to_s
|
152
|
+
puts "BEGINs".ljust(15) + ": " + @big_begins.to_s
|
153
|
+
puts "ENDs".ljust(15) + ": " + @big_ends.to_s
|
154
|
+
puts "Exits".ljust(15) + ": " + @exits.to_s
|
155
|
+
puts "Defined?".ljust(15) + ": " + @defines.to_s
|
156
|
+
puts "Is_a? ".ljust(15) + ": " + @is_a_kind_ofs.to_s
|
157
|
+
puts "M missing".ljust(15) + ": " + @method_missing.to_s
|
158
|
+
puts "Nexts".ljust(15) + ": " + @nexts.to_s
|
159
|
+
puts "Breaks".ljust(15) + ": " + @breaks.to_s
|
160
|
+
puts "Cases".ljust(15) + ": " + @cases.to_s
|
161
|
+
puts "Assignments".ljust(15) + ": " + @assignments.to_s
|
162
|
+
puts "","",""
|
163
|
+
|
164
|
+
if @constants.size > 0
|
165
|
+
puts "","",""
|
166
|
+
puts "Constants ->", @constants.sort.uniq
|
167
|
+
end
|
168
|
+
|
169
|
+
if @static_methods.size > 0
|
170
|
+
puts "","",""
|
171
|
+
puts "Static methods ->", @static_methods.sort.uniq
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
puts "","----------------------------"
|
176
|
+
end
|
177
|
+
|
178
|
+
def check_token(tok, stack)
|
179
|
+
i = 1
|
180
|
+
case tok
|
181
|
+
when TkCLASS
|
182
|
+
name, i = find_identifier_name(stack)
|
183
|
+
@classes<< name
|
184
|
+
when TkMODULE
|
185
|
+
name, i = find_identifier_name(stack)
|
186
|
+
@modules<< name
|
187
|
+
when TkCONSTANT
|
188
|
+
#@contants<< tok.name
|
189
|
+
i = find_static_method_call(tok, stack)
|
190
|
+
when TkDEF
|
191
|
+
name, i = find_identifier_name(stack)
|
192
|
+
@methods<< name
|
193
|
+
when TkIDENTIFIER, TkFID
|
194
|
+
#@
|
195
|
+
case tok.name
|
196
|
+
when "require"
|
197
|
+
@requires<< nil
|
198
|
+
when "raise"
|
199
|
+
@raises += 1
|
200
|
+
when "eval", "instance_eval", "class_eval", "module_eval"
|
201
|
+
@evals += 1
|
202
|
+
when "is_a?", "kind_of?"
|
203
|
+
@is_a_kind_ofs += 1
|
204
|
+
when "method_missing"
|
205
|
+
@method_missing += 1
|
206
|
+
when /^exit!?$/
|
207
|
+
@exits += 1
|
208
|
+
end
|
209
|
+
|
210
|
+
when TkSYMBEG
|
211
|
+
name, i = find_identifier_name(stack)
|
212
|
+
@symbols<< name
|
213
|
+
when TkCVAR
|
214
|
+
@c_vars<< tok.name
|
215
|
+
when TkIVAR
|
216
|
+
@i_vars<< tok.name
|
217
|
+
when TkGVAR
|
218
|
+
@g_vars << tok.name
|
219
|
+
when TkDO,TkfLBRACE
|
220
|
+
# do
|
221
|
+
@blocks += 1
|
222
|
+
when TkYIELD
|
223
|
+
@yields += 1
|
224
|
+
when TkEND
|
225
|
+
when TkRESCUE
|
226
|
+
@rescues += 1
|
227
|
+
when TklBEGIN
|
228
|
+
@big_begins += 1
|
229
|
+
when TklEND
|
230
|
+
@big_ends += 1
|
231
|
+
when TkDEFINED
|
232
|
+
@defines += 1
|
233
|
+
when TkRETRY
|
234
|
+
@retries += 1
|
235
|
+
when TkREDO
|
236
|
+
@redoes += 1
|
237
|
+
when TkNEXT
|
238
|
+
@nexts += 1
|
239
|
+
when TkBREAK
|
240
|
+
@breaks += 1
|
241
|
+
when TkENSURE
|
242
|
+
@ensures += 1
|
243
|
+
when TkUNDEF
|
244
|
+
@undefs << nil
|
245
|
+
when TkCASE
|
246
|
+
@cases += 1
|
247
|
+
when TkALIAS
|
248
|
+
name, i = find_identifier_name(stack)
|
249
|
+
@aliases << name
|
250
|
+
when TkASSIGN, TkOPASGN
|
251
|
+
@assignments += 1
|
252
|
+
else
|
253
|
+
|
254
|
+
end
|
255
|
+
i
|
256
|
+
end
|
257
|
+
|
258
|
+
def find_identifier_name(stream, check_types = [TkIDENTIFIER, TkFID, TkCONSTANT])
|
259
|
+
i = 1
|
260
|
+
name = ""
|
261
|
+
found = false
|
262
|
+
|
263
|
+
while !found && stream[i]
|
264
|
+
tok = stream[i]
|
265
|
+
if check_types.include?(tok.class)
|
266
|
+
found = true
|
267
|
+
name = tok.name
|
268
|
+
else
|
269
|
+
i += 1
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
[name, i]
|
274
|
+
end
|
275
|
+
|
276
|
+
def find_static_method_call(tok, stream)
|
277
|
+
sname = tok.name.dup
|
278
|
+
searching = true
|
279
|
+
i = 1
|
280
|
+
while searching
|
281
|
+
val = stream[i]
|
282
|
+
case val
|
283
|
+
when TkDOT
|
284
|
+
sname<< "."
|
285
|
+
when TkCOLON2
|
286
|
+
sname<< "::"
|
287
|
+
when TkCONSTANT, TkIDENTIFIER, TkFID
|
288
|
+
sname << val.name
|
289
|
+
when TkNL, TkSPACE
|
290
|
+
searching = false
|
291
|
+
when TkLPAREN
|
292
|
+
sname<< "()"
|
293
|
+
searching = false
|
294
|
+
when TkfLBRACK
|
295
|
+
sname<< "[]"
|
296
|
+
searching = false
|
297
|
+
when TkfLBRACE
|
298
|
+
searching = false
|
299
|
+
when TkRPAREN, TkRBRACK, TkRBRACE, TkDOT2, TkDOT3, TkBITOR, TkBITAND,
|
300
|
+
TkCOMMA, TkSEMICOLON, TkPLUS, TkMINUS, TkMULT, TkDIV
|
301
|
+
# close of a method call
|
302
|
+
searching = false
|
303
|
+
when TkASSIGN, TkMOD
|
304
|
+
# could be class = ? most likely a constant
|
305
|
+
searching = false
|
306
|
+
when TkDSTRING, TkSTRING
|
307
|
+
# ignore the constant as it is wrapped in a string
|
308
|
+
searching = false
|
309
|
+
else
|
310
|
+
puts "NEW CASE #{ val.class } for #{self.file}"
|
311
|
+
puts "Line: #{ @tl_ref[val.object_id] }"
|
312
|
+
searching = false
|
313
|
+
end
|
314
|
+
i += 1
|
315
|
+
end
|
316
|
+
if tok.name != sname
|
317
|
+
# Make sure we don't get things like Array[] or Array()
|
318
|
+
if /:/ =~ sname && /^[A-Z]/ =~ sname.split(".").last.split(":").last
|
319
|
+
@constants << sname
|
320
|
+
else
|
321
|
+
@static_methods<< sname
|
322
|
+
end
|
323
|
+
else
|
324
|
+
@constants << sname
|
325
|
+
#puts "JUST A CONST :#{ sname } ----"
|
326
|
+
#puts "Line: #{ @tl_ref[tok.object_id] }"
|
327
|
+
end
|
328
|
+
i
|
329
|
+
end
|
330
|
+
|
331
|
+
def group_methods(tokens)
|
332
|
+
# meth_groups = []
|
333
|
+
#
|
334
|
+
# cur_group_toks = []
|
335
|
+
# indent = 0
|
336
|
+
# indef = false
|
337
|
+
#
|
338
|
+
# tokens.each do |tok|
|
339
|
+
# cur_group_toks<< tok
|
340
|
+
#
|
341
|
+
# if tok.class == TkDEF
|
342
|
+
# indent = tok.indent
|
343
|
+
# indef = true
|
344
|
+
# cur_group_toks = [ tok ]
|
345
|
+
# elsif tok.class == TkEND
|
346
|
+
# if indef && (tok.indent == (indent - 1))
|
347
|
+
# indef = false
|
348
|
+
# indent = tok.indent
|
349
|
+
#
|
350
|
+
# meth_groups<< cur_group_toks
|
351
|
+
# cur_group_toks = []
|
352
|
+
# end
|
353
|
+
# end
|
354
|
+
# end
|
355
|
+
#
|
356
|
+
# puts "Meth group #{ meth_groups.size }"
|
357
|
+
# pp meth_groups
|
358
|
+
# exit
|
359
|
+
#
|
360
|
+
groups, idx = group_tokens(tokens, [TkDEF])
|
361
|
+
puts "Meth groups cnt #{ groups.size }"
|
362
|
+
pp groups
|
363
|
+
exit
|
364
|
+
end
|
365
|
+
|
366
|
+
|
367
|
+
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
|
372
|
+
|
373
|
+
class FileAnalyzerGroup
|
374
|
+
def initialize(files)
|
375
|
+
@fa_files = []
|
376
|
+
files.each do |file|
|
377
|
+
@fa_files<< FileAnalyzer.new(file).analyze
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def summarize
|
382
|
+
fst = @fa_files.first
|
383
|
+
vals = fst.instance_variables.map { |iv| iv[1..-1] }
|
384
|
+
# remove the instance variables we set that are not related to data tracking.
|
385
|
+
vals = vals - ["file", "tl_ref"]
|
386
|
+
t_v = vals.map do |v|
|
387
|
+
res = fst.send(v)
|
388
|
+
|
389
|
+
if res.is_a?(Array)
|
390
|
+
p = Proc.new { |v| v.size }
|
391
|
+
else
|
392
|
+
p = Proc.new { |v| v.to_i }
|
393
|
+
end
|
394
|
+
[v, p]
|
395
|
+
end
|
396
|
+
|
397
|
+
res = Hash.new { |h,k| h[k] = [] }
|
398
|
+
@fa_files.each do |f|
|
399
|
+
t_v.each do |method, calc|
|
400
|
+
res[method]<< calc.call( f.send(method) )
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
base_sum = Hash.new(0)
|
406
|
+
|
407
|
+
res.sort.each do |key, val|
|
408
|
+
r = val.inject(0) { |t, v| t + v }
|
409
|
+
base_sum[key] = r
|
410
|
+
end
|
411
|
+
|
412
|
+
print_summary_header(base_sum)
|
413
|
+
|
414
|
+
extra_summary( [ ["static_methods", "static methods"],
|
415
|
+
["g_vars", "global variables"],
|
416
|
+
["c_vars", "class variables"],
|
417
|
+
["i_vars", "instance variables"],
|
418
|
+
["constants", "constants"],
|
419
|
+
["methods", "methods"],
|
420
|
+
["classes", "classes"],
|
421
|
+
["modules", "modules"],
|
422
|
+
["symbols", "symbols"],
|
423
|
+
["aliases", "aliases"],
|
424
|
+
["undefs", "undefs"]
|
425
|
+
] )
|
426
|
+
|
427
|
+
end
|
428
|
+
|
429
|
+
def print_summary_header(summary)
|
430
|
+
puts "Summary"
|
431
|
+
summary.sort.each do |key, res|
|
432
|
+
puts "#{ key.ljust(15) } : #{res}"
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
def extra_summary(methods)
|
437
|
+
ttl_files = @fa_files.size
|
438
|
+
|
439
|
+
methods.each do |mcall, mname|
|
440
|
+
cnt = @fa_files.inject(0) { |cnt, f| cnt += (f.send(mcall).size > 0 ? 1 : 0) }
|
441
|
+
if cnt == 0
|
442
|
+
next
|
443
|
+
end
|
444
|
+
msg = "#{ cnt } out of #{ ttl_files }" +
|
445
|
+
" (#{sprintf("%.2f",(cnt.to_f / ttl_files) * 100)} %) use #{ mname}"
|
446
|
+
|
447
|
+
|
448
|
+
uniqs = Hash.new { |h,k| h[k] = [] }
|
449
|
+
@fa_files.each do |f|
|
450
|
+
f.send(mcall).uniq.each do |sm|
|
451
|
+
uniqs[sm] << f.file
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
print_extra_summary(mname, msg, uniqs)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
def print_extra_summary(name, msg, uniqs)
|
460
|
+
puts "" , "---" + msg + "---"
|
461
|
+
puts "#{name} used by files:", ""
|
462
|
+
uniqs.sort.each do |k, m|
|
463
|
+
puts k.to_s.ljust(30) + " -> ", " " + m.join("\n "), ""
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
end
|
468
|
+
|
469
|
+
class BaseType
|
470
|
+
include RubyToken
|
471
|
+
|
472
|
+
attr_accessor :name, :start_line, :end_line, :children, :tokens
|
473
|
+
|
474
|
+
def initialize(tokens = Array.new)
|
475
|
+
@children = Array.new
|
476
|
+
|
477
|
+
# These may be computed by tokens?
|
478
|
+
@start_line = 0
|
479
|
+
@end_line = 0
|
480
|
+
@tokens = tokens
|
481
|
+
@rem_tokens = []
|
482
|
+
|
483
|
+
create_child_groupings
|
484
|
+
analyse_self
|
485
|
+
end
|
486
|
+
|
487
|
+
def sub_types
|
488
|
+
@children.each { |c| yield c }
|
489
|
+
end
|
490
|
+
|
491
|
+
|
492
|
+
def self.create_types_from_tokens(tokens)
|
493
|
+
groups, idx = group_tokens(tokens, [TkCLASS, TkMODULE, TkDEF])
|
494
|
+
|
495
|
+
|
496
|
+
remainder_tokens = []
|
497
|
+
types = []
|
498
|
+
|
499
|
+
groups.each do |sub_tokens|
|
500
|
+
|
501
|
+
case sub_tokens.first
|
502
|
+
when TkCLASS
|
503
|
+
types<< ClassType.new(sub_tokens)
|
504
|
+
when TkMODULE
|
505
|
+
types<< ModuleType.new(sub_tokens)
|
506
|
+
when TkDEF
|
507
|
+
types<< DefType.new(sub_tokens)
|
508
|
+
else
|
509
|
+
remainder_tokens<< sub_tokens
|
510
|
+
end
|
511
|
+
|
512
|
+
end
|
513
|
+
|
514
|
+
[types, remainder_tokens]
|
515
|
+
end
|
516
|
+
|
517
|
+
private
|
518
|
+
def self.group_tokens(tokens, trigger, indent_target = nil, idx = 0)
|
519
|
+
groups = []
|
520
|
+
|
521
|
+
while idx < tokens.size
|
522
|
+
tok = tokens[idx]
|
523
|
+
idx += 1
|
524
|
+
|
525
|
+
if trigger.include?(tok.class)
|
526
|
+
indent = tok.indent
|
527
|
+
|
528
|
+
if indent_target.nil?
|
529
|
+
indent_target = indent - 1
|
530
|
+
end
|
531
|
+
|
532
|
+
|
533
|
+
sub_group, idx = collect_into_group(tokens, indent - 1, idx)
|
534
|
+
|
535
|
+
groups << [tok] + sub_group
|
536
|
+
end
|
537
|
+
|
538
|
+
end
|
539
|
+
|
540
|
+
[groups, idx]
|
541
|
+
end
|
542
|
+
|
543
|
+
|
544
|
+
def self.collect_into_group(tokens, indent_target, idx)
|
545
|
+
group = []
|
546
|
+
|
547
|
+
while idx < tokens.size
|
548
|
+
tok = tokens[idx]
|
549
|
+
idx += 1
|
550
|
+
|
551
|
+
group << tok
|
552
|
+
if tok.class == TkEND
|
553
|
+
if tok.indent == indent_target
|
554
|
+
return [group, idx]
|
555
|
+
elsif tok.indent < indent_target
|
556
|
+
puts "ERROR INDENT lower than TARGET"
|
557
|
+
return [group, idx]
|
558
|
+
else
|
559
|
+
# puts "NO CARE"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
puts "ERROR FELL out while looking for END!"
|
565
|
+
return [group, idx]
|
566
|
+
end
|
567
|
+
|
568
|
+
|
569
|
+
def create_child_groupings
|
570
|
+
# ignore first so that we do not get stuck on self and last end
|
571
|
+
@children, rem = BaseType.create_types_from_tokens(@tokens[1..-2])
|
572
|
+
@rem_tokens = rem
|
573
|
+
end
|
574
|
+
|
575
|
+
def analyse_self
|
576
|
+
end
|
577
|
+
|
578
|
+
end
|
579
|
+
|
580
|
+
|
581
|
+
class ClassType < BaseType
|
582
|
+
attr_accessor :super_class, :includes
|
583
|
+
|
584
|
+
def analyse_self
|
585
|
+
|
586
|
+
|
587
|
+
@name = find_c_m_name
|
588
|
+
@super_class = find_super_class
|
589
|
+
@includes = find_includes
|
590
|
+
end
|
591
|
+
|
592
|
+
def find_c_m_name
|
593
|
+
""
|
594
|
+
end
|
595
|
+
|
596
|
+
def find_super_class
|
597
|
+
nil
|
598
|
+
end
|
599
|
+
|
600
|
+
def find_includes
|
601
|
+
[]
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
class ModuleType < BaseType
|
606
|
+
attr_accessor :includes
|
607
|
+
end
|
608
|
+
|
609
|
+
class DefType < BaseType
|
610
|
+
attr_accessor :arguments, :raises
|
611
|
+
|
612
|
+
def analyse_self
|
613
|
+
find_name
|
614
|
+
find_arguments
|
615
|
+
find_raises
|
616
|
+
end
|
617
|
+
|
618
|
+
def find_name
|
619
|
+
end
|
620
|
+
|
621
|
+
def find_arguments
|
622
|
+
end
|
623
|
+
|
624
|
+
def find_raises
|
625
|
+
end
|
626
|
+
|
627
|
+
end
|
628
|
+
|
629
|
+
if __FILE__ == $0
|
630
|
+
FileAnalyzerGroup.new(ARGV).summarize
|
631
|
+
end
|
632
|
+
|
633
|
+
__END__
|
634
|
+
|
635
|
+
# LOOK for initialize calls with calling super
|
636
|
+
#
|
637
|
+
# More things to look for
|
638
|
+
# Misspelled local/variables
|
639
|
+
# No method comments
|
640
|
+
# Unused globals and locals (module or variable)
|
641
|
+
# Unused function/method arguments (can ignore self)
|
642
|
+
|
643
|
+
#LOOK for EMPTY RESCUE blocks.
|
644
|
+
# I.e. a rescue that just catches the exception and does nothing.
|
645
|
+
|
646
|
+
# Count # of parameters per method (over 7 warn!)
|
647
|
+
# check for similar sequence of parameters in methods
|
648
|
+
# Warn if inconsistent?
|
649
|
+
# i.e. foo(a, b, c) and moo(c, a , b)
|
650
|
+
|
651
|
+
# Unused method parameters! unless it is a overriding subclass?
|
652
|
+
|
653
|
+
# Have view to show all those who subclass/include/extend a class or module (Fan in)
|
654
|
+
# Also view for normal fan out?
|
655
|
+
|
656
|
+
# Check block parameters!
|
657
|
+
|
658
|
+
# Variable bad things (smalltalk best practice p.103)
|
659
|
+
# long extent (how long its value lasts )
|
660
|
+
# wide scope (how long it can be accessed)
|
661
|
+
# large type (How many messages it can respond to)
|
662
|
+
|
663
|
+
|
664
|
+
|
665
|
+
# Method call
|
666
|
+
Line: 63 17 Token #<RubyToken::TkIDENTIFIER:0xb7d33790>: interface
|
667
|
+
Line: 63 18 Token #<RubyToken::TkDOT:0xb7d335d8>
|
668
|
+
Line: 63 31 Token #<RubyToken::TkIDENTIFIER:0xb7d33024>: billing_steps
|
669
|
+
|
670
|
+
Line: 64 13 Token #<RubyToken::TkFID:0xb7d30504>: nil?
|
671
|
+
|
672
|
+
Line: 68 2513 Token #<RubyToken::TkNIL:0xb7d27c24>: nil
|
673
|
+
|
674
|
+
|
675
|
+
Line: 78 12 Token #<RubyToken::TkCONSTANT:0xb7d1b7bc>: Registry
|
676
|
+
Line: 78 13 Token #<RubyToken::TkDOT:0xb7d1b5dc>
|
677
|
+
Line: 78 26 Token #<RubyToken::TkIDENTIFIER:0xb7d1b104>: each_behavior
|
678
|
+
|