hashira 0.4.0 → 0.5.0

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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/README.md +86 -25
  4. data/lib/hashira/analysis/finding.rb +2 -2
  5. data/lib/hashira/ci/ratchet.rb +5 -9
  6. data/lib/hashira/cli/fail_on.rb +15 -5
  7. data/lib/hashira/cli/run.rb +8 -4
  8. data/lib/hashira/cli/usage.rb +8 -5
  9. data/lib/hashira/complexity/boolean_run.rb +5 -3
  10. data/lib/hashira/complexity/cognitive_score.rb +21 -14
  11. data/lib/hashira/complexity/if_chain.rb +14 -14
  12. data/lib/hashira/complexity/method_finding.rb +3 -20
  13. data/lib/hashira/complexity/rescue_scan.rb +9 -9
  14. data/lib/hashira/complexity/{analyzer.rb → scores.rb} +6 -5
  15. data/lib/hashira/coupling/audiences.rb +49 -0
  16. data/lib/hashira/{analysis → coupling}/catalog.rb +3 -3
  17. data/lib/hashira/{analysis → coupling}/census.rb +17 -8
  18. data/lib/hashira/{analysis → coupling}/constant_registry.rb +6 -2
  19. data/lib/hashira/coupling/cycle_findings.rb +31 -0
  20. data/lib/hashira/{analysis → coupling}/cycle_search.rb +1 -1
  21. data/lib/hashira/{analysis → coupling}/cycles.rb +2 -2
  22. data/lib/hashira/{analysis → coupling}/definition.rb +1 -1
  23. data/lib/hashira/{analysis → coupling}/definitions.rb +1 -1
  24. data/lib/hashira/{analysis → coupling}/edge.rb +1 -1
  25. data/lib/hashira/{analysis → coupling}/edge_map.rb +10 -10
  26. data/lib/hashira/{analysis → coupling}/folder_placement.rb +2 -2
  27. data/lib/hashira/{analysis → coupling}/folding.rb +4 -2
  28. data/lib/hashira/{analysis → coupling}/graph.rb +18 -8
  29. data/lib/hashira/{analysis → coupling}/metric.rb +1 -1
  30. data/lib/hashira/coupling/mixed_audience_findings.rb +32 -0
  31. data/lib/hashira/{analysis → coupling}/namespace_placement.rb +3 -3
  32. data/lib/hashira/{analysis → coupling}/namespace_prefix.rb +1 -1
  33. data/lib/hashira/{analysis → coupling}/naming.rb +2 -2
  34. data/lib/hashira/{analysis → coupling}/no_folding.rb +1 -1
  35. data/lib/hashira/{analysis → coupling}/placement.rb +3 -3
  36. data/lib/hashira/coupling/references.rb +55 -0
  37. data/lib/hashira/coupling/report.rb +28 -0
  38. data/lib/hashira/coupling/roll_call.rb +45 -0
  39. data/lib/hashira/coupling/roll_call_findings.rb +30 -0
  40. data/lib/hashira/{analysis → coupling}/roster.rb +2 -2
  41. data/lib/hashira/{analysis → coupling}/rule.rb +1 -1
  42. data/lib/hashira/{analysis/resolver.rb → coupling/scope.rb} +2 -2
  43. data/lib/hashira/{analysis → coupling}/sdp_check.rb +1 -1
  44. data/lib/hashira/coupling/sdp_violation_findings.rb +25 -0
  45. data/lib/hashira/coupling/wide_edge_findings.rb +25 -0
  46. data/lib/hashira/coupling/words.rb +29 -0
  47. data/lib/hashira/diagram/{renderer.rb → source.rb} +2 -2
  48. data/lib/hashira/duplication/{analyzer.rb → clones.rb} +3 -3
  49. data/lib/hashira/duplication/{clusterer.rb → clusters.rb} +3 -3
  50. data/lib/hashira/duplication/delta.rb +6 -14
  51. data/lib/hashira/duplication/duplication_finding.rb +6 -10
  52. data/lib/hashira/duplication/{extractor.rb → harvest.rb} +1 -1
  53. data/lib/hashira/duplication/sequence.rb +3 -1
  54. data/lib/hashira/duplication/similarity.rb +5 -1
  55. data/lib/hashira/duplication/variance.rb +3 -3
  56. data/lib/hashira/hotspots/rollup.rb +1 -1
  57. data/lib/hashira/pipeline.rb +15 -10
  58. data/lib/hashira/report/complexity_table.rb +1 -1
  59. data/lib/hashira/report/finding_lines.rb +1 -1
  60. data/lib/hashira/report/json.rb +5 -3
  61. data/lib/hashira/report/phrases.rb +98 -0
  62. data/lib/hashira/report/smell_phrases.rb +68 -0
  63. data/lib/hashira/smells/census.rb +32 -0
  64. data/lib/hashira/smells/check.rb +30 -0
  65. data/lib/hashira/smells/conditions.rb +44 -0
  66. data/lib/hashira/smells/contexts.rb +64 -0
  67. data/lib/hashira/smells/control_parameter.rb +22 -0
  68. data/lib/hashira/smells/data_clump.rb +30 -0
  69. data/lib/hashira/smells/duplicate_method_call.rb +54 -0
  70. data/lib/hashira/smells/feature_envy.rb +17 -0
  71. data/lib/hashira/smells/instance_variable_assumption.rb +44 -0
  72. data/lib/hashira/smells/manual_dispatch.rb +16 -0
  73. data/lib/hashira/smells/module_initialize.rb +9 -0
  74. data/lib/hashira/smells/nil_check.rb +32 -0
  75. data/lib/hashira/smells/param_check.rb +51 -0
  76. data/lib/hashira/smells/refs.rb +53 -0
  77. data/lib/hashira/smells/repeated_conditional.rb +29 -0
  78. data/lib/hashira/smells/report.rb +31 -0
  79. data/lib/hashira/smells/scope.rb +29 -0
  80. data/lib/hashira/smells/too_many_instance_variables.rb +24 -0
  81. data/lib/hashira/smells/utility_function.rb +13 -0
  82. data/lib/hashira/smells/visibility.rb +64 -0
  83. data/lib/hashira/version.rb +1 -1
  84. data/lib/hashira.rb +65 -30
  85. metadata +60 -31
  86. data/lib/hashira/analysis/cycle_findings.rb +0 -38
  87. data/lib/hashira/analysis/references.rb +0 -40
  88. data/lib/hashira/analysis/sdp_violation_findings.rb +0 -29
