Move dependency "devtools" to the repo
This commit is contained in:
parent
51f2ff7efc
commit
443fdc93f1
54 changed files with 2025 additions and 39 deletions
2
Gemfile
2
Gemfile
|
@ -4,4 +4,6 @@ source 'https://rubygems.org'
|
|||
|
||||
gemspec name: 'mutant'
|
||||
|
||||
gem 'devtools', path: 'devtools'
|
||||
|
||||
eval_gemfile File.expand_path('Gemfile.shared', __dir__)
|
||||
|
|
80
Gemfile.lock
80
Gemfile.lock
|
@ -20,6 +20,29 @@ PATH
|
|||
mutant (~> 0.8.24)
|
||||
rspec-core (>= 3.4.0, < 4.0.0)
|
||||
|
||||
PATH
|
||||
remote: devtools
|
||||
specs:
|
||||
devtools (0.1.22)
|
||||
abstract_type (~> 0.0.7)
|
||||
adamantium (~> 0.2.0)
|
||||
anima (~> 0.3.0)
|
||||
concord (~> 0.1.5)
|
||||
flay (~> 2.12.0)
|
||||
flog (~> 4.6.2)
|
||||
mutant (~> 0.8.19)
|
||||
mutant-rspec (~> 0.8.19)
|
||||
procto (~> 0.0.3)
|
||||
rake (~> 12.3.0)
|
||||
reek (~> 5.2.0)
|
||||
rspec (~> 3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-its (~> 1.2.0)
|
||||
rubocop (~> 0.60.0)
|
||||
simplecov (~> 0.16.1)
|
||||
yard (~> 0.9.16)
|
||||
yardstick (~> 0.9.9)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
|
@ -45,41 +68,22 @@ GEM
|
|||
equalizer (~> 0.0.9)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
devtools (0.1.22)
|
||||
abstract_type (~> 0.0.7)
|
||||
adamantium (~> 0.2.0)
|
||||
anima (~> 0.3.0)
|
||||
concord (~> 0.1.5)
|
||||
flay (~> 2.12.0)
|
||||
flog (~> 4.6.2)
|
||||
mutant (~> 0.8.19)
|
||||
mutant-rspec (~> 0.8.19)
|
||||
procto (~> 0.0.3)
|
||||
rake (~> 12.3.0)
|
||||
reek (~> 5.2.0)
|
||||
rspec (~> 3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-its (~> 1.2.0)
|
||||
rubocop (~> 0.60.0)
|
||||
simplecov (~> 0.16.1)
|
||||
yard (~> 0.9.16)
|
||||
yardstick (~> 0.9.9)
|
||||
diff-lcs (1.3)
|
||||
docile (1.3.1)
|
||||
docile (1.3.5)
|
||||
equalizer (0.0.11)
|
||||
erubis (2.7.0)
|
||||
flay (2.12.0)
|
||||
flay (2.12.1)
|
||||
erubis (~> 2.7.0)
|
||||
path_expander (~> 1.0)
|
||||
ruby_parser (~> 3.0)
|
||||
sexp_processor (~> 4.0)
|
||||
flog (4.6.2)
|
||||
flog (4.6.4)
|
||||
path_expander (~> 1.0)
|
||||
ruby_parser (~> 3.1, > 3.1.0)
|
||||
sexp_processor (~> 4.8)
|
||||
ice_nine (0.11.2)
|
||||
jaro_winkler (1.5.1)
|
||||
json (2.1.0)
|
||||
jaro_winkler (1.5.4)
|
||||
json (2.5.1)
|
||||
kwalify (0.7.2)
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
|
@ -95,11 +99,11 @@ GEM
|
|||
parallel (1.12.1)
|
||||
parser (2.5.3.0)
|
||||
ast (~> 2.4.0)
|
||||
path_expander (1.0.3)
|
||||
powerpack (0.1.2)
|
||||
path_expander (1.1.0)
|
||||
powerpack (0.1.3)
|
||||
procto (0.0.3)
|
||||
rainbow (3.0.0)
|
||||
rake (12.3.1)
|
||||
rake (12.3.3)
|
||||
reek (5.2.0)
|
||||
codeclimate-engine-rb (~> 0.4.0)
|
||||
kwalify (~> 0.7.0)
|
||||
|
@ -110,18 +114,18 @@ GEM
|
|||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-core (3.8.0)
|
||||
rspec-core (3.8.2)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-expectations (3.8.2)
|
||||
rspec-expectations (3.8.6)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-its (1.2.0)
|
||||
rspec-core (>= 3.0.0)
|
||||
rspec-expectations (>= 3.0.0)
|
||||
rspec-mocks (3.8.0)
|
||||
rspec-mocks (3.8.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-support (3.8.0)
|
||||
rspec-support (3.8.3)
|
||||
rubocop (0.60.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
|
@ -130,17 +134,17 @@ GEM
|
|||
rainbow (>= 2.2.2, < 4.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.4.0)
|
||||
ruby-progressbar (1.10.0)
|
||||
ruby_parser (3.11.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby_parser (3.15.1)
|
||||
sexp_processor (~> 4.9)
|
||||
sexp_processor (4.11.0)
|
||||
sexp_processor (4.15.2)
|
||||
simplecov (0.16.1)
|
||||
docile (~> 1.1)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.2)
|
||||
thread_safe (0.3.6)
|
||||
unicode-display_width (1.4.0)
|
||||
unicode-display_width (1.4.1)
|
||||
unparser (0.4.2)
|
||||
abstract_type (~> 0.0.7)
|
||||
adamantium (~> 0.2.0)
|
||||
|
@ -154,7 +158,7 @@ GEM
|
|||
coercible (~> 1.0)
|
||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||
equalizer (~> 0.0, >= 0.0.9)
|
||||
yard (0.9.16)
|
||||
yard (0.9.26)
|
||||
yardstick (0.9.9)
|
||||
yard (~> 0.8, >= 0.8.7.2)
|
||||
|
||||
|
@ -162,9 +166,9 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
devtools (~> 0.1.22)
|
||||
devtools!
|
||||
mutant!
|
||||
parallel (~> 1.3)
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.1
|
||||
1.17.3
|
||||
|
|
41
devtools/.circleci/config.yml
Normal file
41
devtools/.circleci/config.yml
Normal file
|
@ -0,0 +1,41 @@
|
|||
defaults: &defaults
|
||||
working_directory: ~/mutant
|
||||
docker:
|
||||
- image: circleci/ruby:2.5.3
|
||||
version: 2
|
||||
jobs:
|
||||
unit_specs:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run: bundle install
|
||||
- run: bundle exec rspec spec/unit
|
||||
integration_specs:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run: bundle install
|
||||
- run: bundle exec rspec spec/integration
|
||||
metrics:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run: bundle install
|
||||
- run: bundle exec rake metrics:rubocop
|
||||
- run: bundle exec rake metrics:reek
|
||||
- run: bundle exec rake metrics:flay
|
||||
- run: bundle exec rake metrics:flog
|
||||
mutant:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run: bundle install
|
||||
- run: bundle exec rake metrics:mutant
|
||||
workflows:
|
||||
version: 2
|
||||
test:
|
||||
jobs:
|
||||
- unit_specs
|
||||
- integration_specs
|
||||
- metrics
|
||||
- mutant
|
37
devtools/.gitignore
vendored
Normal file
37
devtools/.gitignore
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
## MAC OS
|
||||
.DS_Store
|
||||
|
||||
## TEXTMATE
|
||||
*.tmproj
|
||||
tmtags
|
||||
|
||||
## EMACS
|
||||
*~
|
||||
\#*
|
||||
.\#*
|
||||
|
||||
## VIM
|
||||
*.sw[op]
|
||||
|
||||
## Rubinius
|
||||
*.rbc
|
||||
.rbx
|
||||
|
||||
## PROJECT::GENERAL
|
||||
*.gem
|
||||
coverage
|
||||
profiling
|
||||
turbulence
|
||||
rdoc
|
||||
pkg
|
||||
tmp
|
||||
doc
|
||||
log
|
||||
.yardoc
|
||||
measurements
|
||||
|
||||
## BUNDLER
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
|
||||
## PROJECT::SPECIFIC
|
6
devtools/.rspec
Normal file
6
devtools/.rspec
Normal file
|
@ -0,0 +1,6 @@
|
|||
--color
|
||||
--format progress
|
||||
--profile
|
||||
--warnings
|
||||
--order random
|
||||
--require spec_helper
|
4
devtools/.rubocop.yml
Normal file
4
devtools/.rubocop.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
AllCops:
|
||||
DisplayCopNames: true
|
||||
Include:
|
||||
- 'Gemfile'
|
3
devtools/Gemfile
Normal file
3
devtools/Gemfile
Normal file
|
@ -0,0 +1,3 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gemspec
|
20
devtools/LICENSE
Normal file
20
devtools/LICENSE
Normal file
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2014 Markus Schirp
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
47
devtools/README.md
Normal file
47
devtools/README.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# devtools
|
||||
|
||||
[](https://circleci.com/gh/mbj/devtools/tree/master)
|
||||
[](https://gemnasium.com/mbj/devtools)
|
||||
[](https://codeclimate.com/github/datamapper/devtools)
|
||||
<!-- [](https://codeclimate.com/github/mbj/devtools) -->
|
||||
|
||||
Metagem to assist development.
|
||||
Used to centralize metric setup and development gem dependencies.
|
||||
|
||||
## Installation
|
||||
|
||||
Add the gem to your Gemfile's development section.
|
||||
|
||||
```ruby
|
||||
group :development, :test do
|
||||
gem 'devtools', '~> 0.1.x'
|
||||
end
|
||||
```
|
||||
|
||||
## RSpec support
|
||||
|
||||
If you're using RSpec and want to have access to our common setup just adjust
|
||||
`spec/spec_helper.rb` to include
|
||||
|
||||
```ruby
|
||||
require 'devtools/spec_helper'
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
The whole [ROM](https://github.com/rom-rb) team that created and maintained all
|
||||
these tasks before they were centralized here.
|
||||
|
||||
## Contributing
|
||||
|
||||
* Fork the project.
|
||||
* Make your feature addition or bug fix.
|
||||
* Add tests for it. This is important so I don't break it in a
|
||||
future version unintentionally.
|
||||
* Commit, do not mess with Rakefile or version
|
||||
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
||||
* Send me a pull request. Bonus points for topic branches.
|
||||
|
||||
## License
|
||||
|
||||
See `LICENSE` file.
|
5
devtools/Rakefile
Normal file
5
devtools/Rakefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'devtools'
|
||||
|
||||
ENV['DEVTOOLS_SELF'] = '1'
|
||||
|
||||
Devtools.init_rake_tasks
|
2
devtools/config/devtools.yml
Normal file
2
devtools/config/devtools.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
unit_test_timeout: 0.2
|
3
devtools/config/flay.yml
Normal file
3
devtools/config/flay.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
threshold: 8
|
||||
total_score: 122
|
2
devtools/config/flog.yml
Normal file
2
devtools/config/flog.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
threshold: 13.5
|
4
devtools/config/mutant.yml
Normal file
4
devtools/config/mutant.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
name: devtools
|
||||
namespace: Devtools
|
||||
ignore_subjects:
|
||||
- "Devtools::Flay::Scale#flay"
|
106
devtools/config/reek.yml
Normal file
106
devtools/config/reek.yml
Normal file
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
detectors:
|
||||
Attribute:
|
||||
enabled: false
|
||||
exclude: []
|
||||
BooleanParameter:
|
||||
enabled: true
|
||||
exclude: []
|
||||
ClassVariable:
|
||||
enabled: true
|
||||
exclude: []
|
||||
ControlParameter:
|
||||
enabled: true
|
||||
exclude: []
|
||||
DataClump:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_copies: 0
|
||||
min_clump_size: 2
|
||||
DuplicateMethodCall:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_calls: 1
|
||||
allow_calls: []
|
||||
FeatureEnvy:
|
||||
enabled: true
|
||||
exclude: []
|
||||
IrresponsibleModule:
|
||||
enabled: true
|
||||
exclude: []
|
||||
LongParameterList:
|
||||
enabled: true
|
||||
exclude:
|
||||
- Devtools::Config#self.attribute
|
||||
max_params: 2
|
||||
overrides: {}
|
||||
LongYieldList:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_params: 0
|
||||
NestedIterators:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_allowed_nesting: 1
|
||||
ignore_iterators: []
|
||||
NilCheck:
|
||||
enabled: true
|
||||
exclude: []
|
||||
RepeatedConditional:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_ifs: 1
|
||||
TooManyConstants:
|
||||
enabled: true
|
||||
exclude:
|
||||
- Devtools
|
||||
TooManyInstanceVariables:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_instance_variables: 2
|
||||
TooManyMethods:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_methods: 15
|
||||
TooManyStatements:
|
||||
enabled: true
|
||||
exclude: []
|
||||
max_statements: 5
|
||||
UncommunicativeMethodName:
|
||||
enabled: true
|
||||
exclude: []
|
||||
reject:
|
||||
- '/^[a-z]$/'
|
||||
- '/[0-9]$/'
|
||||
- '/[A-Z]/'
|
||||
accept: []
|
||||
UncommunicativeModuleName:
|
||||
enabled: true
|
||||
exclude: []
|
||||
reject:
|
||||
- '/^.$/'
|
||||
- '/[0-9]$/'
|
||||
accept: []
|
||||
UncommunicativeParameterName:
|
||||
enabled: true
|
||||
exclude: []
|
||||
reject:
|
||||
- '/^.$/'
|
||||
- '/[0-9]$/'
|
||||
- '/[A-Z]/'
|
||||
accept: []
|
||||
UncommunicativeVariableName:
|
||||
enabled: true
|
||||
exclude: []
|
||||
reject:
|
||||
- '/^.$/'
|
||||
- '/[0-9]$/'
|
||||
- '/[A-Z]/'
|
||||
accept: []
|
||||
UnusedParameters:
|
||||
enabled: true
|
||||
exclude: []
|
||||
UtilityFunction:
|
||||
enabled: true
|
||||
exclude:
|
||||
- Devtools::Project::Initializer::Rspec#require_files # intentional for deduplication
|
151
devtools/config/rubocop.yml
Normal file
151
devtools/config/rubocop.yml
Normal file
|
@ -0,0 +1,151 @@
|
|||
inherit_from: ../.rubocop.yml
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.5.0
|
||||
|
||||
Metrics/BlockLength:
|
||||
Exclude:
|
||||
# Ignore RSpec DSL
|
||||
- spec/**/*
|
||||
# Ignore gemspec DSL
|
||||
- '*.gemspec'
|
||||
|
||||
Naming/FileName:
|
||||
Exclude:
|
||||
- Rakefile
|
||||
|
||||
# Avoid parameter lists longer than five parameters.
|
||||
ParameterLists:
|
||||
Max: 3
|
||||
CountKeywordArgs: true
|
||||
|
||||
# Avoid more than `Max` levels of nesting.
|
||||
BlockNesting:
|
||||
Max: 3
|
||||
|
||||
# Align with the style guide.
|
||||
CollectionMethods:
|
||||
PreferredMethods:
|
||||
collect: 'map'
|
||||
inject: 'reduce'
|
||||
find: 'detect'
|
||||
find_all: 'select'
|
||||
|
||||
# Do not force public/protected/private keyword to be indented at the same
|
||||
# level as the def keyword. My personal preference is to outdent these keywords
|
||||
# because I think when scanning code it makes it easier to identify the
|
||||
# sections of code and visually separate them. When the keyword is at the same
|
||||
# level I think it sort of blends in with the def keywords and makes it harder
|
||||
# to scan the code and see where the sections are.
|
||||
AccessModifierIndentation:
|
||||
Enabled: false
|
||||
|
||||
# Limit line length
|
||||
LineLength:
|
||||
Max: 106
|
||||
|
||||
# Disable documentation checking until a class needs to be documented once
|
||||
Documentation:
|
||||
Enabled: false
|
||||
|
||||
# Do not always use &&/|| instead of and/or.
|
||||
AndOr:
|
||||
Enabled: false
|
||||
|
||||
# Do not favor modifier if/unless usage when you have a single-line body
|
||||
IfUnlessModifier:
|
||||
Enabled: false
|
||||
|
||||
# Allow case equality operator (in limited use within the specs)
|
||||
CaseEquality:
|
||||
Enabled: false
|
||||
|
||||
# Constants do not always have to use SCREAMING_SNAKE_CASE
|
||||
ConstantName:
|
||||
Enabled: false
|
||||
|
||||
# Not all trivial readers/writers can be defined with attr_* methods
|
||||
TrivialAccessors:
|
||||
Enabled: false
|
||||
|
||||
# Allow empty lines around class body
|
||||
EmptyLinesAroundClassBody:
|
||||
Enabled: false
|
||||
|
||||
# Allow empty lines around module body
|
||||
EmptyLinesAroundModuleBody:
|
||||
Enabled: false
|
||||
|
||||
# Allow empty lines around block body
|
||||
EmptyLinesAroundBlockBody:
|
||||
Enabled: false
|
||||
|
||||
# Allow multiple line operations to not require indentation
|
||||
MultilineOperationIndentation:
|
||||
Enabled: false
|
||||
|
||||
# Prefer String#% over Kernel#sprintf
|
||||
FormatString:
|
||||
Enabled: false
|
||||
|
||||
# Use square brackets for literal Array objects
|
||||
PercentLiteralDelimiters:
|
||||
PreferredDelimiters:
|
||||
'%': '{}'
|
||||
'%i': '[]'
|
||||
'%q': ()
|
||||
'%Q': ()
|
||||
'%r': '{}'
|
||||
'%s': ()
|
||||
'%w': '[]'
|
||||
'%W': '[]'
|
||||
'%x': ()
|
||||
|
||||
# Align if/else blocks with the variable assignment
|
||||
EndAlignment:
|
||||
EnforcedStyleAlignWith: variable
|
||||
|
||||
# Do not always align parameters when it is easier to read
|
||||
AlignParameters:
|
||||
Exclude:
|
||||
- spec/**/*_spec.rb
|
||||
|
||||
# Prefer #kind_of? over #is_a?
|
||||
ClassCheck:
|
||||
EnforcedStyle: kind_of?
|
||||
|
||||
# Do not prefer double quotes to be used when %q or %Q is more appropriate
|
||||
UnneededPercentQ:
|
||||
Enabled: false
|
||||
|
||||
# Allow a maximum ABC score
|
||||
Metrics/AbcSize:
|
||||
Max: 20.1
|
||||
|
||||
# Do not prefer lambda.call(...) over lambda.(...)
|
||||
LambdaCall:
|
||||
Enabled: false
|
||||
|
||||
# Allow additional spaces
|
||||
ExtraSpacing:
|
||||
Enabled: false
|
||||
|
||||
# All objects can still be mutated if their eigenclass is patched
|
||||
RedundantFreeze:
|
||||
Enabled: false
|
||||
|
||||
# Prefer using `fail` when raising and `raise` when reraising
|
||||
SignalException:
|
||||
EnforcedStyle: semantic
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
|
||||
Style/CommentedKeyword:
|
||||
Enabled: false
|
||||
|
||||
Style/MixinGrouping:
|
||||
Enabled: false
|
||||
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
EnforcedStyle: indented
|
2
devtools/config/yardstick.yml
Normal file
2
devtools/config/yardstick.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
threshold: 100
|
36
devtools/devtools.gemspec
Normal file
36
devtools/devtools.gemspec
Normal file
|
@ -0,0 +1,36 @@
|
|||
Gem::Specification.new do |gem|
|
||||
gem.name = 'devtools'
|
||||
gem.version = '0.1.22'
|
||||
gem.authors = ['Markus Schirp']
|
||||
gem.email = ['mbj@schirp-dso.com']
|
||||
gem.description = 'A metagem wrapping development tools'
|
||||
gem.summary = gem.description
|
||||
gem.homepage = 'https://github.com/rom-rb/devtools'
|
||||
gem.license = 'MIT'
|
||||
|
||||
gem.require_paths = %w[lib]
|
||||
gem.files = `git ls-files`.split("\n")
|
||||
gem.executables = %w[]
|
||||
gem.test_files = `git ls-files -- spec`.split("\n")
|
||||
gem.extra_rdoc_files = %w[README.md]
|
||||
gem.required_ruby_version = '>= 2.5'
|
||||
|
||||
gem.add_runtime_dependency 'abstract_type', '~> 0.0.7'
|
||||
gem.add_runtime_dependency 'adamantium', '~> 0.2.0'
|
||||
gem.add_runtime_dependency 'anima', '~> 0.3.0'
|
||||
gem.add_runtime_dependency 'concord', '~> 0.1.5'
|
||||
gem.add_runtime_dependency 'flay', '~> 2.12.0'
|
||||
gem.add_runtime_dependency 'flog', '~> 4.6.2'
|
||||
gem.add_runtime_dependency 'mutant', '~> 0.8.19'
|
||||
gem.add_runtime_dependency 'mutant-rspec', '~> 0.8.19'
|
||||
gem.add_runtime_dependency 'procto', '~> 0.0.3'
|
||||
gem.add_runtime_dependency 'rake', '~> 12.3.0'
|
||||
gem.add_runtime_dependency 'reek', '~> 5.2.0'
|
||||
gem.add_runtime_dependency 'rspec', '~> 3.8.0'
|
||||
gem.add_runtime_dependency 'rspec-core', '~> 3.8.0'
|
||||
gem.add_runtime_dependency 'rspec-its', '~> 1.2.0'
|
||||
gem.add_runtime_dependency 'rubocop', '~> 0.60.0'
|
||||
gem.add_runtime_dependency 'simplecov', '~> 0.16.1'
|
||||
gem.add_runtime_dependency 'yard', '~> 0.9.16'
|
||||
gem.add_runtime_dependency 'yardstick', '~> 0.9.9'
|
||||
end
|
96
devtools/lib/devtools.rb
Normal file
96
devtools/lib/devtools.rb
Normal file
|
@ -0,0 +1,96 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Stdlib infrastructure
|
||||
require 'pathname'
|
||||
require 'rake'
|
||||
require 'timeout'
|
||||
require 'yaml'
|
||||
require 'fileutils'
|
||||
|
||||
# Non stdlib infrastructure
|
||||
require 'abstract_type'
|
||||
require 'procto'
|
||||
require 'anima'
|
||||
require 'concord'
|
||||
require 'adamantium'
|
||||
|
||||
# Wrapped tools
|
||||
require 'flay'
|
||||
require 'rspec'
|
||||
require 'rspec/its'
|
||||
require 'simplecov'
|
||||
|
||||
# Main devtools namespace population
|
||||
module Devtools
|
||||
ROOT = Pathname.new(__FILE__).parent.parent.freeze
|
||||
PROJECT_ROOT = Pathname.pwd.freeze
|
||||
SHARED_PATH = ROOT.join('shared').freeze
|
||||
SHARED_SPEC_PATH = SHARED_PATH.join('spec').freeze
|
||||
DEFAULT_CONFIG_PATH = ROOT.join('default/config').freeze
|
||||
RAKE_FILES_GLOB = ROOT.join('tasks/**/*.rake').to_s.freeze
|
||||
LIB_DIRECTORY_NAME = 'lib'
|
||||
SPEC_DIRECTORY_NAME = 'spec'
|
||||
RAKE_FILE_NAME = 'Rakefile'
|
||||
SHARED_SPEC_PATTERN = '{shared,support}/**/*.rb'
|
||||
UNIT_TEST_PATH_REGEXP = %r{\bspec/unit/}
|
||||
DEFAULT_CONFIG_DIR_NAME = 'config'
|
||||
|
||||
private_constant(*constants(false))
|
||||
|
||||
# React to metric violation
|
||||
#
|
||||
# @param [String] msg
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
def self.notify_metric_violation(msg)
|
||||
abort(msg)
|
||||
end
|
||||
|
||||
# Initialize project and load tasks
|
||||
#
|
||||
# Should *only* be called from your $application_root/Rakefile
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api public
|
||||
def self.init_rake_tasks
|
||||
Project::Initializer::Rake.call
|
||||
self
|
||||
end
|
||||
|
||||
# Return devtools root path
|
||||
#
|
||||
# @return [Pathname]
|
||||
#
|
||||
# @api private
|
||||
def self.root
|
||||
ROOT
|
||||
end
|
||||
|
||||
# Return project
|
||||
#
|
||||
# @return [Project]
|
||||
#
|
||||
# @api private
|
||||
def self.project
|
||||
PROJECT
|
||||
end
|
||||
|
||||
end # module Devtools
|
||||
|
||||
# Devtools implementation
|
||||
require 'devtools/config'
|
||||
require 'devtools/project'
|
||||
require 'devtools/project/initializer'
|
||||
require 'devtools/project/initializer/rake'
|
||||
require 'devtools/project/initializer/rspec'
|
||||
require 'devtools/flay'
|
||||
require 'devtools/rake/flay'
|
||||
|
||||
# Devtools self initialization
|
||||
module Devtools
|
||||
# The project devtools is active for
|
||||
PROJECT = Project.new(PROJECT_ROOT)
|
||||
end
|
180
devtools/lib/devtools/config.rb
Normal file
180
devtools/lib/devtools/config.rb
Normal file
|
@ -0,0 +1,180 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Devtools
|
||||
# Abstract base class of tool configuration
|
||||
class Config
|
||||
include Adamantium::Flat, AbstractType, Concord.new(:config_dir)
|
||||
|
||||
# Represent no configuration
|
||||
DEFAULT_CONFIG = {}.freeze
|
||||
|
||||
# Simple named type check representation
|
||||
class TypeCheck
|
||||
# Type check against expected class
|
||||
include Concord.new(:name, :allowed_classes)
|
||||
|
||||
ERROR_FORMAT = '%<name>s: Got instance of %<got>s expected %<allowed>s'
|
||||
CLASS_DELIM = ','
|
||||
|
||||
# Check value for instance of expected class
|
||||
#
|
||||
# @param [Object] value
|
||||
#
|
||||
# @return [Object]
|
||||
def call(value)
|
||||
klass = value.class
|
||||
format_values = {
|
||||
name: name,
|
||||
got: klass,
|
||||
allowed: allowed_classes.join(CLASS_DELIM)
|
||||
}
|
||||
|
||||
unless allowed_classes.any?(&klass.method(:equal?))
|
||||
fail TypeError, format(ERROR_FORMAT, format_values)
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
end # TypeCheck
|
||||
|
||||
private_constant(*constants(false))
|
||||
|
||||
# Error raised on type errors
|
||||
TypeError = Class.new(RuntimeError)
|
||||
|
||||
# Declare an attribute
|
||||
#
|
||||
# @param [Symbol] name
|
||||
# @param [Array<Class>] classes
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
def self.attribute(name, classes, **options)
|
||||
default = [options.fetch(:default)] if options.key?(:default)
|
||||
type_check = TypeCheck.new(name, classes)
|
||||
key = name.to_s
|
||||
|
||||
define_method(name) do
|
||||
type_check.call(raw.fetch(key, *default))
|
||||
end
|
||||
end
|
||||
private_class_method :attribute
|
||||
|
||||
# Return config path
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def config_file
|
||||
config_dir.join(self.class::FILE)
|
||||
end
|
||||
memoize :config_file
|
||||
|
||||
private
|
||||
|
||||
# Return raw data
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def raw
|
||||
yaml_config || DEFAULT_CONFIG
|
||||
end
|
||||
memoize :raw
|
||||
|
||||
# Return the raw config data from a yaml file
|
||||
#
|
||||
# @return [Hash]
|
||||
# returned if the yaml file is found
|
||||
# @return [nil]
|
||||
# returned if the yaml file is not found
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def yaml_config
|
||||
IceNine.deep_freeze(YAML.load_file(config_file)) if config_file.file?
|
||||
end
|
||||
|
||||
# Rubocop configuration
|
||||
class Rubocop < self
|
||||
FILE = 'rubocop.yml'
|
||||
end # Rubocop
|
||||
|
||||
# Reek configuration
|
||||
class Reek < self
|
||||
FILE = 'reek.yml'
|
||||
end # Reek
|
||||
|
||||
# Flay configuration
|
||||
#
|
||||
class Flay < self
|
||||
FILE = 'flay.yml'
|
||||
DEFAULT_LIB_DIRS = %w[lib].freeze
|
||||
DEFAULT_EXCLUDES = %w[].freeze
|
||||
|
||||
attribute :total_score, [0.class]
|
||||
attribute :threshold, [0.class]
|
||||
attribute :lib_dirs, [Array], default: DEFAULT_LIB_DIRS
|
||||
attribute :excludes, [Array], default: DEFAULT_EXCLUDES
|
||||
end # Flay
|
||||
|
||||
# Yardstick configuration
|
||||
class Yardstick < self
|
||||
FILE = 'yardstick.yml'
|
||||
OPTIONS = %w[
|
||||
threshold
|
||||
rules
|
||||
verbose
|
||||
path
|
||||
require_exact_threshold
|
||||
].freeze
|
||||
|
||||
# Options hash that Yardstick understands
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
# @api private
|
||||
def options
|
||||
OPTIONS.each_with_object({}) do |name, hash|
|
||||
hash[name] = raw.fetch(name, nil)
|
||||
end
|
||||
end
|
||||
end # Yardstick
|
||||
|
||||
# Flog configuration
|
||||
class Flog < self
|
||||
FILE = 'flog.yml'
|
||||
DEFAULT_LIB_DIRS = %w[lib].freeze
|
||||
|
||||
attribute :total_score, [Float]
|
||||
attribute :threshold, [Float]
|
||||
attribute :lib_dirs, [Array], default: DEFAULT_LIB_DIRS
|
||||
end # Flog
|
||||
|
||||
# Mutant configuration
|
||||
class Mutant < self
|
||||
FILE = 'mutant.yml'
|
||||
DEFAULT_NAME = ''
|
||||
DEFAULT_STRATEGY = 'rspec'
|
||||
|
||||
attribute :name, [String], default: DEFAULT_NAME
|
||||
attribute :strategy, [String], default: DEFAULT_STRATEGY
|
||||
attribute :zombify, [TrueClass, FalseClass], default: false
|
||||
attribute :since, [String, NilClass], default: nil
|
||||
attribute :ignore_subjects, [Array], default: []
|
||||
attribute :namespace, [String]
|
||||
end # Mutant
|
||||
|
||||
# Devtools configuration
|
||||
class Devtools < self
|
||||
FILE = 'devtools.yml'
|
||||
DEFAULT_UNIT_TEST_TIMEOUT = 0.1 # 100ms
|
||||
|
||||
attribute :unit_test_timeout, [Float], default: DEFAULT_UNIT_TEST_TIMEOUT
|
||||
end # Devtools
|
||||
end # Config
|
||||
end # Devtools
|
98
devtools/lib/devtools/flay.rb
Normal file
98
devtools/lib/devtools/flay.rb
Normal file
|
@ -0,0 +1,98 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Devtools
|
||||
module Flay
|
||||
# Measure flay mass relative to size of duplicated sexps
|
||||
class Scale
|
||||
include Adamantium
|
||||
include Anima.new(:minimum_mass, :files)
|
||||
include Procto.call(:measure)
|
||||
|
||||
# Measure duplication mass
|
||||
#
|
||||
# @return [Array<Rational>]
|
||||
#
|
||||
# @api private
|
||||
def measure
|
||||
flay.masses.map do |hash, mass|
|
||||
Rational(mass, flay.hashes.fetch(hash).size)
|
||||
end
|
||||
end
|
||||
|
||||
# Report flay output
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
def flay_report
|
||||
flay.report
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Memoized flay instance
|
||||
#
|
||||
# @return [Flay]
|
||||
#
|
||||
# @api private
|
||||
def flay
|
||||
::Flay.new(mass: minimum_mass).tap do |flay|
|
||||
flay.process(*files)
|
||||
flay.analyze
|
||||
end
|
||||
end
|
||||
memoize :flay, freezer: :noop
|
||||
end
|
||||
|
||||
# Expand include and exclude file settings for flay
|
||||
class FileList
|
||||
include Procto.call, Concord.new(:includes, :excludes)
|
||||
|
||||
GLOB = '**/*.{rb,erb}'
|
||||
|
||||
# Expand includes and filter by excludes
|
||||
#
|
||||
# @return [Set<Pathname>]
|
||||
#
|
||||
# @api private
|
||||
def call
|
||||
include_set - exclude_set
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Set of excluded files
|
||||
#
|
||||
# @return [Set<Pathname>]
|
||||
#
|
||||
# @api private
|
||||
def exclude_set
|
||||
excludes.flat_map(&Pathname.method(:glob))
|
||||
end
|
||||
|
||||
# Set of included files
|
||||
#
|
||||
# Expanded using flay's file expander which takes into
|
||||
# account flay's plugin support
|
||||
#
|
||||
# @return [Set<Pathname>]
|
||||
#
|
||||
# @api private
|
||||
def include_set
|
||||
Set.new(flay_includes.map(&method(:Pathname)))
|
||||
end
|
||||
|
||||
# Expand includes using flay
|
||||
#
|
||||
# Expanded using flay's file expander which takes into
|
||||
# account flay's plugin support
|
||||
#
|
||||
# @return [Array<String>]
|
||||
#
|
||||
# @api private
|
||||
def flay_includes
|
||||
PathExpander.new(includes.dup, GLOB).process
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
80
devtools/lib/devtools/project.rb
Normal file
80
devtools/lib/devtools/project.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
module Devtools
|
||||
|
||||
# The project devtools supports
|
||||
class Project
|
||||
include Concord.new(:root)
|
||||
|
||||
CONFIGS = {
|
||||
devtools: Config::Devtools,
|
||||
flay: Config::Flay,
|
||||
flog: Config::Flog,
|
||||
reek: Config::Reek,
|
||||
mutant: Config::Mutant,
|
||||
rubocop: Config::Rubocop,
|
||||
yardstick: Config::Yardstick
|
||||
}.freeze
|
||||
|
||||
private_constant(*constants(false))
|
||||
|
||||
attr_reader(*CONFIGS.keys)
|
||||
|
||||
# The spec root
|
||||
#
|
||||
# @return [Pathname]
|
||||
#
|
||||
# @api private
|
||||
attr_reader :spec_root
|
||||
|
||||
# Initialize object
|
||||
#
|
||||
# @param [Pathname] root
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(root)
|
||||
super(root)
|
||||
|
||||
initialize_environment
|
||||
initialize_configs
|
||||
end
|
||||
|
||||
# Init rspec
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
def init_rspec
|
||||
Initializer::Rspec.call(self)
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Initialize environment
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize_environment
|
||||
@spec_root = root.join(SPEC_DIRECTORY_NAME)
|
||||
end
|
||||
|
||||
# Initialize configs
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize_configs
|
||||
config_dir = root.join(DEFAULT_CONFIG_DIR_NAME)
|
||||
|
||||
CONFIGS.each do |name, klass|
|
||||
instance_variable_set(:"@#{name}", klass.new(config_dir))
|
||||
end
|
||||
end
|
||||
|
||||
end # class Project
|
||||
end # module Devtools
|
11
devtools/lib/devtools/project/initializer.rb
Normal file
11
devtools/lib/devtools/project/initializer.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
module Devtools
|
||||
class Project
|
||||
|
||||
# Base class for project initializers
|
||||
class Initializer
|
||||
include AbstractType
|
||||
|
||||
abstract_singleton_method :call
|
||||
end # class Initializer
|
||||
end # class Project
|
||||
end # module Devtools
|
23
devtools/lib/devtools/project/initializer/rake.rb
Normal file
23
devtools/lib/devtools/project/initializer/rake.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
module Devtools
|
||||
class Project
|
||||
class Initializer
|
||||
# Imports all devtools rake tasks into a project
|
||||
class Rake < self
|
||||
include AbstractType
|
||||
|
||||
# Initialize rake tasks
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api rpivate
|
||||
def self.call
|
||||
FileList
|
||||
.glob(RAKE_FILES_GLOB)
|
||||
.each(&::Rake.application.method(:add_import))
|
||||
self
|
||||
end
|
||||
|
||||
end # class Rake
|
||||
end # class Initializer
|
||||
end # class Project
|
||||
end # module Devtools
|
80
devtools/lib/devtools/project/initializer/rspec.rb
Normal file
80
devtools/lib/devtools/project/initializer/rspec.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
module Devtools
|
||||
class Project
|
||||
class Initializer
|
||||
|
||||
# Requires all shared specs in a project's spec_helper
|
||||
# Also installs a configurable unit test timeout
|
||||
class Rspec < self
|
||||
include Concord.new(:project)
|
||||
|
||||
# Call initializer for project
|
||||
#
|
||||
# @param [Project] project
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
def self.call(project)
|
||||
new(project).__send__(:call)
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Setup RSpec for project
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
def call
|
||||
require_shared_spec_files
|
||||
enable_unit_test_timeout
|
||||
end
|
||||
|
||||
# Timeout unit tests that take longer than configured amount of time
|
||||
#
|
||||
# @param [Numeric] timeout
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @raise [Timeout::Error]
|
||||
# raised when the times are outside the timeout
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def enable_unit_test_timeout
|
||||
timeout = project.devtools.unit_test_timeout
|
||||
RSpec
|
||||
.configuration
|
||||
.around(file_path: UNIT_TEST_PATH_REGEXP) do |example|
|
||||
Timeout.timeout(timeout, &example)
|
||||
end
|
||||
end
|
||||
|
||||
# Trigger the require of shared spec files
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def require_shared_spec_files
|
||||
require_files(SHARED_SPEC_PATH)
|
||||
require_files(project.spec_root)
|
||||
end
|
||||
|
||||
# Require files with pattern
|
||||
#
|
||||
# @param [Pathname] dir
|
||||
# the directory containing the files to require
|
||||
#
|
||||
# @return [self]
|
||||
#
|
||||
# @api private
|
||||
def require_files(dir)
|
||||
Dir.glob(dir.join(SHARED_SPEC_PATTERN)).each(&Kernel.method(:require))
|
||||
end
|
||||
|
||||
end # class Rspec
|
||||
end # class Initializer
|
||||
end # class Project
|
||||
end # module Devtools
|
155
devtools/lib/devtools/rake/flay.rb
Normal file
155
devtools/lib/devtools/rake/flay.rb
Normal file
|
@ -0,0 +1,155 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Devtools
|
||||
module Rake
|
||||
# Flay metric runner
|
||||
class Flay
|
||||
include Anima.new(:threshold, :total_score, :lib_dirs, :excludes),
|
||||
Procto.call(:verify),
|
||||
Adamantium
|
||||
|
||||
BELOW_THRESHOLD = 'Adjust flay threshold down to %<mass>d'
|
||||
TOTAL_MISMATCH = 'Flay total is now %<mass>d, but expected %<expected>d'
|
||||
ABOVE_THRESHOLD = '%<mass>d chunks have a duplicate mass > %<threshold>d'
|
||||
|
||||
# Verify code specified by `files` does not violate flay expectations
|
||||
#
|
||||
# @raise [SystemExit] if a violation is found
|
||||
# @return [undefined] otherwise
|
||||
#
|
||||
#
|
||||
# @api private
|
||||
def verify
|
||||
# Run flay first to ensure the max mass matches the threshold
|
||||
below_threshold_message if below_threshold?
|
||||
|
||||
total_mismatch_message if total_mismatch?
|
||||
|
||||
# Run flay a second time with the threshold set
|
||||
return unless above_threshold?
|
||||
|
||||
restricted_flay_scale.flay_report
|
||||
above_threshold_message
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# List of files flay will analyze
|
||||
#
|
||||
# @return [Set<Pathname>]
|
||||
#
|
||||
# @api private
|
||||
def files
|
||||
Devtools::Flay::FileList.call(lib_dirs, excludes)
|
||||
end
|
||||
|
||||
# Is there mass duplication which exceeds specified `threshold`
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
def above_threshold?
|
||||
restricted_mass_size.nonzero?
|
||||
end
|
||||
|
||||
# Is the specified `threshold` greater than the largest flay mass
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
def below_threshold?
|
||||
threshold > largest_mass
|
||||
end
|
||||
|
||||
# Is the expected mass total different from the actual mass total
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
def total_mismatch?
|
||||
!total_mass.equal?(total_score)
|
||||
end
|
||||
|
||||
# Above threshold message
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
def above_threshold_message
|
||||
format_values = { mass: restricted_mass_size, threshold: threshold }
|
||||
Devtools.notify_metric_violation(
|
||||
format(ABOVE_THRESHOLD, format_values)
|
||||
)
|
||||
end
|
||||
|
||||
# Below threshold message
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
def below_threshold_message
|
||||
Devtools.notify_metric_violation(
|
||||
format(BELOW_THRESHOLD, mass: largest_mass)
|
||||
)
|
||||
end
|
||||
|
||||
# Total mismatch message
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
def total_mismatch_message
|
||||
Devtools.notify_metric_violation(
|
||||
format(TOTAL_MISMATCH, mass: total_mass, expected: total_score)
|
||||
)
|
||||
end
|
||||
|
||||
# Size of mass measured by `Flay::Scale` and filtered by `threshold`
|
||||
#
|
||||
# @return [Integer]
|
||||
#
|
||||
# @api private
|
||||
def restricted_mass_size
|
||||
restricted_flay_scale.measure.size
|
||||
end
|
||||
|
||||
# Sum of all flay mass
|
||||
#
|
||||
# @return [Integer]
|
||||
#
|
||||
# @api private
|
||||
def total_mass
|
||||
flay_masses.reduce(:+).to_i
|
||||
end
|
||||
|
||||
# Largest flay mass found
|
||||
#
|
||||
# @return [Integer]
|
||||
#
|
||||
# @api private
|
||||
def largest_mass
|
||||
flay_masses.max.to_i
|
||||
end
|
||||
|
||||
# Flay scale which only measures mass above `threshold`
|
||||
#
|
||||
# @return [Flay::Scale]
|
||||
#
|
||||
# @api private
|
||||
def restricted_flay_scale
|
||||
Devtools::Flay::Scale.new(minimum_mass: threshold.succ, files: files)
|
||||
end
|
||||
memoize :restricted_flay_scale
|
||||
|
||||
# All flay masses found in `files`
|
||||
#
|
||||
# @return [Array<Rational>]
|
||||
#
|
||||
# @api private
|
||||
def flay_masses
|
||||
Devtools::Flay::Scale.call(minimum_mass: 0, files: files)
|
||||
end
|
||||
memoize :flay_masses
|
||||
end
|
||||
end
|
||||
end
|
3
devtools/lib/devtools/spec_helper.rb
Normal file
3
devtools/lib/devtools/spec_helper.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
require 'devtools'
|
||||
|
||||
Devtools::PROJECT.init_rspec
|
16
devtools/shared/spec/shared/abstract_type_behavior.rb
Normal file
16
devtools/shared/spec/shared/abstract_type_behavior.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
shared_examples_for 'an abstract type' do
|
||||
context 'called on a subclass' do
|
||||
let(:object) { Class.new(described_class) }
|
||||
|
||||
it { should be_instance_of(object) }
|
||||
end
|
||||
|
||||
context 'called on the class' do
|
||||
let(:object) { described_class }
|
||||
|
||||
specify do
|
||||
expect { subject }
|
||||
.to raise_error(NotImplementedError, "#{object} is an abstract type")
|
||||
end
|
||||
end
|
||||
end
|
5
devtools/shared/spec/shared/command_method_behavior.rb
Normal file
5
devtools/shared/spec/shared/command_method_behavior.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
shared_examples_for 'a command method' do
|
||||
it 'returns self' do
|
||||
should equal(object)
|
||||
end
|
||||
end
|
13
devtools/shared/spec/shared/each_method_behaviour.rb
Normal file
13
devtools/shared/spec/shared/each_method_behaviour.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
shared_examples_for 'an #each method' do
|
||||
it_should_behave_like 'a command method'
|
||||
|
||||
context 'with no block' do
|
||||
subject { object.each }
|
||||
|
||||
it { should be_instance_of(to_enum.class) }
|
||||
|
||||
it 'yields the expected values' do
|
||||
expect(subject.to_a).to eql(object.to_a)
|
||||
end
|
||||
end
|
||||
end
|
11
devtools/shared/spec/shared/idempotent_method_behavior.rb
Normal file
11
devtools/shared/spec/shared/idempotent_method_behavior.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
shared_examples_for 'an idempotent method' do
|
||||
it 'is idempotent' do
|
||||
first = subject
|
||||
error = 'RSpec not configured for threadsafety'
|
||||
fail error unless RSpec.configuration.threadsafe?
|
||||
mutex = __memoized.instance_variable_get(:@mutex)
|
||||
memoized = __memoized.instance_variable_get(:@memoized)
|
||||
mutex.synchronize { memoized.delete(:subject) }
|
||||
should equal(first)
|
||||
end
|
||||
end
|
12
devtools/shared/spec/support/ice_nine_config.rb
Normal file
12
devtools/shared/spec/support/ice_nine_config.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
if defined?(IceNine)
|
||||
module IceNine
|
||||
|
||||
# Freezer namespace
|
||||
class Freezer
|
||||
|
||||
# Rspec freezer
|
||||
class RSpec < NoFreeze; end
|
||||
|
||||
end # Freezer
|
||||
end # IceNine
|
||||
end
|
196
devtools/spec/integration/devtools/rake/flay/verify_spec.rb
Normal file
196
devtools/spec/integration/devtools/rake/flay/verify_spec.rb
Normal file
|
@ -0,0 +1,196 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Devtools::Rake::Flay, '#verify' do
|
||||
let(:tempfile) { Tempfile.new(%w[file .rb], Dir.mktmpdir) }
|
||||
let(:file) { Pathname(tempfile.path) }
|
||||
let(:directories) { [file.dirname.to_s] }
|
||||
|
||||
let(:ruby) do
|
||||
<<-ERUBY
|
||||
def foo; end
|
||||
def bar; end
|
||||
ERUBY
|
||||
end
|
||||
|
||||
around(:each) do |example|
|
||||
begin
|
||||
# silence other flay output
|
||||
$stdout = $stderr = StringIO.new
|
||||
|
||||
tempfile.write(ruby)
|
||||
tempfile.close
|
||||
|
||||
example.run
|
||||
ensure
|
||||
$stdout = STDOUT
|
||||
$stderr = STDERR
|
||||
|
||||
file.unlink
|
||||
end
|
||||
end
|
||||
|
||||
context 'reporting' do
|
||||
let(:options) do
|
||||
{ threshold: 3, total_score: 3, lib_dirs: directories, excludes: [] }
|
||||
end
|
||||
|
||||
let(:instance) { described_class.new(options) }
|
||||
|
||||
it 'measures total mass' do
|
||||
allow(::Flay).to receive(:new).and_call_original
|
||||
|
||||
instance.verify
|
||||
|
||||
expect(::Flay).to have_received(:new).with(hash_including(mass: 0))
|
||||
end
|
||||
|
||||
it 'does not report the files it is processing' do
|
||||
expect { instance.verify }.to_not output(/Processing #{file}/).to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
context 'when theshold is too low' do
|
||||
let(:instance) do
|
||||
described_class.new(
|
||||
threshold: 0,
|
||||
total_score: 0,
|
||||
lib_dirs: directories,
|
||||
excludes: []
|
||||
)
|
||||
end
|
||||
|
||||
specify do
|
||||
expect { instance.verify }
|
||||
.to raise_error(SystemExit)
|
||||
.with_message('Flay total is now 3, but expected 0')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when threshold is too high' do
|
||||
let(:instance) do
|
||||
described_class.new(
|
||||
threshold: 1000,
|
||||
total_score: 0,
|
||||
lib_dirs: directories,
|
||||
excludes: []
|
||||
)
|
||||
end
|
||||
|
||||
specify do
|
||||
expect { instance.verify }
|
||||
.to raise_error(SystemExit)
|
||||
.with_message('Adjust flay threshold down to 3')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when total is too high' do
|
||||
let(:instance) do
|
||||
described_class.new(
|
||||
threshold: 3,
|
||||
total_score: 50,
|
||||
lib_dirs: directories,
|
||||
excludes: []
|
||||
)
|
||||
end
|
||||
|
||||
specify do
|
||||
expect { instance.verify }
|
||||
.to raise_error(SystemExit)
|
||||
.with_message('Flay total is now 3, but expected 50')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when duplicate mass is greater than 0' do
|
||||
let(:ruby) do
|
||||
<<-ERUBY
|
||||
def foo
|
||||
:hi if baz?
|
||||
end
|
||||
|
||||
def bar
|
||||
:hi if baz?
|
||||
end
|
||||
ERUBY
|
||||
end
|
||||
|
||||
let(:report) do
|
||||
<<~REPORT
|
||||
Total score (lower is better) = 10
|
||||
|
||||
1) Similar code found in :defn (mass = 10)
|
||||
#{file}:1
|
||||
#{file}:5
|
||||
REPORT
|
||||
end
|
||||
|
||||
let(:instance) do
|
||||
described_class.new(
|
||||
threshold: 3,
|
||||
total_score: 5,
|
||||
lib_dirs: directories,
|
||||
excludes: []
|
||||
)
|
||||
end
|
||||
|
||||
specify do
|
||||
expect { instance.verify }
|
||||
.to raise_error(SystemExit)
|
||||
.with_message('1 chunks have a duplicate mass > 3')
|
||||
end
|
||||
|
||||
specify do
|
||||
expect { instance.verify }
|
||||
.to raise_error(SystemExit)
|
||||
.and output(report).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
context 'when multiple duplicate masses' do
|
||||
let(:ruby) do
|
||||
<<-ERUBY
|
||||
def foo; end
|
||||
def bar; end
|
||||
|
||||
class Foo
|
||||
def initialize
|
||||
@a = 1
|
||||
end
|
||||
end
|
||||
class Bar
|
||||
def initialize
|
||||
@a = 1
|
||||
end
|
||||
end
|
||||
ERUBY
|
||||
end
|
||||
|
||||
let(:instance) do
|
||||
described_class.new(
|
||||
threshold: 5,
|
||||
total_score: 8,
|
||||
lib_dirs: directories,
|
||||
excludes: []
|
||||
)
|
||||
end
|
||||
|
||||
it 'sums masses for total' do
|
||||
expect { instance.verify }.to_not raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no duplication masses' do
|
||||
let(:ruby) { '' }
|
||||
let(:instance) do
|
||||
described_class.new(
|
||||
threshold: 0,
|
||||
total_score: 0,
|
||||
lib_dirs: directories,
|
||||
excludes: []
|
||||
)
|
||||
end
|
||||
|
||||
specify do
|
||||
expect { instance.verify }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
24
devtools/spec/spec_helper.rb
Normal file
24
devtools/spec/spec_helper.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
require 'devtools/spec_helper'
|
||||
require 'tempfile'
|
||||
require 'tmpdir'
|
||||
|
||||
if ENV['COVERAGE'] == 'true'
|
||||
formatter = [SimpleCov::Formatter::HTMLFormatter]
|
||||
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(formatter)
|
||||
|
||||
SimpleCov.start do
|
||||
command_name 'spec:unit'
|
||||
|
||||
add_filter 'config'
|
||||
add_filter 'spec'
|
||||
add_filter 'vendor'
|
||||
|
||||
minimum_coverage 100
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.expect_with :rspec do |expect_with|
|
||||
expect_with.syntax = :expect
|
||||
end
|
||||
end
|
17
devtools/spec/unit/devtools/config/yardstick_spec.rb
Normal file
17
devtools/spec/unit/devtools/config/yardstick_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
RSpec.describe Devtools::Config::Yardstick do
|
||||
let(:object) { described_class.new(Devtools.root.join('config')) }
|
||||
|
||||
describe '#options' do
|
||||
subject { object.options }
|
||||
|
||||
specify do
|
||||
should eql(
|
||||
'threshold' => 100,
|
||||
'rules' => nil,
|
||||
'verbose' => nil,
|
||||
'path' => nil,
|
||||
'require_exact_threshold' => nil
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
78
devtools/spec/unit/devtools/config_spec.rb
Normal file
78
devtools/spec/unit/devtools/config_spec.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
RSpec.describe Devtools::Config do
|
||||
|
||||
describe '.attribute' do
|
||||
let(:raw) do
|
||||
{
|
||||
'a' => 'bar',
|
||||
'c' => []
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
let(:config_path) { instance_double(Pathname) }
|
||||
|
||||
let(:class_under_test) do
|
||||
expect(config_path).to receive(:file?)
|
||||
.and_return(file?)
|
||||
expect(config_path).to receive(:frozen?)
|
||||
.and_return(true)
|
||||
expect(config_path).to receive(:join)
|
||||
.with('bar.yml')
|
||||
.and_return(config_path)
|
||||
|
||||
Class.new(described_class) do
|
||||
attribute :a, [String]
|
||||
attribute :b, [Array], default: []
|
||||
attribute :c, [TrueClass, FalseClass]
|
||||
|
||||
const_set(:FILE, 'bar.yml')
|
||||
end
|
||||
end
|
||||
|
||||
subject do
|
||||
class_under_test.new(config_path)
|
||||
end
|
||||
|
||||
context 'on present config' do
|
||||
let(:class_under_test) do
|
||||
# Setup message expectation in a lasy way, not in a before
|
||||
# block to make sure the around hook setting timeouts from the
|
||||
# code under test is not affected.
|
||||
expect(YAML).to receive(:load_file)
|
||||
.with(config_path)
|
||||
.and_return(raw)
|
||||
|
||||
expect(IceNine).to receive(:deep_freeze)
|
||||
.with(raw)
|
||||
.and_return(raw)
|
||||
|
||||
super()
|
||||
end
|
||||
|
||||
let(:file?) { true }
|
||||
|
||||
it 'allows to receive existing keys' do
|
||||
expect(subject.a).to eql('bar')
|
||||
end
|
||||
|
||||
it 'allows to receive absent keys with defaults' do
|
||||
expect(subject.b).to eql([])
|
||||
end
|
||||
|
||||
it 'executes checks when configured' do
|
||||
expect { subject.c }.to raise_error(
|
||||
Devtools::Config::TypeError,
|
||||
'c: Got instance of Array expected TrueClass,FalseClass'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'on absent config' do
|
||||
let(:file?) { false }
|
||||
|
||||
it 'defaults to absent keys' do
|
||||
expect(subject.b).to eql([])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
19
devtools/spec/unit/devtools/flay/file_list/call_spec.rb
Normal file
19
devtools/spec/unit/devtools/flay/file_list/call_spec.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
describe Devtools::Flay::FileList, '.call' do
|
||||
subject(:output) { described_class.call([tmpdir.to_s].freeze, [exclude]) }
|
||||
|
||||
let(:tmpdir) { Dir.mktmpdir }
|
||||
let(:one) { Pathname(tmpdir).join('1.rb') }
|
||||
let(:two) { Pathname(tmpdir).join('2.erb') }
|
||||
let(:three) { Pathname(tmpdir).join('3.rb') }
|
||||
let(:exclude) { Pathname(tmpdir).join('3*').to_s }
|
||||
|
||||
around(:each) do |example|
|
||||
[one, two, three].map(&FileUtils.method(:touch))
|
||||
|
||||
example.run
|
||||
|
||||
FileUtils.rm_rf(tmpdir)
|
||||
end
|
||||
|
||||
it { should eql(Set.new([one, two])) }
|
||||
end
|
17
devtools/spec/unit/devtools/flay/scale/flay_report_spec.rb
Normal file
17
devtools/spec/unit/devtools/flay/scale/flay_report_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
describe Devtools::Flay::Scale, '#flay_report' do
|
||||
subject(:instance) { described_class.new(minimum_mass: 0, files: []) }
|
||||
|
||||
let(:flay) do
|
||||
instance_double(::Flay, process: nil, analyze: nil, masses: {})
|
||||
end
|
||||
|
||||
before do
|
||||
allow(::Flay).to receive(:new).with(mass: 0).and_return(flay)
|
||||
end
|
||||
|
||||
specify do
|
||||
allow(flay).to receive(:report)
|
||||
instance.flay_report
|
||||
expect(flay).to have_received(:report)
|
||||
end
|
||||
end
|
43
devtools/spec/unit/devtools/flay/scale/measure_spec.rb
Normal file
43
devtools/spec/unit/devtools/flay/scale/measure_spec.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
describe Devtools::Flay::Scale, '#measure' do
|
||||
subject(:measure) { instance.measure }
|
||||
|
||||
let(:minimum_mass) { 0 }
|
||||
let(:files) { [instance_double(File)] }
|
||||
let(:flay_masses) { { 0 => 5, 1 => 10 } }
|
||||
|
||||
let(:instance) do
|
||||
described_class.new(minimum_mass: minimum_mass, files: files)
|
||||
end
|
||||
|
||||
let(:flay_hashes) do
|
||||
{
|
||||
0 => instance_double(Array, size: 3),
|
||||
1 => instance_double(Array, size: 11)
|
||||
}
|
||||
end
|
||||
|
||||
let(:flay) do
|
||||
instance_double(
|
||||
::Flay,
|
||||
analyze: nil,
|
||||
masses: flay_masses,
|
||||
hashes: flay_hashes
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(::Flay).to receive(:new).with(mass: minimum_mass).and_return(flay)
|
||||
allow(flay).to receive(:process).with(*files)
|
||||
end
|
||||
|
||||
it { should eql([Rational(5, 3), Rational(10, 11)]) }
|
||||
|
||||
context 'when minimum mass is not 0' do
|
||||
let(:minimum_mass) { 1 }
|
||||
|
||||
specify do
|
||||
measure
|
||||
expect(::Flay).to have_received(:new).with(mass: 1)
|
||||
end
|
||||
end
|
||||
end
|
21
devtools/spec/unit/devtools/project/initializer/rake_spec.rb
Normal file
21
devtools/spec/unit/devtools/project/initializer/rake_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
describe Devtools::Project::Initializer::Rake do
|
||||
describe '.call' do
|
||||
subject do
|
||||
described_class.call
|
||||
end
|
||||
|
||||
it 'performs expected rake initialization' do
|
||||
path_a = instance_double(Pathname)
|
||||
path_b = instance_double(Pathname)
|
||||
|
||||
expect(FileList).to receive(:glob)
|
||||
.with(Devtools.root.join('tasks/**/*.rake').to_s)
|
||||
.and_return([path_a, path_b])
|
||||
|
||||
expect(Rake.application).to receive(:add_import).with(path_a)
|
||||
expect(Rake.application).to receive(:add_import).with(path_b)
|
||||
|
||||
expect(subject).to be(described_class)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
describe Devtools::Project::Initializer::Rspec do
|
||||
let(:spec_root) { Devtools.root.join('spec') }
|
||||
let(:unit_test_timeout) { instance_double(Float) }
|
||||
|
||||
let(:project) do
|
||||
instance_double(
|
||||
Devtools::Project,
|
||||
spec_root: spec_root,
|
||||
devtools: instance_double(
|
||||
Devtools::Config::Devtools,
|
||||
unit_test_timeout: unit_test_timeout
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
describe '.call' do
|
||||
subject do
|
||||
described_class.call(project)
|
||||
end
|
||||
|
||||
it 'performs expected rspec initialization' do
|
||||
called = false
|
||||
example = -> { called = true }
|
||||
|
||||
expect(Dir).to receive(:glob)
|
||||
.with(Devtools.root.join('shared/spec/{shared,support}/**/*.rb'))
|
||||
.and_return(%w[shared-a shared-b])
|
||||
|
||||
expect(Kernel).to receive(:require).with('shared-a')
|
||||
expect(Kernel).to receive(:require).with('shared-b')
|
||||
|
||||
expect(Dir).to receive(:glob)
|
||||
.with(Devtools.root.join('spec/{shared,support}/**/*.rb'))
|
||||
.and_return(%w[support-a support-b])
|
||||
|
||||
expect(Kernel).to receive(:require).with('support-a')
|
||||
expect(Kernel).to receive(:require).with('support-b')
|
||||
|
||||
expect(Timeout).to receive(:timeout).with(unit_test_timeout) do |&block|
|
||||
block.call
|
||||
end
|
||||
|
||||
expect(RSpec.configuration).to receive(:around)
|
||||
.with(file_path: %r{\bspec/unit/})
|
||||
.and_yield(example)
|
||||
|
||||
expect(subject).to be(described_class)
|
||||
|
||||
expect(called).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
35
devtools/spec/unit/devtools/project_spec.rb
Normal file
35
devtools/spec/unit/devtools/project_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
RSpec.describe Devtools::Project do
|
||||
let(:object) { described_class.new(Devtools.root) }
|
||||
|
||||
describe '#init_rspec' do
|
||||
subject { object.init_rspec }
|
||||
|
||||
it 'calls the rspec initializer' do
|
||||
expect(Devtools::Project::Initializer::Rspec)
|
||||
.to receive(:call).with(Devtools.project)
|
||||
expect(subject).to be(object)
|
||||
end
|
||||
end
|
||||
|
||||
{
|
||||
devtools: Devtools::Config::Devtools,
|
||||
flay: Devtools::Config::Flay,
|
||||
flog: Devtools::Config::Flog,
|
||||
reek: Devtools::Config::Reek,
|
||||
mutant: Devtools::Config::Mutant,
|
||||
rubocop: Devtools::Config::Rubocop,
|
||||
yardstick: Devtools::Config::Yardstick
|
||||
}.each do |name, klass|
|
||||
describe "##{name}" do
|
||||
subject { object.send(name) }
|
||||
|
||||
specify { should eql(klass.new(Devtools.root.join('config'))) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#spec_root' do
|
||||
subject { object.spec_root }
|
||||
|
||||
specify { should eql(Devtools.root.join('spec')) }
|
||||
end
|
||||
end
|
14
devtools/spec/unit/devtools_spec.rb
Normal file
14
devtools/spec/unit/devtools_spec.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
describe Devtools do
|
||||
describe '.project' do
|
||||
specify do
|
||||
expect(Devtools.project).to equal(Devtools::PROJECT)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.init_rake_tasks' do
|
||||
specify do
|
||||
expect(Devtools::Project::Initializer::Rake).to receive(:call)
|
||||
expect(Devtools.init_rake_tasks).to be(Devtools)
|
||||
end
|
||||
end
|
||||
end
|
17
devtools/tasks/metrics/ci.rake
Normal file
17
devtools/tasks/metrics/ci.rake
Normal file
|
@ -0,0 +1,17 @@
|
|||
desc 'Run all specs, metrics and mutant'
|
||||
task ci: %w[ci:metrics metrics:mutant]
|
||||
|
||||
namespace :ci do
|
||||
tasks = %w[
|
||||
metrics:coverage
|
||||
metrics:yardstick:verify
|
||||
metrics:rubocop
|
||||
metrics:flog
|
||||
metrics:flay
|
||||
metrics:reek
|
||||
spec:integration
|
||||
]
|
||||
|
||||
desc 'Run metrics (except mutant)'
|
||||
task metrics: tasks
|
||||
end
|
13
devtools/tasks/metrics/coverage.rake
Normal file
13
devtools/tasks/metrics/coverage.rake
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace :metrics do
|
||||
desc 'Measure code coverage'
|
||||
task :coverage do
|
||||
begin
|
||||
# rubocop:disable Style/ParallelAssignment
|
||||
original, ENV['COVERAGE'] = ENV['COVERAGE'], 'true'
|
||||
# rubocop:enable Style/ParallelAssignment
|
||||
Rake::Task['spec:unit'].execute
|
||||
ensure
|
||||
ENV['COVERAGE'] = original
|
||||
end
|
||||
end
|
||||
end
|
21
devtools/tasks/metrics/flay.rake
Normal file
21
devtools/tasks/metrics/flay.rake
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace :metrics do
|
||||
require 'flay'
|
||||
|
||||
project = Devtools.project
|
||||
config = project.flay
|
||||
|
||||
# Original code by Marty Andrews:
|
||||
# http://blog.martyandrews.net/2009/05/enforcing-ruby-code-quality.html
|
||||
desc 'Measure code duplication'
|
||||
task :flay do
|
||||
threshold = config.threshold
|
||||
total_score = config.total_score
|
||||
|
||||
Devtools::Rake::Flay.call(
|
||||
threshold: threshold,
|
||||
total_score: total_score,
|
||||
lib_dirs: config.lib_dirs,
|
||||
excludes: config.excludes
|
||||
)
|
||||
end
|
||||
end
|
43
devtools/tasks/metrics/flog.rake
Normal file
43
devtools/tasks/metrics/flog.rake
Normal file
|
@ -0,0 +1,43 @@
|
|||
# rubocop:disable Metrics/BlockLength
|
||||
namespace :metrics do
|
||||
require 'flog'
|
||||
require 'flog_cli'
|
||||
|
||||
project = Devtools.project
|
||||
config = project.flog
|
||||
|
||||
# Original code by Marty Andrews:
|
||||
# http://blog.martyandrews.net/2009/05/enforcing-ruby-code-quality.html
|
||||
desc 'Measure code complexity'
|
||||
task :flog do
|
||||
threshold = config.threshold.to_f.round(1)
|
||||
flog = Flog.new
|
||||
flog.flog(*PathExpander.new(config.lib_dirs.dup, '**/*.rb').process)
|
||||
|
||||
totals = flog
|
||||
.totals
|
||||
.reject { |name, _score| name.end_with?('#none') }
|
||||
.map { |name, score| [name, score.round(1)] }
|
||||
.sort_by { |_name, score| score }
|
||||
|
||||
if totals.any?
|
||||
max = totals.last[1]
|
||||
unless max >= threshold
|
||||
Devtools.notify_metric_violation "Adjust flog score down to #{max}"
|
||||
end
|
||||
end
|
||||
|
||||
bad_methods = totals.select { |_name, score| score > threshold }
|
||||
|
||||
if bad_methods.any?
|
||||
bad_methods.reverse_each do |name, score|
|
||||
printf "%8.1f: %s\n", score, name
|
||||
end
|
||||
|
||||
Devtools.notify_metric_violation(
|
||||
"#{bad_methods.size} methods have a flog complexity > #{threshold}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/BlockLength
|
43
devtools/tasks/metrics/mutant.rake
Normal file
43
devtools/tasks/metrics/mutant.rake
Normal file
|
@ -0,0 +1,43 @@
|
|||
# rubocop:disable Metrics/BlockLength
|
||||
namespace :metrics do
|
||||
config = Devtools.project.mutant
|
||||
|
||||
desc 'Measure mutation coverage'
|
||||
task mutant: :coverage do
|
||||
require 'mutant'
|
||||
|
||||
namespace =
|
||||
if config.zombify
|
||||
Mutant.zombify
|
||||
Zombie::Mutant
|
||||
else
|
||||
Mutant
|
||||
end
|
||||
|
||||
namespaces = Array(config.namespace).map { |n| "#{n}*" }
|
||||
|
||||
ignore_subjects = config.ignore_subjects.flat_map do |matcher|
|
||||
%W[--ignore #{matcher}]
|
||||
end
|
||||
|
||||
jobs = ENV.key?('CIRCLECI') ? %w[--jobs 4] : []
|
||||
|
||||
since =
|
||||
if config.since
|
||||
%W[--since #{config.since}]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
arguments = %W[
|
||||
--include lib
|
||||
--require #{config.name}
|
||||
--use #{config.strategy}
|
||||
].concat(ignore_subjects).concat(namespaces).concat(since).concat(jobs)
|
||||
|
||||
unless namespace::CLI.run(arguments)
|
||||
Devtools.notify_metric_violation('Mutant task is not successful')
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/BlockLength
|
8
devtools/tasks/metrics/reek.rake
Normal file
8
devtools/tasks/metrics/reek.rake
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace :metrics do
|
||||
require 'reek/rake/task'
|
||||
|
||||
Reek::Rake::Task.new do |reek|
|
||||
reek.source_files = '{app,lib}/**/*.rb'
|
||||
reek.config_file = 'config/reek.yml'
|
||||
end
|
||||
end
|
13
devtools/tasks/metrics/rubocop.rake
Normal file
13
devtools/tasks/metrics/rubocop.rake
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace :metrics do
|
||||
desc 'Check with code style guide'
|
||||
task :rubocop do
|
||||
require 'rubocop'
|
||||
config = Devtools.project.rubocop
|
||||
begin
|
||||
exit_status = RuboCop::CLI.new.run(%W[--config #{config.config_file}])
|
||||
fail 'Rubocop not successful' unless exit_status.zero?
|
||||
rescue Encoding::CompatibilityError => exception
|
||||
Devtools.notify_metric_violation exception.message
|
||||
end
|
||||
end
|
||||
end
|
12
devtools/tasks/metrics/yardstick.rake
Normal file
12
devtools/tasks/metrics/yardstick.rake
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace :metrics do
|
||||
namespace :yardstick do
|
||||
require 'yardstick/rake/measurement'
|
||||
require 'yardstick/rake/verify'
|
||||
|
||||
options = Devtools.project.yardstick.options
|
||||
|
||||
Yardstick::Rake::Measurement.new(:measure, options)
|
||||
|
||||
Yardstick::Rake::Verify.new(:verify, options)
|
||||
end
|
||||
end
|
34
devtools/tasks/spec.rake
Normal file
34
devtools/tasks/spec.rake
Normal file
|
@ -0,0 +1,34 @@
|
|||
begin
|
||||
require 'rspec/core/rake_task'
|
||||
|
||||
# Remove existing same-named tasks
|
||||
%w[spec spec:unit spec:integration].each do |task|
|
||||
klass = Rake::Task
|
||||
klass[task].clear if klass.task_defined?(task)
|
||||
end
|
||||
|
||||
desc 'Run all specs'
|
||||
RSpec::Core::RakeTask.new(:spec) do |task|
|
||||
task.pattern = 'spec/{unit,integration}/**/*_spec.rb'
|
||||
end
|
||||
|
||||
namespace :spec do
|
||||
desc 'Run unit specs'
|
||||
RSpec::Core::RakeTask.new(:unit) do |task|
|
||||
task.pattern = 'spec/unit/**/*_spec.rb'
|
||||
end
|
||||
|
||||
desc 'Run integration specs'
|
||||
RSpec::Core::RakeTask.new(:integration) do |task|
|
||||
task.pattern = 'spec/integration/**/*_spec.rb'
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
%w[spec spec:unit spec:integration].each do |name|
|
||||
task name do
|
||||
warn "In order to run #{name}, do: gem install rspec"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task test: :spec
|
9
devtools/tasks/yard.rake
Normal file
9
devtools/tasks/yard.rake
Normal file
|
@ -0,0 +1,9 @@
|
|||
begin
|
||||
require 'yard'
|
||||
|
||||
YARD::Rake::YardocTask.new
|
||||
rescue LoadError
|
||||
task :yard do
|
||||
warn 'In order to run yard, you must: gem install yard'
|
||||
end
|
||||
end
|
|
@ -36,6 +36,5 @@ Gem::Specification.new do |gem|
|
|||
gem.add_runtime_dependency('regexp_parser', '~> 1.2')
|
||||
gem.add_runtime_dependency('unparser', '~> 0.4.2')
|
||||
|
||||
gem.add_development_dependency('devtools', '~> 0.1.22')
|
||||
gem.add_development_dependency('parallel', '~> 1.3')
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue