Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More detailed introspection of app #1

Merged
merged 14 commits into from
Nov 4, 2014
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -2,3 +2,5 @@ source 'https://rubygems.org'

# Specify your gem's dependencies in rails_stats.gemspec
gemspec

gem 'debugger'
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -5,23 +5,34 @@ See stuff about a Rails app.
```bash
$ bundle exec thor stats:calculate ../../path/to/app/

Directory: ../../path/to/app/
Directory: ~/path/to/app/

+----------------------+-------+-------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers | 1848 | 1483 | 32 | 174 | 5 | 6 |
| Helpers | 2257 | 1892 | 45 | 245 | 5 | 5 |
| Jobs | 399 | 295 | 11 | 33 | 3 | 6 |
| Models | 4584 | 3509 | 61 | 526 | 8 | 4 |
| Observers | 42 | 22 | 2 | 5 | 2 | 2 |
| Libraries | 2987 | 2272 | 30 | 287 | 9 | 5 |
| Helper tests | 124 | 107 | 3 | 16 | 5 | 4 |
| Model tests | 3397 | 2522 | 0 | 18 | 0 | 138 |
| Integration tests | 91 | 73 | 0 | 1 | 0 | 71 |
| Library tests | 856 | 721 | 0 | 2 | 0 | 358 |
| Cucumber tests | 3450 | 3008 | 29 | 146 | 5 | 18 |
| Configuration | 1233 | 669 | 4 | 17 | 4 | 37 |
| Spec Support | 1416 | 1152 | 4 | 30 | 7 | 36 |
| Integration Tests | 91 | 73 | 0 | 1 | 0 | 71 |
| Lib Tests | 101 | 83 | 0 | 1 | 0 | 81 |
| Model Tests | 3397 | 2522 | 0 | 18 | 0 | 138 |
| Cucumber Support | 739 | 521 | 0 | 1 | 0 | 519 |
| Cucumber Features | 2711 | 2487 | 29 | 145 | 5 | 15 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total | 19636 | 15609 | 202 | 1420 | 7 | 8 |
| Total | 21805 | 16980 | 218 | 1483 | 6 | 9 |
+----------------------+-------+-------+---------+---------+-----+-------+
Code LOC: 9156 Test LOC: 3944 Code to Test Ratio: 1:0.4
```
Code LOC: 10142 Test LOC: 6838 Code to Test Ratio: 1:0.7

