metric_fu 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,19 +5,25 @@ describe Flay do
5
5
  MetricFu::Flay.stub!(:verify_dependencies!).and_return(true)
6
6
  end
7
7
  describe "emit method" do
8
- before :each do
8
+ it "should look at the dirs" do
9
9
  MetricFu::Configuration.run {|config| config.flay = { :dirs_to_flay => ['app', 'lib'] } }
10
10
  File.stub!(:directory?).and_return(true)
11
- @flay = MetricFu::Flay.new('base_dir')
12
-
13
- end
11
+ @flay = MetricFu::Flay.new('base_dir')
14
12
 
15
- it "should look at the dirs" do
16
13
  Dir.should_receive(:[]).with(File.join("app", "**/*.rb")).and_return("path/to/app")
17
14
  Dir.should_receive(:[]).with(File.join("lib", "**/*.rb")).and_return("path/to/lib")
18
15
  @flay.should_receive(:`).with("flay path/to/app path/to/lib")
19
16
  output = @flay.emit
20
17
  end
18
+
19
+ it "should limit flay scores by the minimum_score" do
20
+ MetricFu::Configuration.run {|config| config.flay = { :dirs_to_flay => [], :minimum_score => 99 } }
21
+ File.stub!(:directory?).and_return(true)
22
+ @flay = MetricFu::Flay.new('base_dir')
23
+
24
+ @flay.should_receive(:`).with("flay --mass 99 ")
25
+ output = @flay.emit
26
+ end
21
27
  end
22
28
 
23
29
  describe "analyze method" do
@@ -61,6 +61,8 @@ end
61
61
 
62
62
  describe Reek do
63
63
  before :each do
64
+ MetricFu::Reek.stub!(:verify_dependencies!).and_return(true)
65
+ MetricFu::Configuration.run {}
64
66
  @reek = MetricFu::Reek.new
65
67
  @lines11 = <<-HERE
66
68
  "app/controllers/activity_reports_controller.rb" -- 4 warnings:
@@ -110,9 +112,6 @@ app/controllers/newline_controller.rb -- 1 warnings:
110
112
  end
111
113
 
112
114
  context 'with Reek 1.2 output format' do
113
- before :each do
114
- @reek = MetricFu::Reek.new
115
- end
116
115
  it 'detects 1.2 format output' do
117
116
  @reek.instance_variable_set(:@output, @lines12)
118
117
  @reek.should be_reek_12
@@ -9,7 +9,8 @@ describe "Bluff graphers responding to #graph!" do
9
9
  end
10
10
  graphs.each do |key, val|
11
11
  val.graph!
12
- lambda{ File.open(File.join(MetricFu.output_directory, "#{key.to_s.downcase}.js")) }.should_not raise_error
12
+ output_dir = File.expand_path(File.join(MetricFu.output_directory))
13
+ lambda{ File.open(File.join(output_dir, "#{key.to_s.downcase}.js")) }.should_not raise_error
13
14
  end
14
15
  end
15
16
  end
@@ -1,13 +1,108 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+ describe MetricFu::GchartGrapher do
3
+ describe "determine_y_axis_scale" do
4
+ it "should set defaults when empty array" do
5
+ grapher = Object.new.extend(MetricFu::GchartGrapher)
6
+ grapher.determine_y_axis_scale([])
7
+ grapher.instance_variable_get(:@max_value).should == 10
8
+ grapher.instance_variable_get(:@yaxis).should == [0, 2, 4, 6, 8, 10]
9
+ end
10
+
11
+ it "should set max value of the graph above largest value" do
12
+ grapher = Object.new.extend(MetricFu::GchartGrapher)
13
+ grapher.determine_y_axis_scale([19])
14
+ grapher.instance_variable_get(:@max_value).should == 20
15
+
16
+ grapher.determine_y_axis_scale([20])
17
+ grapher.instance_variable_get(:@max_value).should == 25
18
+ end
19
+ end
20
+ end
2
21
 
3
- describe "Gchart graphers responding to #graph!" do
4
- MetricFu::AVAILABLE_GRAPHS.each do |metric|
5
- it "should write chart file for #{metric}" do
6
- MetricFu.configuration
7
- grapher = MetricFu.const_get("#{metric.to_s.capitalize}GchartGrapher").new
8
- grapher.flog_average, grapher.top_five_percent_average = [7.0],[20.0] if metric == :flog #googlecharts gem has problems with [[],[]] as data
22
+ describe "Gchart graphers" do
23
+ before :each do
24
+ MetricFu.configuration
25
+ end
26
+
27
+ describe "FlayGchartGrapher graph! method" do
28
+ it "should set static values for graph" do
29
+ grapher = FlayGchartGrapher.new
30
+ expected = {
31
+ :size => MetricFu::GchartGrapher::GCHART_GRAPH_SIZE,
32
+ :title => URI.escape("Flay: duplication"),
33
+ :axis_with_labels => 'x,y',
34
+ :format => 'file',
35
+ :filename => File.join(MetricFu.output_directory, 'flay.png'),
36
+ }
37
+ Gchart.should_receive(:line).with(hash_including(expected))
38
+ grapher.graph!
39
+ end
40
+ end
41
+
42
+ describe "FlogGchartGrapher graph! method" do
43
+ it "should set static values for graph" do
44
+ grapher = FlogGchartGrapher.new
45
+ expected = {
46
+ :size => MetricFu::GchartGrapher::GCHART_GRAPH_SIZE,
47
+ :title => URI.escape("Flog: code complexity"),
48
+ :stacked => false,
49
+ :bar_colors => MetricFu::GchartGrapher::COLORS[0..1],
50
+ :legend => ['average', 'top 5%25 average'],
51
+ :axis_with_labels => 'x,y',
52
+ :format => 'file',
53
+ :filename => File.join(MetricFu.output_directory, 'flog.png'),
54
+ }
55
+ Gchart.should_receive(:line).with(hash_including(expected))
56
+ grapher.graph!
57
+ end
58
+ end
59
+
60
+ describe "RcovGchartGrapher graph! method" do
61
+ it "should set static values for graph" do
62
+ grapher = RcovGchartGrapher.new
63
+ expected = {
64
+ :size => MetricFu::GchartGrapher::GCHART_GRAPH_SIZE,
65
+ :title => URI.escape("Rcov: code coverage"),
66
+ :max_value => 101,
67
+ :axis_with_labels => 'x,y',
68
+ :axis_labels => [grapher.labels.values, [0,20,40,60,80,100]],
69
+ :format => 'file',
70
+ :filename => File.join(MetricFu.output_directory, 'rcov.png'),
71
+ }
72
+ Gchart.should_receive(:line).with(hash_including(expected))
73
+ grapher.graph!
74
+ end
75
+ end
76
+
77
+ describe "ReekGchartGrapher graph! method" do
78
+ it "should set static values for graph" do
79
+ grapher = ReekGchartGrapher.new
80
+ expected = {
81
+ :size => MetricFu::GchartGrapher::GCHART_GRAPH_SIZE,
82
+ :title => URI.escape("Reek: code smells"),
83
+ :stacked => false,
84
+ :bar_colors => MetricFu::GchartGrapher::COLORS,
85
+ :axis_with_labels => 'x,y',
86
+ :format => 'file',
87
+ :filename => File.join(MetricFu.output_directory, 'reek.png'),
88
+ }
89
+ Gchart.should_receive(:line).with(hash_including(expected))
90
+ grapher.graph!
91
+ end
92
+ end
93
+
94
+ describe "RoodiGchartGrapher graph! method" do
95
+ it "should set static values for graph" do
96
+ grapher = RoodiGchartGrapher.new
97
+ expected = {
98
+ :size => MetricFu::GchartGrapher::GCHART_GRAPH_SIZE,
99
+ :title => URI.escape("Roodi: potential design problems"),
100
+ :axis_with_labels => 'x,y',
101
+ :format => 'file',
102
+ :filename => File.join(MetricFu.output_directory, 'roodi.png'),
103
+ }
104
+ Gchart.should_receive(:line).with(hash_including(expected))
9
105
  grapher.graph!
10
- lambda{ File.open(File.join(MetricFu.output_directory, "#{metric.to_s.downcase}.png")) }.should_not raise_error
11
106
  end
12
107
  end
13
- end
108
+ end
@@ -1,8 +1,6 @@
1
1
  --colour
2
- --format
3
- specdoc
4
2
  --reverse
5
- --timeout
6
- 20
3
+ --format
4
+ profile
7
5
  --loadby
8
6
  mtime
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metric_fu
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Scruggs
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-01-09 00:00:00 -06:00
19
+ date: 2010-01-26 00:00:00 -06:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -59,6 +59,56 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: 0.2.3
61
61
  version:
62
+ - !ruby/object:Gem::Dependency
63
+ name: churn
64
+ type: :runtime
65
+ version_requirement:
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 0.0.7
71
+ version:
72
+ - !ruby/object:Gem::Dependency
73
+ name: Saikuro
74
+ type: :runtime
75
+ version_requirement:
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 1.1.0
81
+ version:
82
+ - !ruby/object:Gem::Dependency
83
+ name: activesupport
84
+ type: :runtime
85
+ version_requirement:
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 2.2.3
91
+ version:
92
+ - !ruby/object:Gem::Dependency
93
+ name: rspec
94
+ type: :development
95
+ version_requirement:
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 1.2.0
101
+ version:
102
+ - !ruby/object:Gem::Dependency
103
+ name: test-construct
104
+ type: :development
105
+ version_requirement:
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 1.2.0
111
+ version:
62
112
  description: Code metrics from Flog, Flay, RCov, Saikuro, Churn, Reek, Roodi and Rails' stats task
63
113
  email: jake.scruggs@gmail.com
64
114
  executables: []
@@ -128,8 +178,6 @@ files:
128
178
  - lib/templates/standard/standard_template.rb
129
179
  - lib/templates/standard/stats.html.erb
130
180
  - tasks/metric_fu.rake
131
- - vendor/_fonts/monaco.ttf
132
- - vendor/saikuro/saikuro.rb
133
181
  - Manifest.txt
134
182
  has_rdoc: true
135
183
  homepage: http://metric-fu.rubyforge.org/