data/lib/hashira.rb CHANGED
@@ -7,6 +7,9 @@ module Hashira
7
7
  module CI
8
8
  end
9
9
 
10
+ module Coupling
11
+ end
12
+
10
13
  module Complexity
11
14
  end
12
15
 
@@ -21,35 +24,13 @@ module Hashira
21
24
 
22
25
  module Report
23
26
  end
27
+
28
+ module Smells
29
+ end
24
30
  end
25
31
 
26
- require_relative "hashira/analysis/catalog"
27
- require_relative "hashira/analysis/census"
28
- require_relative "hashira/analysis/constant_registry"
29
- require_relative "hashira/analysis/cycle_findings"
30
- require_relative "hashira/analysis/cycle_search"
31
- require_relative "hashira/analysis/cycles"
32
- require_relative "hashira/analysis/definition"
33
- require_relative "hashira/analysis/definitions"
34
- require_relative "hashira/analysis/edge"
35
- require_relative "hashira/analysis/edge_map"
36
32
  require_relative "hashira/analysis/finding"
37
- require_relative "hashira/analysis/folder_placement"
38
- require_relative "hashira/analysis/folding"
39
- require_relative "hashira/analysis/graph"
40
- require_relative "hashira/analysis/metric"
41
- require_relative "hashira/analysis/namespace_placement"
42
- require_relative "hashira/analysis/namespace_prefix"
43
- require_relative "hashira/analysis/naming"
44
- require_relative "hashira/analysis/no_folding"
45
33
  require_relative "hashira/analysis/node_walk"
46
- require_relative "hashira/analysis/placement"
47
- require_relative "hashira/analysis/references"
48
- require_relative "hashira/analysis/resolver"
49
- require_relative "hashira/analysis/roster"
50
- require_relative "hashira/analysis/rule"
51
- require_relative "hashira/analysis/sdp_check"
52
- require_relative "hashira/analysis/sdp_violation_findings"
53
34
  require_relative "hashira/analysis/syntax"
54
35
  require_relative "hashira/analysis/type_walk"
55
36
  require_relative "hashira/churn"
@@ -71,7 +52,6 @@ require_relative "hashira/cli/package_by"
71
52
  require_relative "hashira/cli/run"
72
53
  require_relative "hashira/cli/skip"
73
54
  require_relative "hashira/cli/usage"
74
- require_relative "hashira/complexity/analyzer"
75
55
  require_relative "hashira/complexity/boolean_run"
76
56
  require_relative "hashira/complexity/cognitive_score"
77
57
  require_relative "hashira/complexity/if_chain"
@@ -79,17 +59,50 @@ require_relative "hashira/complexity/method_finding"
79
59
  require_relative "hashira/complexity/method_score"
80
60
  require_relative "hashira/complexity/rescue_scan"
81
61
  require_relative "hashira/complexity/rollup"
62
+ require_relative "hashira/complexity/scores"
63
+ require_relative "hashira/coupling/audiences"
64
+ require_relative "hashira/coupling/catalog"
65
+ require_relative "hashira/coupling/census"
66
+ require_relative "hashira/coupling/constant_registry"
67
+ require_relative "hashira/coupling/cycle_findings"
68
+ require_relative "hashira/coupling/cycle_search"
69
+ require_relative "hashira/coupling/cycles"
70
+ require_relative "hashira/coupling/definition"
71
+ require_relative "hashira/coupling/definitions"
72
+ require_relative "hashira/coupling/edge"
73
+ require_relative "hashira/coupling/edge_map"
74
+ require_relative "hashira/coupling/folder_placement"
75
+ require_relative "hashira/coupling/folding"
76
+ require_relative "hashira/coupling/graph"
77
+ require_relative "hashira/coupling/metric"
78
+ require_relative "hashira/coupling/mixed_audience_findings"
79
+ require_relative "hashira/coupling/namespace_placement"
80
+ require_relative "hashira/coupling/namespace_prefix"
81
+ require_relative "hashira/coupling/naming"
82
+ require_relative "hashira/coupling/no_folding"
83
+ require_relative "hashira/coupling/placement"
84
+ require_relative "hashira/coupling/references"
85
+ require_relative "hashira/coupling/report"
86
+ require_relative "hashira/coupling/roll_call"
87
+ require_relative "hashira/coupling/roll_call_findings"
88
+ require_relative "hashira/coupling/roster"
89
+ require_relative "hashira/coupling/rule"
90
+ require_relative "hashira/coupling/scope"
91
+ require_relative "hashira/coupling/sdp_check"
92
+ require_relative "hashira/coupling/sdp_violation_findings"
93
+ require_relative "hashira/coupling/wide_edge_findings"
94
+ require_relative "hashira/coupling/words"
82
95
  require_relative "hashira/diagram/dot"
83
96
  require_relative "hashira/diagram/mermaid"
84
- require_relative "hashira/diagram/renderer"
85
- require_relative "hashira/duplication/analyzer"
97
+ require_relative "hashira/diagram/source"
98
+ require_relative "hashira/duplication/clones"
86
99
  require_relative "hashira/duplication/cluster"
87
- require_relative "hashira/duplication/clusterer"
100
+ require_relative "hashira/duplication/clusters"
88
101
  require_relative "hashira/duplication/delta"
89
102
  require_relative "hashira/duplication/duplication_finding"
90
- require_relative "hashira/duplication/extractor"
91
103
  require_relative "hashira/duplication/fragment"
92
104
  require_relative "hashira/duplication/grouping"
105
+ require_relative "hashira/duplication/harvest"
93
106
  require_relative "hashira/duplication/index"
94
107
  require_relative "hashira/duplication/maximal"
95
108
  require_relative "hashira/duplication/near_miss"
@@ -109,6 +122,28 @@ require_relative "hashira/report/graph_payload"
109
122
  require_relative "hashira/report/hotspot_table"
110
123
  require_relative "hashira/report/json"