```

### TODO

* option to print out by app directory (stats per engine)
* Add views (jbuilder, erb, haml) but don't count towards ratios
* Support JS for projects that have it in public
* Add css but don't count towards ratios
12 changes: 12 additions & 0 deletions lib/rails_stats.rb
Original file line number Diff line number Diff line change
@@ -4,5 +4,17 @@ module RailsStats

end

require 'debugger'

require 'rails_stats/inflector'
require 'rails_stats/code_statistics_calculator'
require 'rails_stats/util'
require 'rails_stats/app_statistics'
require 'rails_stats/spec_statistics'
require 'rails_stats/cucumber_statistics'
require 'rails_stats/root_statistics'
require 'rails_stats/gem_statistics'
require 'rails_stats/code_statistics'

require "rails_stats/rake"
RailsStats.extend RailsStats::Rake
61 changes: 61 additions & 0 deletions lib/rails_stats/app_statistics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module RailsStats
class AppStatistics
attr_reader :statistics, :total, :test

def initialize(directory)
@test = false
@directory = directory
@statistics = calculate_statistics
@total = calculate_total
end

def key_concepts
directories.collect{ |path| File.basename(path) }
end

private

def calculate_total
out = CodeStatisticsCalculator.new
@statistics.each do |key, stats|
out.add(stats)
end
out
end

def calculate_statistics
Util.calculate_statistics(directories)
end

def directories
return @directories if @directories
out = []
Dir.foreach(@directory) do |file_name|
path = File.join(@directory, file_name)
next unless File.directory?(path)
next if (/^\./ =~ file_name)
next if file_name == "assets" # doing separately
next if file_name == "views" # TODO
out << path
end

assets = File.join(@directory, "assets")
if File.directory?(assets)
Dir.foreach(assets) do |file_name|
path = File.join(assets, file_name)
next unless File.directory?(path)
next if (/^\./ =~ file_name)

case file_name
when "javascripts"
out << path
# TODO when "css"
end
end
end

out
end
end

end
118 changes: 84 additions & 34 deletions lib/rails_stats/code_statistics.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
# railties/lib/rails/code_statistics.rb

require 'rails_stats/code_statistics_calculator'

module RailsStats
class CodeStatistics #:nodoc:

TEST_TYPES = ['Controller tests',
'Helper tests',
'Model tests',
'Mailer tests',
'Integration tests',
'Request tests',
'Library tests',
'Cucumber tests',
'Functional tests (old)',
'Unit tests (old)']

def initialize(*pairs)
@pairs = pairs
@statistics = calculate_statistics
@total = calculate_total if pairs.length > 1
class CodeStatistics

RAILS_APP_FOLDERS = ['models',
'controllers',
'helpers',
'mailers',
'views',
'assets']

def initialize(root_directory)
@root_directory = root_directory
@key_concepts = calculate_key_concepts
@projects = calculate_projects
@statistics = calculate_statistics
@total = calculate_total
end

def to_s
print_header
@pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
@statistics.each { |key, stats| print_line(key, stats) }
print_splitter

if @total
@@ -36,26 +32,80 @@ def to_s
end

private
def calculate_statistics
Hash[@pairs.map{|pair| [pair.first, calculate_directory_statistics(pair.last)]}]
def calculate_key_concepts
# returns names of main things like models, controllers, services, etc
concepts = {}
app_projects.each do |project|
project.key_concepts.each do |key|
concepts[key] = true
end
end

# TODO: maybe gem names?

concepts.keys
end

def calculate_directory_statistics(directory, pattern = /.*\.(rb|js|coffee|feature)$/)
stats = CodeStatisticsCalculator.new
def calculate_projects
out = []
out += app_projects
out += calculate_root_projects
out += calculate_gem_projects
out += calculate_spec_projects
out += calculate_test_projects
out += calculate_cucumber_projects
out
end

Dir.foreach(directory) do |file_name|
path = "#{directory}/#{file_name}"
def app_projects
@app_projects ||= calculate_app_projects
end

if File.directory?(path) && (/^\./ !~ file_name)
stats.add(calculate_directory_statistics(path, pattern))
end
def calculate_app_projects
apps = Util.calculate_projects(@root_directory, "**", "app", RAILS_APP_FOLDERS)
apps.collect do |root_path|
AppStatistics.new(root_path)
end
end

def calculate_gem_projects
gems = Util.calculate_projects(@root_directory, "**", "*.gemspec")
gems.collect do |root_path|
GemStatistics.new(root_path)
end
end

def calculate_spec_projects
specs = Util.calculate_projects(@root_directory, "**", "spec", "spec_helper.rb")
specs.collect do |root_path|
SpecStatistics.new(root_path, @key_concepts)
end
end

def calculate_test_projects
[] # TODO: test unit
end

next unless file_name =~ pattern
def calculate_root_projects
[RootStatistics.new(@root_directory)]
end

stats.add_by_file_path(path)
def calculate_cucumber_projects
cukes = Util.calculate_projects(@root_directory, "**", "*.feature")
cukes.collect do |root_path|
CucumberStatistics.new(root_path)
end
end

stats
def calculate_statistics
out = {}
@projects.each do |project|
project.statistics.each do |key, stats|
out[key] ||= CodeStatisticsCalculator.new(project.test)
out[key].add(stats)
end
end
out
end

def calculate_total
@@ -66,13 +116,13 @@ def calculate_total

def calculate_code
code_loc = 0
@statistics.each { |k, v| code_loc += v.code_lines unless TEST_TYPES.include? k }
@statistics.each { |k, v| code_loc += v.code_lines unless v.test }
code_loc
end

def calculate_tests
test_loc = 0
@statistics.each { |k, v| test_loc += v.code_lines if TEST_TYPES.include? k }
@statistics.each { |k, v| test_loc += v.code_lines if v.test }
test_loc
end

13 changes: 7 additions & 6 deletions lib/rails_stats/code_statistics_calculator.rb
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

module RailsStats
class CodeStatisticsCalculator #:nodoc:
attr_reader :lines, :code_lines, :classes, :methods
attr_reader :lines, :code_lines, :classes, :methods, :test

PATTERNS = {
rb: {
@@ -31,11 +31,12 @@ class CodeStatisticsCalculator #:nodoc:
}
}

def initialize(lines = 0, code_lines = 0, classes = 0, methods = 0)
@lines = lines
@code_lines = code_lines
@classes = classes
@methods = methods
def initialize(test=false)
@test = test
@lines = 0
@code_lines = 0
@classes = 0
@methods = 0
end

def add(code_statistics_calculator)
46 changes: 46 additions & 0 deletions lib/rails_stats/cucumber_statistics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module RailsStats
class CucumberStatistics
attr_reader :statistics, :total, :test

def initialize(directory)
@test = true
@directory = directory
@statistics = calculate_statistics
@total = calculate_total
end

private

def calculate_total
out = CodeStatisticsCalculator.new(true)
@statistics.each do |key, stats|
out.add(stats)
end
out
end

def calculate_statistics
out = {}
categorize_files.each do |key, list|
out[key] = Util.calculate_file_statistics(list)
end
out
end

def categorize_files
out = {}
Dir[File.join(@directory, "**", "*.rb")].each do |file_path|
out["Cucumber Support"] ||= []
out["Cucumber Support"] << file_path
end

Dir[File.join(@directory, "**", "*.feature")].each do |file_path|
out["Cucumber Features"] ||= []
out["Cucumber Features"] << file_path
end

out
end
end

end
31 changes: 31 additions & 0 deletions lib/rails_stats/gem_statistics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module RailsStats
class GemStatistics
attr_reader :statistics, :total, :test

def initialize(directory)
@test = false
@directory = directory
@statistics = calculate_statistics
@total = calculate_total
end

private

def calculate_total
out = CodeStatisticsCalculator.new
@statistics.each do |key, stats|
out.add(stats)
end
out
end

def calculate_statistics
# ignore gem/app so as to not double-count engines
lib = File.join(@directory, "lib")
Util.calculate_statistics([lib]) do |path|
"Gems"
end
end
end

end
Loading