Binary file
@@ -1,1219 +0,0 @@
1
- # $Id: saikuro 33 2006-12-07 16:09:55Z zev $
2
- # Version 0.2
3
- # == Usage
4
- #
5
- # ruby saikuro.rb [ -h ] [-o output_directory] [-f type] [ -c, -t ]
6
- # [ -y, -w, -e, -k, -s, -d - number ] ( -p file | -i directory )
7
- #
8
- # == Help
9
- #
10
- # -o, --output_directory (directory) : A directory to ouput the results in.
11
- # The current directory is used if this option is not passed.
12
- #
13
- # -h, --help : This help message.
14
- #
15
- # -f, --formater (html | text) : The format to output the results in.
16
- # The default is html
17
- #
18
- # -c, --cyclo : Compute the cyclomatic complexity of the input.
19
- #
20
- # -t, --token : Count the number of tokens per line of the input.
21
- #
22
- # -y, --filter_cyclo (number) : Filter the output to only include methods
23
- # whose cyclomatic complexity are greater than the passed number.
24
- #
25
- # -w, --warn_cyclo (number) : Highlight with a warning methods whose
26
- # cyclomatic complexity are greather than or equal to the passed number.
27
- #
28
- #
29
- # -e, --error_cyclo (number) : Highligh with an error methods whose
30
- # cyclomatic complexity are greather than or equal to the passed number.
31
- #
32
- #
33
- # -k, --filter_token (number) : Filter the output to only include lines
34
- # whose token count are greater than the passed number.
35
- #
36
- #
37
- # -s, --warn_token (number) : Highlight with a warning lines whose
38
- # token count are greater than or equal to the passed number.
39
- #
40
- #
41
- # -d, --error_token (number) : Highlight with an error lines whose
42
- # token count are greater than or equal to the passed number.
43
- #
44
- #
45
- # -p, --parse_file (file) : A file to use as input.
46
- #
47
- # -i, --input_directory (directory) : All ruby files found recursively
48
- # inside the directory are passed as input.
49
-
50
- # == License
51
- # Saikruo uses the BSD license.
52
- #
53
- # Copyright (c) 2005, Ubiquitous Business Technology (http://ubit.com)
54
- # All rights reserved.
55
- #
56
- # Redistribution and use in source and binary forms, with or without
57
- # modification, are permitted provided that the following conditions are
58
- # met:
59
- #
60
- #
61
- # * Redistributions of source code must retain the above copyright
62
- # notice, this list of conditions and the following disclaimer.
63
- #
64
- # * Redistributions in binary form must reproduce the above
65
- # copyright notice, this list of conditions and the following
66
- # disclaimer in the documentation and/or other materials provided
67
- # with the distribution.
68
- #
69
- # * Neither the name of Ubiquitous Business Technology nor the names
70
- # of its contributors may be used to endorse or promote products
71
- # derived from this software without specific prior written
72
- # permission.
73
- #
74
- #
75
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
76
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
77
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
78
- # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
79
- # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
80
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
81
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
82
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
83
- # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
84
- # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
85
- # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
86
- #
87
- # == Author
88
- # Zev Blut (zb@ubit.com)
89
-
90
- require 'irb/ruby-lex'
91
- require 'yaml'
92
-
93
- # States to watch for
94
- # once in def get the token after space, because it may also
95
- # be something like + or << for operator overloading.
96
-
97
- # Counts the number of tokens in each line.
98
- class TokenCounter
99
- include RubyToken
100
-
101
- attr_reader :current_file
102
-
103
- def initialize
104
- @files = Hash.new
105
- @tokens_per_line = Hash.new(0)
106
- @current_file = ""
107
- end
108
-
109
- # Mark file to associate with the token count.
110
- def set_current_file(file)
111
- @current_file = file
112
- @tokens_per_line = Hash.new(0)
113
- @files[@current_file] = @tokens_per_line
114
- end
115
-
116
- # Iterate through all tracked files, passing the
117
- # the provided formater the token counts.
118
- def list_tokens_per_line(formater)
119
- formater.start_count(@files.size)
120
- @files.each do |fname, tok_per_line|
121
- formater.start_file(fname)
122
- tok_per_line.sort.each do |line,num|
123
- formater.line_token_count(line,num)
124
- end
125
- formater.end_file
126
- end
127
- end
128
-
129
- # Count the token for the passed line.
130
- def count_token(line_no,token)
131
- case token
132
- when TkSPACE, TkNL, TkRD_COMMENT
133
- # Do not count these as tokens
134
- when TkCOMMENT
135
- # Ignore this only for comments in a statement?
136
- # Ignore TkCOLON,TkCOLON2 and operators? like "." etc..
137
- when TkRBRACK, TkRPAREN, TkRBRACE
138
- # Ignore the closing of an array/index/hash/paren
139
- # The opening is counted, but no more.
140
- # Thus [], () {} is counted as 1 token not 2.
141
- else
142
- # may want to filter out comments...
143
- @tokens_per_line[line_no] += 1
144
- end
145
- end
146
-
147
- end
148
-
149
- # Main class and structure used to compute the
150
- # cyclomatic complexity of Ruby programs.
151
- class ParseState
152
- include RubyToken
153
- attr_accessor :name, :children, :complexity, :parent, :lines
154
-
155
- @@top_state = nil
156
- def ParseState.make_top_state()
157
- @@top_state = ParseState.new(nil)
158
- @@top_state.name = "__top__"
159
- @@top_state
160
- end
161
-
162
- @@token_counter = TokenCounter.new
163
- def ParseState.set_token_counter(counter)
164
- @@token_counter = counter
165
- end
166
- def ParseState.get_token_counter
167
- @@token_counter
168
- end
169
-
170
- def initialize(lexer,parent=nil)
171
- @name = ""
172
- @children = Array.new
173
- @complexity = 0
174
- @parent = parent
175
- @lexer = lexer
176
- @run = true
177
- # To catch one line def statements, We always have one line.
178
- @lines = 0
179
- @last_token_line_and_char = Array.new
180
- end
181
-
182
- def top_state?
183
- self == @@top_state
184
- end
185
-
186
- def lexer=(lexer)
187
- @run = true
188
- @lexer = lexer
189
- end
190
-
191
- def make_state(type,parent = nil)
192
- cstate = type.new(@lexer,self)
193
- parent.children<< cstate
194
- cstate
195
- end
196
-
197
- def calc_complexity
198
- complexity = @complexity
199
- children.each do |child|
200
- complexity += child.calc_complexity
201
- end
202
- complexity
203
- end
204
-
205
- def calc_lines
206
- lines = @lines
207
- children.each do |child|
208
- lines += child.calc_lines
209
- end
210
- lines
211
- end
212
-
213
- def compute_state(formater)
214
- if top_state?
215
- compute_state_for_global(formater)
216
- end
217
-
218
- @children.each do |s|
219
- s.compute_state(formater)
220
- end
221
- end
222
-
223
- def compute_state_for_global(formater)
224
- global_def, @children = @children.partition do |s|
225
- !s.kind_of?(ParseClass)
226
- end
227
- return if global_def.empty?
228
- gx = global_def.inject(0) { |c,s| s.calc_complexity }
229
- gl = global_def.inject(0) { |c,s| s.calc_lines }
230
- formater.start_class_compute_state("Global", "", gx, gl)
231
- global_def.each do |s|
232
- s.compute_state(formater)
233
- end
234
- formater.end_class_compute_state("")
235
- end
236
-
237
- # Count the tokens parsed if true else ignore them.
238
- def count_tokens?
239
- true
240
- end
241
-
242
- def parse
243
- while @run do
244
- tok = @lexer.token
245
- @run = false if tok.nil?
246
- if lexer_loop?(tok)
247
- STDERR.puts "Lexer loop at line : #{@lexer.line_no} char #{@lexer.char_no}."
248
- @run = false
249
- end
250
- @last_token_line_and_char<< [@lexer.line_no.to_i, @lexer.char_no.to_i, tok]
251
- if $VERBOSE
252
- puts "DEBUG: #{@lexer.line_no} #{tok.class}:#{tok.name if tok.respond_to?(:name)}"
253
- end
254
- @@token_counter.count_token(@lexer.line_no, tok) if count_tokens?
255
- parse_token(tok)
256
- end
257
- end
258
-
259
- # Ruby-Lexer can go into a loop if the file does not end with a newline.
260
- def lexer_loop?(token)
261
- return false if @last_token_line_and_char.empty?
262
- loop_flag = false
263
- last = @last_token_line_and_char.last
264
- line = last[0]
265
- char = last[1]
266
- ltok = last[2]
267
-
268
- if ( (line == @lexer.line_no.to_i) &&
269
- (char == @lexer.char_no.to_i) &&
270
- (ltok.class == token.class) )
271
- # We are potentially in a loop
272
- if @last_token_line_and_char.size >= 3
273
- loop_flag = true
274
- end
275
- else
276
- # Not in a loop so clear stack
277
- @last_token_line_and_char = Array.new
278
- end
279
-
280
- loop_flag
281
- end
282
-
283
- def do_begin_token(token)
284
- make_state(EndableParseState, self)
285
- end
286
-
287
- def do_class_token(token)
288
- make_state(ParseClass,self)
289
- end
290
-
291
- def do_module_token(token)
292
- make_state(ParseModule,self)
293
- end
294
-
295
- def do_def_token(token)
296
- make_state(ParseDef,self)
297
- end
298
-
299
- def do_constant_token(token)
300
- nil
301
- end
302
-
303
- def do_identifier_token(token)
304
- if (token.name == "__END__" && token.char_no.to_i == 0)
305
- # The Ruby code has stopped and the rest is data so cease parsing.
306
- @run = false
307
- end
308
- nil
309
- end
310
-
311
- def do_right_brace_token(token)
312
- nil
313
- end
314
-
315
- def do_end_token(token)
316
- end_debug
317
- nil
318
- end
319
-
320
- def do_block_token(token)
321
- make_state(ParseBlock,self)
322
- end
323
-
324
- def do_conditional_token(token)
325
- make_state(ParseCond,self)
326
- end
327
-
328
- def do_conditional_do_control_token(token)
329
- make_state(ParseDoCond,self)
330
- end
331
-
332
- def do_case_token(token)
333
- make_state(EndableParseState, self)
334
- end
335
-
336
- def do_one_line_conditional_token(token)
337
- # This is an if with no end
338
- @complexity += 1
339
- #STDOUT.puts "got IF_MOD: #{self.to_yaml}" if $VERBOSE
340
- #if state.type != "class" && state.type != "def" && state.type != "cond"
341
- #STDOUT.puts "Changing IF_MOD Parent" if $VERBOSE
342
- #state = state.parent
343
- #@run = false
344
- nil
345
- end
346
-
347
- def do_else_token(token)
348
- STDOUT.puts "Ignored/Unknown Token:#{token.class}" if $VERBOSE
349
- nil
350
- end
351
-
352
- def do_comment_token(token)
353
- make_state(ParseComment, self)
354
- end
355
-
356
- def do_symbol_token(token)
357
- make_state(ParseSymbol, self)
358
- end
359
-
360
- def parse_token(token)
361
- state = nil
362
- case token
363
- when TkCLASS
364
- state = do_class_token(token)
365
- when TkMODULE
366
- state = do_module_token(token)
367
- when TkDEF
368
- state = do_def_token(token)
369
- when TkCONSTANT
370
- # Nothing to do with a constant at top level?
371
- state = do_constant_token(token)
372
- when TkIDENTIFIER,TkFID
373
- # Nothing to do at top level?
374
- state = do_identifier_token(token)
375
- when TkRBRACE
376
- # Nothing to do at top level
377
- state = do_right_brace_token(token)
378
- when TkEND
379
- state = do_end_token(token)
380
- # At top level this might be an error...
381
- when TkDO,TkfLBRACE
382
- state = do_block_token(token)
383
- when TkIF,TkUNLESS
384
- state = do_conditional_token(token)
385
- when TkWHILE,TkUNTIL,TkFOR
386
- state = do_conditional_do_control_token(token)
387
- when TkELSIF #,TkELSE
388
- @complexity += 1
389
- when TkELSE
390
- # Else does not increase complexity
391
- when TkCASE
392
- state = do_case_token(token)
393
- when TkWHEN
394
- @complexity += 1
395
- when TkBEGIN
396
- state = do_begin_token(token)
397
- when TkRESCUE
398
- # Maybe this should add complexity and not begin
399
- @complexity += 1
400
- when TkIF_MOD, TkUNLESS_MOD, TkUNTIL_MOD, TkWHILE_MOD, TkQUESTION
401
- state = do_one_line_conditional_token(token)
402
- when TkNL
403
- #
404
- @lines += 1
405
- when TkRETURN
406
- # Early returns do not increase complexity as the condition that
407
- # calls the return is the one that increases it.
408
- when TkCOMMENT
409
- state = do_comment_token(token)
410
- when TkSYMBEG
411
- state = do_symbol_token(token)
412
- when TkError
413
- STDOUT.puts "Lexer received an error for line #{@lexer.line_no} char #{@lexer.char_no}"
414
- else
415
- state = do_else_token(token)
416
- end
417
- state.parse if state
418
- end
419
-
420
- def end_debug
421
- STDOUT.puts "got an end: #{@name} in #{self.class.name}" if $VERBOSE
422
- if @parent.nil?
423
- STDOUT.puts "DEBUG: Line #{@lexer.line_no}"
424
- STDOUT.puts "DEBUG: #{@name}; #{self.class}"
425
- # to_yaml can cause an infinite loop?
426
- #STDOUT.puts "TOP: #{@@top_state.to_yaml}"
427
- #STDOUT.puts "TOP: #{@@top_state.inspect}"
428
-
429
- # This may not be an error?
430
- #exit 1
431
- end
432
- end
433
-
434
- end
435
-
436
- # Read and consume tokens in comments until a new line.
437
- class ParseComment < ParseState
438
-
439
- # While in a comment state do not count the tokens.
440
- def count_tokens?
441
- false
442
- end
443
-
444
- def parse_token(token)
445
- if token.is_a?(TkNL)
446
- @lines += 1
447
- @run = false
448
- end
449
- end
450
- end
451
-
452
- class ParseSymbol < ParseState
453
- def initialize(lexer, parent = nil)
454
- super
455
- STDOUT.puts "STARTING SYMBOL" if $VERBOSE
456
- end
457
-
458
- def parse_token(token)
459
- STDOUT.puts "Symbol's token is #{token.class}" if $VERBOSE
460
- # Consume the next token and stop
461
- @run = false
462
- nil
463
- end
464
- end
465
-
466
- class EndableParseState < ParseState
467
- def initialize(lexer,parent=nil)
468
- super(lexer,parent)
469
- STDOUT.puts "Starting #{self.class}" if $VERBOSE
470
- end
471
-
472
- def do_end_token(token)
473
- end_debug
474
- @run = false
475
- nil
476
- end
477
- end
478
-
479
- class ParseClass < EndableParseState
480
- def initialize(lexer,parent=nil)
481
- super(lexer,parent)
482
- @type_name = "Class"
483
- end
484
-
485
- def do_constant_token(token)
486
- @name = token.name if @name.empty?
487
- nil
488
- end
489
-
490
- def compute_state(formater)
491
- # Seperate the Module and Class Children out
492
- cnm_children, @children = @children.partition do |child|
493
- child.kind_of?(ParseClass)
494
- end
495
-
496
- formater.start_class_compute_state(@type_name,@name,self.calc_complexity,self.calc_lines)
497
- super(formater)
498
- formater.end_class_compute_state(@name)
499
-
500
- cnm_children.each do |child|
501
- child.name = @name + "::" + child.name
502
- child.compute_state(formater)
503
- end
504
- end
505
- end
506
-
507
- class ParseModule < ParseClass
508
- def initialize(lexer,parent=nil)
509
- super(lexer,parent)
510
- @type_name = "Module"
511
- end
512
- end
513
-
514
- class ParseDef < EndableParseState
515
-
516
- def initialize(lexer,parent=nil)
517
- super(lexer,parent)
518
- @complexity = 1
519
- @looking_for_name = true
520
- @first_space = true
521
- end
522
-
523
- # This way I don't need to list all possible overload
524
- # tokens.
525
- def create_def_name(token)
526
- case token
527
- when TkSPACE
528
- # mark first space so we can stop at next space
529
- if @first_space
530
- @first_space = false
531
- else
532
- @looking_for_name = false
533
- end
534
- when TkNL,TkLPAREN,TkfLPAREN,TkSEMICOLON
535
- # we can also stop at a new line or left parenthesis
536
- @looking_for_name = false
537
- when TkDOT
538
- @name<< "."
539
- when TkCOLON2
540
- @name<< "::"
541
- when TkASSIGN
542
- @name<< "="
543
- when TkfLBRACK
544
- @name<< "["
545
- when TkRBRACK
546
- @name<< "]"
547
- else
548
- begin
549
- @name<< token.name.to_s
550
- rescue Exception => err
551
- #what is this?
552
- STDOUT.puts @@token_counter.current_file
553
- STDOUT.puts @name
554
- STDOUT.puts token.inspect
555
- STDOUT.puts err.message
556
- exit 1
557
- end
558
- end
559
- end
560
-
561
- def parse_token(token)
562
- if @looking_for_name
563
- create_def_name(token)
564
- end
565
- super(token)
566
- end
567
-
568
- def compute_state(formater)
569
- formater.def_compute_state(@name, self.calc_complexity, self.calc_lines)
570
- super(formater)
571
- end
572
- end
573
-
574
- class ParseCond < EndableParseState
575
- def initialize(lexer,parent=nil)
576
- super(lexer,parent)
577
- @complexity = 1
578
- end
579
- end
580
-
581
- class ParseDoCond < ParseCond
582
- def initialize(lexer,parent=nil)
583
- super(lexer,parent)
584
- @looking_for_new_line = true
585
- end
586
-
587
- # Need to consume the do that can appear at the
588
- # end of these control structures.
589
- def parse_token(token)
590
- if @looking_for_new_line
591
- if token.is_a?(TkDO)
592
- nil
593
- else
594
- if token.is_a?(TkNL)
595
- @looking_for_new_line = false
596
- end
597
- super(token)
598
- end
599
- else
600
- super(token)
601
- end
602
- end
603
-
604
- end
605
-
606
- class ParseBlock < EndableParseState
607
-
608
- def initialize(lexer,parent=nil)
609
- super(lexer,parent)
610
- @complexity = 1
611
- @lbraces = Array.new
612
- end
613
-
614
- # Because the token for a block and hash right brace is the same,
615
- # we need to track the hash left braces to determine when an end is
616
- # encountered.
617
- def parse_token(token)
618
- if token.is_a?(TkLBRACE)
619
- @lbraces.push(true)
620
- elsif token.is_a?(TkRBRACE)
621
- if @lbraces.empty?
622
- do_right_brace_token(token)
623
- #do_end_token(token)
624
- else
625
- @lbraces.pop
626
- end
627
- else
628
- super(token)
629
- end
630
- end
631
-
632
- def do_right_brace_token(token)
633
- # we are done ? what about a hash in a block :-/
634
- @run = false
635
- nil
636
- end
637
-
638
- end
639
-
640
- # ------------ END Analyzer logic ------------------------------------
641
-
642
- class Filter
643
- attr_accessor :limit, :error, :warn
644
-
645
- def initialize(limit = -1, error = 11, warn = 8)
646
- @limit = limit
647
- @error = error
648
- @warn = warn
649
- end
650
-
651
- def ignore?(count)
652
- count < @limit
653
- end
654
-
655
- def warn?(count)
656
- count >= @warn
657
- end
658
-
659
- def error?(count)
660
- count >= @error
661
- end
662
-
663
- end
664
-
665
-
666
- class BaseFormater
667
- attr_accessor :warnings, :errors, :current
668
-
669
- def initialize(out, filter = nil)
670
- @out = out
671
- @filter = filter
672
- reset_data
673
- end
674
-
675
- def warn_error?(num, marker)
676
- klass = ""
677
-
678
- if @filter.error?(num)
679
- klass = ' class="error"'
680
- @errors<< [@current, marker, num]
681
- elsif @filter.warn?(num)
682
- klass = ' class="warning"'
683
- @warnings<< [@current, marker, num]
684
- end
685
-
686
- klass
687
- end
688
-
689
- def reset_data
690
- @warnings = Array.new
691
- @errors = Array.new
692
- @current = ""
693
- end
694
-
695
- end
696
-
697
- class TokenCounterFormater < BaseFormater
698
-
699
- def start(new_out=nil)
700
- reset_data
701
- @out = new_out if new_out
702
- @out.puts "Token Count"
703
- end
704
-
705
- def start_count(number_of_files)
706
- @out.puts "Counting tokens for #{number_of_files} files."
707
- end
708
-
709
- def start_file(file_name)
710
- @current = file_name
711
- @out.puts "File:#{file_name}"
712
- end
713
-
714
- def line_token_count(line_number,number_of_tokens)
715
- return if @filter.ignore?(number_of_tokens)
716
- warn_error?(number_of_tokens, line_number)
717
- @out.puts "Line:#{line_number} ; Tokens : #{number_of_tokens}"
718
- end
719
-
720
- def end_file
721
- @out.puts ""
722
- end
723
-
724
- def end_count
725
- end
726
-
727
- def end
728
- end
729
-
730
- end
731
-
732
- module HTMLStyleSheet
733
- def HTMLStyleSheet.style_sheet
734
- out = StringIO.new
735
-
736
- out.puts "<style>"
737
- out.puts 'body {'
738
- out.puts ' margin: 20px;'
739
- out.puts ' padding: 0;'
740
- out.puts ' font-size: 12px;'
741
- out.puts ' font-family: bitstream vera sans, verdana, arial, sans serif;'
742
- out.puts ' background-color: #efefef;'
743
- out.puts '}'
744
- out.puts ''
745
- out.puts 'table { '
746
- out.puts ' border-collapse: collapse;'
747
- out.puts ' /*border-spacing: 0;*/'
748
- out.puts ' border: 1px solid #666;'
749
- out.puts ' background-color: #fff;'
750
- out.puts ' margin-bottom: 20px;'
751
- out.puts '}'
752
- out.puts ''
753
- out.puts 'table, th, th+th, td, td+td {'
754
- out.puts ' border: 1px solid #ccc;'
755
- out.puts '}'
756
- out.puts ''
757
- out.puts 'table th {'
758
- out.puts ' font-size: 12px;'
759
- out.puts ' color: #fc0;'
760
- out.puts ' padding: 4px 0;'
761
- out.puts ' background-color: #336;'
762
- out.puts '}'
763
- out.puts ''
764
- out.puts 'th, td {'
765
- out.puts ' padding: 4px 10px;'
766
- out.puts '}'
767
- out.puts ''
768
- out.puts 'td { '
769
- out.puts ' font-size: 13px;'
770
- out.puts '}'
771
- out.puts ''
772
- out.puts '.class_name {'
773
- out.puts ' font-size: 17px;'
774
- out.puts ' margin: 20px 0 0;'
775
- out.puts '}'
776
- out.puts ''
777
- out.puts '.class_complexity {'
778
- out.puts 'margin: 0 auto;'
779
- out.puts '}'
780
- out.puts ''
781
- out.puts '.class_complexity>.class_complexity {'
782
- out.puts ' margin: 0;'
783
- out.puts '}'
784
- out.puts ''
785
- out.puts '.class_total_complexity, .class_total_lines, .start_token_count, .file_count {'
786
- out.puts ' font-size: 13px;'
787
- out.puts ' font-weight: bold;'
788
- out.puts '}'
789
- out.puts ''
790
- out.puts '.class_total_complexity, .class_total_lines {'
791
- out.puts ' color: #c00;'
792
- out.puts '}'
793
- out.puts ''
794
- out.puts '.start_token_count, .file_count {'
795
- out.puts ' color: #333;'
796
- out.puts '}'
797
- out.puts ''
798
- out.puts '.warning {'
799
- out.puts ' background-color: yellow;'
800
- out.puts '}'
801
- out.puts ''
802
- out.puts '.error {'
803
- out.puts ' background-color: #f00;'
804
- out.puts '}'
805
- out.puts "</style>"
806
-
807
- out.string
808
- end
809
-
810
- def style_sheet
811
- HTMLStyleSheet.style_sheet
812
- end
813
- end
814
-
815
-
816
- class HTMLTokenCounterFormater < TokenCounterFormater
817
- include HTMLStyleSheet
818
-
819
- def start(new_out=nil)
820
- reset_data
821
- @out = new_out if new_out
822
- @out.puts "<html>"
823
- @out.puts style_sheet
824
- @out.puts "<body>"
825
- end
826
-
827
- def start_count(number_of_files)
828
- @out.puts "<div class=\"start_token_count\">"
829
- @out.puts "Number of files: #{number_of_files}"
830
- @out.puts "</div>"
831
- end
832
-
833
- def start_file(file_name)
834
- @current = file_name
835
- @out.puts "<div class=\"file_count\">"
836
- @out.puts "<p class=\"file_name\">"
837
- @out.puts "File: #{file_name}"
838
- @out.puts "</p>"
839
- @out.puts "<table width=\"100%\" border=\"1\">"
840
- @out.puts "<tr><th>Line</th><th>Tokens</th></tr>"
841
- end
842
-
843
- def line_token_count(line_number,number_of_tokens)
844
- return if @filter.ignore?(number_of_tokens)
845
- klass = warn_error?(number_of_tokens, line_number)
846
- @out.puts "<tr><td>#{line_number}</td><td#{klass}>#{number_of_tokens}</td></tr>"
847
- end
848
-
849
- def end_file
850
- @out.puts "</table>"
851
- end
852
-
853
- def end_count
854
- end
855
-
856
- def end
857
- @out.puts "</body>"
858
- @out.puts "</html>"
859
- end
860
- end
861
-
862
- class ParseStateFormater < BaseFormater
863
-
864
- def start(new_out=nil)
865
- reset_data
866
- @out = new_out if new_out
867
- end
868
-
869
- def end
870
- end
871
-
872
- def start_class_compute_state(type_name,name,complexity,lines)
873
- @current = name
874
- @out.puts "-- START #{name} --"
875
- @out.puts "Type:#{type_name} Name:#{name} Complexity:#{complexity} Lines:#{lines}"
876
- end
877
-
878
- def end_class_compute_state(name)
879
- @out.puts "-- END #{name} --"
880
- end
881
-
882
- def def_compute_state(name,complexity,lines)
883
- return if @filter.ignore?(complexity)
884
- warn_error?(complexity, name)
885
- @out.puts "Type:Def Name:#{name} Complexity:#{complexity} Lines:#{lines}"
886
- end
887
-
888
- end
889
-
890
-
891
-
892
- class StateHTMLComplexityFormater < ParseStateFormater
893
- include HTMLStyleSheet
894
-
895
- def start(new_out=nil)
896
- reset_data
897
- @out = new_out if new_out
898
- @out.puts "<html><head><title>Cyclometric Complexity</title></head>"
899
- @out.puts style_sheet
900
- @out.puts "<body>"
901
- end
902
-
903
- def end
904
- @out.puts "</body>"
905
- @out.puts "</html>"
906
- end
907
-
908
- def start_class_compute_state(type_name,name,complexity,lines)
909
- @current = name
910
- @out.puts "<div class=\"class_complexity\">"
911
- @out.puts "<h2 class=\"class_name\">#{type_name} : #{name}</h2>"
912
- @out.puts "<div class=\"class_total_complexity\">Total Complexity: #{complexity}</div>"
913
- @out.puts "<div class=\"class_total_lines\">Total Lines: #{lines}</div>"
914
- @out.puts "<table width=\"100%\" border=\"1\">"
915
- @out.puts "<tr><th>Method</th><th>Complexity</th><th># Lines</th></tr>"
916
- end
917
-
918
- def end_class_compute_state(name)
919
- @out.puts "</table>"
920
- @out.puts "</div>"
921
- end
922
-
923
- def def_compute_state(name, complexity, lines)
924
- return if @filter.ignore?(complexity)
925
- klass = warn_error?(complexity, name)
926
- @out.puts "<tr><td>#{name}</td><td#{klass}>#{complexity}</td><td>#{lines}</td></tr>"
927
- end
928
-
929
- end
930
-
931
-
932
- module ResultIndexGenerator
933
- def summarize_errors_and_warnings(enw, header)
934
- return "" if enw.empty?
935
- f = StringIO.new
936
- erval = Hash.new { |h,k| h[k] = Array.new }
937
- wval = Hash.new { |h,k| h[k] = Array.new }
938
-
939
- enw.each do |fname, warnings, errors|
940
- errors.each do |c,m,v|
941
- erval[v] << [fname, c, m]
942
- end
943
- warnings.each do |c,m,v|
944
- wval[v] << [fname, c, m]
945
- end
946
- end
947
-
948
- f.puts "<h2 class=\"class_name\">Errors and Warnings</h2>"
949
- f.puts "<table width=\"100%\" border=\"1\">"
950
- f.puts header
951
-
952
- f.puts print_summary_table_rows(erval, "error")
953
- f.puts print_summary_table_rows(wval, "warning")
954
- f.puts "</table>"
955
-
956
- f.string
957
- end
958
-
959
- def print_summary_table_rows(ewvals, klass_type)
960
- f = StringIO.new
961
- ewvals.sort { |a,b| b <=> a}.each do |v, vals|
962
- vals.sort.each do |fname, c, m|
963
- f.puts "<tr><td><a href=\"./#{fname}\">#{c}</a></td><td>#{m}</td>"
964
- f.puts "<td class=\"#{klass_type}\">#{v}</td></tr>"
965
- end
966
- end
967
- f.string
968
- end
969
-
970
- def list_analyzed_files(files)
971
- f = StringIO.new
972
- f.puts "<h2 class=\"class_name\">Analyzed Files</h2>"
973
- f.puts "<ul>"
974
- files.each do |fname, warnings, errors|
975
- readname = fname.split("_")[0...-1].join("_")
976
- f.puts "<li>"
977
- f.puts "<p class=\"file_name\"><a href=\"./#{fname}\">#{readname}</a>"
978
- f.puts "</li>"
979
- end
980
- f.puts "</ul>"
981
- f.string
982
- end
983
-
984
- def write_index(files, filename, title, header)
985
- return if files.empty?
986
-
987
- File.open(filename,"w") do |f|
988
- f.puts "<html><head><title>#{title}</title></head>"
989
- f.puts "#{HTMLStyleSheet.style_sheet}\n<body>"
990
- f.puts "<h1>#{title}</h1>"
991
-
992
- enw = files.find_all { |fn,w,e| (!w.empty? || !e.empty?) }
993
-
994
- f.puts summarize_errors_and_warnings(enw, header)
995
-
996
- f.puts "<hr/>"
997
- f.puts list_analyzed_files(files)
998
- f.puts "</body></html>"
999
- end
1000
- end
1001
-
1002
- def write_cyclo_index(files, output_dir)
1003
- header = "<tr><th>Class</th><th>Method</th><th>Complexity</th></tr>"
1004
- write_index(files,
1005
- "#{output_dir}/index_cyclo.html",
1006
- "Index for cyclomatic complexity",
1007
- header)
1008
- end
1009
-
1010
- def write_token_index(files, output_dir)
1011
- header = "<tr><th>File</th><th>Line #</th><th>Tokens</th></tr>"
1012
- write_index(files,
1013
- "#{output_dir}/index_token.html",
1014
- "Index for tokens per line",
1015
- header)
1016
- end
1017
-
1018
- end
1019
-
1020
- module Saikuro
1021
- def Saikuro.analyze(files, state_formater, token_count_formater, output_dir)
1022
-
1023
- idx_states = Array.new
1024
- idx_tokens = Array.new
1025
-
1026
- # parse each file
1027
- files.each do |file|
1028
- begin
1029
- STDOUT.puts "Parsing #{file}"
1030
- # create top state
1031
- top = ParseState.make_top_state
1032
- STDOUT.puts "TOP State made" if $VERBOSE
1033
- token_counter = TokenCounter.new
1034
- ParseState.set_token_counter(token_counter)
1035
- token_counter.set_current_file(file)
1036
-
1037
- STDOUT.puts "Setting up Lexer" if $VERBOSE
1038
- lexer = RubyLex.new
1039
- # Turn of this, because it aborts when a syntax error is found...
1040
- lexer.exception_on_syntax_error = false
1041
- lexer.set_input(File.new(file,"r"))
1042
- top.lexer = lexer
1043
- STDOUT.puts "Parsing" if $VERBOSE
1044
- top.parse
1045
-
1046
-
1047
- fdir_path = seperate_file_from_path(file)
1048
- FileUtils.makedirs("#{output_dir}/#{fdir_path}")
1049
-
1050
- if state_formater
1051
- # output results
1052
- state_io = StringIO.new
1053
- state_formater.start(state_io)
1054
- top.compute_state(state_formater)
1055
- state_formater.end
1056
-
1057
- fname = "#{file}_cyclo.html"
1058
- puts "writing cyclomatic #{file}" if $VERBOSE
1059
- File.open("#{output_dir}/#{fname}","w") do |f|
1060
- f.write state_io.string
1061
- end
1062
- idx_states<< [
1063
- fname,
1064
- state_formater.warnings.dup,
1065
- state_formater.errors.dup,
1066
- ]
1067
- end
1068
-
1069
- if token_count_formater
1070
- token_io = StringIO.new
1071
- token_count_formater.start(token_io)
1072
- token_counter.list_tokens_per_line(token_count_formater)
1073
- token_count_formater.end
1074
-
1075
- fname = "#{file}_token.html"
1076
- puts "writing token #{file}" if $VERBOSE
1077
- File.open("#{output_dir}/#{fname}","w") do |f|
1078
- f.write token_io.string
1079
- end
1080
- idx_tokens<< [
1081
- fname,
1082
- token_count_formater.warnings.dup,
1083
- token_count_formater.errors.dup,
1084
- ]
1085
- end
1086
-
1087
- rescue RubyLex::SyntaxError => synerr
1088
- STDOUT.puts "Lexer error for file #{file} on line #{lexer.line_no}"
1089
- STDOUT.puts "#{synerr.class.name} : #{synerr.message}"
1090
- rescue StandardError => err
1091
- STDOUT.puts "Error while parsing file : #{file}"
1092
- STDOUT.puts err.class,err.message,err.backtrace.join("\n")
1093
- rescue Exception => ex
1094
- STDOUT.puts "Error while parsing file : #{file}"
1095
- STDOUT.puts ex.class,ex.message,ex.backtrace.join("\n")
1096
- end
1097
- end
1098
-
1099
- [idx_states, idx_tokens]
1100
- end
1101
- end
1102
-
1103
- if __FILE__ == $0
1104
- require 'stringio'
1105
- require 'getoptlong'
1106
- require 'fileutils'
1107
- require 'find'
1108
- begin
1109
- require 'rdoc/ri/ri_paths'
1110
- require 'rdoc/usage'
1111
- rescue LoadError
1112
- # these requires cause problems in Ruby 1.9x that I'm not really sure how to fix
1113
- require 'rdoc/ri/paths'
1114
- #Still not sure what the equivalent of rdoc/usage is. I know it has been removed from 1.9
1115
- end
1116
- include ResultIndexGenerator
1117
-
1118
- #Returns the path without the file
1119
- def seperate_file_from_path(path)
1120
- res = path.split("/")
1121
- if res.size == 1
1122
- ""
1123
- else
1124
- res[0..res.size - 2].join("/")
1125
- end
1126
- end
1127
-
1128
- def get_ruby_files(input_path)
1129
- files = Array.new
1130
- input_path.split("|").each do |path|
1131
- Find.find(path.strip) do |f|
1132
- files << f if !FileTest.directory?(f) && f =~ /\.rb$/
1133
- end
1134
- end
1135
- files
1136
- end
1137
-
1138
- files = Array.new
1139
- output_dir = "./"
1140
- formater = "html"
1141
- state_filter = Filter.new(5)
1142
- token_filter = Filter.new(10, 25, 50)
1143
- comp_state = comp_token = false
1144
- begin
1145
- opt = GetoptLong.new(
1146
- ["-o","--output_directory", GetoptLong::REQUIRED_ARGUMENT],
1147
- ["-h","--help", GetoptLong::NO_ARGUMENT],
1148
- ["-f","--formater", GetoptLong::REQUIRED_ARGUMENT],
1149
- ["-c","--cyclo", GetoptLong::NO_ARGUMENT],
1150
- ["-t","--token", GetoptLong::NO_ARGUMENT],
1151
- ["-y","--filter_cyclo", GetoptLong::REQUIRED_ARGUMENT],
1152
- ["-k","--filter_token", GetoptLong::REQUIRED_ARGUMENT],
1153
- ["-w","--warn_cyclo", GetoptLong::REQUIRED_ARGUMENT],
1154
- ["-s","--warn_token", GetoptLong::REQUIRED_ARGUMENT],
1155
- ["-e","--error_cyclo", GetoptLong::REQUIRED_ARGUMENT],
1156
- ["-d","--error_token", GetoptLong::REQUIRED_ARGUMENT],
1157
- ["-p","--parse_file", GetoptLong::REQUIRED_ARGUMENT],
1158
- ["-i","--input_directory", GetoptLong::REQUIRED_ARGUMENT],
1159
- ["-v","--verbose", GetoptLong::NO_ARGUMENT]
1160
- )
1161
-
1162
- opt.each do |arg,val|
1163
- case arg
1164
- when "-o"
1165
- output_dir = val
1166
- when "-h"
1167
- RDoc.usage('help')
1168
- when "-f"
1169
- formater = val
1170
- when "-c"
1171
- comp_state = true
1172
- when "-t"
1173
- comp_token = true
1174
- when "-k"
1175
- token_filter.limit = val.to_i
1176
- when "-s"
1177
- token_filter.warn = val.to_i
1178
- when "-d"
1179
- token_filter.error = val.to_i
1180
- when "-y"
1181
- state_filter.limit = val.to_i
1182
- when "-w"
1183
- state_filter.warn = val.to_i
1184
- when "-e"
1185
- state_filter.error = val.to_i
1186
- when "-p"
1187
- files<< val
1188
- when "-i"
1189
- files.concat(get_ruby_files(val))
1190
- when "-v"
1191
- STDOUT.puts "Verbose mode on"
1192
- $VERBOSE = true
1193
- end
1194
-
1195
- end
1196
- RDoc.usage if !comp_state && !comp_token
1197
- rescue => err
1198
- RDoc.usage
1199
- end
1200
-
1201
- if formater =~ /html/i
1202
- state_formater = StateHTMLComplexityFormater.new(STDOUT,state_filter)
1203
- token_count_formater = HTMLTokenCounterFormater.new(STDOUT,token_filter)
1204
- else
1205
- state_formater = ParseStateFormater.new(STDOUT,state_filter)
1206
- token_count_formater = TokenCounterFormater.new(STDOUT,token_filter)
1207
- end
1208
-
1209
- state_formater = nil if !comp_state
1210
- token_count_formater = nil if !comp_token
1211
-
1212
- idx_states, idx_tokens = Saikuro.analyze(files,
1213
- state_formater,
1214
- token_count_formater,
1215
- output_dir)
1216
-
1217
- write_cyclo_index(idx_states, output_dir)
1218
- write_token_index(idx_tokens, output_dir)
1219
- end