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'
|
gemspec name: 'mutant'
|
||||||
|
|
||||||
|
gem 'devtools', path: 'devtools'
|
||||||
|
|
||||||
eval_gemfile File.expand_path('Gemfile.shared', __dir__)
|
eval_gemfile File.expand_path('Gemfile.shared', __dir__)
|
||||||
|
|
80
Gemfile.lock
80
Gemfile.lock
|
@ -20,6 +20,29 @@ PATH
|
||||||
mutant (~> 0.8.24)
|
mutant (~> 0.8.24)
|
||||||
rspec-core (>= 3.4.0, < 4.0.0)
|
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
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
@ -45,41 +68,22 @@ GEM
|
||||||
equalizer (~> 0.0.9)
|
equalizer (~> 0.0.9)
|
||||||
descendants_tracker (0.0.4)
|
descendants_tracker (0.0.4)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
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)
|
diff-lcs (1.3)
|
||||||
docile (1.3.1)
|
docile (1.3.5)
|
||||||
equalizer (0.0.11)
|
equalizer (0.0.11)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
flay (2.12.0)
|
flay (2.12.1)
|
||||||
erubis (~> 2.7.0)
|
erubis (~> 2.7.0)
|
||||||
path_expander (~> 1.0)
|
path_expander (~> 1.0)
|
||||||
ruby_parser (~> 3.0)
|
ruby_parser (~> 3.0)
|
||||||
sexp_processor (~> 4.0)
|
sexp_processor (~> 4.0)
|
||||||
flog (4.6.2)
|
flog (4.6.4)
|
||||||
path_expander (~> 1.0)
|
path_expander (~> 1.0)
|
||||||
ruby_parser (~> 3.1, > 3.1.0)
|
ruby_parser (~> 3.1, > 3.1.0)
|
||||||
sexp_processor (~> 4.8)
|
sexp_processor (~> 4.8)
|
||||||
ice_nine (0.11.2)
|
ice_nine (0.11.2)
|
||||||
jaro_winkler (1.5.1)
|
jaro_winkler (1.5.4)
|
||||||
json (2.1.0)
|
json (2.5.1)
|
||||||
kwalify (0.7.2)
|
kwalify (0.7.2)
|
||||||
memoizable (0.4.2)
|
memoizable (0.4.2)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
|
@ -95,11 +99,11 @@ GEM
|
||||||
parallel (1.12.1)
|
parallel (1.12.1)
|
||||||
parser (2.5.3.0)
|
parser (2.5.3.0)
|
||||||
ast (~> 2.4.0)
|
ast (~> 2.4.0)
|
||||||
path_expander (1.0.3)
|
path_expander (1.1.0)
|
||||||
powerpack (0.1.2)
|
powerpack (0.1.3)
|
||||||
procto (0.0.3)
|
procto (0.0.3)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
rake (12.3.1)
|
rake (12.3.3)
|
||||||
reek (5.2.0)
|
reek (5.2.0)
|
||||||
codeclimate-engine-rb (~> 0.4.0)
|
codeclimate-engine-rb (~> 0.4.0)
|
||||||
kwalify (~> 0.7.0)
|
kwalify (~> 0.7.0)
|
||||||
|
@ -110,18 +114,18 @@ GEM
|
||||||
rspec-core (~> 3.8.0)
|
rspec-core (~> 3.8.0)
|
||||||
rspec-expectations (~> 3.8.0)
|
rspec-expectations (~> 3.8.0)
|
||||||
rspec-mocks (~> 3.8.0)
|
rspec-mocks (~> 3.8.0)
|
||||||
rspec-core (3.8.0)
|
rspec-core (3.8.2)
|
||||||
rspec-support (~> 3.8.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-expectations (3.8.2)
|
rspec-expectations (3.8.6)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.8.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-its (1.2.0)
|
rspec-its (1.2.0)
|
||||||
rspec-core (>= 3.0.0)
|
rspec-core (>= 3.0.0)
|
||||||
rspec-expectations (>= 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)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.8.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-support (3.8.0)
|
rspec-support (3.8.3)
|
||||||
rubocop (0.60.0)
|
rubocop (0.60.0)
|
||||||
jaro_winkler (~> 1.5.1)
|
jaro_winkler (~> 1.5.1)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
|
@ -130,17 +134,17 @@ GEM
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (~> 1.4.0)
|
unicode-display_width (~> 1.4.0)
|
||||||
ruby-progressbar (1.10.0)
|
ruby-progressbar (1.11.0)
|
||||||
ruby_parser (3.11.0)
|
ruby_parser (3.15.1)
|
||||||
sexp_processor (~> 4.9)
|
sexp_processor (~> 4.9)
|
||||||
sexp_processor (4.11.0)
|
sexp_processor (4.15.2)
|
||||||
simplecov (0.16.1)
|
simplecov (0.16.1)
|
||||||
docile (~> 1.1)
|
docile (~> 1.1)
|
||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.2)
|
simplecov-html (0.10.2)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
unicode-display_width (1.4.0)
|
unicode-display_width (1.4.1)
|
||||||
unparser (0.4.2)
|
unparser (0.4.2)
|
||||||
abstract_type (~> 0.0.7)
|
abstract_type (~> 0.0.7)
|
||||||
adamantium (~> 0.2.0)
|
adamantium (~> 0.2.0)
|
||||||
|
@ -154,7 +158,7 @@ GEM
|
||||||
coercible (~> 1.0)
|
coercible (~> 1.0)
|
||||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
descendants_tracker (~> 0.0, >= 0.0.3)
|
||||||
equalizer (~> 0.0, >= 0.0.9)
|
equalizer (~> 0.0, >= 0.0.9)
|
||||||
yard (0.9.16)
|
yard (0.9.26)
|
||||||
yardstick (0.9.9)
|
yardstick (0.9.9)
|
||||||
yard (~> 0.8, >= 0.8.7.2)
|
yard (~> 0.8, >= 0.8.7.2)
|
||||||
|
|
||||||
|
@ -162,9 +166,9 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
devtools (~> 0.1.22)
|
devtools!
|
||||||
mutant!
|
mutant!
|
||||||
parallel (~> 1.3)
|
parallel (~> 1.3)
|
||||||
|
|
||||||
BUNDLED WITH
|
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('regexp_parser', '~> 1.2')
|
||||||
gem.add_runtime_dependency('unparser', '~> 0.4.2')
|
gem.add_runtime_dependency('unparser', '~> 0.4.2')
|
||||||
|
|
||||||
gem.add_development_dependency('devtools', '~> 0.1.22')
|
|
||||||
gem.add_development_dependency('parallel', '~> 1.3')
|
gem.add_development_dependency('parallel', '~> 1.3')
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue