Module: FeatureMap
- Defined in:
- lib/feature_map/commit.rb,
lib/feature_map.rb,
lib/feature_map/cli.rb,
lib/feature_map/mapper.rb,
lib/feature_map/private.rb,
lib/feature_map/constants.rb,
lib/feature_map/validator.rb,
lib/feature_map/code_features.rb,
lib/feature_map/configuration.rb,
lib/feature_map/private/code_cov.rb,
lib/feature_map/private/glob_cache.rb,
lib/feature_map/code_features/plugin.rb,
lib/feature_map/private/metrics_file.rb,
lib/feature_map/private/todo_inspector.rb,
lib/feature_map/private/assignments_file.rb,
lib/feature_map/private/extension_loader.rb,
lib/feature_map/private/feature_assigner.rb,
lib/feature_map/private/health_calculator.rb,
lib/feature_map/private/test_pyramid_file.rb,
lib/feature_map/private/documentation_site.rb,
lib/feature_map/private/test_coverage_file.rb,
lib/feature_map/private/test_pyramid/mapper.rb,
lib/feature_map/private/assignment_applicator.rb,
lib/feature_map/code_features/plugins/identity.rb,
lib/feature_map/private/additional_metrics_file.rb,
lib/feature_map/private/lines_of_code_calculator.rb,
lib/feature_map/private/test_pyramid/jest_mapper.rb,
lib/feature_map/private/test_pyramid/rspec_mapper.rb,
lib/feature_map/private/feature_metrics_calculator.rb,
lib/feature_map/private/feature_plugins/assignment.rb,
lib/feature_map/private/release_notification_builder.rb,
lib/feature_map/private/percentile_metrics_calculator.rb,
lib/feature_map/private/validations/features_up_to_date.rb,
lib/feature_map/private/validations/files_have_features.rb,
lib/feature_map/private/assignment_mappers/feature_globs.rb,
lib/feature_map/private/cyclomatic_complexity_calculator.rb,
lib/feature_map/private/assignment_mappers/file_annotations.rb,
lib/feature_map/private/validations/files_have_unique_features.rb,
lib/feature_map/private/assignment_mappers/directory_assignment.rb,
lib/feature_map/private/assignment_mappers/feature_definition_assignment.rb
Overview
frozen_string_literal: true
Defined Under Namespace
Modules: CodeFeatures, Constants, Mapper, Validator
Classes: Cli, Commit, Configuration, InvalidFeatureMapConfigurationError
Constant Summary
collapse
- ALL_TEAMS_KEY =
'All Teams'
- NO_FEATURE_KEY =
'No Feature'
Class Method Summary
collapse
-
.apply_assignments!(assignments_file_path) ⇒ Object
-
.bust_caches! ⇒ Object
Generally, you should not ever need to do this, because once your ruby process loads, cached content should not change.
-
.configuration ⇒ Object
-
.first_assigned_file_for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or caller, find the first assigned file in it, useful for figuring out which file is being blamed.
-
.for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or caller, find the first line that corresponds to a file with an assigned feature.
-
.for_class(klass) ⇒ Object
-
.for_feature(feature) ⇒ Object
-
.for_file(file) ⇒ Object
-
.gather_simplecov_test_coverage!(simplecov_resultsets) ⇒ Object
-
.gather_test_coverage!(commit_sha, code_cov_token) ⇒ Object
-
.generate_additional_metrics! ⇒ Object
-
.generate_docs!(git_ref) ⇒ Object
-
.generate_release_notification(commits_by_feature) ⇒ Object
Generates a block kit message grouping the provided commits into sections for each feature impacted by the cheanges.
-
.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path) ⇒ Object
-
.group_commits(commits) ⇒ Object
Groups the provided list of commits (e.g. the changes being deployed in a release) by both the feature they impact and the teams responsible for these features.
-
.remove_file_annotation!(filename) ⇒ Object
-
.validate!(autocorrect: true, stage_changes: true, files: nil) ⇒ Object
Class Method Details
.apply_assignments!(assignments_file_path) ⇒ Object
21
22
23
|
# File 'lib/feature_map.rb', line 21
def apply_assignments!(assignments_file_path)
Private.apply_assignments!(assignments_file_path)
end
|
.bust_caches! ⇒ Object
Generally, you should not ever need to do this, because once your ruby process loads, cached content should not change. Namely, the set of files, and directories which are tracked for feature assignment should not change. The primary reason this is helpful is for clients of FeatureMap who want to test their code, and each test context has different feature assignments and tracked files.
221
222
223
224
225
226
|
# File 'lib/feature_map.rb', line 221
def self.bust_caches!
@for_file = nil
@memoized_values = nil
Private.bust_caches!
Mapper.all.each(&:bust_caches!)
end
|
.configuration ⇒ Object
228
229
230
|
# File 'lib/feature_map.rb', line 228
def self.configuration
Private.configuration
end
|
.first_assigned_file_for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or caller, find the first assigned file in it, useful for figuring out which file is being blamed.
120
121
122
123
124
125
126
127
128
|
# File 'lib/feature_map.rb', line 120
def first_assigned_file_for_backtrace(backtrace, excluded_features: [])
backtrace_with_feature_assignments(backtrace).each do |(feature, file)|
if feature && !excluded_features.include?(feature)
return [feature, file]
end
end
nil
end
|
.for_backtrace(backtrace, excluded_features: []) ⇒ Object
Given a backtrace from either ‘Exception#backtrace` or caller, find the first line that corresponds to a file with an assigned feature
114
115
116
|
# File 'lib/feature_map.rb', line 114
def for_backtrace(backtrace, excluded_features: [])
first_assigned_file_for_backtrace(backtrace, excluded_features: excluded_features)&.first
end
|
.for_class(klass) ⇒ Object
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
# File 'lib/feature_map.rb', line 162
def for_class(klass)
@memoized_values ||= {}
if @memoized_values.key?(klass.to_s)
@memoized_values[klass.to_s]
else
path = Private.path_from_klass(klass)
return nil if path.nil?
value_to_memoize = for_file(path)
@memoized_values[klass.to_s] = value_to_memoize
value_to_memoize
end
end
|
.for_feature(feature) ⇒ Object
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
# File 'lib/feature_map.rb', line 42
def for_feature(feature)
feature = CodeFeatures.find(feature)
feature_report = []
feature_report << "# Report for `#{feature.name}` Feature"
Private.glob_cache.raw_cache_contents.each do |mapper_description, glob_to_assigned_feature_map|
feature_report << "## #{mapper_description}"
file_assignments_for_mapper = []
glob_to_assigned_feature_map.each do |glob, assigned_feature|
next if assigned_feature != feature
file_assignments_for_mapper << "- #{glob}"
end
if file_assignments_for_mapper.empty?
feature_report << 'This feature does not have any files in this category.'
else
feature_report += file_assignments_for_mapper.sort
end
feature_report << ''
end
feature_report.join("\n")
end
|
.for_file(file) ⇒ Object
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
# File 'lib/feature_map.rb', line 25
def for_file(file)
@for_file ||= {}
return nil if file.start_with?('./')
return @for_file[file] if @for_file.key?(file)
Private.load_configuration!
feature = nil
Mapper.all.each do |mapper|
feature = mapper.map_file_to_feature(file)
break if feature end
@for_file[file] = feature
end
|
.gather_simplecov_test_coverage!(simplecov_resultsets) ⇒ Object
100
101
102
|
# File 'lib/feature_map.rb', line 100
def gather_simplecov_test_coverage!(simplecov_resultsets)
Private.gather_simplecov_test_coverage!(simplecov_resultsets)
end
|
.gather_test_coverage!(commit_sha, code_cov_token) ⇒ Object
104
105
106
|
# File 'lib/feature_map.rb', line 104
def gather_test_coverage!(commit_sha, code_cov_token)
Private.gather_test_coverage!(commit_sha, code_cov_token)
end
|
.generate_additional_metrics! ⇒ Object
108
109
110
|
# File 'lib/feature_map.rb', line 108
def generate_additional_metrics!
Private.generate_additional_metrics!
end
|
.generate_docs!(git_ref) ⇒ Object
92
93
94
|
# File 'lib/feature_map.rb', line 92
def generate_docs!(git_ref)
Private.generate_docs!(git_ref)
end
|
.generate_release_notification(commits_by_feature) ⇒ Object
Generates a block kit message grouping the provided commits into sections for each feature impacted by the cheanges.
212
213
214
|
# File 'lib/feature_map.rb', line 212
def generate_release_notification(commits_by_feature)
Private.generate_release_notification(commits_by_feature)
end
|
.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path) ⇒ Object
96
97
98
|
# File 'lib/feature_map.rb', line 96
def generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path)
Private.generate_test_pyramid!(unit_path, integration_path, regression_path, regression_assignments_path)
end
|
.group_commits(commits) ⇒ Object
Groups the provided list of commits (e.g. the changes being deployed in a release) by both the feature they impact and the teams responsible for these features. Returns a hash with keys for each team with features modified within these commits and values that are a hash of features to the set of commits that impact each feature.
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
# File 'lib/feature_map.rb', line 181
def group_commits(commits)
commits.each_with_object({}) do |commit, hash|
commit_features = commit.files.map do |file|
feature = FeatureMap.for_file(file)
next nil unless feature
teams = Private.all_teams_for_feature(feature)
team_names = teams.empty? ? [ALL_TEAMS_KEY] : teams.map(&:name)
team_names.sort.each do |team_name|
hash[team_name] ||= {}
hash[team_name][feature.name] ||= []
hash[team_name][feature.name] << commit unless hash[team_name][feature.name].include?(commit)
end
feature
end
next unless commit_features.compact.empty?
hash[ALL_TEAMS_KEY] ||= {}
hash[ALL_TEAMS_KEY][NO_FEATURE_KEY] ||= []
hash[ALL_TEAMS_KEY][NO_FEATURE_KEY] << commit
end
end
|
.remove_file_annotation!(filename) ⇒ Object
.validate!(autocorrect: true, stage_changes: true, files: nil) ⇒ Object
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# File 'lib/feature_map.rb', line 76
def validate!(
autocorrect: true,
stage_changes: true,
files: nil
)
Private.load_configuration!
tracked_file_subset = if files
files.select { |f| Private.file_tracked?(f) }
else
Private.tracked_files
end
Private.validate!(files: tracked_file_subset, autocorrect: autocorrect, stage_changes: stage_changes)
end
|