Credit: Stefan Lang

#### Problem

You want to gather statistics about your Ruby project, like the total number of lines of code.

#### Solution

Heres a class that parses Ruby source files and gathers statistics. Put this in scriptlines.rb in your projects top-level directory.

```	# scriptlines.rb
# A ScriptLines instance analyses a Ruby script and maintains
# counters for the total number of lines, lines of
code, etc.
class ScriptLines

attr_accessor :bytes, :lines, :lines_of_code, :comment_lines

LINE_FORMAT = \%8s %8s %8s %8s %s

sprintf LINE_FORMAT, "BYTES", "LINES", "LOC", "COMMENT", "FILE"
end

# The
ame argument is usually a filename
def initialize(name)
@name = name
@bytes = 0
@lines = 0 # total number of lines
@lines_of_code = 0
@comment_lines = 0
end

# Iterates over all the lines in io (io might be a file or a
# string), analyses them and appropriately increases the counter
# attributes.
in_multiline_comment = false
io.each { |line|
@lines += 1
@bytes += line.size
case line
when /^=begin(s|\$)/
in_multiline_comment = true
@comment_lines += 1
when /^=end(s|\$)/:
@comment_lines += 1
in_multiline_comment = false
when /^s*#/
@comment_lines += 1
when /^s*\$/
# empty/whitespace only line
else
if in_multiline_comment
@comment_lines += 1
else
@lines_of_code += 1
end
end
}
end

# Get a new ScriptLines instance whose counters hold the
# sum of self and other.
def +(other)
sum = self.dup
sum.bytes += other.bytes
sum.lines += other.lines
sum.lines_of_
code += other.lines_of_code
sum.comment_lines += other.comment_lines
sum
end

# Get a formatted string containing all counter numbers and the
# name of this instance.
def to_s
sprintf LINE_FORMAT,
@bytes, @lines, @lines_of_code, @comment_lines, @name
end
end
```

To tie the class into your build system, give your Rakefile a stats task like the following. This task assumes that the Rakefile and scriptlines.rb are in the same directory:

```	task stats do
require scriptlines

files = FileList[lib/**/*.rb]

sum = ScriptLines.new("TOTAL (#{files.size} file(s))")

# Print stats for each file.
files.each do |fn|
File.open(fn) do |file|
script_lines = ScriptLines.new(fn)
sum += script_lines
puts script_lines
end
end

# Print total stats.
puts sum
end
```

#### Discussion

ScriptLines performs a very basic parsing of Ruby code: it divides a source file into blank lines, comment lines, and lines containing Ruby code. If you want more detailed information, you can include each file and get more information about the defined classes and methods with reflection or an extension like Parse Tree.

Invoke the stats task to run all the Ruby scripts beneath your lib/ directory through ScriptLines. The following example output is for the highline library:

```	\$ rake stats
(in /usr/local/lib/ruby/gems/1.8/gems/highline-1.0.1)
BYTES LINES LOC COMMENT FILE
18626 617 360 196 lib/highline.rb
15760 430 181 227 lib/highline/question.rb
801 25 7 14 lib/highline/import.rb
47932 1447 716 618 TOTAL (4 scripts)
```

BYTES is the file size in bytes, LINES the number of total lines in each file, LOC stands for "Lines Of Code," and COMMENT is the number of comment-only lines.

These simple metrics are good for gauging the complexity of a project, but don use them as a measure of day-to-day progress. Complexity is not the same as progress, and a good days work might consist of replacing a hundred lines of code with ten.

• ri Kernel#sprintf
• The RDoc documentation for Rakes FileList class (http://rake.rubyforge.org/classes/Rake/FileList.html)
• The ParseTree extension (http://rubyforge.org/projects/parsetree/)

Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399