Class: Flog

Inherits:
SexpProcessor
  • Object
show all
Defined in:
lib/flog.rb

Constant Summary collapse

VERSION =
"3.2.3"
THRESHOLD =
0.60
SCORES =
Hash.new 1
BRANCHING =
[ :and, :case, :else, :if, :or, :rescue, :until, :when, :while ]
OTHER_SCORES =

Various non-call constructs

{
  :alias          => 2,
  :assignment     => 1,
  :block          => 1,
  :block_pass     => 1,
  :branch         => 1,
  :lit_fixnum     => 0.25,
  :sclass         => 5,
  :super          => 1,
  :to_proc_icky!  => 10,
  :to_proc_lasgn  => 15,
  :to_proc_normal => 5,
  :yield          => 1,
}
@@no_class =
:main
@@no_method =
:none

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(option = {}) ⇒ Flog

Returns a new instance of Flog.



340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/flog.rb', line 340

def initialize option = {}
  super()
  @option              = option
  @sclass              = nil
  @class_stack         = []
  @method_stack        = []
  @method_locations    = {}
  @mass                = {}
  @parser              = nil
  self.auto_shift_type = true
  self.reset
end

Instance Attribute Details

#callsObject (readonly)

Returns the value of attribute calls.



82
83
84
# File 'lib/flog.rb', line 82

def calls
  @calls
end

#class_stackObject (readonly)

Returns the value of attribute class_stack.



82
83
84
# File 'lib/flog.rb', line 82

def class_stack
  @class_stack
end

#massObject (readonly)

Returns the value of attribute mass.



82
83
84
# File 'lib/flog.rb', line 82

def mass
  @mass
end

#method_locationsObject (readonly)

Returns the value of attribute method_locations.



83
84
85
# File 'lib/flog.rb', line 83

def method_locations
  @method_locations
end

#method_stackObject (readonly)

Returns the value of attribute method_stack.



82
83
84
# File 'lib/flog.rb', line 82

def method_stack
  @method_stack
end

#multiplierObject

Returns the value of attribute multiplier.



81
82
83
# File 'lib/flog.rb', line 81

def multiplier
  @multiplier
end

#optionObject (readonly)

Returns the value of attribute option.



82
83
84
# File 'lib/flog.rb', line 82

def option
  @option
end

#sclassObject (readonly)

Returns the value of attribute sclass.



82
83
84
# File 'lib/flog.rb', line 82

def sclass
  @sclass
end

Class Method Details

.expand_dirs_to_files(*dirs) ⇒ Object

REFACTOR: from flay



121
122
123
124
125
126
127
128
129
130
131
# File 'lib/flog.rb', line 121

def self.expand_dirs_to_files *dirs
  extensions = %w[rb rake]

  dirs.flatten.map { |p|
    if File.directory? p then
      Dir[File.join(p, '**', "*.{#{extensions.join(',')}}")]
    else
      p
    end
  }.flatten.sort
end

.load_pluginsObject

TODO: I think I want to do this more like hoe’s plugin system. Generalize?



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
# File 'lib/flog.rb', line 90

def self.load_plugins
  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(args = ARGV) ⇒ Object



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
# File 'lib/flog.rb', line 133

def self.parse_options args = ARGV
  option = {
    :quiet    => false,
    :continue => false,
    :parser   => RubyParser,
  }

  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("-s", "--score", "Display total score only.") do
      option[:score] = true
    end

    opts.on("-v", "--verbose", "Display progress during processing.") do
      option[:verbose] = true
    end

    opts.on("--18", "Use a ruby 1.8 parser.") do
      option[:parser] = Ruby18Parser
    end

    opts.on("--19", "Use a ruby 1.9 parser.") do
      option[:parser] = Ruby19Parser
    end

    next if self.plugins.empty?
    opts.separator "Plugin options:"

    extra = self.methods.grep(/parse_options/) - %w(parse_options)

    extra.sort.each do |msg|
      self.send msg, opts, option
    end

  end.parse! Array(args)

  option
end

.pluginsObject



85
86
87
# File 'lib/flog.rb', line 85

def self.plugins
  @plugins ||= {}
end

Instance Method Details

#add_to_score(name, score = ) ⇒ Object

Add a score to the tally. Score can be predetermined or looked up automatically. Uses multiplier for additional spankings. Spankings!



217
218
219
# File 'lib/flog.rb', line 217

def add_to_score name, score = OTHER_SCORES[name]
  @calls[signature][name] += score * @multiplier
end

#alias_methodObject

Various “magic” usually used for “clever code”



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/flog.rb', line 52

SCORES.merge!(:alias_method               => 2,
:extend                     => 2,
:include                    => 2,
:instance_method            => 2,
:instance_methods           => 2,
:method_added               => 2,
:method_defined?            => 2,
:method_removed             => 2,
:method_undefined           => 2,
:private_class_method       => 2,
:private_instance_methods   => 2,
:private_method_defined?    => 2,
:protected_instance_methods => 2,
:protected_method_defined?  => 2,
:public_class_method        => 2,
:public_instance_methods    => 2,
:public_method_defined?     => 2,
:remove_method              => 2,
:send                       => 3,
:undef_method               => 2)

#averageObject

really?



224
225
226
227
# File 'lib/flog.rb', line 224

def average
  return 0 if calls.size == 0
  total / calls.size
end

#define_methodObject

Eval forms



43
44
45
46
47
# File 'lib/flog.rb', line 43

SCORES.merge!(:define_method => 5,
:eval          => 5,
:module_eval   => 5,
:class_eval    => 5,
:instance_eval => 5)

#dsl_name?(args) ⇒ Boolean

Returns:

  • (Boolean)


681
682
683
684
685
686
687
688
# File 'lib/flog.rb', line 681

def dsl_name? args
  return false unless args and not args.empty?

  first_arg = args.first
  first_arg = first_arg[1] if first_arg[0] == :hash

  [:lit, :str].include? first_arg[0] and first_arg[1]
end

#each_by_score(max = nil) ⇒ Object

Iterate over the calls sorted (descending) by score.



232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/flog.rb', line 232

def each_by_score max = nil
  my_totals = totals
  current   = 0

  calls.sort_by { |k,v| -my_totals[k] }.each do |class_method, call_list|
    score = my_totals[class_method]

    yield class_method, score, call_list

    current += score
    break if max and current >= max
  end
end

#flog(*files_or_dirs) ⇒ Object

Flog the given files or directories. Smart. Deals with “-”, syntax errors, and traversing subdirectories intelligently.



250
251
252
253
254
255
256
257
258
259
260
# File 'lib/flog.rb', line 250

def flog(*files_or_dirs)
  files = Flog.expand_dirs_to_files(*files_or_dirs)

  files.each do |file|
    next unless file == '-' or File.readable? file

    ruby = file == '-' ? $stdin.read : File.binread(file)

    flog_ruby ruby, file
  end
end

#flog_ruby(ruby, file = "-", timeout = 10) ⇒ Object

Flog the given ruby source, optionally using file to provide paths for methods. Smart. Handles syntax errors and timeouts so you don’t have to.



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/flog.rb', line 267

def flog_ruby ruby, file="-", timeout = 10
  flog_ruby! ruby, file, timeout
rescue Timeout::Error
  warn "TIMEOUT parsing #{file}. Skipping."
rescue RubyParser::SyntaxError, Racc::ParseError => e
  q = option[:quiet]
  if e.inspect =~ /<\%|%\>/ or ruby =~ /<\%|%\>/ then
    return if q
    warn "#{e.inspect} at #{e.backtrace.first(5).join(', ')}"
    warn "\n...stupid lemmings and their bad erb templates... skipping"
  else
    warn "ERROR: parsing ruby file #{file}" unless q
    unless option[:continue] then
      warn "ERROR! Aborting. You may want to run with --continue."
      raise e
    end
    return if q
    warn "%s: %s at:\n  %s" % [e.class, e.message.strip,
                               e.backtrace.first(5).join("\n  ")]
  end
end

#flog_ruby!(ruby, file = "-", timeout = 10) ⇒ Object

Flog the given ruby source, optionally using file to provide paths for methods. Does not handle timeouts or syntax errors. See #flog_ruby.



293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/flog.rb', line 293

def flog_ruby! ruby, file="-", timeout = 10
  @parser = (option[:parser] || RubyParser).new

  warn "** flogging #{file}" if option[:verbose]

  ast = @parser.process ruby, file, timeout

  return unless ast

  mass[file] = ast.mass
  process ast
end

#in_klass(name) ⇒ Object

Adds name to the class stack, for the duration of the block



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/flog.rb', line 309