111
124
  require_relative "hashira/report/metrics_table"
125
+ require_relative "hashira/report/phrases"
126
+ require_relative "hashira/report/smell_phrases"
112
127
  require_relative "hashira/report/text"
113
128
  require_relative "hashira/report/view"
129
+ require_relative "hashira/smells/census"
130
+ require_relative "hashira/smells/check"
131
+ require_relative "hashira/smells/conditions"
132
+ require_relative "hashira/smells/contexts"
133
+ require_relative "hashira/smells/control_parameter"
134
+ require_relative "hashira/smells/data_clump"
135
+ require_relative "hashira/smells/duplicate_method_call"
136
+ require_relative "hashira/smells/feature_envy"
137
+ require_relative "hashira/smells/instance_variable_assumption"
138
+ require_relative "hashira/smells/manual_dispatch"
139
+ require_relative "hashira/smells/module_initialize"
140
+ require_relative "hashira/smells/nil_check"
141
+ require_relative "hashira/smells/param_check"
142
+ require_relative "hashira/smells/refs"
143
+ require_relative "hashira/smells/repeated_conditional"
144
+ require_relative "hashira/smells/report"
145
+ require_relative "hashira/smells/scope"
146
+ require_relative "hashira/smells/too_many_instance_variables"
147
+ require_relative "hashira/smells/utility_function"
148
+ require_relative "hashira/smells/visibility"
114
149
  require_relative "hashira/version"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashira
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Giacomo GK
@@ -26,33 +26,8 @@ files:
26
26
  - README.md
27
27
  - exe/hashira
28
28
  - lib/hashira.rb
29
- - lib/hashira/analysis/catalog.rb
30
- - lib/hashira/analysis/census.rb
31
- - lib/hashira/analysis/constant_registry.rb
32
- - lib/hashira/analysis/cycle_findings.rb
33
- - lib/hashira/analysis/cycle_search.rb
34
- - lib/hashira/analysis/cycles.rb
35
- - lib/hashira/analysis/definition.rb
36
- - lib/hashira/analysis/definitions.rb
37
- - lib/hashira/analysis/edge.rb
38
- - lib/hashira/analysis/edge_map.rb
39
29
  - lib/hashira/analysis/finding.rb
40
- - lib/hashira/analysis/folder_placement.rb
41
- - lib/hashira/analysis/folding.rb
42
- - lib/hashira/analysis/graph.rb
43
- - lib/hashira/analysis/metric.rb
44
- - lib/hashira/analysis/namespace_placement.rb
45
- - lib/hashira/analysis/namespace_prefix.rb
46
- - lib/hashira/analysis/naming.rb
47
- - lib/hashira/analysis/no_folding.rb
48
30
  - lib/hashira/analysis/node_walk.rb
49
- - lib/hashira/analysis/placement.rb
50
- - lib/hashira/analysis/references.rb
51
- - lib/hashira/analysis/resolver.rb
52
- - lib/hashira/analysis/roster.rb
53
- - lib/hashira/analysis/rule.rb
54
- - lib/hashira/analysis/sdp_check.rb
55
- - lib/hashira/analysis/sdp_violation_findings.rb
56
31
  - lib/hashira/analysis/syntax.rb
57
32
  - lib/hashira/analysis/type_walk.rb
58
33
  - lib/hashira/churn.rb
@@ -74,7 +49,6 @@ files:
74
49
  - lib/hashira/cli/run.rb
75
50
  - lib/hashira/cli/skip.rb
76
51
  - lib/hashira/cli/usage.rb
77
- - lib/hashira/complexity/analyzer.rb
78
52
  - lib/hashira/complexity/boolean_run.rb
79
53
  - lib/hashira/complexity/cognitive_score.rb
80
54
  - lib/hashira/complexity/if_chain.rb
@@ -82,17 +56,50 @@ files:
82
56
  - lib/hashira/complexity/method_score.rb
83
57
  - lib/hashira/complexity/rescue_scan.rb
84
58
  - lib/hashira/complexity/rollup.rb
