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

Support JSON output #20

Merged
merged 5 commits into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,18 @@ Directory: /users/brian/examples/spree

```

#### JSON Format

If you want to export the details using JSON, you can use this command:

```
$ rake stats\[test/dummy,json\]

Directory: /Users/etagwerker/Projects/fastruby/rails_stats/test/dummy

[{"name":"Mailers","lines":"4","loc":"4","classes":"1","methods":"0","m_over_c":"0","loc_over_m":"0"},{"name":"Models","lines":"3","loc":"3","classes":"1","methods":"0","m_over_c":"0","loc_over_m":"0"},{"name":"Javascripts","lines":"27","loc":"7","classes":"0","methods":"0","m_over_c":"0","loc_over_m":"0"},{"name":"Jobs","lines":"7","loc":"2","classes":"1","methods":"0","m_over_c":"0","loc_over_m":"0"},{"name":"Controllers","lines":"7","loc":"6","classes":"1","methods":"1","m_over_c":"1","loc_over_m":"4"},{"name":"Helpers","lines":"3","loc":"3","classes":"0","methods":"0","m_over_c":"0","loc_over_m":"0"},{"name":"Channels","lines":"8","loc":"8","classes":"2","methods":"0","m_over_c":"0","loc_over_m":"0"},{"name":"Configuration","lines":"417","loc":"111","classes":"1","methods":"0","m_over_c":"0","loc_over_m":"0"},{"name":"Total","lines":"476","loc":"144","classes":"7","methods":"1","m_over_c":"0","loc_over_m":"142","code_to_test_ratio":"0.0","total":true}]
```

### Testing

In order to run the tests for this gem:
Expand Down
4 changes: 4 additions & 0 deletions lib/rails_stats/all.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
require 'rails_stats/stats_calculator'
require 'rails_stats/stats_formatter'
require 'rails_stats/json_formatter'
require 'rails_stats/console_formatter'
require 'rails_stats/inflector'
require 'rails_stats/code_statistics_calculator'
require 'rails_stats/util'
Expand Down
155 changes: 9 additions & 146 deletions lib/rails_stats/code_statistics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,160 +2,23 @@

module RailsStats
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
@code_total, @tests_total, @grand_total = calculate_totals
def initialize(root_directory, opts = {})
@calculator = RailsStats::StatsCalculator.new(root_directory)
@formatter = load_formatter(opts)
end

def to_s
print_header
sorted_keys = @statistics.keys.sort
sorted_keys.each { |key| print_line(key, @statistics[key]) }
print_splitter

print_line("Code", @code_total)
print_line("Tests", @tests_total)
print_line("Total", @grand_total)
print_splitter

print_code_test_stats
@formatter.to_s
end

private
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_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

def app_projects
@app_projects ||= calculate_app_projects
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_shared_projects("spec", @root_directory, "**", "spec", "**", "*_spec.rb")
specs.collect do |root_path|
SpecStatistics.new(root_path, @key_concepts)
end
end

def calculate_test_projects
tests = Util.calculate_shared_projects("test", @root_directory, "**", "test", "**", "*_test.rb")
tests.collect do |root_path|
TestStatistics.new(root_path, @key_concepts)
end
end

def calculate_root_projects
[RootStatistics.new(@root_directory)]
end

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

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
def load_formatter(opts = {})
if opts[:format] == "json"
RailsStats::JSONFormatter.new(@calculator, opts)
else
RailsStats::ConsoleFormatter.new(@calculator, opts)
end
out
end

def calculate_totals
# TODO: make this a single loop
code_total = @statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, code_total|
code_total.add(pair.last) unless pair.last.test
end

tests_total = @statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, tests_total|
tests_total.add(pair.last) if pair.last.test
end

grand_total = @statistics.each_with_object(CodeStatisticsCalculator.new) do |pair, total|
total.add(pair.last)
end

[code_total, tests_total, grand_total]
end

def print_header
print_splitter
puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
print_splitter
end

def print_splitter
puts "+----------------------+---------+---------+---------+---------+-----+-------+"
end

def print_line(name, statistics)
m_over_c = (statistics.methods / statistics.classes) rescue m_over_c = 0
loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue loc_over_m = 0

puts "| #{name.ljust(20)} " \
"| #{statistics.lines.to_s.rjust(7)} " \
"| #{statistics.code_lines.to_s.rjust(7)} " \
"| #{statistics.classes.to_s.rjust(7)} " \
"| #{statistics.methods.to_s.rjust(7)} " \
"| #{m_over_c.to_s.rjust(3)} " \
"| #{loc_over_m.to_s.rjust(5)} |"
end

def print_code_test_stats
code_to_test_ratio = @tests_total.code_lines.to_f / @code_total.code_lines
puts " Code LOC: #{@code_total.code_lines} Test LOC: #{@tests_total.code_lines} Code to Test Ratio: 1:#{sprintf("%.1f", code_to_test_ratio)}"
puts ""
end
end
end
52 changes: 52 additions & 0 deletions lib/rails_stats/console_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module RailsStats
class ConsoleFormatter < StatsFormatter
def to_s
print_header
sorted_keys = @statistics.keys.sort
sorted_keys.each { |key| print_line(key, @statistics[key]) }
print_splitter

if @grand_total
print_line("Code", @code_total)
print_line("Tests", @tests_total)
print_line("Total", @grand_total)
print_splitter
end

print_code_test_stats
end

private

def print_header
print_splitter
puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
print_splitter
end

def print_splitter
puts "+----------------------+---------+---------+---------+---------+-----+-------+"
end

def print_line(name, statistics)
m_over_c = (statistics.methods / statistics.classes) rescue m_over_c = 0
loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue loc_over_m = 0

puts "| #{name.ljust(20)} " \
"| #{statistics.lines.to_s.rjust(7)} " \
"| #{statistics.code_lines.to_s.rjust(7)} " \
"| #{statistics.classes.to_s.rjust(7)} " \
"| #{statistics.methods.to_s.rjust(7)} " \
"| #{m_over_c.to_s.rjust(3)} " \
"| #{loc_over_m.to_s.rjust(5)} |"
end

def print_code_test_stats
code = calculator.code_loc
tests = calculator.test_loc

puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
puts ""
end
end
end
45 changes: 45 additions & 0 deletions lib/rails_stats/json_formatter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require "json"

module RailsStats
class JSONFormatter < StatsFormatter
def result
@result = @statistics.map { |key, stats| stat_hash(key, stats) }

if @grand_total
@result << stat_hash("Total", @grand_total).merge(code_test_hash)
end

@result
end

def to_s
puts result.to_json
end

private

def code_test_hash
code = calculator.code_loc
tests = calculator.test_loc

{
"code_to_test_ratio" => "#{sprintf("%.1f", tests.to_f/code)}", "total" => true
}
end

def stat_hash(name, statistics)
m_over_c = (statistics.methods / statistics.classes) rescue m_over_c = 0
loc_over_m = (statistics.code_lines / statistics.methods) - 2 rescue loc_over_m = 0

{
"name" => name,
"lines" => statistics.lines.to_s,
"loc" => statistics.code_lines.to_s,
"classes" => statistics.classes.to_s,
"methods" => statistics.methods.to_s,
"m_over_c" => m_over_c.to_s,
"loc_over_m" => loc_over_m.to_s
}
end
end
end
Loading