def in_klass name
  if Sexp === name then
    name = case name.first
           when :colon2 then
             name = name.flatten
             name.delete :const
             name.delete :colon2
             name.join("::")
           when :colon3 then
             name.last.to_s
           else
             raise "unknown type #{name.inspect}"
           end
  end

  @class_stack.unshift name
  yield
  @class_stack.shift
end

#in_method(name, file, line) ⇒ Object

Adds name to the method stack, for the duration of the block



332
333
334
335
336
337
338
# File 'lib/flog.rb', line 332

def in_method(name, file, line)
  method_name = Regexp === name ? name.inspect : name.to_s
  @method_stack.unshift method_name
  @method_locations[signature] = "#{file}:#{line}"
  yield
  @method_stack.shift
end

#injectObject

Calls that are ALMOST ALWAYS ABUSED!



76
# File 'lib/flog.rb', line 76

SCORES.merge!(:inject => 2)

#klass_nameObject

Returns the first class in the list, or @@no_class if there are none.



357
358
359
360
361
362
363
364
365
366
367
# File 'lib/flog.rb', line 357

def klass_name
  name = @class_stack.first

  if Sexp === name then
    raise "you shouldn't see me"
  elsif @class_stack.any?
    @class_stack.reverse.join("::").sub(/\([^\)]+\)$/, '')
  else
    @@no_class
  end
end

#max_methodObject



514
515
516
# File 'lib/flog.rb', line 514

def max_method
  totals.max_by { |_, score| score }
end

#max_scoreObject



510
511
512
# File 'lib/flog.rb', line 510

def max_score
  max_method.last
end

#method_nameObject

Returns the first method in the list, or “#none” if there are none.



373
374
375
376
377
# File 'lib/flog.rb', line 373

def method_name
  m = @method_stack.first || @@no_method
  m = "##{m}" unless m =~ /::/
  m
end

#output_details(io, max = nil) ⇒ Object

Output the report up to a given max or report everything, if nil.



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/flog.rb', line 382

def output_details io, max = nil
  io.puts

  each_by_score max do |class_method, score, call_list|
    return 0 if option[:methods] and class_method =~ /##{@@no_method}/

    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
  # io.puts
end

#output_details_grouped(io, max = nil) ⇒ Object

Output the report, grouped by class/module, up to a given max or report everything, if nil.



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/flog.rb', line 404