59
+ - lib/hashira/complexity/scores.rb
60
+ - lib/hashira/coupling/audiences.rb
61
+ - lib/hashira/coupling/catalog.rb
62
+ - lib/hashira/coupling/census.rb
63
+ - lib/hashira/coupling/constant_registry.rb
64
+ - lib/hashira/coupling/cycle_findings.rb
65
+ - lib/hashira/coupling/cycle_search.rb
66
+ - lib/hashira/coupling/cycles.rb
67
+ - lib/hashira/coupling/definition.rb
68
+ - lib/hashira/coupling/definitions.rb
69
+ - lib/hashira/coupling/edge.rb
70
+ - lib/hashira/coupling/edge_map.rb
71
+ - lib/hashira/coupling/folder_placement.rb
72
+ - lib/hashira/coupling/folding.rb
73
+ - lib/hashira/coupling/graph.rb
74
+ - lib/hashira/coupling/metric.rb
75
+ - lib/hashira/coupling/mixed_audience_findings.rb
76
+ - lib/hashira/coupling/namespace_placement.rb
77
+ - lib/hashira/coupling/namespace_prefix.rb
78
+ - lib/hashira/coupling/naming.rb
79
+ - lib/hashira/coupling/no_folding.rb
80
+ - lib/hashira/coupling/placement.rb
81
+ - lib/hashira/coupling/references.rb
82
+ - lib/hashira/coupling/report.rb
83
+ - lib/hashira/coupling/roll_call.rb
84
+ - lib/hashira/coupling/roll_call_findings.rb
85
+ - lib/hashira/coupling/roster.rb
86
+ - lib/hashira/coupling/rule.rb
87
+ - lib/hashira/coupling/scope.rb
88
+ - lib/hashira/coupling/sdp_check.rb
89
+ - lib/hashira/coupling/sdp_violation_findings.rb
90
+ - lib/hashira/coupling/wide_edge_findings.rb
91
+ - lib/hashira/coupling/words.rb
85
92
  - lib/hashira/diagram/dot.rb
86
93
  - lib/hashira/diagram/mermaid.rb
87
- - lib/hashira/diagram/renderer.rb
88
- - lib/hashira/duplication/analyzer.rb
94
+ - lib/hashira/diagram/source.rb
95
+ - lib/hashira/duplication/clones.rb
89
96
  - lib/hashira/duplication/cluster.rb
90
- - lib/hashira/duplication/clusterer.rb
97
+ - lib/hashira/duplication/clusters.rb
91
98
  - lib/hashira/duplication/delta.rb
92
99
  - lib/hashira/duplication/duplication_finding.rb
93
- - lib/hashira/duplication/extractor.rb
94
100
  - lib/hashira/duplication/fragment.rb
95
101
  - lib/hashira/duplication/grouping.rb
102
+ - lib/hashira/duplication/harvest.rb
96
103
  - lib/hashira/duplication/index.rb
97
104
  - lib/hashira/duplication/maximal.rb
98
105
  - lib/hashira/duplication/near_miss.rb
@@ -112,8 +119,30 @@ files:
112
119
  - lib/hashira/report/hotspot_table.rb
113
120
  - lib/hashira/report/json.rb
114
121
  - lib/hashira/report/metrics_table.rb
122
+ - lib/hashira/report/phrases.rb
123
+ - lib/hashira/report/smell_phrases.rb
115
124
  - lib/hashira/report/text.rb
116
125
  - lib/hashira/report/view.rb
126
+ - lib/hashira/smells/census.rb
127
+ - lib/hashira/smells/check.rb
128
+ - lib/hashira/smells/conditions.rb
129
+ - lib/hashira/smells/contexts.rb
130
+ - lib/hashira/smells/control_parameter.rb
131
+ - lib/hashira/smells/data_clump.rb
132
+ - lib/hashira/smells/duplicate_method_call.rb
133
+ - lib/hashira/smells/feature_envy.rb
134
+ - lib/hashira/smells/instance_variable_assumption.rb
135
+ - lib/hashira/smells/manual_dispatch.rb
136
+ - lib/hashira/smells/module_initialize.rb
137
+ - lib/hashira/smells/nil_check.rb
138
+ - lib/hashira/smells/param_check.rb
139
+ - lib/hashira/smells/refs.rb
140
+ - lib/hashira/smells/repeated_conditional.rb
141
+ - lib/hashira/smells/report.rb
142
+ - lib/hashira/smells/scope.rb
143
+ - lib/hashira/smells/too_many_instance_variables.rb
144
+ - lib/hashira/smells/utility_function.rb
145
+ - lib/hashira/smells/visibility.rb
117
146
  - lib/hashira/version.rb
