-
-
Notifications
You must be signed in to change notification settings - Fork 74
/
Copy pathflog_cli.rb
246 lines (187 loc) · 5.85 KB
/
flog_cli.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
require "rubygems"
require "optparse"
require "forwardable"
require "path_expander"
require "flog"
##
# This is the CLI interface for Flog, responsible for processing
# options, finding files, loading plugins, and reporting results.
class FlogCLI
extend Forwardable
def_delegators :@flog, :average, :calculate, :each_by_score, :option
def_delegators :@flog, :method_locations, :method_scores, :reset, :scores
def_delegators :@flog, :threshold, :total_score, :no_method, :calculate_total_scores
def_delegators :@flog, :max_method
##
# This kicks off the whole thing.
def self.run args = ARGV, extra = {}
load_plugins
expander = PathExpander.new args, "**/*.{rb,rake}"
files = expander.process
options = parse_options args, extra
abort "no files or stdin (-) to process, aborting." if
files.empty? and args.empty?
flogger = new options
flogger.flog(*files)
flogger.report
end
##
# Loads all flog plugins. Files must be named "flog/*.rb".
def self.load_plugins
# TODO: I think I want to do this more like hoe's plugin system. Generalize?
loaded, found = {}, {}
Gem.find_files("flog/*.rb").reverse.each do |path|
found[File.basename(path, ".rb").intern] = path
end
found.each do |name, plugin|
next if loaded[name]
begin
warn "loading #{plugin}" # if $DEBUG
loaded[name] = load plugin
rescue LoadError => e
warn "error loading #{plugin.inspect}: #{e.message}. skipping..."
end
end
self.plugins.merge loaded
names = Flog.constants.map {|s| s.to_s}.reject {|n| n =~ /^[A-Z_]+$/}
names.each do |name|
# next unless Hoe.plugins.include? name.downcase.intern
mod = Flog.const_get(name)
next if Class === mod
warn "extend #{mod}" if $DEBUG
# self.extend mod
end
end
##
# Parse options in +args+ (defaults to ARGV).
def self.parse_options args = ARGV, extra_options = {}
option = {
:quiet => false,
:continue => false,
:parser => RubyParser,
}.merge extra_options
OptionParser.new do |opts|
opts.separator "Standard options:"
opts.on("-a", "--all", "Display all flog results, not top 60%.") do
option[:all] = true
end
opts.on("-b", "--blame", "Include blame information for methods.") do
option[:blame] = true
end
opts.on("-c", "--continue", "Continue despite syntax errors.") do
option[:continue] = true
end
opts.on("-d", "--details", "Show method details.") do
option[:details] = true
end
opts.on("-g", "--group", "Group and sort by class.") do
option[:group] = true
end
opts.on("-h", "--help", "Show this message.") do
puts opts
exit
end
opts.on("-I dir1,dir2,dir3", Array, "Add to LOAD_PATH.") do |dirs|
dirs.each do |dir|
$: << dir
end
end
opts.on("-m", "--methods-only", "Skip code outside of methods.") do
option[:methods] = true
end
opts.on("-q", "--quiet", "Don't show parse errors.") do
option[:quiet] = true
end
opts.on("-e", "--extended", "Put file:line on a separate line (for rubymine & friends).") do
option[:extended] = true
end
opts.on("-s", "--score", "Display total score only.") do
option[:score] = true
end
opts.on("-tN", "--threshold=N", Integer, "Set the report cutoff threshold (def: 60%).") do |n|
option[:threshold] = n / 100.0
end
opts.on("-v", "--verbose", "Display progress during processing.") do
option[:verbose] = true
end
next if self.plugins.empty?
opts.separator "Plugin options:"
extra = self.method_scores.grep(/parse_options/) - %w(parse_options)
extra.sort.each do |msg|
self.send msg, opts, option
end
end.parse! Array(args)
option
end
##
# The known plugins for Flog. See Flog.load_plugins.
def self.plugins
@plugins ||= {}
end
##
# Flog the given files. Deals with "-", syntax errors, and
# traversing subdirectories intelligently. Use PathExpander to
# process dirs into files.
def flog(*files)
files << "-" if files.empty?
@flog.flog(*files)
end
##
# Creates a new Flog instance with +options+.
def initialize options = {}
@flog = Flog.new options
end
##
# Output the report up to a given max or report everything, if nil.
def output_details io, max = nil
io.puts
each_by_score max do |class_method, score, call_list|
self.print_score io, class_method, score
if option[:details] then
call_list.sort_by { |k,v| -v }.each do |call, count|
io.puts " %6.1f: %s" % [count, call]
end
io.puts
end
end
end
##
# Output the report, grouped by class/module, up to a given max or
# report everything, if nil.
def output_details_grouped io, threshold = nil
calculate
scores.sort_by { |_, n| -n }.each do |klass, total|
io.puts
io.puts "%8.1f: %s" % [total, "#{klass} total"]
method_scores[klass].each do |name, score|
self.print_score io, name, score
end
end
end
##
# Print out one formatted score.
def print_score io, name, score
location = method_locations[name]
if location then
sep = " "
sep = "%-11s" % "\n" if option[:extended]
io.puts "%8.1f: %-32s%s%s" % [score, name, sep, location]
else
io.puts "%8.1f: %s" % [score, name]
end
end
##
# Report results to #io, STDOUT by default.
def report(io = $stdout)
io.puts "%8.1f: %s" % [total_score, "flog total"]
io.puts "%8.1f: %s" % [average, "flog/method average"]
return if option[:score]
if option[:group] then
output_details_grouped io, threshold
else
output_details io, threshold
end
ensure
self.reset
end
end