rdist 0.0.1
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/ChangeLog +5 -0
- data/README +71 -0
- data/Rakefile +135 -0
- data/bin/rdist +7 -0
- data/lib/rdist/analyzer/base.rb +109 -0
- data/lib/rdist/analyzer/macros.rb +18 -0
- data/lib/rdist/analyzer/methodlength.rb +9 -0
- data/lib/rdist/analyzer/numlinesinclass.rb +12 -0
- data/lib/rdist/analyzer/nummethodsinclass.rb +12 -0
- data/lib/rdist/analyzer/state/base.rb +23 -0
- data/lib/rdist/analyzer/state/inblock.rb +85 -0
- data/lib/rdist/analyzer/state/inmultilinecomment.rb +18 -0
- data/lib/rdist/analyzer/state/waitingblock.rb +52 -0
- data/lib/rdist/analyzer/state.rb +4 -0
- data/lib/rdist/analyzer.rb +6 -0
- data/lib/rdist/commandlineinterface.rb +76 -0
- data/lib/rdist/debuglogger.rb +30 -0
- data/lib/rdist/histogram.rb +136 -0
- data/lib/rdist/ranking.rb +34 -0
- data/lib/rdist/setting/macros.rb +20 -0
- data/lib/rdist/setting.rb +115 -0
- data/lib/rdist/targetfilefinder.rb +43 -0
- data/lib/rdist.rb +16 -0
- data/spec/commandlineinterface_spec.rb +43 -0
- data/spec/fixtures/normal_code.rb +57 -0
- data/spec/ranking_spec.rb +41 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +97 -0
data/ChangeLog
ADDED
data/README
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
= RDist - method length distribution reporter for Ruby.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
== Description
|
|
6
|
+
|
|
7
|
+
RDist reports the distribution of method length in your Ruby codes.
|
|
8
|
+
It helps you to find the longest methods by reporting the rankings together.
|
|
9
|
+
|
|
10
|
+
RDist can also report the distribution of number of method definitions (or that of lines) in Class/Module.
|
|
11
|
+
|
|
12
|
+
== Installation
|
|
13
|
+
|
|
14
|
+
=== Archive Installation
|
|
15
|
+
|
|
16
|
+
rake install
|
|
17
|
+
|
|
18
|
+
=== Gem Installation
|
|
19
|
+
|
|
20
|
+
gem install rdist
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
== Features/Problems
|
|
24
|
+
|
|
25
|
+
RDist simply check the indent level during the analysis, so it can't correctly analyze the code with incomplete indent.
|
|
26
|
+
|
|
27
|
+
== Synopsis
|
|
28
|
+
|
|
29
|
+
See the method length distribution.
|
|
30
|
+
$ rdist $GEM_HOME/gems/rdist-0.0.1/lib
|
|
31
|
+
Method length Distribution:
|
|
32
|
+
0- 4: 77% 71 ***************************************
|
|
33
|
+
5- 9: 96% 18 **********
|
|
34
|
+
10- 14: 100% 3 **
|
|
35
|
+
Total: 100% 92
|
|
36
|
+
|
|
37
|
+
Ranking Top 5:
|
|
38
|
+
11: analyze (at lib/rdist/analyzer/base.rb:34)
|
|
39
|
+
10: find (at lib/rdist/targetfilefinder.rb:6)
|
|
40
|
+
10: analyze (at lib/rdist/analyzer/state/inblock.rb:13)
|
|
41
|
+
9: initialize (at lib/rdist/analyzer/base.rb:8)
|
|
42
|
+
8: initialize (at lib/rdist/commandlineinterface.rb:16)
|
|
43
|
+
|
|
44
|
+
You can also see the distribution of number of method definitions in Class/Module (<code>-C</code>, <code>--num-methods-in-class</code> option).
|
|
45
|
+
$ rdist -C $GEM_HOME/gems/rdist-0.0.1/lib
|
|
46
|
+
Distribution of number of method definitons in Class/Module:
|
|
47
|
+
0- 4: 60% 9 ******************************
|
|
48
|
+
5- 9: 73% 2 *******
|
|
49
|
+
10- 14: 93% 3 **********
|
|
50
|
+
15- 19: 93% 0
|
|
51
|
+
20- 24: 100% 1 ****
|
|
52
|
+
Total: 100% 15
|
|
53
|
+
|
|
54
|
+
Ranking Top 5:
|
|
55
|
+
23: Histogram (at lib/rdist/histogram.rb:2)
|
|
56
|
+
13: Base (at lib/rdist/analyzer/base.rb:5)
|
|
57
|
+
12: InBlock (at lib/rdist/analyzer/state/inblock.rb:4)
|
|
58
|
+
10: CommandLineInterface (at lib/rdist/commandlineinterface.rb:2)
|
|
59
|
+
8: Setting (at lib/rdist/setting.rb:5)
|
|
60
|
+
|
|
61
|
+
See <b>$ rdist -h</b> for more information.
|
|
62
|
+
|
|
63
|
+
== See also
|
|
64
|
+
|
|
65
|
+
* Flog - Seattle.rb (http://seattlerb.rubyforge.org)
|
|
66
|
+
|
|
67
|
+
== Copyright
|
|
68
|
+
|
|
69
|
+
Author:: Yoshifumi Shimono <yoshifumi.shimono@gmail.com>
|
|
70
|
+
Copyright:: Copyright (c) 2008 Yoshifumi Shimono
|
|
71
|
+
License:: Ruby's
|
data/Rakefile
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'rake/clean'
|
|
4
|
+
require 'rake/testtask'
|
|
5
|
+
require 'rake/packagetask'
|
|
6
|
+
require 'rake/gempackagetask'
|
|
7
|
+
require 'rake/rdoctask'
|
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
|
9
|
+
require 'rake/contrib/sshpublisher'
|
|
10
|
+
require 'spec/rake/spectask'
|
|
11
|
+
require 'rubyforge'
|
|
12
|
+
require 'fileutils'
|
|
13
|
+
include FileUtils
|
|
14
|
+
|
|
15
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib'
|
|
16
|
+
require 'rdist'
|
|
17
|
+
|
|
18
|
+
NAME = "rdist"
|
|
19
|
+
AUTHOR = "Yoshifumi Shimono"
|
|
20
|
+
EMAIL = "yoshifumi.shimono@gmail.com"
|
|
21
|
+
DESCRIPTION = "reports the distribution of method length in your Ruby codes."
|
|
22
|
+
RUBYFORGE_PROJECT = "rdist"
|
|
23
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
|
24
|
+
BIN_FILES = %w( rdist )
|
|
25
|
+
VERS = RDist::VERSION
|
|
26
|
+
|
|
27
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
|
28
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
|
29
|
+
RDOC_OPTS = [
|
|
30
|
+
'--title', "#{NAME} documentation",
|
|
31
|
+
"--charset", "utf-8",
|
|
32
|
+
"--opname", "index.html",
|
|
33
|
+
"--line-numbers",
|
|
34
|
+
"--main", "README",
|
|
35
|
+
"--inline-source",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
task :default => [:spec]
|
|
39
|
+
task :package => [:clean]
|
|
40
|
+
|
|
41
|
+
Spec::Rake::SpecTask.new do |t|
|
|
42
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
|
43
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
|
44
|
+
t.rcov = false
|
|
45
|
+
t.verbose = true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
spec = Gem::Specification.new do |s|
|
|
49
|
+
s.name = NAME
|
|
50
|
+
s.version = VERS
|
|
51
|
+
s.platform = Gem::Platform::RUBY
|
|
52
|
+
s.has_rdoc = true
|
|
53
|
+
s.extra_rdoc_files = ["README", "ChangeLog"]
|
|
54
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
|
55
|
+
s.summary = DESCRIPTION
|
|
56
|
+
s.description = DESCRIPTION
|
|
57
|
+
s.author = AUTHOR
|
|
58
|
+
s.email = EMAIL
|
|
59
|
+
s.homepage = HOMEPATH
|
|
60
|
+
s.executables = BIN_FILES
|
|
61
|
+
s.rubyforge_project = RUBYFORGE_PROJECT
|
|
62
|
+
s.bindir = "bin"
|
|
63
|
+
s.require_path = "lib"
|
|
64
|
+
s.test_files = Dir["test/test_*.rb"]
|
|
65
|
+
|
|
66
|
+
#s.add_dependency('activesupport', '>=1.3.1')
|
|
67
|
+
#s.required_ruby_version = '>= 1.8.2'
|
|
68
|
+
|
|
69
|
+
s.files = %w(README ChangeLog Rakefile) +
|
|
70
|
+
Dir.glob("{bin,doc,spec,test,lib,templates,generator,extras,website,script}/**/*") +
|
|
71
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
|
72
|
+
Dir.glob("examples/**/*.rb") +
|
|
73
|
+
Dir.glob("tools/*.rb")
|
|
74
|
+
|
|
75
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
Rake::GemPackageTask.new(spec) do |p|
|
|
79
|
+
p.need_tar = true
|
|
80
|
+
p.gem_spec = spec
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
task :install do
|
|
84
|
+
name = "#{NAME}-#{VERS}.gem"
|
|
85
|
+
sh %{rake package}
|
|
86
|
+
sh %{sudo gem install pkg/#{name}}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
task :uninstall => [:clean] do
|
|
90
|
+
sh %{sudo gem uninstall #{NAME}}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
Rake::RDocTask.new do |rdoc|
|
|
95
|
+
rdoc.rdoc_dir = 'html'
|
|
96
|
+
rdoc.options += RDOC_OPTS
|
|
97
|
+
rdoc.template = "resh"
|
|
98
|
+
#rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
|
99
|
+
if ENV['DOC_FILES']
|
|
100
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
|
101
|
+
else
|
|
102
|
+
rdoc.rdoc_files.include('README', 'ChangeLog')
|
|
103
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
104
|
+
#rdoc.rdoc_files.include('ext/**/*.c')
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
desc "Publish to RubyForge"
|
|
109
|
+
task :rubyforge => [:rdoc, :package] do
|
|
110
|
+
Rake::RubyForgePublisher.new(RUBYFORGE_PROJECT, 'shimono').upload
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
desc 'Package and upload the release to rubyforge.'
|
|
114
|
+
task :release => [:clean, :package] do |t|
|
|
115
|
+
v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
|
|
116
|
+
abort "Versions don't match #{v} vs #{VERS}" unless v == VERS
|
|
117
|
+
pkg = "pkg/#{NAME}-#{VERS}"
|
|
118
|
+
|
|
119
|
+
rf = RubyForge.new
|
|
120
|
+
puts "Logging in"
|
|
121
|
+
rf.login
|
|
122
|
+
|
|
123
|
+
c = rf.userconfig
|
|
124
|
+
# c["release_notes"] = description if description
|
|
125
|
+
# c["release_changes"] = changes if changes
|
|
126
|
+
c["preformatted"] = true
|
|
127
|
+
|
|
128
|
+
files = [
|
|
129
|
+
"#{pkg}.tgz",
|
|
130
|
+
"#{pkg}.gem"
|
|
131
|
+
].compact
|
|
132
|
+
|
|
133
|
+
puts "Releasing #{NAME} v. #{VERS}"
|
|
134
|
+
rf.add_release RUBYFORGE_PROJECT, NAME, VERS, *files
|
|
135
|
+
end
|
data/bin/rdist
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'rdist/analyzer/macros'
|
|
2
|
+
|
|
3
|
+
module RDist
|
|
4
|
+
module Analyzer
|
|
5
|
+
class Base
|
|
6
|
+
extend Macros
|
|
7
|
+
|
|
8
|
+
def initialize(pattern_open_token, pattern_count_target,
|
|
9
|
+
pattern_close_token, allow_nesting,
|
|
10
|
+
histogram_interval=5)
|
|
11
|
+
@pattern_open_token = pattern_open_token
|
|
12
|
+
@pattern_count_target = pattern_count_target
|
|
13
|
+
@pattern_close_token = pattern_close_token
|
|
14
|
+
@allow_nesting = allow_nesting
|
|
15
|
+
@histogram_interval = histogram_interval
|
|
16
|
+
init_count()
|
|
17
|
+
init_states()
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_reader :pattern_open_token
|
|
21
|
+
attr_reader :pattern_count_target
|
|
22
|
+
attr_reader :pattern_close_token
|
|
23
|
+
attr_writer :histogram_interval
|
|
24
|
+
|
|
25
|
+
public
|
|
26
|
+
def analyze_all(pathes)
|
|
27
|
+
init_count()
|
|
28
|
+
pathes.each do |path|
|
|
29
|
+
analyze(path)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def analyze(path)
|
|
34
|
+
@path = path
|
|
35
|
+
DebugLogger.debug "Analyzing ``#{@path}''"
|
|
36
|
+
set_state_waiting_block()
|
|
37
|
+
open(path, 'r') do |input|
|
|
38
|
+
input.each_line do |line|
|
|
39
|
+
@current_line_id = input.lineno
|
|
40
|
+
analyze_line(line)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
@current_line_id = nil
|
|
44
|
+
reset_states()
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def histogram
|
|
48
|
+
Histogram.new(@count_of, @histogram_interval)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def ranking
|
|
52
|
+
Ranking.new(@count_of)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def reset
|
|
56
|
+
init_count()
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def add_count_of(block_name, line_id)
|
|
60
|
+
block_id = "#{block_name} (at #{@path}:#{line_id})"
|
|
61
|
+
@count_of[block_id] ||= 0
|
|
62
|
+
@count_of[block_id] += 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def allow_nesting?
|
|
66
|
+
@allow_nesting
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def deny_nesting?
|
|
70
|
+
not @allow_nesting
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def_state_setter :waiting_block
|
|
74
|
+
def_state_setter :in_multi_line_comment
|
|
75
|
+
def_state_setter :in_block
|
|
76
|
+
alias __set_state_in_block__ set_state_in_block
|
|
77
|
+
|
|
78
|
+
def set_state_in_block(indent, block_name)
|
|
79
|
+
__set_state_in_block__()
|
|
80
|
+
@state.push_to_stacks(indent, block_name,
|
|
81
|
+
@current_line_id)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
def analyze_line(line)
|
|
86
|
+
@state.analyze(line)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def init_count
|
|
90
|
+
@count_of = {}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def init_states
|
|
94
|
+
@state_for = {
|
|
95
|
+
:waiting_block => State::WaitingBlock.new(self),
|
|
96
|
+
:in_block => State::InBlock.new(self),
|
|
97
|
+
:in_multi_line_comment \
|
|
98
|
+
=> State::InMultiLineComment.new(self),
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def reset_states
|
|
103
|
+
@state_for.each do |symbol, state|
|
|
104
|
+
state.reset
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
module Analyzer
|
|
3
|
+
module Macros
|
|
4
|
+
private
|
|
5
|
+
def def_state_setter(state_symbol)
|
|
6
|
+
method_name = get_state_setter_name(state_symbol)
|
|
7
|
+
define_method(method_name) do
|
|
8
|
+
next_state = instance_variable_get(:@state_for)[state_symbol]
|
|
9
|
+
instance_variable_set(:@state, next_state)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_state_setter_name(state_symbol)
|
|
14
|
+
"set_state_#{state_symbol}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
module Analyzer
|
|
3
|
+
class NumLinesInClass < Base
|
|
4
|
+
def initialize
|
|
5
|
+
open_tokens = %w{module class}
|
|
6
|
+
pattern_open_tokens = Regexp.union(*open_tokens)
|
|
7
|
+
super(pattern_open_tokens, /$/,
|
|
8
|
+
/end/, RDist::ALLOW_NESTING, 30)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
module Analyzer
|
|
3
|
+
class NumMethodsInClass < Base
|
|
4
|
+
def initialize
|
|
5
|
+
open_tokens = %w{module class}
|
|
6
|
+
pattern_open_tokens = Regexp.union(*open_tokens)
|
|
7
|
+
super(pattern_open_tokens, /(?:^|[^\w]+) def [^\w]+/nxm,
|
|
8
|
+
/end/, RDist::ALLOW_NESTING)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
module Analyzer
|
|
3
|
+
module State
|
|
4
|
+
PATTERN_MULTI_LINE_COMMENT_BEGIN = /\A \=begin/nxm
|
|
5
|
+
PATTERN_MULTI_LINE_COMMENT_END = /\A \=end/nxm
|
|
6
|
+
|
|
7
|
+
class Base
|
|
8
|
+
def initialize(analyzer)
|
|
9
|
+
@analyzer = analyzer
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
public
|
|
13
|
+
def analyze(line)
|
|
14
|
+
raise 'Must be overridden in sub-class'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def reset
|
|
18
|
+
# do nothing
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
module Analyzer
|
|
3
|
+
module State
|
|
4
|
+
class InBlock < WaitingBlock
|
|
5
|
+
def initialize(analyzer)
|
|
6
|
+
super(analyzer)
|
|
7
|
+
@pattern_count_target = analyzer.pattern_count_target
|
|
8
|
+
@pattern_close_token = analyzer.pattern_close_token
|
|
9
|
+
init_stacks()
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
public
|
|
13
|
+
def analyze(line)
|
|
14
|
+
if have_close_token?(line)
|
|
15
|
+
return accept_close_token()
|
|
16
|
+
end
|
|
17
|
+
if have_open_token?(line)
|
|
18
|
+
return super(line) # call WaitingBlock#analyze(line)
|
|
19
|
+
end
|
|
20
|
+
if have_count_target?(line)
|
|
21
|
+
@analyzer.add_count_of(@block_names.last,
|
|
22
|
+
@line_ids.last)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def push_to_stacks(indent, block_name, line_id)
|
|
27
|
+
@indents << indent
|
|
28
|
+
@block_names << block_name
|
|
29
|
+
@line_ids << line_id
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def reset
|
|
33
|
+
init_stacks()
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def init_stacks
|
|
38
|
+
@indents = []
|
|
39
|
+
@block_names = []
|
|
40
|
+
@line_ids = []
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def have_close_token?(line)
|
|
44
|
+
pattern_have_close_token.match(line)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
PATTERN_SECOND_TOKEN = /[^\s]+/nxm
|
|
48
|
+
PATTERN_TOKEN_GAP = /[^\w]+/nxm
|
|
49
|
+
def pattern_have_close_token
|
|
50
|
+
%r/
|
|
51
|
+
\A
|
|
52
|
+
#{Regexp.escape(@indents.last)}
|
|
53
|
+
#{@pattern_close_token}
|
|
54
|
+
#{PATTERN_TOKEN_GAP}
|
|
55
|
+
/nxm
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def have_count_target?(line)
|
|
59
|
+
pattern_have_count_target.match(line)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def pattern_have_count_target
|
|
63
|
+
@pattern_count_target
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def accept_close_token
|
|
67
|
+
pop_stacks()
|
|
68
|
+
if analyzing_toplevel?
|
|
69
|
+
@analyzer.set_state_waiting_block
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def pop_stacks
|
|
74
|
+
@indents.pop
|
|
75
|
+
@block_names.pop
|
|
76
|
+
@line_ids.pop
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def analyzing_toplevel?
|
|
80
|
+
@analyzer.deny_nesting? || @indents.empty?
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
module Analyzer
|
|
3
|
+
module State
|
|
4
|
+
class InMultiLineComment < Base
|
|
5
|
+
public
|
|
6
|
+
def analyze(line)
|
|
7
|
+
return unless multi_line_comment_end?(line)
|
|
8
|
+
@analyzer.set_state_waiting_block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
def multi_line_comment_end?(line)
|
|
13
|
+
PATTERN_MULTI_LINE_COMMENT_END =~ line
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
module Analyzer
|
|
3
|
+
module State
|
|
4
|
+
class WaitingBlock < Base
|
|
5
|
+
def initialize(analyzer)
|
|
6
|
+
super(analyzer)
|
|
7
|
+
@pattern_open_token = analyzer.pattern_open_token
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
public
|
|
11
|
+
def analyze(line)
|
|
12
|
+
if multi_line_comment_begin?(line)
|
|
13
|
+
return @analyzer.set_state_in_multi_line_comment
|
|
14
|
+
end
|
|
15
|
+
return unless (match_data = have_open_token?(line))
|
|
16
|
+
indent = get_indent_from(match_data)
|
|
17
|
+
block_name = get_block_name_from(match_data)
|
|
18
|
+
@analyzer.set_state_in_block(indent, block_name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
def multi_line_comment_begin?(line)
|
|
23
|
+
PATTERN_MULTI_LINE_COMMENT_BEGIN =~ line
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def have_open_token?(line)
|
|
27
|
+
pattern_have_open_token.match(line)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
PATTERN_INDENT = /\s*/nxm
|
|
31
|
+
PATTERN_SECOND_TOKEN = /[\w\.\:\=\?]+/nxm
|
|
32
|
+
def pattern_have_open_token
|
|
33
|
+
%r/
|
|
34
|
+
\A
|
|
35
|
+
(#{PATTERN_INDENT})
|
|
36
|
+
#{@pattern_open_token}
|
|
37
|
+
\s+
|
|
38
|
+
(#{PATTERN_SECOND_TOKEN})
|
|
39
|
+
/nxm
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_indent_from(match_data)
|
|
43
|
+
match_data[1]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def get_block_name_from(match_data)
|
|
47
|
+
match_data[2]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
# CommandLineInterface is the main class used for the rdist command.
|
|
3
|
+
class CommandLineInterface
|
|
4
|
+
DEFAULT_OUTPUT = $stdout
|
|
5
|
+
|
|
6
|
+
private_class_method :new
|
|
7
|
+
|
|
8
|
+
# Parses +argv+ and print analysis result to +output+.
|
|
9
|
+
# Analysis result is the distribution histogram and the rankings.
|
|
10
|
+
# If +argv+ includes '--verbose' option,
|
|
11
|
+
# then print all analyzing file names too.
|
|
12
|
+
def self.analyze(argv, output=DEFAULT_OUTPUT)
|
|
13
|
+
begin
|
|
14
|
+
new(argv, output)
|
|
15
|
+
rescue OptionParser::ParseError => err
|
|
16
|
+
puts err
|
|
17
|
+
exit 1
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(argv, output) #:nodoc:
|
|
22
|
+
@output = output
|
|
23
|
+
@setting = Setting.for_argv(argv)
|
|
24
|
+
@analyzer = @setting.analyzer
|
|
25
|
+
@num_ranking = @setting.num_ranking
|
|
26
|
+
|
|
27
|
+
analyze_all_targets(argv)
|
|
28
|
+
print_banner()
|
|
29
|
+
print_result()
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
def print_banner
|
|
34
|
+
DebugLogger.debug ''
|
|
35
|
+
@output.puts @setting.banner
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def analyze_all_targets(argv)
|
|
39
|
+
TargetFileFinder.find(argv) do |path|
|
|
40
|
+
@analyzer.analyze(path)
|
|
41
|
+
end
|
|
42
|
+
@histogram = @analyzer.histogram
|
|
43
|
+
@ranking = @analyzer.ranking
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def print_result
|
|
47
|
+
print_histogram()
|
|
48
|
+
print_ranking()
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def print_histogram
|
|
52
|
+
@output.puts @histogram
|
|
53
|
+
@output.puts
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def print_ranking
|
|
57
|
+
@output.puts ranking_banner()
|
|
58
|
+
if rank_all?
|
|
59
|
+
@output.puts @ranking.to_s
|
|
60
|
+
else
|
|
61
|
+
@output.puts @ranking.string_top(@num_ranking)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def ranking_banner
|
|
66
|
+
if rank_all?
|
|
67
|
+
return 'Entire Ranking:'
|
|
68
|
+
end
|
|
69
|
+
"Ranking Top #{@num_ranking}:"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def rank_all?
|
|
73
|
+
:all == @num_ranking
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module RDist
|
|
6
|
+
class DebugLogger
|
|
7
|
+
include Singleton
|
|
8
|
+
extend Forwardable
|
|
9
|
+
|
|
10
|
+
def self.debug(*s)
|
|
11
|
+
s.each{|x| instance.debug(x) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.fatal(*s)
|
|
15
|
+
s.each{|x| instance.fatal(x) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@logger = Logger.new($stdout)
|
|
20
|
+
@logger.level = $DEBUG ? Logger::DEBUG : Logger::FATAL
|
|
21
|
+
@logger.progname = 'RDist'
|
|
22
|
+
@logger.formatter = Proc.new do
|
|
23
|
+
|severity, datetime, progname, msg| "#{msg}\n"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def_delegator :@logger, :debug, :debug
|
|
28
|
+
def_delegator :@logger, :fatal, :fatal
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
class Histogram
|
|
3
|
+
DEFAULT_RANGE_UNIT_SIZE = 5
|
|
4
|
+
# Creates a new histogram from +count_of+.
|
|
5
|
+
# The keys of +count_of+ are divided into some ranges by its count.
|
|
6
|
+
# The size of each range is specified with +range_unit_size+.
|
|
7
|
+
def initialize(count_of,
|
|
8
|
+
range_unit_size=DEFAULT_RANGE_UNIT_SIZE)
|
|
9
|
+
raise 'Initialized with illegal object' unless count_of
|
|
10
|
+
@count_of = count_of
|
|
11
|
+
@range_unit_size = range_unit_size
|
|
12
|
+
init_count_in_range()
|
|
13
|
+
init_cumulative_percentages()
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
public
|
|
17
|
+
WHITESPACE = ' '.freeze
|
|
18
|
+
PERCENTAGE_MAX = 100
|
|
19
|
+
def to_s
|
|
20
|
+
str = ''
|
|
21
|
+
@count_in_range.each_with_index do |count, range_id|
|
|
22
|
+
str << heading_of(range_id)
|
|
23
|
+
str << string_cumulative_percentage_at(range_id)
|
|
24
|
+
str << string_count(count) << WHITESPACE
|
|
25
|
+
str << bar(count) << NEWLINE
|
|
26
|
+
end
|
|
27
|
+
str << footer()
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
def init_count_in_range
|
|
32
|
+
@count_in_range = []
|
|
33
|
+
@count_of.each{|block_name, count|
|
|
34
|
+
range_id = range_id_of(count)
|
|
35
|
+
@count_in_range[range_id] ||= 0
|
|
36
|
+
@count_in_range[range_id] += 1
|
|
37
|
+
}
|
|
38
|
+
replace_nil_by_zero(@count_in_range)
|
|
39
|
+
@count_in_range
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def range_id_of(count)
|
|
43
|
+
(count / @range_unit_size).to_i
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def init_cumulative_percentages
|
|
47
|
+
@cumulative_percentages = get_cumulative_percentages()
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_cumulative_percentages
|
|
51
|
+
cumulative_counts \
|
|
52
|
+
= get_cumulative_counts_from(@count_in_range)
|
|
53
|
+
cumulative_counts.collect do |cumulative_count|
|
|
54
|
+
percentage_of(cumulative_count)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get_cumulative_counts_from(ary)
|
|
59
|
+
cumulative_counts = []
|
|
60
|
+
prev = 0
|
|
61
|
+
ary.each_with_index do |item, index|
|
|
62
|
+
cumulative_counts[index] = prev += item
|
|
63
|
+
end
|
|
64
|
+
cumulative_counts
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def replace_nil_by_zero(ary)
|
|
68
|
+
ary.each_index do |index|
|
|
69
|
+
ary[index] ||= 0
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def sum_of_count
|
|
74
|
+
get_sum_of(@count_in_range)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def get_sum_of(ary)
|
|
78
|
+
sum = 0
|
|
79
|
+
ary.each do |item|
|
|
80
|
+
sum += item
|
|
81
|
+
end
|
|
82
|
+
sum
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
LENGTH_RANGE_MIN = 3
|
|
86
|
+
LENGTH_RANGE_MAX = LENGTH_RANGE_MIN
|
|
87
|
+
FORMAT_RANGE_HEADING \
|
|
88
|
+
= "%#{LENGTH_RANGE_MIN}d-%#{LENGTH_RANGE_MAX}d: ".freeze
|
|
89
|
+
def heading_of(range_id)
|
|
90
|
+
sprintf(FORMAT_RANGE_HEADING,
|
|
91
|
+
min_of(range_id), max_of(range_id))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def min_of(range_id)
|
|
95
|
+
range_id * @range_unit_size
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def max_of(range_id)
|
|
99
|
+
(range_id + 1) * @range_unit_size - 1
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def string_cumulative_percentage_at(range_id)
|
|
103
|
+
string_percentage(@cumulative_percentages[range_id])
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
FORMAT_PERCENTAGE = '%3d%% '.freeze
|
|
107
|
+
def string_percentage(percentage)
|
|
108
|
+
sprintf(FORMAT_PERCENTAGE, percentage)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
FORMAT_COUNT = '%5d'.freeze
|
|
112
|
+
def string_count(count)
|
|
113
|
+
sprintf(FORMAT_COUNT, count)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
UNIT_CHAR_FOR_BAR = '*'.freeze
|
|
117
|
+
def bar(count)
|
|
118
|
+
UNIT_CHAR_FOR_BAR * bar_length_for(count)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def bar_length_for(count)
|
|
122
|
+
(percentage_of(count) / 2).ceil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def percentage_of(count)
|
|
126
|
+
(count.to_f / sum_of_count) * PERCENTAGE_MAX
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def footer
|
|
130
|
+
str = ' Total: '
|
|
131
|
+
str << string_percentage(PERCENTAGE_MAX)
|
|
132
|
+
str << string_count(sum_of_count())
|
|
133
|
+
str << NEWLINE
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
class Ranking
|
|
3
|
+
# Creates a new ranking from +score_of+.
|
|
4
|
+
# The items are sorted by ascending order.
|
|
5
|
+
def initialize(score_of)
|
|
6
|
+
@rankings = score_of.sort_by{|key, score| [-score, key] }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
public
|
|
10
|
+
# Returns items from 1st to +n+-th.
|
|
11
|
+
# Each item is an Array as [key, score].
|
|
12
|
+
def top(n)
|
|
13
|
+
@rankings.first(n)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
FORMAT = '%7d: %s'.freeze
|
|
17
|
+
FORMATTER = Proc.new{|key, score| sprintf(FORMAT, score, key) }
|
|
18
|
+
# Returns formatted string of top +n+ items.
|
|
19
|
+
def string_top(n)
|
|
20
|
+
str = ''
|
|
21
|
+
n.times do |i|
|
|
22
|
+
key, score = @rankings[i]
|
|
23
|
+
str << FORMATTER.call(key, score)
|
|
24
|
+
str << NEWLINE
|
|
25
|
+
end
|
|
26
|
+
str
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns formatted string of all items.
|
|
30
|
+
def to_s
|
|
31
|
+
string_top(@rankings.size)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module RDist
|
|
2
|
+
class Setting
|
|
3
|
+
module Marcos
|
|
4
|
+
private
|
|
5
|
+
def def_option(*args, &block)
|
|
6
|
+
method_symbol = method_symbol_of(args)
|
|
7
|
+
block = Proc.new{} unless block
|
|
8
|
+
define_method(method_symbol, &block)
|
|
9
|
+
const_get(:OPTION_SEEDS) << [args, method_symbol]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def method_symbol_of(option_args)
|
|
13
|
+
str_option = option_args.first
|
|
14
|
+
str_converted = str_option.split(/\s/).first
|
|
15
|
+
str_converted.gsub!(/-/, '_')
|
|
16
|
+
:"__option_switch#{str_converted}__"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'rdist/setting/macros'
|
|
3
|
+
|
|
4
|
+
module RDist
|
|
5
|
+
class Setting
|
|
6
|
+
extend Marcos
|
|
7
|
+
|
|
8
|
+
private_class_method :new
|
|
9
|
+
|
|
10
|
+
def self.for_argv(argv)
|
|
11
|
+
new(argv)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(argv)
|
|
15
|
+
@argv = argv
|
|
16
|
+
init_attributes()
|
|
17
|
+
configure_option_parser()
|
|
18
|
+
parse_options()
|
|
19
|
+
init_analyzer()
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :analyzer
|
|
23
|
+
attr_reader :banner
|
|
24
|
+
attr_reader :num_ranking
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
DEFAULT_NUM_RANKING = 5
|
|
28
|
+
def init_attributes
|
|
29
|
+
@analyzer_class = Analyzer::MethodLength
|
|
30
|
+
@banner = 'Method length Distribution:'
|
|
31
|
+
@num_ranking = DEFAULT_NUM_RANKING
|
|
32
|
+
@interval = nil
|
|
33
|
+
init_parser()
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def init_parser
|
|
37
|
+
@option_parser = OptionParser.new
|
|
38
|
+
@option_parser.banner = 'Usage: rdist [options] FILE...'
|
|
39
|
+
@option_parser.summary_width = 30
|
|
40
|
+
@option_parser.summary_indent = ' '
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parse_options
|
|
44
|
+
@option_parser.parse!(@argv)
|
|
45
|
+
@argv << '.' if @argv.empty?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def init_analyzer
|
|
49
|
+
@analyzer = @analyzer_class.new
|
|
50
|
+
@analyzer.histogram_interval = @interval if @interval
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
OPTION_SEEDS = [] # Do NOT freeze!!
|
|
54
|
+
def configure_option_parser
|
|
55
|
+
OPTION_SEEDS.each do |args, method_symbol|
|
|
56
|
+
method_call = method_call(method_symbol)
|
|
57
|
+
@option_parser.on(*args, &method_call)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def method_call(symbol)
|
|
62
|
+
Proc.new do |*args|
|
|
63
|
+
__send__(symbol, *args)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def_option('-m',
|
|
68
|
+
'--num-lines-in-method',
|
|
69
|
+
'--method-length',
|
|
70
|
+
'count number of lines in a method',
|
|
71
|
+
' (default)')
|
|
72
|
+
|
|
73
|
+
def_option('-c',
|
|
74
|
+
'--num-lines-in-class',
|
|
75
|
+
'count number of lines in Class/Module') do
|
|
76
|
+
@analyzer_class = Analyzer::NumLinesInClass
|
|
77
|
+
@banner = 'Distribution of number of lines in Class/Module:'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def_option('-C',
|
|
81
|
+
'--num-methods-in-class',
|
|
82
|
+
'ount number of method definitions',
|
|
83
|
+
' in Class/Module') do
|
|
84
|
+
@analyzer_class = Analyzer::NumMethodsInClass
|
|
85
|
+
@banner = 'Distribution of number of method definitons in Class/Module:'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def_option('-i', '--interval [N]', Integer,
|
|
89
|
+
'set histogram interval') do |n|
|
|
90
|
+
@interval = n if n
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def_option('-r', '--rank [N]', Integer,
|
|
94
|
+
'show ranking up to Nth item (default 5)') do |n|
|
|
95
|
+
@num_ranking = n if n
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def_option('--verbose',
|
|
99
|
+
'print verbose information to STDOUT',
|
|
100
|
+
' (analysis process, entire ranking)') do
|
|
101
|
+
$DEBUG = true
|
|
102
|
+
@num_ranking = :all
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def_option('-h', '--help', 'show this message') do
|
|
106
|
+
puts @option_parser.help
|
|
107
|
+
exit
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def_option('-v', '--version', 'show version') do
|
|
111
|
+
puts "RDist-#{VERSION} - method length reporter for Ruby"
|
|
112
|
+
exit
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'find'
|
|
2
|
+
|
|
3
|
+
module RDist
|
|
4
|
+
module TargetFileFinder
|
|
5
|
+
module_function
|
|
6
|
+
def find(argv)
|
|
7
|
+
Find.find(*argv) do |path|
|
|
8
|
+
if FileTest.directory?(path)
|
|
9
|
+
Find.prune if hidden?(path)
|
|
10
|
+
next
|
|
11
|
+
end
|
|
12
|
+
next if hidden?(path)
|
|
13
|
+
next unless ruby_code?(path)
|
|
14
|
+
next if test_code?(path)
|
|
15
|
+
yield path
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
PATTERN_HIDDEN = %r/
|
|
20
|
+
\A
|
|
21
|
+
\.
|
|
22
|
+
[^\.] # pattern to accept parent directory: ``..''
|
|
23
|
+
/nxm
|
|
24
|
+
def hidden?(path)
|
|
25
|
+
PATTERN_HIDDEN =~ File.basename(path)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
EXTNAME_RUBY_CODE = '.rb'.freeze
|
|
29
|
+
EXTNAME_RUBY_EXEC_CODE = '.rbw'.freeze
|
|
30
|
+
def ruby_code?(path)
|
|
31
|
+
case File.extname(path)
|
|
32
|
+
when EXTNAME_RUBY_CODE, EXTNAME_RUBY_EXEC_CODE then true
|
|
33
|
+
else false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
PATTERN_TEST_CODE \
|
|
38
|
+
= /(?:\A test_ | _spec \z)/nxm.freeze
|
|
39
|
+
def test_code?(path)
|
|
40
|
+
PATTERN_TEST_CODE =~ File.basename(path)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/rdist.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'rdist/analyzer'
|
|
2
|
+
require 'rdist/commandlineinterface'
|
|
3
|
+
require 'rdist/debuglogger'
|
|
4
|
+
require 'rdist/histogram'
|
|
5
|
+
require 'rdist/ranking'
|
|
6
|
+
require 'rdist/setting'
|
|
7
|
+
require 'rdist/targetfilefinder'
|
|
8
|
+
|
|
9
|
+
module RDist
|
|
10
|
+
VERSION = '0.0.1'
|
|
11
|
+
|
|
12
|
+
ALLOW_NESTING = true
|
|
13
|
+
DENY_NESTING = false
|
|
14
|
+
|
|
15
|
+
NEWLINE = "\n".freeze
|
|
16
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
|
|
3
|
+
describe CommandLineInterface, %q[when ``normal_code.rb'' given] do
|
|
4
|
+
def check_output(string_io, count_of,
|
|
5
|
+
histogram_interval, num_ranking,
|
|
6
|
+
banner='Method length Distribution:',
|
|
7
|
+
ranking_banner='Ranking Top 5:')
|
|
8
|
+
histogram = Histogram.new(count_of, histogram_interval)
|
|
9
|
+
ranking = Ranking.new(count_of)
|
|
10
|
+
expected_string = "#{banner}\n"
|
|
11
|
+
expected_string << histogram.to_s
|
|
12
|
+
expected_string << "\n#{ranking_banner}\n"
|
|
13
|
+
expected_string << ranking.string_top(num_ranking)
|
|
14
|
+
string_io.string.should == expected_string
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
before do
|
|
18
|
+
@fixture_path = FIXTURE_DIR + '/normal_code.rb'
|
|
19
|
+
@string_io = StringIO.new
|
|
20
|
+
@expected_count_of = {
|
|
21
|
+
'initialize (at ./spec/../spec/fixtures/normal_code.rb:3)' => 4,
|
|
22
|
+
'each_vertex (at ./spec/../spec/fixtures/normal_code.rb:14)' => 3,
|
|
23
|
+
'surrounded? (at ./spec/../spec/fixtures/normal_code.rb:20)' => 1,
|
|
24
|
+
'concat (at ./spec/../spec/fixtures/normal_code.rb:24)' => 6,
|
|
25
|
+
'init_edge_vertices (at ./spec/../spec/fixtures/normal_code.rb:34)' => 10,
|
|
26
|
+
'dame_vertices (at ./spec/../spec/fixtures/normal_code.rb:47)' => 3,
|
|
27
|
+
'space? (at ./spec/../spec/fixtures/normal_code.rb:53)' => 1,
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should count number of method length correctly' do
|
|
32
|
+
CommandLineInterface.analyze(['--interval', '1', @fixture_path],
|
|
33
|
+
@string_io)
|
|
34
|
+
check_output(@string_io, @expected_count_of, 1, 5)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it %q[should show entire ranking if '--verbose' option is given] do
|
|
38
|
+
CommandLineInterface.analyze(['--verbose', @fixture_path], @string_io)
|
|
39
|
+
check_output(@string_io, @expected_count_of, 5, 7,
|
|
40
|
+
'Method length Distribution:',
|
|
41
|
+
'Entire Ranking:')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Go
|
|
2
|
+
class Ren
|
|
3
|
+
def initialize(board, color, vertices)
|
|
4
|
+
@board = board
|
|
5
|
+
@color = color
|
|
6
|
+
@vertices = vertices
|
|
7
|
+
init_edge_vertices()
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :color
|
|
11
|
+
attr_reader :vertices
|
|
12
|
+
|
|
13
|
+
public
|
|
14
|
+
def each_vertex
|
|
15
|
+
@vertices.each do |vertex|
|
|
16
|
+
yield vertex
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def surrounded?
|
|
21
|
+
dame_vertices.empty?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def concat(other)
|
|
25
|
+
if other.color != @color
|
|
26
|
+
raise %q[Can't contat enemy's ren]
|
|
27
|
+
end
|
|
28
|
+
@vertices.concat(other.vertices)
|
|
29
|
+
init_edge_vertices()
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
def init_edge_vertices
|
|
35
|
+
@edge_vertices = []
|
|
36
|
+
each_vertex do |vertex|
|
|
37
|
+
vertex.each_neighbor do |neighbor|
|
|
38
|
+
next unless @board.include?(neighbor)
|
|
39
|
+
next if @vertices.include?(neighbor)
|
|
40
|
+
next if @edge_vertices.include?(neighbor)
|
|
41
|
+
@edge_vertices << neighbor
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
@edge_vertices
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def dame_vertices
|
|
48
|
+
@edge_vertices.select do |vertex|
|
|
49
|
+
space?(vertex)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def space?(vertex)
|
|
54
|
+
@board.have_space_on?(vertex)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
describe Ranking, 'when initialized with scores of 5 people' do
|
|
2
|
+
before do
|
|
3
|
+
score_of = {
|
|
4
|
+
'George' => 0,
|
|
5
|
+
'John' => 100,
|
|
6
|
+
'Bob' => 50,
|
|
7
|
+
'Peter' => 0,
|
|
8
|
+
'Ben' => 100,
|
|
9
|
+
}
|
|
10
|
+
@ranking = Ranking.new(score_of)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'should show top 3' do
|
|
14
|
+
ary = [
|
|
15
|
+
['Ben', 100],
|
|
16
|
+
['John', 100],
|
|
17
|
+
['Bob', 50],
|
|
18
|
+
]
|
|
19
|
+
@ranking.top(3).should == ary
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'should serialize top 2' do
|
|
23
|
+
expected_string = <<-'END_STRING'
|
|
24
|
+
100: Ben
|
|
25
|
+
100: John
|
|
26
|
+
50: Bob
|
|
27
|
+
END_STRING
|
|
28
|
+
@ranking.string_top(3).should == expected_string
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should serialize entire rankings' do
|
|
32
|
+
expected_string = <<-'END_STRING'
|
|
33
|
+
100: Ben
|
|
34
|
+
100: John
|
|
35
|
+
50: Bob
|
|
36
|
+
0: George
|
|
37
|
+
0: Peter
|
|
38
|
+
END_STRING
|
|
39
|
+
@ranking.to_s.should == expected_string
|
|
40
|
+
end
|
|
41
|
+
end
|
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color --require spec/spec_helper
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rdist
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yoshifumi Shimono
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2008-02-26 00:00:00 +09:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: reports the distribution of method length in your Ruby codes.
|
|
17
|
+
email: yoshifumi.shimono@gmail.com
|
|
18
|
+
executables:
|
|
19
|
+
- rdist
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files:
|
|
23
|
+
- README
|
|
24
|
+
- ChangeLog
|
|
25
|
+
files:
|
|
26
|
+
- README
|
|
27
|
+
- ChangeLog
|
|
28
|
+
- Rakefile
|
|
29
|
+
- bin/rdist
|
|
30
|
+
- spec/spec.opts
|
|
31
|
+
- spec/spec_helper.rb
|
|
32
|
+
- spec/commandlineinterface_spec.rb
|
|
33
|
+
- spec/fixtures
|
|
34
|
+
- spec/ranking_spec.rb
|
|
35
|
+
- spec/fixtures/normal_code.rb
|
|
36
|
+
- lib/rdist
|
|
37
|
+
- lib/rdist.rb
|
|
38
|
+
- lib/rdist/histogram.rb
|
|
39
|
+
- lib/rdist/targetfilefinder.rb
|
|
40
|
+
- lib/rdist/analyzer.rb
|
|
41
|
+
- lib/rdist/setting.rb
|
|
42
|
+
- lib/rdist/setting
|
|
43
|
+
- lib/rdist/debuglogger.rb
|
|
44
|
+
- lib/rdist/analyzer
|
|
45
|
+
- lib/rdist/ranking.rb
|
|
46
|
+
- lib/rdist/commandlineinterface.rb
|
|
47
|
+
- lib/rdist/setting/macros.rb
|
|
48
|
+
- lib/rdist/analyzer/macros.rb
|
|
49
|
+
- lib/rdist/analyzer/methodlength.rb
|
|
50
|
+
- lib/rdist/analyzer/state.rb
|
|
51
|
+
- lib/rdist/analyzer/numlinesinclass.rb
|
|
52
|
+
- lib/rdist/analyzer/base.rb
|
|
53
|
+
- lib/rdist/analyzer/state
|
|
54
|
+
- lib/rdist/analyzer/nummethodsinclass.rb
|
|
55
|
+
- lib/rdist/analyzer/state/inmultilinecomment.rb
|
|
56
|
+
- lib/rdist/analyzer/state/inblock.rb
|
|
57
|
+
- lib/rdist/analyzer/state/waitingblock.rb
|
|
58
|
+
- lib/rdist/analyzer/state/base.rb
|
|
59
|
+
has_rdoc: true
|
|
60
|
+
homepage: http://rdist.rubyforge.org
|
|
61
|
+
post_install_message:
|
|
62
|
+
rdoc_options:
|
|
63
|
+
- --title
|
|
64
|
+
- rdist documentation
|
|
65
|
+
- --charset
|
|
66
|
+
- utf-8
|
|
67
|
+
- --opname
|
|
68
|
+
- index.html
|
|
69
|
+
- --line-numbers
|
|
70
|
+
- --main
|
|
71
|
+
- README
|
|
72
|
+
- --inline-source
|
|
73
|
+
- --exclude
|
|
74
|
+
- ^(examples|extras)/
|
|
75
|
+
require_paths:
|
|
76
|
+
- lib
|
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: "0"
|
|
82
|
+
version:
|
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: "0"
|
|
88
|
+
version:
|
|
89
|
+
requirements: []
|
|
90
|
+
|
|
91
|
+
rubyforge_project: rdist
|
|
92
|
+
rubygems_version: 1.0.1
|
|
93
|
+
signing_key:
|
|
94
|
+
specification_version: 2
|
|
95
|
+
summary: reports the distribution of method length in your Ruby codes.
|
|
96
|
+
test_files: []
|
|
97
|
+
|