def output_details_grouped io, max = nil
  scores  = Hash.new 0
  methods = Hash.new { |h,k| h[k] = [] }

  each_by_score max do |class_method, score, call_list|
    klass = class_method.split(/#|::/).first

    methods[klass] << [class_method, score]
    scores[klass]  += score
  end

  scores.sort_by { |_, n| -n }.each do |klass, total|
    io.puts

    io.puts "%8.1f: %s" % [total, "#{klass} total"]

    methods[klass].each do |name, score|
      self.print_score io, name, score
    end
  end
end

#penalize_by(bonus) ⇒ Object

For the duration of the block the complexity factor is increased by #bonus This allows the complexity of sub-expressions to be influenced by the expressions in which they are found. Yields 42 to the supplied block.



432
433
434
435
436
# File 'lib/flog.rb', line 432

def penalize_by bonus
  @multiplier += bonus
  yield
  @multiplier -= bonus
end

Print out one formatted score.



441
442
443
444
445
446
447
448
# File 'lib/flog.rb', line 441

def print_score io, name, score
  location = @method_locations[name]
  if location then
    io.puts "%8.1f: %-32s %s" % [score, name, location]
  else
    io.puts "%8.1f: %s" % [score, name]
  end
end

#process_alias(exp) ⇒ Object

Process Methods:



541
542
543
544
545
546
# File 'lib/flog.rb', line 541

def process_alias(exp)
  process exp.shift
  process exp.shift
  add_to_score :alias
  s()
end

#process_and(exp) ⇒ Object Also known as: process_or



548
549
550
551
552
553
554
555
# File 'lib/flog.rb', line 548

def process_and(exp)
  add_to_score :branch
  penalize_by 0.1 do
    process exp.shift # lhs
    process exp.shift # rhs
  end
  s()
end

#process_attrasgn(exp) ⇒ Object



558
559
560
561
562
563
564
# File 'lib/flog.rb', line 558

def process_attrasgn(exp)
  add_to_score :assignment
  process exp.shift # lhs
  exp.shift # name
  process_until_empty exp # rhs
  s()
end

#process_block(exp) ⇒ Object



566
567
568
569
570
571
# File 'lib/flog.rb', line 566

def process_block(exp)
  penalize_by 0.1 do
    process_until_empty exp
  end
  s()
end

#process_block_pass(exp) ⇒ Object



573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/flog.rb', line 573

def process_block_pass(exp)
  arg = exp.shift

  add_to_score :block_pass

  case arg.first
  when :lvar, :dvar, :ivar, :cvar, :self, :const, :colon2, :nil then # f(&b)
    # do nothing
  when :lit, :call then                                              # f(&:b)
    add_to_score :to_proc_normal
  when :lasgn then                                                   # f(&l=b)
    add_to_score :to_proc_lasgn
  when :iter, :dsym, :dstr, *BRANCHING then                          # below
    # f(&proc { ... })
    # f(&"#{...}")
    # f(&:"#{...}")
    # f(&if ... then ... end") and all other branching forms
    add_to_score :to_proc_icky!
  else
    raise({:block_pass_even_ickier! => arg}.inspect)
  end

  process arg

  s()
end

#process_call(exp) ⇒ Object



600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
# File 'lib/flog.rb', line 600

def process_call(exp)
  penalize_by 0.2 do
    process exp.shift # recv
  end

  name = exp.shift

  penalize_by 0.2 do
    process_until_empty exp
  end

  add_to_score name, SCORES[name]

  s()
end

#process_case(exp) ⇒ Object



616
617
618
619
620
621
622
623
# File 'lib/flog.rb', line 616

def process_case(exp)
  add_to_score :branch
  process exp.shift # recv
  penalize_by 0.1 do
    process_until_empty exp
  end
  s()
end

#process_class(exp) ⇒ Object



625
626
627
628
629
630
631
632
633
# File 'lib/flog.rb', line 625

def process_class(exp)
  in_klass exp.shift do
    penalize_by 1.0 do
      process exp.shift # superclass expression
    end
    process_until_empty exp
  end
  s()
end

#process_dasgn_curr(exp) ⇒ Object Also known as: process_iasgn, process_lasgn

FIX: remove



635
636
637
638
639
640
# File 'lib/flog.rb', line 635

def process_dasgn_curr(exp) # FIX: remove
  add_to_score :assignment
  exp.shift # name
  process exp.shift # assigment, if any
  s()
end

#process_defn(exp) ⇒ Object



644
645
646
647
648
649
650
# File 'lib/flog.rb', line 644

def process_defn(exp)
  name = @sclass ? "::#{exp.shift}" : exp.shift
  in_method name, exp.file, exp.line do
    process_until_empty exp
  end
  s()
end

#process_defs(exp) ⇒ Object



652
653
654
655
656
657
658
# File 'lib/flog.rb', line 652

def process_defs(exp)
  process exp.shift # recv
  in_method "::#{exp.shift}", exp.file, exp.line do
    process_until_empty exp
  end
  s()
end

#process_else(exp) ⇒ Object Also known as: process_rescue, process_when

TODO: it’s not clear to me whether this can be generated at all.



661
662
663
664
665
666
667
# File 'lib/flog.rb', line 661

def process_else(exp)
  add_to_score :branch
  penalize_by 0.1 do
    process_until_empty exp
  end
  s()
end

#process_if(exp) ⇒ Object



671
672
673
674
675
676
677
678
679
# File 'lib/flog.rb', line 671

def process_if(exp)
  add_to_score :branch
  process exp.shift # cond
  penalize_by 0.1 do
    process exp.shift # true
    process exp.shift # false
  end
  s()
end

#process_iter(exp) ⇒ Object



690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
# File 'lib/flog.rb', line 690

def process_iter(exp)
  context = (self.context - [:class, :module, :scope])
  context = context.uniq.sort_by { |s| s.to_s }

  exp.delete 0 # { || ... } has 0 in arg slot

  if context == [:block, :iter] or context == [:iter] then
    recv = exp.first

    # DSL w/ names. eg task :name do ... end
    #   looks like s(:call, nil, :task, s(:lit, :name))
    #           or s(:call, nil, :task, s(:str, "name"))
    #           or s(:call, nil, :task, s(:hash, s(:lit, :name) ...))

    t, r, m, *a = recv

    if t == :call and r == nil and submsg = dsl_name?(a) then
      m = "#{m}(#{submsg})" if m and [String, Symbol].include?(submsg.class)
      in_klass m do                             # :task/namespace
        in_method submsg, exp.file, exp.line do # :name
          process_until_empty exp
        end
      end
      return s()
    end
  end

  add_to_score :branch

  process exp.shift # no penalty for LHS

  penalize_by 0.1 do
    process_until_empty exp
  end

  s()
end

#process_lit(exp) ⇒ Object



728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
# File 'lib/flog.rb', line 728

def process_lit(exp)
  value = exp.shift
  case value
  when 0, -1 then
    # ignore those because they're used as array indicies instead of
    # first/last
  when Integer then
    add_to_score :lit_fixnum
  when Float, Symbol, Regexp, Range then
    # do nothing
  else
    raise value.inspect
  end
  s()
end

#process_masgn(exp) ⇒ Object



744
745
746
747
748
749
750
751
# File 'lib/flog.rb', line 744

def process_masgn(exp)
  add_to_score :assignment

  exp.map! { |s| Sexp === s ? s : s(:lasgn, s) }

  process_until_empty exp
  s()
end

#process_module(exp) ⇒ Object



753
754
755
756
757
758
# File 'lib/flog.rb', line 753

def process_module(exp)
  in_klass exp.shift do
    process_until_empty exp
  end
  s()
end

#process_sclass(exp) ⇒ Object



760
761
762
763
764
765
766
767
768
769
770
# File 'lib/flog.rb', line 760

def process_sclass(exp)
  @sclass = true
  penalize_by 0.5 do
    process exp.shift # recv
    process_until_empty exp
  end
  @sclass = nil

  add_to_score :sclass
  s()
end

#process_super(exp) ⇒ Object



772
773
774
775
776
# File 'lib/flog.rb', line 772

def process_super(exp)
  add_to_score :super
  process_until_empty exp
  s()
end

#process_until_empty(exp) ⇒ Object

Process each element of #exp in turn.



453
454
455
# File 'lib/flog.rb', line 453

def process_until_empty exp
  process exp.shift until exp.empty?
end

#process_while(exp) ⇒ Object Also known as: process_until



778
779
780
781
782
783
784
785
786
# File 'lib/flog.rb', line 778

def process_while(exp)
  add_to_score :branch
  penalize_by 0.1 do
    process exp.shift # cond
    process exp.shift # body
  end
  exp.shift # pre/post
  s()
end

#process_yield(exp) ⇒ Object



789
790
791
792
793
# File 'lib/flog.rb', line 789

def process_yield(exp)
  add_to_score :yield
  process_until_empty exp
  s()
end

#report(io = $stdout) ⇒ Object

Report results to #io, STDOUT by default.



460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
# File 'lib/flog.rb', line 460

def report(io = $stdout)
  io.puts "%8.1f: %s" % [total, "flog total"]
  io.puts "%8.1f: %s" % [average, "flog/method average"]

  return if option[:score]

  max = option[:all] ? nil : total * THRESHOLD
  if option[:group] then
    output_details_grouped io, max
  else
    output_details io, max
  end
ensure
  self.reset
end

#resetObject

Reset score data



479
480
481
482
483
# File 'lib/flog.rb', line 479

def reset
  @totals     = @total_score = nil
  @multiplier = 1.0
  @calls      = Hash.new { |h,k| h[k] = Hash.new 0 }
end

#score_method(tally) ⇒ Object

Compute the distance formula for a given tally



488
489
490
491
492
493
494
495
496
497
498
# File 'lib/flog.rb', line 488

def score_method(tally)
  a, b, c = 0, 0, 0
  tally.each do |cat, score|
    case cat
    when :assignment then a += score
    when :branch     then b += score
    else                  c += score
    end
  end
  Math.sqrt(a*a + b*b + c*c)
end

#signatureObject



500
501
502
# File 'lib/flog.rb', line 500

def signature
  "#{klass_name}#{method_name}"
end

#totalObject

FIX: I hate this indirectness



504
505
506
507
508
# File 'lib/flog.rb', line 504

def total # FIX: I hate this indirectness
  totals unless @total_score # calculates total_score as well

  @total_score
end

#totalsObject

Return the total score and populates @totals.



521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/flog.rb', line 521

def totals
  unless @totals then
    @total_score = 0
    @totals = Hash.new(0)

    calls.each do |meth, tally|
      next if option[:methods] and meth =~ /##{@@no_method}$/
      score = score_method(tally)

      @totals[meth] = score
      @total_score += score
    end
  end

  @totals
end