118
147
  homepage: https://github.com/giacope/hashira
119
148
  licenses:
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "rule"
4
-
5
- class Hashira::Analysis::CycleFindings < Hashira::Analysis::Rule
6
- KIND = "cycle"
7
-
8
- def list
9
- loops.map { entry(it) }
10
- end
11
-
12
- private
13
-
14
- def loops
15
- cycles = graph.cycles
16
- graph.packages.select { cycles.through?(it) }.sort.map { cycles.path(it) }.uniq { it[..-2].sort }
17
- end
18
-
19
- def entry(path)
20
- package = path.first
21
- finding(
22
- package:, cycle: path, evidence: evidence(path),
23
- message: message(package, path, graph.cycles.weakest(path))
24
- )
25
- end
26
-
27
- def message(package, path, weak_edge)
28
- from, to = weak_edge
29
- weight = graph.weight(from, to)
30
- "#{package} can reach itself: #{path.join(" -> ")} — any change may ripple back " \
31
- "around. The lightest edge on this cycle is #{from} -> #{to} " \
32
- "(#{weight} ref#{"s" unless weight == 1})."
33
- end
34
-
35
- def evidence(path)
36
- path.each_cons(2).flat_map { |from, to| graph.evidence(from, to).to_a.first(2) }
37
- end
38
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "prism"
4
-
5
- module Hashira
6
- module Analysis
7
- module References
8
- module_function
9
-
10
- def list(tree)
11
- sightings(tree).map(&:first)
12
- end
13
-
14
- def sightings(tree, roots = nil)
15
- [].tap { collect(tree, [], it, roots) }
16
- end
17
-
18
- def collect(node, nesting, accumulator, roots, home = nesting)
19
- return unless node
20
- return accumulator << sighting(node, nesting, home) if constant?(node)
21
- return enter(node, nesting, accumulator, roots) if definition?(node)
22
- node.compact_child_nodes.each { collect(it, nesting, accumulator, roots, home) }
23
- end
24
-
25
- def sighting(node, nesting, home)
26
- [Syntax.segments(node), node.location.start_line, Syntax.cbase?(node) ? nil : nesting, home]
27
- end
28
-
29
- def constant?(node) = node.is_a?(Prism::ConstantPathNode) || node.is_a?(Prism::ConstantReadNode)
30
-
31
- def definition?(node) = node.is_a?(Prism::ClassNode) || node.is_a?(Prism::ModuleNode)
32
-
33
- def enter(node, nesting, accumulator, roots)
34
- opened = nesting + [Syntax.anchor(nesting, Syntax.segments(node.constant_path), roots)]
35
- collect(node.superclass, nesting, accumulator, roots, opened) if node.is_a?(Prism::ClassNode)
36
- collect(node.body, opened, accumulator, roots)
37
- end
38
- end
39
- end
40
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "rule"
4
-
5
- class Hashira::Analysis::SdpViolationFindings < Hashira::Analysis::Rule
6
- KIND = "sdp_violation"
7
-
8
- def list
9
- ranked.map { |from, to| violation(from, to) }
10
- end
11
-
12
- private
13
-
14
- def ranked = graph.violations.sort_by { |from, to| instability(from) - instability(to) }
15
-
16
- def violation(from, to)
17
- finding(package: from, evidence: graph.evidence(from, to).to_a.first(5), message: message(from, to))
18
- end
19
-
20
- def message(from, to)
21
- "#{from} (I=#{label(from)}) depends on the LESS stable #{to} " \
22
- "(I=#{label(to)}) — churn in #{to} will force churn in #{from}. " \
23
- "Invert the edge or extract the stable part of #{to} that #{from} needs."
24
- end
25
-
26
- def instability(package) = metrics[package].instability
27
-
28
- def label(package) = format("%.2f", instability(package))
29
- end