codeclimate-fede 0.85.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/check +18 -0
- data/bin/codeclimate +21 -0
- data/bin/prep-release +45 -0
- data/bin/publish +47 -0
- data/bin/release +41 -0
- data/bin/validate-release +18 -0
- data/config/engines.yml +322 -0
- data/lib/cc/analyzer/bridge.rb +106 -0
- data/lib/cc/analyzer/composite_container_listener.rb +21 -0
- data/lib/cc/analyzer/container/result.rb +74 -0
- data/lib/cc/analyzer/container.rb +208 -0
- data/lib/cc/analyzer/container_listener.rb +9 -0
- data/lib/cc/analyzer/engine.rb +125 -0
- data/lib/cc/analyzer/engine_output.rb +74 -0
- data/lib/cc/analyzer/engine_output_filter.rb +36 -0
- data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
- data/lib/cc/analyzer/filesystem.rb +50 -0
- data/lib/cc/analyzer/formatters/formatter.rb +53 -0
- data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
- data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
- data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
- data/lib/cc/analyzer/formatters/spinner.rb +35 -0
- data/lib/cc/analyzer/formatters.rb +21 -0
- data/lib/cc/analyzer/issue.rb +69 -0
- data/lib/cc/analyzer/issue_sorter.rb +30 -0
- data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
- data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
- data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
- data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
- data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
- data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
- data/lib/cc/analyzer/issue_validations.rb +26 -0
- data/lib/cc/analyzer/issue_validator.rb +11 -0
- data/lib/cc/analyzer/location_description.rb +45 -0
- data/lib/cc/analyzer/logging_container_listener.rb +24 -0
- data/lib/cc/analyzer/measurement.rb +22 -0
- data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
- data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
- data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
- data/lib/cc/analyzer/measurement_validations.rb +16 -0
- data/lib/cc/analyzer/measurement_validator.rb +11 -0
- data/lib/cc/analyzer/mounted_path.rb +80 -0
- data/lib/cc/analyzer/raising_container_listener.rb +32 -0
- data/lib/cc/analyzer/source_buffer.rb +47 -0
- data/lib/cc/analyzer/source_extractor.rb +79 -0
- data/lib/cc/analyzer/source_fingerprint.rb +40 -0
- data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
- data/lib/cc/analyzer/validator.rb +38 -0
- data/lib/cc/analyzer.rb +50 -0
- data/lib/cc/cli/analyze/engine_failure.rb +11 -0
- data/lib/cc/cli/analyze.rb +90 -0
- data/lib/cc/cli/command.rb +85 -0
- data/lib/cc/cli/console.rb +12 -0
- data/lib/cc/cli/engines/engine_command.rb +15 -0
- data/lib/cc/cli/engines/install.rb +35 -0
- data/lib/cc/cli/engines/list.rb +18 -0
- data/lib/cc/cli/engines.rb +5 -0
- data/lib/cc/cli/file_store.rb +42 -0
- data/lib/cc/cli/global_cache.rb +47 -0
- data/lib/cc/cli/global_config.rb +35 -0
- data/lib/cc/cli/help.rb +51 -0
- data/lib/cc/cli/output.rb +34 -0
- data/lib/cc/cli/prepare.rb +98 -0
- data/lib/cc/cli/runner.rb +75 -0
- data/lib/cc/cli/validate_config.rb +84 -0
- data/lib/cc/cli/version.rb +16 -0
- data/lib/cc/cli/version_checker.rb +107 -0
- data/lib/cc/cli.rb +39 -0
- data/lib/cc/config/checks_adapter.rb +40 -0
- data/lib/cc/config/default_adapter.rb +54 -0
- data/lib/cc/config/engine.rb +41 -0
- data/lib/cc/config/engine_set.rb +47 -0
- data/lib/cc/config/json_adapter.rb +17 -0
- data/lib/cc/config/prepare.rb +92 -0
- data/lib/cc/config/validation/check_validator.rb +34 -0
- data/lib/cc/config/validation/engine_validator.rb +93 -0
- data/lib/cc/config/validation/fetch_validator.rb +78 -0
- data/lib/cc/config/validation/file_validator.rb +112 -0
- data/lib/cc/config/validation/hash_validations.rb +52 -0
- data/lib/cc/config/validation/json.rb +31 -0
- data/lib/cc/config/validation/prepare_validator.rb +40 -0
- data/lib/cc/config/validation/yaml.rb +66 -0
- data/lib/cc/config/yaml_adapter.rb +73 -0
- data/lib/cc/config.rb +70 -0
- data/lib/cc/engine_registry.rb +74 -0
- data/lib/cc/resolv.rb +39 -0
- data/lib/cc/workspace/exclusion.rb +34 -0
- data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
- data/lib/cc/workspace/path_tree/file_node.rb +31 -0
- data/lib/cc/workspace/path_tree.rb +49 -0
- data/lib/cc/workspace.rb +39 -0
- metadata +279 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class IssueSorter
|
4
|
+
def initialize(issues)
|
5
|
+
@issues = issues
|
6
|
+
end
|
7
|
+
|
8
|
+
def by_location
|
9
|
+
@issues.sort_by { |i| line_or_offset(i) }
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def line_or_offset(issue)
|
15
|
+
location = issue["location"]
|
16
|
+
|
17
|
+
case
|
18
|
+
when location["lines"]
|
19
|
+
[location["lines"]["begin"].to_i]
|
20
|
+
when location["positions"] && location["positions"]["begin"]["line"]
|
21
|
+
[location["positions"]["begin"]["line"].to_i, location["positions"]["begin"]["column"].to_i]
|
22
|
+
when location["positions"] && location["positions"]["begin"]["offset"]
|
23
|
+
[1_000_000_000] # push offsets to end of list
|
24
|
+
else
|
25
|
+
[0] # whole-file issues are first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class CategoryValidation < Validation
|
5
|
+
CATEGORIES = [
|
6
|
+
"Bug Risk".freeze,
|
7
|
+
"Clarity".freeze,
|
8
|
+
"Compatibility".freeze,
|
9
|
+
"Complexity".freeze,
|
10
|
+
"Duplication".freeze,
|
11
|
+
"Performance".freeze,
|
12
|
+
"Security".freeze,
|
13
|
+
"Style".freeze,
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
def valid?
|
17
|
+
object["categories"].present? && no_invalid_categories?
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
"Category must be at least one of #{CATEGORIES.join(", ")}"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def no_invalid_categories?
|
27
|
+
(CATEGORIES | object["categories"]) == CATEGORIES
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class ContentValidation < Validation
|
5
|
+
def valid?
|
6
|
+
!has_content? || (content.is_a?(Hash) && content["body"].is_a?(String))
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"Content must be a hash containing a 'body' key with string contents"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def has_content?
|
16
|
+
object.key?("content")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class LocationFormatValidation < Validation
|
5
|
+
class Validator
|
6
|
+
def initialize(location)
|
7
|
+
@location = location
|
8
|
+
check_validity
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
error.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
if error
|
17
|
+
"Location is not formatted correctly: #{error}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_accessor :error
|
24
|
+
|
25
|
+
attr_reader :location
|
26
|
+
|
27
|
+
def check_validity
|
28
|
+
if location["lines"]
|
29
|
+
self.error = "location.lines is not valid: #{JSON.dump(location["lines"])}" unless valid_lines?(location["lines"])
|
30
|
+
elsif location["positions"]
|
31
|
+
self.error = "location.positions is not valid: #{JSON.dump(location["positions"])}" unless valid_positions?(location["positions"])
|
32
|
+
else
|
33
|
+
self.error = "location.lines or location.positions must be present"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid_positions?(positions)
|
38
|
+
positions.is_a?(Hash) &&
|
39
|
+
valid_position?(positions["begin"]) &&
|
40
|
+
valid_position?(positions["end"])
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid_position?(position)
|
44
|
+
position &&
|
45
|
+
(
|
46
|
+
[position["line"], position["column"]].all? { |value| value.is_a?(Integer) } ||
|
47
|
+
position["offset"].is_a?(Integer)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def valid_lines?(lines)
|
52
|
+
lines.is_a?(Hash) && [lines["begin"], lines["end"]].all? { |value| value.is_a?(Integer) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid?
|
57
|
+
validation.valid?
|
58
|
+
end
|
59
|
+
|
60
|
+
def message
|
61
|
+
validation.message
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def validation
|
67
|
+
@validation ||= Validator.new(object.fetch("location", {}))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class OtherLocationsFormatValidation < Validation
|
5
|
+
CHECKS = [
|
6
|
+
LocationFormatValidation,
|
7
|
+
PathExistenceValidation,
|
8
|
+
PathPresenceValidation,
|
9
|
+
RelativePathValidation,
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
def initialize(object)
|
13
|
+
super
|
14
|
+
@other_location_valid = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid?
|
18
|
+
if object["other_locations"]
|
19
|
+
object["other_locations"].is_a?(Array) &&
|
20
|
+
object["other_locations"].all?(&method(:other_location_valid?))
|
21
|
+
else
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def message
|
27
|
+
"Other locations are not formatted correctly"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def other_location_valid?(location)
|
33
|
+
path = location && location["path"]
|
34
|
+
@other_location_valid[path] ||= CHECKS.all? do |klass|
|
35
|
+
klass.new("location" => location).valid?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module CC
|
4
|
+
module Analyzer
|
5
|
+
module IssueValidations
|
6
|
+
class RelativePathValidation < Validation
|
7
|
+
BUILDER_CODE_PATH = "/tmp/workspace/code".freeze
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
path &&
|
11
|
+
!path.start_with?("/") && (
|
12
|
+
relative_to?(MountedPath.code.container_path) ||
|
13
|
+
relative_to?(BUILDER_CODE_PATH)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
"Path must be relative to the project directory"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def relative_to?(directory)
|
24
|
+
expanded_base = Pathname.new(directory).expand_path.to_s
|
25
|
+
expanded_path = Pathname.new(path).expand_path.to_s
|
26
|
+
|
27
|
+
expanded_path.start_with?(expanded_base)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class RemediationPointsValidation < Validation
|
5
|
+
def valid?
|
6
|
+
remediation_points.nil? || positive_integer?(remediation_points)
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"Remediation points must be a non-negative integer"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def remediation_points
|
16
|
+
@remediation_points ||= object["remediation_points"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def positive_integer?(x)
|
20
|
+
x.is_a?(Integer) && x >= 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class SeverityValidation < Validation
|
5
|
+
INFO = "info".freeze
|
6
|
+
MINOR = "minor".freeze
|
7
|
+
MAJOR = "major".freeze
|
8
|
+
CRITICAL = "critical".freeze
|
9
|
+
BLOCKER = "blocker".freeze
|
10
|
+
|
11
|
+
DEPRECATED_SEVERITIES = [
|
12
|
+
NORMAL = "normal".freeze,
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
VALID_SEVERITIES = ([
|
16
|
+
INFO,
|
17
|
+
MINOR,
|
18
|
+
MAJOR,
|
19
|
+
CRITICAL,
|
20
|
+
BLOCKER,
|
21
|
+
] + DEPRECATED_SEVERITIES).freeze
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
severity.nil? || VALID_SEVERITIES.include?(severity)
|
25
|
+
end
|
26
|
+
|
27
|
+
def message
|
28
|
+
"Permitted severities include #{VALID_SEVERITIES.join(", ")}"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def severity
|
34
|
+
@severity ||= object["severity"]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
class Validation
|
5
|
+
def initialize(object)
|
6
|
+
@object = object
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def message
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :object
|
20
|
+
|
21
|
+
def path
|
22
|
+
object.fetch("location", {})["path"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def type
|
26
|
+
object["type"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def content
|
30
|
+
object["content"]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module IssueValidations
|
4
|
+
autoload :CategoryValidation, "cc/analyzer/issue_validations/category_validation"
|
5
|
+
autoload :CheckNamePresenceValidation, "cc/analyzer/issue_validations/check_name_presence_validation"
|
6
|
+
autoload :ContentValidation, "cc/analyzer/issue_validations/content_validation"
|
7
|
+
autoload :DescriptionPresenceValidation, "cc/analyzer/issue_validations/description_presence_validation"
|
8
|
+
autoload :LocationFormatValidation, "cc/analyzer/issue_validations/location_format_validation"
|
9
|
+
autoload :OtherLocationsFormatValidation, "cc/analyzer/issue_validations/other_locations_format_validation"
|
10
|
+
autoload :PathExistenceValidation, "cc/analyzer/issue_validations/path_existence_validation"
|
11
|
+
autoload :PathIsFileValidation, "cc/analyzer/issue_validations/path_is_file_validation"
|
12
|
+
autoload :PathPresenceValidation, "cc/analyzer/issue_validations/path_presence_validation"
|
13
|
+
autoload :RelativePathValidation, "cc/analyzer/issue_validations/relative_path_validation"
|
14
|
+
autoload :RemediationPointsValidation, "cc/analyzer/issue_validations/remediation_points_validation"
|
15
|
+
autoload :SeverityValidation, "cc/analyzer/issue_validations/severity_validation"
|
16
|
+
autoload :TypeValidation, "cc/analyzer/issue_validations/type_validation"
|
17
|
+
autoload :Validation, "cc/analyzer/issue_validations/validation"
|
18
|
+
|
19
|
+
def self.validations
|
20
|
+
constants.sort.map(&method(:const_get)).select do |klass|
|
21
|
+
klass.is_a?(Class) && klass.superclass == Validation
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class LocationDescription
|
4
|
+
def initialize(source_buffer, location, suffix = "")
|
5
|
+
@source_buffer = source_buffer
|
6
|
+
@location = location
|
7
|
+
@suffix = suffix
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
if location["lines"]
|
12
|
+
begin_line = location["lines"]["begin"]
|
13
|
+
end_line = location["lines"]["end"]
|
14
|
+
elsif location["positions"]
|
15
|
+
begin_line = position_to_line(location["positions"]["begin"])
|
16
|
+
end_line = position_to_line(location["positions"]["end"])
|
17
|
+
end
|
18
|
+
|
19
|
+
str = render_lines(begin_line, end_line)
|
20
|
+
str << suffix unless str.blank?
|
21
|
+
str
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :location, :suffix
|
27
|
+
|
28
|
+
def render_lines(begin_line, end_line)
|
29
|
+
if end_line == begin_line
|
30
|
+
begin_line.to_s
|
31
|
+
else
|
32
|
+
"#{begin_line}-#{end_line}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def position_to_line(position)
|
37
|
+
if position["line"]
|
38
|
+
position["line"]
|
39
|
+
else
|
40
|
+
@source_buffer.decompose_position(position["offset"]).first
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class LoggingContainerListener < ContainerListener
|
4
|
+
def initialize(logger)
|
5
|
+
@logger = logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def started(engine, _details)
|
9
|
+
logger.info("starting engine #{engine.name}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def finished(engine, _details, result)
|
13
|
+
logger.info("finished engine #{engine.name}")
|
14
|
+
if result.skipped?
|
15
|
+
logger.warn("skipped engine #{engine.name}: #{result.stderr}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :logger
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
class Measurement
|
4
|
+
def initialize(engine_name, output)
|
5
|
+
@engine_name = engine_name
|
6
|
+
@output = output
|
7
|
+
end
|
8
|
+
|
9
|
+
def as_json(*)
|
10
|
+
parsed_output.merge("engine_name" => engine_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :engine_name, :output
|
16
|
+
|
17
|
+
def parsed_output
|
18
|
+
@parsed_output ||= JSON.parse(output)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module MeasurementValidations
|
4
|
+
class NameValidation < Validation
|
5
|
+
REGEX = /^[A-Za-z0-9_\.\-]+$/
|
6
|
+
|
7
|
+
def valid?
|
8
|
+
name&.is_a?(String) && REGEX.match?(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
"Name must be present and contain only letters, numbers, periods, hyphens, and underscores"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def name
|
18
|
+
object["name"]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module MeasurementValidations
|
4
|
+
class TypeValidation < Validation
|
5
|
+
def valid?
|
6
|
+
type&.casecmp("measurement")&.zero?
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"Type must be 'measurement' but was '#{type}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module MeasurementValidations
|
4
|
+
class Validation
|
5
|
+
def initialize(object)
|
6
|
+
@object = object
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def message
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :object
|
20
|
+
|
21
|
+
def type
|
22
|
+
object["type"]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module MeasurementValidations
|
4
|
+
class ValueValidation < Validation
|
5
|
+
def valid?
|
6
|
+
value&.is_a?(Numeric)
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"Value must be present and numeric"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def value
|
16
|
+
object["value"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CC
|
2
|
+
module Analyzer
|
3
|
+
module MeasurementValidations
|
4
|
+
autoload :NameValidation, "cc/analyzer/measurement_validations/name_validation"
|
5
|
+
autoload :TypeValidation, "cc/analyzer/measurement_validations/type_validation"
|
6
|
+
autoload :ValueValidation, "cc/analyzer/measurement_validations/value_validation"
|
7
|
+
autoload :Validation, "cc/analyzer/measurement_validations/validation"
|
8
|
+
|
9
|
+
def self.validations
|
10
|
+
constants.sort.map(&method(:const_get)).select do |klass|
|
11
|
+
klass.is_a?(Class) && klass.superclass == Validation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|