rails_stats 0.0.4
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +2 -0
- data/lib/rails_stats/app_statistics.rb +61 -0
- data/lib/rails_stats/code_statistics.rb +160 -0
- data/lib/rails_stats/code_statistics_calculator.rb +89 -0
- data/lib/rails_stats/cucumber_statistics.rb +46 -0
- data/lib/rails_stats/gem_statistics.rb +31 -0
- data/lib/rails_stats/inflector.rb +605 -0
- data/lib/rails_stats/rake.rb +37 -0
- data/lib/rails_stats/root_statistics.rb +42 -0
- data/lib/rails_stats/spec_statistics.rb +74 -0
- data/lib/rails_stats/util.rb +97 -0
- data/lib/rails_stats/version.rb +3 -0
- data/lib/rails_stats.rb +20 -0
- data/rails_stats.gemspec +24 -0
- data/stats.thor +8 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 944d0c8334fdcb76cec1a8f46b4df162318e79a8
|
4
|
+
data.tar.gz: 937553b382a844f1eaf2e7aade385ba8440ec1b6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8ab477ff73f0e87134f71761397358388fb10947f8afad91da6ddc828c235eaf872116097be0747273a3c40d441808528e507c6e7814a89b5ea9da9702db832d
|
7
|
+
data.tar.gz: 5803869bde15c3d38e2e41b6f1f279dc4993187cfe77e2d4148f67093b6b82fceab9e966e895e2ce0a874b1e80dbeb43b0084c555e34f868808257d97e4d4b81
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# RailsStats
|
2
|
+
|
3
|
+
See stuff about a Rails app.
|
4
|
+
|
5
|
+
```bash
|
6
|
+
$ bundle exec thor stats:calculate ~/path/to/app/
|
7
|
+
|
8
|
+
Directory: ~/path/to/app/
|
9
|
+
|
10
|
+
+----------------------+-------+-------+---------+---------+-----+-------+
|
11
|
+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
|
12
|
+
+----------------------+-------+-------+---------+---------+-----+-------+
|
13
|
+
| Controllers | 1848 | 1483 | 32 | 174 | 5 | 6 |
|
14
|
+
| Helpers | 2257 | 1892 | 45 | 245 | 5 | 5 |
|
15
|
+
| Jobs | 399 | 295 | 11 | 33 | 3 | 6 |
|
16
|
+
| Models | 4584 | 3509 | 61 | 526 | 8 | 4 |
|
17
|
+
| Observers | 42 | 22 | 2 | 5 | 2 | 2 |
|
18
|
+
| Libraries | 2987 | 2272 | 30 | 287 | 9 | 5 |
|
19
|
+
| Configuration | 1233 | 669 | 4 | 17 | 4 | 37 |
|
20
|
+
| Spec Support | 1416 | 1152 | 4 | 30 | 7 | 36 |
|
21
|
+
| Integration Tests | 91 | 73 | 0 | 1 | 0 | 71 |
|
22
|
+
| Lib Tests | 101 | 83 | 0 | 1 | 0 | 81 |
|
23
|
+
| Model Tests | 3397 | 2522 | 0 | 18 | 0 | 138 |
|
24
|
+
| Cucumber Support | 739 | 521 | 0 | 1 | 0 | 519 |
|
25
|
+
| Cucumber Features | 2711 | 2487 | 29 | 145 | 5 | 15 |
|
26
|
+
+----------------------+-------+-------+---------+---------+-----+-------+
|
27
|
+
| Total | 21805 | 16980 | 218 | 1483 | 6 | 9 |
|
28
|
+
+----------------------+-------+-------+---------+---------+-----+-------+
|
29
|
+
Code LOC: 10142 Test LOC: 6838 Code to Test Ratio: 1:0.7
|
30
|
+
|
31
|
+
```
|
32
|
+
|
33
|
+
### TODO
|
34
|
+
|
35
|
+
* option to print out by app directory (stats per engine)
|
36
|
+
* Add views (jbuilder, erb, haml) but don't count towards ratios
|
37
|
+
* Support JS for projects that have it in public
|
38
|
+
* Add css but don't count towards ratios
|
39
|
+
* output other metrics like number of tables and columns
|
40
|
+
* test unit support
|
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module RailsStats
|
2
|
+
class AppStatistics
|
3
|
+
attr_reader :statistics, :total, :test
|
4
|
+
|
5
|
+
def initialize(directory)
|
6
|
+
@test = false
|
7
|
+
@directory = directory
|
8
|
+
@statistics = calculate_statistics
|
9
|
+
@total = calculate_total
|
10
|
+
end
|
11
|
+
|
12
|
+
def key_concepts
|
13
|
+
directories.collect{ |path| File.basename(path) }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def calculate_total
|
19
|
+
out = CodeStatisticsCalculator.new
|
20
|
+
@statistics.each do |key, stats|
|
21
|
+
out.add(stats)
|
22
|
+
end
|
23
|
+
out
|
24
|
+
end
|
25
|
+
|
26
|
+
def calculate_statistics
|
27
|
+
Util.calculate_statistics(directories)
|
28
|
+
end
|
29
|
+
|
30
|
+
def directories
|
31
|
+
return @directories if @directories
|
32
|
+
out = []
|
33
|
+
Dir.foreach(@directory) do |file_name|
|
34
|
+
path = File.join(@directory, file_name)
|
35
|
+
next unless File.directory?(path)
|
36
|
+
next if (/^\./ =~ file_name)
|
37
|
+
next if file_name == "assets" # doing separately
|
38
|
+
next if file_name == "views" # TODO
|
39
|
+
out << path
|
40
|
+
end
|
41
|
+
|
42
|
+
assets = File.join(@directory, "assets")
|
43
|
+
if File.directory?(assets)
|
44
|
+
Dir.foreach(assets) do |file_name|
|
45
|
+
path = File.join(assets, file_name)
|
46
|
+
next unless File.directory?(path)
|
47
|
+
next if (/^\./ =~ file_name)
|
48
|
+
|
49
|
+
case file_name
|
50
|
+
when "javascripts"
|
51
|
+
out << path
|
52
|
+
# TODO when "css"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
out
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# railties/lib/rails/code_statistics.rb
|
2
|
+
|
3
|
+
module RailsStats
|
4
|
+
class CodeStatistics
|
5
|
+
|
6
|
+
RAILS_APP_FOLDERS = ['models',
|
7
|
+
'controllers',
|
8
|
+
'helpers',
|
9
|
+
'mailers',
|
10
|
+
'views',
|
11
|
+
'assets']
|
12
|
+
|
13
|
+
def initialize(root_directory)
|
14
|
+
@root_directory = root_directory
|
15
|
+
@key_concepts = calculate_key_concepts
|
16
|
+
@projects = calculate_projects
|
17
|
+
@statistics = calculate_statistics
|
18
|
+
@total = calculate_total
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
print_header
|
23
|
+
@statistics.each { |key, stats| print_line(key, stats) }
|
24
|
+
print_splitter
|
25
|
+
|
26
|
+
if @total
|
27
|
+
print_line("Total", @total)
|
28
|
+
print_splitter
|
29
|
+
end
|
30
|
+
|
31
|
+
print_code_test_stats
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def calculate_key_concepts
|
36
|
+
# returns names of main things like models, controllers, services, etc
|
37
|
+
concepts = {}
|
38
|
+
app_projects.each do |project|
|
39
|
+
project.key_concepts.each do |key|
|
40
|
+
concepts[key] = true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO: maybe gem names?
|
45
|
+
|
46
|
+
concepts.keys
|
47
|
+
end
|
48
|
+
|
49
|
+
def calculate_projects
|
50
|
+
out = []
|
51
|
+
out += app_projects
|
52
|
+
out += calculate_root_projects
|
53
|
+
out += calculate_gem_projects
|
54
|
+
out += calculate_spec_projects
|
55
|
+
out += calculate_test_projects
|
56
|
+
out += calculate_cucumber_projects
|
57
|
+
out
|
58
|
+
end
|
59
|
+
|
60
|
+
def app_projects
|
61
|
+
@app_projects ||= calculate_app_projects
|
62
|
+
end
|
63
|
+
|
64
|
+
def calculate_app_projects
|
65
|
+
apps = Util.calculate_projects(@root_directory, "**", "app", RAILS_APP_FOLDERS)
|
66
|
+
apps.collect do |root_path|
|
67
|
+
AppStatistics.new(root_path)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def calculate_gem_projects
|
72
|
+
gems = Util.calculate_projects(@root_directory, "**", "*.gemspec")
|
73
|
+
gems.collect do |root_path|
|
74
|
+
GemStatistics.new(root_path)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def calculate_spec_projects
|
79
|
+
specs = Util.calculate_projects(@root_directory, "**", "spec", "spec_helper.rb")
|
80
|
+
specs.collect do |root_path|
|
81
|
+
SpecStatistics.new(root_path, @key_concepts)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def calculate_test_projects
|
86
|
+
[] # TODO: test unit
|
87
|
+
end
|
88
|
+
|
89
|
+
def calculate_root_projects
|
90
|
+
[RootStatistics.new(@root_directory)]
|
91
|
+
end
|
92
|
+
|
93
|
+
def calculate_cucumber_projects
|
94
|
+
cukes = Util.calculate_projects(@root_directory, "**", "*.feature")
|
95
|
+
cukes.collect do |root_path|
|
96
|
+
CucumberStatistics.new(root_path)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def calculate_statistics
|
101
|
+
out = {}
|
102
|
+
@projects.each do |project|
|
103
|
+
project.statistics.each do |key, stats|
|
104
|
+
out[key] ||= CodeStatisticsCalculator.new(project.test)
|
105
|
+
out[key].add(stats)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
out
|
109
|
+
end
|
110
|
+
|
111
|
+
def calculate_total
|
112
|
+
@statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, total|
|
113
|
+
total.add(pair.last)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def calculate_code
|
118
|
+
code_loc = 0
|
119
|
+
@statistics.each { |k, v| code_loc += v.code_lines unless v.test }
|
120
|
+
code_loc
|
121
|
+
end
|
122
|
+
|
123
|
+
def calculate_tests
|
124
|
+
test_loc = 0
|
125
|
+
@statistics.each { |k, v| test_loc += v.code_lines if v.test }
|
126
|
+
test_loc
|
127
|
+
end
|
128
|
+
|
129
|
+
def print_header
|
130
|
+
print_splitter
|
131
|
+
puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
|
132
|
+
print_splitter
|
133
|
+
end
|
134
|
+
|
135
|
+
def print_splitter
|
136
|
+
puts "+----------------------+-------+-------+---------+---------+-----+-------+"
|
137
|
+
end
|
138
|
+
|
139
|
+
def print_line(name, statistics)
|
140
|
+
m_over_c = (statistics.methods / statistics.classes) rescue m_over_c = 0
|
141
|
+
loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue loc_over_m = 0
|
142
|
+
|
143
|
+
puts "| #{name.ljust(20)} " \
|
144
|
+
"| #{statistics.lines.to_s.rjust(5)} " \
|
145
|
+
"| #{statistics.code_lines.to_s.rjust(5)} " \
|
146
|
+
"| #{statistics.classes.to_s.rjust(7)} " \
|
147
|
+
"| #{statistics.methods.to_s.rjust(7)} " \
|
148
|
+
"| #{m_over_c.to_s.rjust(3)} " \
|
149
|
+
"| #{loc_over_m.to_s.rjust(5)} |"
|
150
|
+
end
|
151
|
+
|
152
|
+
def print_code_test_stats
|
153
|
+
code = calculate_code
|
154
|
+
tests = calculate_tests
|
155
|
+
|
156
|
+
puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
|
157
|
+
puts ""
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# railties/lib/rails/code_statistics_calculator.rb
|
2
|
+
|
3
|
+
module RailsStats
|
4
|
+
class CodeStatisticsCalculator #:nodoc:
|
5
|
+
attr_reader :lines, :code_lines, :classes, :methods, :test
|
6
|
+
|
7
|
+
PATTERNS = {
|
8
|
+
rb: {
|
9
|
+
line_comment: /^\s*#/,
|
10
|
+
begin_block_comment: /^=begin/,
|
11
|
+
end_block_comment: /^=end/,
|
12
|
+
class: /^\s*class\s+[_A-Z]/,
|
13
|
+
method: /^\s*def\s+[_a-z]/,
|
14
|
+
},
|
15
|
+
js: {
|
16
|
+
line_comment: %r{^\s*//},
|
17
|
+
begin_block_comment: %r{^\s*/\*},
|
18
|
+
end_block_comment: %r{\*/},
|
19
|
+
method: /function(\s+[_a-zA-Z][\da-zA-Z]*)?\s*\(/,
|
20
|
+
},
|
21
|
+
coffee: {
|
22
|
+
line_comment: /^\s*#/,
|
23
|
+
begin_block_comment: /^\s*###/,
|
24
|
+
end_block_comment: /^\s*###/,
|
25
|
+
class: /^\s*class\s+[_A-Z]/,
|
26
|
+
method: /[-=]>/,
|
27
|
+
},
|
28
|
+
feature: {
|
29
|
+
class: /^\s*Feature:/,
|
30
|
+
method: /^\s*Scenario:/,
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
def initialize(test=false)
|
35
|
+
@test = test
|
36
|
+
@lines = 0
|
37
|
+
@code_lines = 0
|
38
|
+
@classes = 0
|
39
|
+
@methods = 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def add(code_statistics_calculator)
|
43
|
+
@lines += code_statistics_calculator.lines
|
44
|
+
@code_lines += code_statistics_calculator.code_lines
|
45
|
+
@classes += code_statistics_calculator.classes
|
46
|
+
@methods += code_statistics_calculator.methods
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_by_file_path(file_path)
|
50
|
+
File.open(file_path) do |f|
|
51
|
+
self.add_by_io(f, file_type(file_path))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_by_io(io, file_type)
|
56
|
+
patterns = PATTERNS[file_type] || {}
|
57
|
+
|
58
|
+
comment_started = false
|
59
|
+
|
60
|
+
while line = io.gets
|
61
|
+
@lines += 1
|
62
|
+
|
63
|
+
if comment_started
|
64
|
+
if patterns[:end_block_comment] && line =~ patterns[:end_block_comment]
|
65
|
+
comment_started = false
|
66
|
+
end
|
67
|
+
next
|
68
|
+
else
|
69
|
+
if patterns[:begin_block_comment] && line =~ patterns[:begin_block_comment]
|
70
|
+
comment_started = true
|
71
|
+
next
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
@classes += 1 if patterns[:class] && line =~ patterns[:class]
|
76
|
+
@methods += 1 if patterns[:method] && line =~ patterns[:method]
|
77
|
+
if line !~ /^\s*$/ && (patterns[:line_comment].nil? || line !~ patterns[:line_comment])
|
78
|
+
@code_lines += 1
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def file_type(file_path)
|
85
|
+
File.extname(file_path).sub(/\A\./, '').downcase.to_sym
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module RailsStats
|
2
|
+
class CucumberStatistics
|
3
|
+
attr_reader :statistics, :total, :test
|
4
|
+
|
5
|
+
def initialize(directory)
|
6
|
+
@test = true
|
7
|
+
@directory = directory
|
8
|
+
@statistics = calculate_statistics
|
9
|
+
@total = calculate_total
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def calculate_total
|
15
|
+
out = CodeStatisticsCalculator.new(true)
|
16
|
+
@statistics.each do |key, stats|
|
17
|
+
out.add(stats)
|
18
|
+
end
|
19
|
+
out
|
20
|
+
end
|
21
|
+
|
22
|
+
def calculate_statistics
|
23
|
+
out = {}
|
24
|
+
categorize_files.each do |key, list|
|
25
|
+
out[key] = Util.calculate_file_statistics(list)
|
26
|
+
end
|
27
|
+
out
|
28
|
+
end
|
29
|
+
|
30
|
+
def categorize_files
|
31
|
+
out = {}
|
32
|
+
Dir[File.join(@directory, "**", "*.rb")].each do |file_path|
|
33
|
+
out["Cucumber Support"] ||= []
|
34
|
+
out["Cucumber Support"] << file_path
|
35
|
+
end
|
36
|
+
|
37
|
+
Dir[File.join(@directory, "**", "*.feature")].each do |file_path|
|
38
|
+
out["Cucumber Features"] ||= []
|
39
|
+
out["Cucumber Features"] << file_path
|
40
|
+
end
|
41
|
+
|
42
|
+
out
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RailsStats
|
2
|
+
class GemStatistics
|
3
|
+
attr_reader :statistics, :total, :test
|
4
|
+
|
5
|
+
def initialize(directory)
|
6
|
+
@test = false
|
7
|
+
@directory = directory
|
8
|
+
@statistics = calculate_statistics
|
9
|
+
@total = calculate_total
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def calculate_total
|
15
|
+
out = CodeStatisticsCalculator.new
|
16
|
+
@statistics.each do |key, stats|
|
17
|
+
out.add(stats)
|
18
|
+
end
|
19
|
+
out
|
20
|
+
end
|
21
|
+
|
22
|
+
def calculate_statistics
|
23
|
+
# ignore gem/app so as to not double-count engines
|
24
|
+
lib = File.join(@directory, "lib")
|
25
|
+
Util.calculate_statistics([lib]) do |path|
|
26
|
+
"Gems"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|