This repository has been archived by the owner on May 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b8e6794
commit 744f207
Showing
7 changed files
with
229 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# frozen_string_literal: true | ||
|
||
module MOSAIK | ||
module Metrics | ||
## | ||
# Cyclomatic complexity (T.J. McCabe, 1976) | ||
# | ||
class Complexity < Metric | ||
def evaluate | ||
# Total complexity | ||
complexity = 0.0 | ||
|
||
# Iterate over each cluster | ||
graph.clusters.each_value do |cluster| | ||
# Find all vertices in the cluster | ||
vertices_in_cluster = cluster.vertices | ||
|
||
# Calculate complexity for the cluster | ||
complexity_c = 0.0 | ||
|
||
# Iterate over all vertices in the cluster | ||
vertices_in_cluster.each do |v| | ||
# Resolve the constant name to a file | ||
file = resolver.resolve_constant!(v.id) | ||
|
||
# Parse file to extract complexities | ||
complexities = Parser | ||
.new | ||
.parse(file) | ||
|
||
# Calculate the complexity for the vertex | ||
complexity_v = (complexities.values.sum / complexities.size).round(2) | ||
|
||
# Store complexity value in the vertex | ||
v.attributes[:complexity] = complexity_v | ||
|
||
# Store complexity value in the cluster | ||
complexity_c += complexity_v | ||
end | ||
|
||
# Store complexity value in the cluster | ||
cluster.attributes[:complexity] = complexity_c | ||
|
||
# Calculate complexity contribution from this cluster | ||
complexity += complexity_c | ||
end | ||
|
||
# Store complexity value in the graph | ||
graph.attributes[:complexity] = complexity | ||
|
||
# Return the total complexity | ||
complexity | ||
end | ||
|
||
private | ||
|
||
def resolver | ||
@resolver ||= Resolver.new( | ||
options[:directory], | ||
MOSAIK.configuration.load_paths, | ||
MOSAIK.configuration.overrides, | ||
) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# frozen_string_literal: true | ||
|
||
module MOSAIK | ||
module Metrics | ||
class Complexity | ||
## | ||
# Parser for Ruby code | ||
# | ||
class Parser | ||
def parse(file) | ||
debug "Parsing file: #{file}" | ||
|
||
# Parse Abstract Syntax Tree | ||
source = RuboCop::AST::ProcessedSource | ||
.new(File.read(file), 3.3) | ||
|
||
# Process AST to extract complexity values from methods | ||
processor = Processor.new | ||
|
||
source.ast.each_node do |node| | ||
processor | ||
.process(node) | ||
end | ||
|
||
processor.complexities | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# frozen_string_literal: true | ||
|
||
module MOSAIK | ||
module Metrics | ||
class Complexity | ||
## | ||
# Abstract Syntax Tree parser for Ruby code | ||
# | ||
class Processor < AST::Processor | ||
attr_reader :complexities | ||
|
||
def initialize | ||
super | ||
|
||
# Initialize complexities | ||
@complexities = {} | ||
end | ||
|
||
# Instance methods | ||
def on_def(node) | ||
method_name = node.children[0].to_s | ||
|
||
# Calculate complexity for the method | ||
_, abc = RuboCop::Cop::Metrics::Utils::AbcSizeCalculator.calculate(node) | ||
|
||
# Extract complexity from <A, B, C> triplet | ||
complexity = abc.split(",")[1].to_f | ||
|
||
# Store complexity for the method | ||
complexities[method_name] = complexity | ||
end | ||
|
||
# Class methods | ||
def on_defs(node) | ||
method_name = "self.#{node.children[1]}" | ||
|
||
# Calculate complexity for the method | ||
_, abc = RuboCop::Cop::Metrics::Utils::AbcSizeCalculator.calculate(node) | ||
|
||
# Extract complexity from <A, B, C> triplet | ||
complexity = abc.split(",")[1].to_i | ||
|
||
# Store complexity for the method | ||
complexities[method_name] = complexity | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# frozen_string_literal: true | ||
# typed: true | ||
|
||
RSpec.describe MOSAIK::Metrics::Complexity::Parser do | ||
subject(:parser) { described_class.new } | ||
|
||
let(:file) { Tempfile.new(["ruby", ".rb"]) } | ||
|
||
before do | ||
File.write file, <<~RUBY | ||
# frozen_string_literal: true | ||
class PagesController < ApplicationController | ||
def index | ||
@posts = model.active.visible_by(current_user) | ||
render "pages/index/page" | ||
end | ||
def search | ||
@posts = model.active.visible_by(current_user).search(params[:q]) | ||
@posts = model.some_process(@posts, current_user) | ||
@posts = model.another_process(@posts, current_user) | ||
render "pages/search/page" | ||
end | ||
end | ||
RUBY | ||
end | ||
|
||
it "parses a Ruby file" do | ||
complexities = parser.parse(file) | ||
|
||
expect(complexities).to eq "index" => 5.0, | ||
"search" => 14.0 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# frozen_string_literal: true | ||
# typed: true | ||
|
||
RSpec.describe MOSAIK::Metrics::Complexity do | ||
subject(:metric) { build(:complexity_metric, options:, graph:) } | ||
|
||
let(:options) { { directory: MOSAIK.root } } | ||
let(:graph) { build(:graph) } | ||
|
||
before do | ||
v1 = graph.add_vertex("CoreExt::Object") | ||
v2 = graph.add_vertex("MOSAIK::Logger") | ||
v3 = graph.add_vertex("MOSAIK::Algorithm") | ||
|
||
c1 = graph.add_cluster("cluster1") | ||
c2 = graph.add_cluster("cluster2") | ||
|
||
c1.add_vertex(v1) | ||
|
||
c2.add_vertex(v2) | ||
c2.add_vertex(v3) | ||
end | ||
|
||
# Cyclomatic complexity calculated by hand: | ||
# | ||
# Cluster 1: 1.0 / 1 = 1.0 | ||
# Cluster 2: 7.0 / 2 + 1.0 / 3 = 5.83 (rounded) | ||
# | ||
# Complexity = 6.83 (rounded) | ||
# | ||
|
||
it "sets the ABC size values for each vertex, cluster, and for the graph" do | ||
metric.evaluate | ||
|
||
expect(graph.find_vertex("CoreExt::Object").attributes[:complexity]).to eq 1.0 | ||
expect(graph.find_vertex("MOSAIK::Logger").attributes[:complexity]).to eq 5.5 | ||
expect(graph.find_vertex("MOSAIK::Algorithm").attributes[:complexity]).to eq 0.33 | ||
|
||
expect(graph.find_cluster("cluster1").attributes[:complexity]).to eq 1.0 | ||
expect(graph.find_cluster("cluster2").attributes[:complexity]).to eq 5.83 | ||
|
||
expect(graph.attributes[:complexity]).to eq 6.83 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters