feature_map 1.2.0 → 1.2.1
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 +4 -4
- data/README.md +34 -232
- data/bin/docs +9 -0
- data/bin/readme +9 -0
- data/lib/feature_map/cli.rb +26 -0
- data/lib/feature_map/private/additional_metrics_file.rb +161 -0
- data/lib/feature_map/private/docs/index.html +75 -75
- data/lib/feature_map/private/documentation_site.rb +4 -1
- data/lib/feature_map/private/feature_metrics_calculator.rb +24 -6
- data/lib/feature_map/private/metrics_file.rb +1 -1
- data/lib/feature_map/private/test_coverage_file.rb +9 -1
- data/lib/feature_map/private.rb +13 -0
- data/lib/feature_map.rb +5 -0
- metadata +19 -2
@@ -74,6 +74,7 @@ module FeatureMap
|
|
74
74
|
feature_metrics: MetricsFile::FeaturesContent,
|
75
75
|
feature_test_coverage: TestCoverageFile::FeaturesContent,
|
76
76
|
feature_test_pyramid: TestPyramidFile::FeaturesContent,
|
77
|
+
feature_additional_metrics: AdditionalMetricsFile::FeaturesContent,
|
77
78
|
project_configuration: T::Hash[T.untyped, T.untyped],
|
78
79
|
git_ref: String
|
79
80
|
).void
|
@@ -83,6 +84,7 @@ module FeatureMap
|
|
83
84
|
feature_metrics,
|
84
85
|
feature_test_coverage,
|
85
86
|
feature_test_pyramid,
|
87
|
+
feature_additional_metrics,
|
86
88
|
project_configuration,
|
87
89
|
git_ref
|
88
90
|
)
|
@@ -95,7 +97,8 @@ module FeatureMap
|
|
95
97
|
assignments: feature_assignments[feature_name],
|
96
98
|
metrics: feature_metrics[feature_name],
|
97
99
|
test_coverage: feature_test_coverage[feature_name],
|
98
|
-
test_pyramid: feature_test_pyramid[feature_name]
|
100
|
+
test_pyramid: feature_test_pyramid[feature_name],
|
101
|
+
additional_metrics: feature_additional_metrics[feature_name]
|
99
102
|
)
|
100
103
|
end
|
101
104
|
|
@@ -11,18 +11,22 @@ module FeatureMap
|
|
11
11
|
CYCLOMATIC_COMPLEXITY_METRIC = 'cyclomatic_complexity'
|
12
12
|
LINES_OF_CODE_METRIC = 'lines_of_code'
|
13
13
|
TODO_LOCATIONS_METRIC = 'todo_locations'
|
14
|
+
COMPLEXITY_RATIO_METRIC = 'complexity_ratio'
|
15
|
+
ENCAPSULATION_RATIO_METRIC = 'encapsulation_ratio'
|
14
16
|
|
15
17
|
SUPPORTED_METRICS = T.let([
|
16
18
|
ABC_SIZE_METRIC,
|
17
19
|
CYCLOMATIC_COMPLEXITY_METRIC,
|
18
20
|
LINES_OF_CODE_METRIC,
|
19
|
-
TODO_LOCATIONS_METRIC
|
21
|
+
TODO_LOCATIONS_METRIC,
|
22
|
+
COMPLEXITY_RATIO_METRIC,
|
23
|
+
ENCAPSULATION_RATIO_METRIC
|
20
24
|
].freeze, T::Array[String])
|
21
25
|
|
22
26
|
FeatureMetrics = T.type_alias do
|
23
27
|
T::Hash[
|
24
28
|
String, # metric name
|
25
|
-
T.any(Integer, Float, T::Hash[String, String]) # score or todo locations with messages
|
29
|
+
T.any(Integer, T.nilable(Float), T::Hash[String, String]) # score or todo locations with messages
|
26
30
|
]
|
27
31
|
end
|
28
32
|
|
@@ -31,15 +35,15 @@ module FeatureMap
|
|
31
35
|
metrics = file_paths.map { |file| calculate_for_file(file) }
|
32
36
|
|
33
37
|
# Handle numeric metrics
|
34
|
-
aggregate_metrics =
|
35
|
-
next if metric_key == TODO_LOCATIONS_METRIC
|
36
|
-
|
38
|
+
aggregate_metrics = [ABC_SIZE_METRIC, CYCLOMATIC_COMPLEXITY_METRIC, LINES_OF_CODE_METRIC].each_with_object({}) do |metric_key, agg|
|
37
39
|
agg[metric_key] = metrics.sum { |m| m[metric_key] || 0 }
|
38
40
|
end
|
39
41
|
|
40
|
-
#
|
42
|
+
# Handle additional supported metrics
|
41
43
|
todo_locations = metrics.map { |m| m[TODO_LOCATIONS_METRIC] }.compact.reduce({}, :merge)
|
42
44
|
aggregate_metrics[TODO_LOCATIONS_METRIC] = todo_locations
|
45
|
+
aggregate_metrics[COMPLEXITY_RATIO_METRIC] = complexity_ratio(aggregate_metrics)
|
46
|
+
aggregate_metrics[ENCAPSULATION_RATIO_METRIC] = encapsulation_ratio(file_paths, aggregate_metrics)
|
43
47
|
|
44
48
|
aggregate_metrics
|
45
49
|
end
|
@@ -71,6 +75,20 @@ module FeatureMap
|
|
71
75
|
TODO_LOCATIONS_METRIC => todo_locations
|
72
76
|
)
|
73
77
|
end
|
78
|
+
|
79
|
+
sig { params(aggregate_metrics: T::Hash[String, T.untyped]).returns(T.nilable(Float)) }
|
80
|
+
def self.complexity_ratio(aggregate_metrics)
|
81
|
+
return 0.0 if aggregate_metrics[LINES_OF_CODE_METRIC].nil? || aggregate_metrics[CYCLOMATIC_COMPLEXITY_METRIC].nil? || aggregate_metrics[CYCLOMATIC_COMPLEXITY_METRIC].zero?
|
82
|
+
|
83
|
+
aggregate_metrics[LINES_OF_CODE_METRIC].to_f / aggregate_metrics[CYCLOMATIC_COMPLEXITY_METRIC]
|
84
|
+
end
|
85
|
+
|
86
|
+
sig { params(file_paths: T.nilable(T::Array[String]), aggregate_metrics: T::Hash[String, T.untyped]).returns(T.nilable(Float)) }
|
87
|
+
def self.encapsulation_ratio(file_paths, aggregate_metrics)
|
88
|
+
return 0.0 if file_paths.nil? || aggregate_metrics[LINES_OF_CODE_METRIC].nil? || aggregate_metrics[LINES_OF_CODE_METRIC].zero?
|
89
|
+
|
90
|
+
file_paths.length.to_f / aggregate_metrics[LINES_OF_CODE_METRIC]
|
91
|
+
end
|
74
92
|
end
|
75
93
|
end
|
76
94
|
end
|
@@ -11,6 +11,8 @@ module FeatureMap
|
|
11
11
|
class TestCoverageFile
|
12
12
|
extend T::Sig
|
13
13
|
|
14
|
+
COVERAGE_RATIO = 'coverage_ratio'
|
15
|
+
|
14
16
|
class FileContentError < StandardError; end
|
15
17
|
|
16
18
|
FEATURES_KEY = 'features'
|
@@ -64,7 +66,7 @@ module FeatureMap
|
|
64
66
|
feature_test_coverage = T.let({}, FeaturesContent)
|
65
67
|
|
66
68
|
Private.feature_file_assignments.each do |feature_name, files|
|
67
|
-
feature_test_coverage[feature_name] = T.let({ 'lines' => 0, 'hits' => 0, 'misses' => 0 }, FeatureCoverage)
|
69
|
+
feature_test_coverage[feature_name] = T.let({ 'lines' => 0, 'hits' => 0, 'misses' => 0, 'coverage_ratio' => 0 }, FeatureCoverage)
|
68
70
|
|
69
71
|
files.each_with_object(T.must(feature_test_coverage[feature_name])) do |file_path, coverage|
|
70
72
|
next unless coverage_stats[file_path]
|
@@ -75,6 +77,12 @@ module FeatureMap
|
|
75
77
|
|
76
78
|
coverage
|
77
79
|
end
|
80
|
+
|
81
|
+
T.must(feature_test_coverage[feature_name])[COVERAGE_RATIO] = if T.must(T.must(feature_test_coverage[feature_name])['lines']).zero?
|
82
|
+
0
|
83
|
+
else
|
84
|
+
((T.must(feature_test_coverage[feature_name])['hits'].to_f / T.must(T.must(feature_test_coverage[feature_name])['lines'])) * 100).round
|
85
|
+
end
|
78
86
|
end
|
79
87
|
|
80
88
|
{ FEATURES_KEY => feature_test_coverage }
|
data/lib/feature_map/private.rb
CHANGED
@@ -20,6 +20,7 @@ require 'feature_map/private/documentation_site'
|
|
20
20
|
require 'feature_map/private/code_cov'
|
21
21
|
require 'feature_map/private/test_coverage_file'
|
22
22
|
require 'feature_map/private/test_pyramid_file'
|
23
|
+
require 'feature_map/private/additional_metrics_file'
|
23
24
|
require 'feature_map/private/feature_plugins/assignment'
|
24
25
|
require 'feature_map/private/validations/files_have_features'
|
25
26
|
require 'feature_map/private/validations/features_up_to_date'
|
@@ -104,11 +105,15 @@ module FeatureMap
|
|
104
105
|
# and review the feature documentation without this data.
|
105
106
|
feature_test_coverage = TestCoverageFile.path.exist? ? TestCoverageFile.load_features! : {}
|
106
107
|
|
108
|
+
# Additional metrics must be calculated after the initial metrics are loaded
|
109
|
+
feature_additional_metrics = AdditionalMetricsFile.path.exist? ? AdditionalMetricsFile.load_features! : {}
|
110
|
+
|
107
111
|
DocumentationSite.generate(
|
108
112
|
feature_assignments,
|
109
113
|
feature_metrics,
|
110
114
|
feature_test_coverage,
|
111
115
|
feature_test_pyramid,
|
116
|
+
feature_additional_metrics,
|
112
117
|
configuration.raw_hash,
|
113
118
|
T.must(git_ref || configuration.repository['main_branch'])
|
114
119
|
)
|
@@ -137,6 +142,14 @@ module FeatureMap
|
|
137
142
|
TestCoverageFile.write!(coverage_stats)
|
138
143
|
end
|
139
144
|
|
145
|
+
sig { void }
|
146
|
+
def self.generate_additional_metrics!
|
147
|
+
feature_metrics = MetricsFile.load_features!
|
148
|
+
feature_test_coverage = TestCoverageFile.path.exist? ? TestCoverageFile.load_features! : {}
|
149
|
+
|
150
|
+
AdditionalMetricsFile.write!(feature_metrics, feature_test_coverage, configuration.raw_hash['documentation_site']['health'])
|
151
|
+
end
|
152
|
+
|
140
153
|
# Returns a string version of the relative path to a Rails constant,
|
141
154
|
# or nil if it can't find something
|
142
155
|
sig { params(klass: T.nilable(T.any(T::Class[T.anything], Module))).returns(T.nilable(String)) }
|
data/lib/feature_map.rb
CHANGED
@@ -135,6 +135,11 @@ module FeatureMap
|
|
135
135
|
Private.gather_test_coverage!(commit_sha, code_cov_token)
|
136
136
|
end
|
137
137
|
|
138
|
+
sig { void }
|
139
|
+
def generate_additional_metrics!
|
140
|
+
Private.generate_additional_metrics!
|
141
|
+
end
|
142
|
+
|
138
143
|
# Given a backtrace from either `Exception#backtrace` or `caller`, find the
|
139
144
|
# first line that corresponds to a file with an assigned feature
|
140
145
|
sig { params(backtrace: T.nilable(T::Array[String]), excluded_features: T::Array[CodeFeatures::Feature]).returns(T.nilable(CodeFeatures::Feature)) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feature_map
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Beyond Finance
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: code_ownership
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '1.9'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: github-pages
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '232'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '232'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: railties
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -203,7 +217,9 @@ extensions: []
|
|
203
217
|
extra_rdoc_files: []
|
204
218
|
files:
|
205
219
|
- README.md
|
220
|
+
- bin/docs
|
206
221
|
- bin/featuremap
|
222
|
+
- bin/readme
|
207
223
|
- lib/feature_map.rb
|
208
224
|
- lib/feature_map/cli.rb
|
209
225
|
- lib/feature_map/code_features.rb
|
@@ -215,6 +231,7 @@ files:
|
|
215
231
|
- lib/feature_map/mapper.rb
|
216
232
|
- lib/feature_map/output_color.rb
|
217
233
|
- lib/feature_map/private.rb
|
234
|
+
- lib/feature_map/private/additional_metrics_file.rb
|
218
235
|
- lib/feature_map/private/assignment_applicator.rb
|
219
236
|
- lib/feature_map/private/assignment_mappers/directory_assignment.rb
|
220
237
|
- lib/feature_map/private/assignment_mappers/feature_definition_assignment.rb
|