central-devtools 0.8.3
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 +7 -0
- data/.gitignore +37 -0
- data/.rspec +6 -0
- data/.rubocop.yml +5 -0
- data/.ruby-gemset +1 -0
- data/Gemfile +3 -0
- data/LICENSE +191 -0
- data/README.md +36 -0
- data/Rakefile +8 -0
- data/central-devtools.gemspec +44 -0
- data/config/container.yml +7 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +4 -0
- data/config/reek.yml +102 -0
- data/config/rubocop.yml +117 -0
- data/config/yardstick.yml +2 -0
- data/lib/central/devtools/config.rb +199 -0
- data/lib/central/devtools/flay.rb +91 -0
- data/lib/central/devtools/project/initializer/rake.rb +23 -0
- data/lib/central/devtools/project/initializer/rspec.rb +70 -0
- data/lib/central/devtools/project/initializer.rb +13 -0
- data/lib/central/devtools/project.rb +80 -0
- data/lib/central/devtools/rake/flay.rb +121 -0
- data/lib/central/devtools/spec_helper.rb +5 -0
- data/lib/central/devtools/version.rb +7 -0
- data/lib/central/devtools.rb +90 -0
- data/shared/spec/shared/abstract_type_behavior.rb +16 -0
- data/shared/spec/shared/command_method_behavior.rb +5 -0
- data/shared/spec/shared/each_method_behaviour.rb +13 -0
- data/shared/spec/shared/hash_method_behavior.rb +7 -0
- data/shared/spec/shared/idempotent_method_behavior.rb +10 -0
- data/shared/spec/support/ice_nine_config.rb +9 -0
- data/shared/tasks/docker.rake +80 -0
- data/shared/tasks/metrics/ci.rake +19 -0
- data/shared/tasks/metrics/coverage.rake +14 -0
- data/shared/tasks/metrics/flay.rake +21 -0
- data/shared/tasks/metrics/flog.rake +38 -0
- data/shared/tasks/metrics/mutant.rake +44 -0
- data/shared/tasks/metrics/reek.rake +10 -0
- data/shared/tasks/metrics/rubocop.rake +15 -0
- data/shared/tasks/metrics/yardstick.rake +15 -0
- data/shared/tasks/spec.rake +36 -0
- data/shared/tasks/yard.rake +11 -0
- data/spec/integration/central/devtools/rake/flay/verify_spec.rb +166 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/unit/central/devtools/config/yardstick_spec.rb +19 -0
- data/spec/unit/central/devtools/config_spec.rb +75 -0
- data/spec/unit/central/devtools/flay/file_list/call_spec.rb +21 -0
- data/spec/unit/central/devtools/flay/scale/flay_report_spec.rb +19 -0
- data/spec/unit/central/devtools/flay/scale/measure_spec.rb +45 -0
- data/spec/unit/central/devtools/project/initializer/rake_spec.rb +23 -0
- data/spec/unit/central/devtools/project/initializer/rspec_spec.rb +55 -0
- data/spec/unit/central/devtools/project_spec.rb +37 -0
- data/spec/unit/central/devtools_spec.rb +16 -0
- metadata +393 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Central
|
4
|
+
module Devtools
|
5
|
+
# Abstract base class of tool configuration
|
6
|
+
class Config
|
7
|
+
include Adamantium::Flat, AbstractType, Concord.new(:config_dir)
|
8
|
+
|
9
|
+
# Represent no configuration
|
10
|
+
DEFAULT_CONFIG = {}.freeze
|
11
|
+
|
12
|
+
# Simple named type check representation
|
13
|
+
class TypeCheck
|
14
|
+
# Type check against expected class
|
15
|
+
include Concord.new(:name, :allowed_classes)
|
16
|
+
|
17
|
+
FORMAT_ERROR = '%<name>s: Got instance of %<got>s expected %<allowed>s'.freeze
|
18
|
+
CLASS_DELIM = ','.freeze
|
19
|
+
|
20
|
+
# Check value for instance of expected class
|
21
|
+
#
|
22
|
+
# @param [Object] value
|
23
|
+
# @return [Object]
|
24
|
+
def call(value)
|
25
|
+
klass = value.class
|
26
|
+
|
27
|
+
unless allowed_classes.any?(&klass.method(:equal?))
|
28
|
+
fail TypeError, FORMAT_ERROR % {
|
29
|
+
name: name,
|
30
|
+
got: klass,
|
31
|
+
allowed: allowed_classes.join(CLASS_DELIM)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private_constant(*constants(false))
|
40
|
+
|
41
|
+
# Error raised on type errors
|
42
|
+
TypeError = Class.new(RuntimeError)
|
43
|
+
|
44
|
+
# Declare an attribute
|
45
|
+
#
|
46
|
+
# @param [Symbol] name
|
47
|
+
# @param [Array<Class>] classes
|
48
|
+
# @return [self]
|
49
|
+
# @api private
|
50
|
+
def self.attribute(name, classes, **options)
|
51
|
+
default = [options.fetch(:default)] if options.key?(:default)
|
52
|
+
type_check = TypeCheck.new(name, classes)
|
53
|
+
key = name.to_s
|
54
|
+
|
55
|
+
define_method(name) do
|
56
|
+
type_check.call(raw.fetch(key, *default))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
private_class_method :attribute
|
60
|
+
|
61
|
+
# Return config path
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
# @api private
|
65
|
+
def config_file
|
66
|
+
config_dir.join(self.class::FILE)
|
67
|
+
end
|
68
|
+
memoize :config_file
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Return raw data
|
73
|
+
#
|
74
|
+
# @return [Hash]
|
75
|
+
# @api private
|
76
|
+
def raw
|
77
|
+
yaml_config || DEFAULT_CONFIG
|
78
|
+
end
|
79
|
+
memoize :raw
|
80
|
+
|
81
|
+
# Return the raw config data from a yaml file
|
82
|
+
#
|
83
|
+
# @return [Hash]
|
84
|
+
# returned if the yaml file is found
|
85
|
+
# @return [nil]
|
86
|
+
# returned if the yaml file is not found
|
87
|
+
# @api private
|
88
|
+
def yaml_config
|
89
|
+
IceNine.deep_freeze(YAML.load_file(config_file)) if config_file.file?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Rubocop configuration
|
93
|
+
class Rubocop < self
|
94
|
+
FILE = 'rubocop.yml'.freeze
|
95
|
+
end
|
96
|
+
|
97
|
+
# Reek configuration
|
98
|
+
class Reek < self
|
99
|
+
FILE = 'reek.yml'.freeze
|
100
|
+
end
|
101
|
+
|
102
|
+
# Flay configuration
|
103
|
+
class Flay < self
|
104
|
+
FILE = 'flay.yml'.freeze
|
105
|
+
DEFAULT_LIB_DIRS = %w(lib).freeze
|
106
|
+
DEFAULT_EXCLUDES = %w().freeze
|
107
|
+
|
108
|
+
attribute :total_score, Fixnum
|
109
|
+
attribute :threshold, Fixnum
|
110
|
+
attribute :lib_dirs, Array, default: DEFAULT_LIB_DIRS
|
111
|
+
attribute :excludes, Array, default: DEFAULT_EXCLUDES
|
112
|
+
end
|
113
|
+
|
114
|
+
# Yardstick configuration
|
115
|
+
class Yardstick < self
|
116
|
+
FILE = 'yardstick.yml'.freeze
|
117
|
+
OPTIONS = %w(
|
118
|
+
threshold
|
119
|
+
rules
|
120
|
+
verbose
|
121
|
+
path
|
122
|
+
require_exact_threshold
|
123
|
+
).freeze
|
124
|
+
|
125
|
+
# Options hash that Yardstick understands
|
126
|
+
#
|
127
|
+
# @return [Hash]
|
128
|
+
# @api private
|
129
|
+
def options
|
130
|
+
OPTIONS.each_with_object({}) do |name, hash|
|
131
|
+
hash[name] = raw.fetch(name, nil)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Flog configuration
|
137
|
+
class Flog < self
|
138
|
+
FILE = 'flog.yml'.freeze
|
139
|
+
DEFAULT_LIB_DIRS = %w(lib).freeze
|
140
|
+
|
141
|
+
attribute :total_score, Float
|
142
|
+
attribute :threshold, Float
|
143
|
+
attribute :lib_dirs, Array, default: DEFAULT_LIB_DIRS
|
144
|
+
end
|
145
|
+
|
146
|
+
# Mutant configuration
|
147
|
+
class Mutant < self
|
148
|
+
FILE = 'mutant.yml'.freeze
|
149
|
+
DEFAULT_NAME = ''.freeze
|
150
|
+
DEFAULT_STRATEGY = 'rspec'.freeze
|
151
|
+
DEFAULT_COVERAGE = '1/1'.freeze
|
152
|
+
|
153
|
+
attribute :name, String, default: DEFAULT_NAME
|
154
|
+
attribute :strategy, String, default: DEFAULT_STRATEGY
|
155
|
+
attribute :zombify, [TrueClass, FalseClass], default: false
|
156
|
+
attribute :since, [String, NilClass], default: nil
|
157
|
+
attribute :ignore_subjects, Array, default: []
|
158
|
+
attribute :expect_coverage, String, default: DEFAULT_COVERAGE
|
159
|
+
attribute :namespace, String
|
160
|
+
end
|
161
|
+
|
162
|
+
# Devtools configuration
|
163
|
+
class Devtools < self
|
164
|
+
FILE = 'devtools.yml'.freeze
|
165
|
+
DEFAULT_UNIT_TEST_TIMEOUT = 0.1
|
166
|
+
|
167
|
+
attribute :unit_test_timeout, Float, default: DEFAULT_UNIT_TEST_TIMEOUT
|
168
|
+
end
|
169
|
+
|
170
|
+
# Container configuration
|
171
|
+
class Container < self
|
172
|
+
FILE = 'container.yml'.freeze
|
173
|
+
OPTIONS = %w(
|
174
|
+
prefix
|
175
|
+
component
|
176
|
+
build_tag
|
177
|
+
from_image
|
178
|
+
from_version
|
179
|
+
).freeze
|
180
|
+
|
181
|
+
# Options hash for the container
|
182
|
+
#
|
183
|
+
# @return [Hash]
|
184
|
+
# @api private
|
185
|
+
def options
|
186
|
+
OPTIONS.each_with_object({}) do |name, hash|
|
187
|
+
hash[name] = raw.fetch(name, nil)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
attribute :prefix, String, default: nil
|
192
|
+
attribute :component, String, default: nil
|
193
|
+
attribute :build_tag, String, default: nil
|
194
|
+
attribute :from_image, String, default: nil
|
195
|
+
attribute :from_version, String, default: nil
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Central
|
4
|
+
module Devtools
|
5
|
+
module Flay
|
6
|
+
# Measure flay mass relative to size of duplicated sexps
|
7
|
+
class Scale
|
8
|
+
include Adamantium
|
9
|
+
include Anima.new(:minimum_mass, :files)
|
10
|
+
include Procto.call(:measure)
|
11
|
+
|
12
|
+
# Measure duplication mass
|
13
|
+
#
|
14
|
+
# @return [Array<Rational>]
|
15
|
+
# @api private
|
16
|
+
def measure
|
17
|
+
flay.masses.map do |hash, mass|
|
18
|
+
Rational(mass, flay.hashes.fetch(hash).size)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Report flay output
|
23
|
+
#
|
24
|
+
# @return [undefined]
|
25
|
+
# @api private
|
26
|
+
def flay_report
|
27
|
+
flay.report
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Memoized flay instance
|
33
|
+
#
|
34
|
+
# @return [Flay]
|
35
|
+
# @api private
|
36
|
+
def flay
|
37
|
+
::Flay.new(mass: minimum_mass).tap do |flay|
|
38
|
+
flay.process(*files)
|
39
|
+
flay.analyze
|
40
|
+
end
|
41
|
+
end
|
42
|
+
memoize :flay, freezer: :noop
|
43
|
+
end
|
44
|
+
|
45
|
+
# Expand include and exclude file settings for flay
|
46
|
+
class FileList
|
47
|
+
include Procto.call, Concord.new(:includes, :excludes)
|
48
|
+
|
49
|
+
# Expand includes and filter by excludes
|
50
|
+
#
|
51
|
+
# @return [Set<Pathname>]
|
52
|
+
# @api private
|
53
|
+
def call
|
54
|
+
include_set - exclude_set
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Set of excluded files
|
60
|
+
#
|
61
|
+
# @return [Set<Pathname>]
|
62
|
+
# @api private
|
63
|
+
def exclude_set
|
64
|
+
excludes.flat_map(&Pathname.method(:glob))
|
65
|
+
end
|
66
|
+
|
67
|
+
# Set of included files
|
68
|
+
#
|
69
|
+
# Expanded using flay's file expander which takes into
|
70
|
+
# account flay's plugin support
|
71
|
+
#
|
72
|
+
# @return [Set<Pathname>]
|
73
|
+
# @api private
|
74
|
+
def include_set
|
75
|
+
Set.new(flay_includes.map(&method(:Pathname)))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Expand includes using flay
|
79
|
+
#
|
80
|
+
# Expanded using flay's file expander which takes into
|
81
|
+
# account flay's plugin support
|
82
|
+
#
|
83
|
+
# @return [Array<String>]
|
84
|
+
# @api private
|
85
|
+
def flay_includes
|
86
|
+
::Flay.expand_dirs_to_files(includes)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Central
|
4
|
+
module Devtools
|
5
|
+
class Project
|
6
|
+
class Initializer
|
7
|
+
# Imports all devtools rake tasks into a project
|
8
|
+
class Rake < self
|
9
|
+
include AbstractType
|
10
|
+
|
11
|
+
# Initialize rake tasks
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
# @api rpivate
|
15
|
+
def self.call
|
16
|
+
FileList.glob(RAKE_FILES_GLOB).each(&::Rake.application.method(:add_import))
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Central
|
4
|
+
module Devtools
|
5
|
+
class Project
|
6
|
+
class Initializer
|
7
|
+
# Requires all shared specs in a project's spec_helper
|
8
|
+
# Also installs a configurable unit test timeout
|
9
|
+
class Rspec < self
|
10
|
+
include Concord.new(:project)
|
11
|
+
|
12
|
+
# Call initializer for project
|
13
|
+
#
|
14
|
+
# @param [Project] project
|
15
|
+
# @return [self]
|
16
|
+
# @api private
|
17
|
+
def self.call(project)
|
18
|
+
new(project).__send__(:call)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Setup RSpec for project
|
25
|
+
#
|
26
|
+
# @return [self]
|
27
|
+
# @api private
|
28
|
+
def call
|
29
|
+
require_shared_spec_files
|
30
|
+
enable_unit_test_timeout
|
31
|
+
end
|
32
|
+
|
33
|
+
# Timeout unit tests that take longer than configured amount of time
|
34
|
+
#
|
35
|
+
# @param [Numeric] timeout
|
36
|
+
# @return [undefined]
|
37
|
+
# @raise [Timeout::Error]
|
38
|
+
# raised when the times are outside the timeout
|
39
|
+
# @api private
|
40
|
+
def enable_unit_test_timeout
|
41
|
+
timeout = project.devtools.unit_test_timeout
|
42
|
+
RSpec.configuration.around(file_path: UNIT_TEST_PATH_REGEXP) do |example|
|
43
|
+
Timeout.timeout(timeout, &example)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Trigger the require of shared spec files
|
48
|
+
#
|
49
|
+
# @return [undefined]
|
50
|
+
# @api private
|
51
|
+
def require_shared_spec_files
|
52
|
+
require_files(SHARED_SPEC_PATH)
|
53
|
+
require_files(project.spec_root)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Require files with pattern
|
57
|
+
#
|
58
|
+
# @param [Pathname] dir
|
59
|
+
# the directory containing the files to require
|
60
|
+
#
|
61
|
+
# @return [self]
|
62
|
+
# @api private
|
63
|
+
def require_files(dir)
|
64
|
+
Dir.glob(dir.join(SHARED_SPEC_PATTERN)).each(&Kernel.method(:require))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Central
|
4
|
+
module Devtools
|
5
|
+
class Project
|
6
|
+
include Concord.new(:root)
|
7
|
+
|
8
|
+
CONFIGS = {
|
9
|
+
container: Config::Container,
|
10
|
+
devtools: Config::Devtools,
|
11
|
+
flay: Config::Flay,
|
12
|
+
flog: Config::Flog,
|
13
|
+
reek: Config::Reek,
|
14
|
+
mutant: Config::Mutant,
|
15
|
+
rubocop: Config::Rubocop,
|
16
|
+
yardstick: Config::Yardstick
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
private_constant(*constants(false))
|
20
|
+
|
21
|
+
attr_reader(*CONFIGS.keys)
|
22
|
+
|
23
|
+
# The spec root
|
24
|
+
#
|
25
|
+
# @return [Pathname]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
attr_reader :spec_root
|
29
|
+
|
30
|
+
# Initialize object
|
31
|
+
#
|
32
|
+
# @param [Pathname] root
|
33
|
+
#
|
34
|
+
# @return [undefined]
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
def initialize(root)
|
39
|
+
super(root)
|
40
|
+
|
41
|
+
initialize_environment
|
42
|
+
initialize_configs
|
43
|
+
end
|
44
|
+
|
45
|
+
# Init rspec
|
46
|
+
#
|
47
|
+
# @return [self]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def init_rspec
|
51
|
+
Initializer::Rspec.call(self)
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Initialize environment
|
58
|
+
#
|
59
|
+
# @return [undefined]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def initialize_environment
|
63
|
+
@spec_root = root.join(SPEC_DIRECTORY_NAME)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Initialize configs
|
67
|
+
#
|
68
|
+
# @return [undefined]
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
def initialize_configs
|
72
|
+
config_dir = root.join(DEFAULT_CONFIG_DIR_NAME)
|
73
|
+
|
74
|
+
CONFIGS.each do |name, klass|
|
75
|
+
instance_variable_set(:"@#{name}", klass.new(config_dir))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Central
|
4
|
+
module Devtools
|
5
|
+
module Rake
|
6
|
+
# Flay metric runner
|
7
|
+
class Flay
|
8
|
+
include Anima.new(:threshold, :total_score, :lib_dirs, :excludes),
|
9
|
+
Procto.call(:verify),
|
10
|
+
Adamantium
|
11
|
+
|
12
|
+
BELOW_THRESHOLD = 'Adjust flay threshold down to %d'.freeze
|
13
|
+
TOTAL_MISMATCH = 'Flay total is now %d, but expected %d'.freeze
|
14
|
+
ABOVE_THRESHOLD = '%d chunks have a duplicate mass > %d'.freeze
|
15
|
+
|
16
|
+
# Verify code specified by `files` does not violate flay expectations
|
17
|
+
#
|
18
|
+
# @raise [SystemExit] if a violation is found
|
19
|
+
# @return [undefined] otherwise
|
20
|
+
#
|
21
|
+
# rubocop:disable MethodLength
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
def verify
|
25
|
+
# Run flay first to ensure the max mass matches the threshold
|
26
|
+
Central::Devtools.notify_metric_violation(
|
27
|
+
BELOW_THRESHOLD % largest_mass
|
28
|
+
) if below_threshold?
|
29
|
+
|
30
|
+
Central::Devtools.notify_metric_violation(
|
31
|
+
TOTAL_MISMATCH % [total_mass, total_score]
|
32
|
+
) if total_mismatch?
|
33
|
+
|
34
|
+
# Run flay a second time with the threshold set
|
35
|
+
return unless above_threshold?
|
36
|
+
|
37
|
+
restricted_flay_scale.flay_report
|
38
|
+
Central::Devtools.notify_metric_violation(
|
39
|
+
ABOVE_THRESHOLD % [restricted_mass_size, threshold]
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# List of files flay will analyze
|
46
|
+
#
|
47
|
+
# @return [Set<Pathname>]
|
48
|
+
# @api private
|
49
|
+
def files
|
50
|
+
Central::Devtools::Flay::FileList.call(lib_dirs, excludes)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Is there mass duplication which exceeds specified `threshold`
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
56
|
+
# @api private
|
57
|
+
def above_threshold?
|
58
|
+
restricted_mass_size.nonzero?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Is the specified `threshold` greater than the largest flay mass
|
62
|
+
#
|
63
|
+
# @return [Boolean]
|
64
|
+
# @api private
|
65
|
+
def below_threshold?
|
66
|
+
threshold > largest_mass
|
67
|
+
end
|
68
|
+
|
69
|
+
# Is the expected mass total different from the actual mass total
|
70
|
+
#
|
71
|
+
# @return [Boolean]
|
72
|
+
# @api private
|
73
|
+
def total_mismatch?
|
74
|
+
!total_mass.equal?(total_score)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Size of mass measured by `Flay::Scale` and filtered by `threshold`
|
78
|
+
#
|
79
|
+
# @return [Integer]
|
80
|
+
# @api private
|
81
|
+
def restricted_mass_size
|
82
|
+
restricted_flay_scale.measure.size
|
83
|
+
end
|
84
|
+
|
85
|
+
# Sum of all flay mass
|
86
|
+
#
|
87
|
+
# @return [Integer]
|
88
|
+
# @api private
|
89
|
+
def total_mass
|
90
|
+
flay_masses.reduce(:+).to_i
|
91
|
+
end
|
92
|
+
|
93
|
+
# Largest flay mass found
|
94
|
+
#
|
95
|
+
# @return [Integer]
|
96
|
+
# @api private
|
97
|
+
def largest_mass
|
98
|
+
flay_masses.max.to_i
|
99
|
+
end
|
100
|
+
|
101
|
+
# Flay scale which only measures mass above `threshold`
|
102
|
+
#
|
103
|
+
# @return [Flay::Scale]
|
104
|
+
# @api private
|
105
|
+
def restricted_flay_scale
|
106
|
+
Central::Devtools::Flay::Scale.new(minimum_mass: threshold.succ, files: files)
|
107
|
+
end
|
108
|
+
memoize :restricted_flay_scale
|
109
|
+
|
110
|
+
# All flay masses found in `files`
|
111
|
+
#
|
112
|
+
# @return [Array<Rational>]
|
113
|
+
# @api private
|
114
|
+
def flay_masses
|
115
|
+
Central::Devtools::Flay::Scale.call(minimum_mass: 0, files: files)
|
116
|
+
end
|
117
|
+
memoize :flay_masses
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Stdlib infrastructure
|
4
|
+
require 'pathname'
|
5
|
+
require 'rake'
|
6
|
+
require 'timeout'
|
7
|
+
require 'yaml'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
# Non stdlib infrastructure
|
11
|
+
require 'procto'
|
12
|
+
require 'anima'
|
13
|
+
require 'concord'
|
14
|
+
require 'adamantium'
|
15
|
+
|
16
|
+
# Wrapped tools
|
17
|
+
require 'flay'
|
18
|
+
require 'rspec'
|
19
|
+
require 'rspec/its'
|
20
|
+
require 'simplecov'
|
21
|
+
|
22
|
+
module Central
|
23
|
+
# The devtools namespace
|
24
|
+
module Devtools
|
25
|
+
ROOT = Pathname.new(__FILE__).parent.parent.parent.freeze
|
26
|
+
PROJECT_ROOT = Pathname.pwd.freeze
|
27
|
+
SHARED_PATH = ROOT.join('shared').freeze
|
28
|
+
SHARED_SPEC_PATH = SHARED_PATH.join('spec').freeze
|
29
|
+
DEFAULT_CONFIG_PATH = ROOT.join('default/config').freeze
|
30
|
+
RAKE_FILES_GLOB = ROOT.join('shared/tasks/**/*.rake').to_s.freeze
|
31
|
+
LIB_DIRECTORY_NAME = 'lib'.freeze
|
32
|
+
SPEC_DIRECTORY_NAME = 'spec'.freeze
|
33
|
+
RAKE_FILE_NAME = 'Rakefile'.freeze
|
34
|
+
SHARED_SPEC_PATTERN = '{shared,support}/**/*.rb'.freeze
|
35
|
+
UNIT_TEST_PATH_REGEXP = %r{\bspec/unit/}
|
36
|
+
DEFAULT_CONFIG_DIR_NAME = 'config'.freeze
|
37
|
+
|
38
|
+
private_constant(*constants(false))
|
39
|
+
|
40
|
+
# Abort with message when a metric violation occurs.
|
41
|
+
#
|
42
|
+
# @param [String] message
|
43
|
+
# @return [undefined]
|
44
|
+
# @api private
|
45
|
+
def self.notify_metric_violation(message)
|
46
|
+
abort(message)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Initialize project and load tasks. This should *only* be called from an
|
50
|
+
# $application_root/Rakefile.
|
51
|
+
#
|
52
|
+
# @return [self]
|
53
|
+
# @api public
|
54
|
+
def self.init
|
55
|
+
Project::Initializer::Rake.call
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return devtools root path.
|
60
|
+
#
|
61
|
+
# @return [Pathname]
|
62
|
+
# @api private
|
63
|
+
def self.root
|
64
|
+
ROOT
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return project.
|
68
|
+
#
|
69
|
+
# @return [Project]
|
70
|
+
# @api private
|
71
|
+
def self.project
|
72
|
+
PROJECT
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
require_relative 'devtools/config'
|
78
|
+
require_relative 'devtools/project'
|
79
|
+
require_relative 'devtools/project/initializer'
|
80
|
+
require_relative 'devtools/project/initializer/rake'
|
81
|
+
require_relative 'devtools/project/initializer/rspec'
|
82
|
+
require_relative 'devtools/flay'
|
83
|
+
require_relative 'devtools/rake/flay'
|
84
|
+
|
85
|
+
module Central
|
86
|
+
# Self initialization
|
87
|
+
module Devtools
|
88
|
+
PROJECT = Project.new(PROJECT_ROOT)
|
89
|
+
end
|
90
|
+
end
|