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 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
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rails_stats.gemspec
4
+ gemspec
5
+
6
+ gem 'debugger'
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,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -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