Add project infrastructure

* This infrastructure, especially the rake tasks should be
  gemified at some point in the future. I copied exactly the same
  bytes many times in the last month.
This commit is contained in:
Markus Schirp 2012-07-23 16:37:44 +02:00
commit 8c1fa8dace
23 changed files with 822 additions and 0 deletions

42
Gemfile Normal file
View file

@ -0,0 +1,42 @@
# encoding: utf-8
source 'https://rubygems.org'
gemspec
group :development do
gem 'rake', '~> 0.9.2'
gem 'rspec', '~> 1.3.2'
gem 'yard', '~> 0.8.1'
end
group :guard do
gem 'guard', '~> 1.1.1'
gem 'guard-bundler', '~> 0.1.3'
gem 'guard-rspec', '~> 0.7.3'
end
group :metrics do
gem 'flay', '~> 1.4.2'
gem 'flog', '~> 2.5.1'
gem 'reek', '~> 1.2.8', :github => 'dkubb/reek'
gem 'roodi', '~> 2.1.0'
gem 'yardstick', '~> 0.5.0'
gem 'yard-spellcheck', '~> 0.1.5'
platforms :mri_18 do
gem 'arrayfields', '~> 4.7.4' # for metric_fu
gem 'fattr', '~> 2.2.0' # for metric_fu
gem 'heckle', '~> 1.4.3'
gem 'json', '~> 1.7.3' # for metric_fu rake task
gem 'map', '~> 6.0.1' # for metric_fu
gem 'metric_fu', '~> 2.1.1'
gem 'mspec', '~> 1.5.17'
gem 'rcov', '~> 1.0.0'
gem 'ruby2ruby', '= 1.2.2'
end
platforms :rbx do
gem 'pelusa', '~> 0.2.1'
end
end

150
Gemfile.lock Normal file
View file

@ -0,0 +1,150 @@
GIT
remote: git://github.com/dkubb/reek.git
revision: b85adad65e992e194023f6346f3e72c3b46b4c56
specs:
reek (1.2.8)
ruby2ruby (~> 1.2)
ruby_parser (~> 2.0)
sexp_processor (~> 3.0)
PATH
remote: .
specs:
mutant (0.0.1)
backports (~> 2.6.1)
GEM
remote: https://rubygems.org/
specs:
ParseTree (3.0.8)
RubyInline (>= 3.7.0)
sexp_processor (>= 3.0.0)
RubyInline (3.11.3)
ZenTest (~> 4.3)
Saikuro (1.1.0)
ZenTest (4.8.1)
activesupport (3.2.6)
i18n (~> 0.6)
multi_json (~> 1.0)
arrayfields (4.7.4)
awesome_print (1.0.2)
backports (2.6.2)
chronic (0.3.0)
churn (0.0.13)
chronic (>= 0.2.3)
hirb
json_pure
main
ruby_parser (~> 2.0.4)
sexp_processor (~> 3.0.3)
colored (1.2)
env (0.3.0)
erubis (2.7.0)
fattr (2.2.1)
ffi (1.1.0)
ffi-hunspell (0.2.5)
env (~> 0.2)
ffi (~> 1.0)
flay (1.4.3)
ruby_parser (~> 2.0)
sexp_processor (~> 3.0)
flog (2.5.3)
ruby_parser (~> 2.0)
sexp_processor (~> 3.0)
guard (1.1.1)
listen (>= 0.4.2)
thor (>= 0.14.6)
guard-bundler (0.1.3)
bundler (>= 1.0.0)
guard (>= 0.2.2)
guard-rspec (0.7.3)
guard (>= 0.10.0)
heckle (1.4.3)
ParseTree (>= 2.0.0)
ZenTest (>= 3.5.2)
ruby2ruby (>= 1.1.6)
hirb (0.7.0)
i18n (0.6.0)
json (1.7.3)
json_pure (1.7.3)
listen (0.4.7)
rb-fchange (~> 0.0.5)
rb-fsevent (~> 0.9.1)
rb-inotify (~> 0.8.8)
main (4.7.1)
map (6.0.1)
metric_fu (2.1.1)
Saikuro (>= 1.1.0)
activesupport (>= 2.0.0)
chronic (~> 0.3.0)
churn (>= 0.0.7)
flay (>= 1.2.1)
flog (>= 2.3.0)
rails_best_practices (>= 0.6.4)
rcov (>= 0.8.3.3)
reek (>= 1.2.6)
roodi (>= 2.1.0)
syntax
mspec (1.5.17)
multi_json (1.3.6)
pelusa (0.2.1)
progressbar (0.11.0)
rails_best_practices (1.10.1)
activesupport
awesome_print
colored
erubis
i18n
progressbar
sexp_processor
rake (0.9.2.2)
rb-fchange (0.0.5)
ffi
rb-fsevent (0.9.1)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
rcov (1.0.0)
roodi (2.1.0)
ruby_parser
rspec (1.3.2)
ruby2ruby (1.2.2)
ParseTree (~> 3.0)
ruby_parser (2.0.6)
sexp_processor (~> 3.0)
sexp_processor (3.0.10)
syntax (1.0.0)
thor (0.15.4)
yard (0.8.2.1)
yard-spellcheck (0.1.5)
ffi-hunspell (~> 0.2)
yard (~> 0.6)
yardstick (0.5.0)
yard (~> 0.8.1)
PLATFORMS
ruby
DEPENDENCIES
arrayfields (~> 4.7.4)
fattr (~> 2.2.0)
flay (~> 1.4.2)
flog (~> 2.5.1)
guard (~> 1.1.1)
guard-bundler (~> 0.1.3)
guard-rspec (~> 0.7.3)
heckle (~> 1.4.3)
json (~> 1.7.3)
map (~> 6.0.1)
metric_fu (~> 2.1.1)
mspec (~> 1.5.17)
mutant!
pelusa (~> 0.2.1)
rake (~> 0.9.2)
rcov (~> 1.0.0)
reek (~> 1.2.8)!
roodi (~> 2.1.0)
rspec (~> 1.3.2)
ruby2ruby (= 1.2.2)
yard (~> 0.8.1)
yard-spellcheck (~> 0.1.5)
yardstick (~> 0.5.0)

18
Guardfile Normal file
View file

@ -0,0 +1,18 @@
# encoding: utf-8
guard :bundler do
watch('Gemfile')
end
guard :rspec do
# run all specs if the spec_helper or supporting files files are modified
watch('spec/spec_helper.rb') { 'spec' }
watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec' }
# run unit specs if associated lib code is modified
watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}"] }
watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec' }
# run a spec if it is modified
watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
end

9
TODO Normal file
View file

@ -0,0 +1,9 @@
* Get a rid of heckle and test mutant with mutant.
This is interesting IMHO mutant should have another entry point
that does not create the ::Mutant namespace.
This will be easy when there is a gem release.
This ::Zombie namespace can be created with dynamically creating a
new library under a differend load path. Also a gem release
could provide this feature.
* Get a rid of rspec-1 (can be done once we do not use heckle anymore)
* Add an infrastructure to whitelist components to heckle.

3
config/flay.yml Normal file
View file

@ -0,0 +1,3 @@
---
threshold: 9
total_score: 41

2
config/flog.yml Normal file
View file

@ -0,0 +1,2 @@
---
threshold: 12.4

26
config/roodi.yml Normal file
View file

@ -0,0 +1,26 @@
---
AbcMetricMethodCheck:
score: 12.1
AssignmentInConditionalCheck: {}
CaseMissingElseCheck: {}
ClassLineCountCheck:
line_count: 200
ClassNameCheck:
pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/
ClassVariableCheck: {}
CyclomaticComplexityBlockCheck:
complexity: 2
CyclomaticComplexityMethodCheck:
complexity: 2
EmptyRescueBodyCheck: {}
ForLoopCheck: {}
MethodLineCountCheck:
line_count: 9
MethodNameCheck:
pattern: !ruby/regexp /\A(?:[a-z\d](?:_?[a-z\d])+[?!=]?|\[\]=?|==|<=>|<<|[+*&|-])\z/
ModuleLineCountCheck:
line_count: 202
ModuleNameCheck:
pattern: !ruby/regexp /\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/
ParameterNumberCheck:
parameter_count: 2

91
config/site.reek Normal file
View file

@ -0,0 +1,91 @@
---
UncommunicativeParameterName:
accept: []
exclude: []
enabled: true
reject:
- !ruby/regexp /^.$/
- !ruby/regexp /[0-9]$/
- !ruby/regexp /[A-Z]/
LargeClass:
max_methods: 10
exclude: []
enabled: true
max_instance_variables: 3
UncommunicativeMethodName:
accept: []
exclude: []
enabled: true
reject:
- !ruby/regexp /^[a-z]$/
- !ruby/regexp /[0-9]$/
- !ruby/regexp /[A-Z]/
LongParameterList:
max_params: 2
exclude: []
enabled: true
overrides: {}
FeatureEnvy:
exclude: []
enabled: true
ClassVariable:
exclude: []
enabled: true
BooleanParameter:
exclude: []
enabled: true
IrresponsibleModule:
exclude: []
enabled: true
UncommunicativeModuleName:
accept: []
exclude: []
enabled: true
reject:
- !ruby/regexp /^.$/
- !ruby/regexp /[0-9]$/
NestedIterators:
ignore_iterators: []
exclude: []
enabled: true
max_allowed_nesting: 1
LongMethod:
max_statements: 6
exclude: []
enabled: true
Duplication:
allow_calls: []
exclude: []
enabled: true
max_calls: 1
UtilityFunction:
max_helper_calls: 0
exclude: []
enabled: true
Attribute:
exclude: []
enabled: false
UncommunicativeVariableName:
accept: []
exclude: []
enabled: true
reject:
- !ruby/regexp /^.$/
- !ruby/regexp /[0-9]$/
- !ruby/regexp /[A-Z]/
SimulatedPolymorphism:
exclude: []
enabled: true
max_ifs: 1
DataClump:
exclude: []
enabled: true
max_copies: 2
min_clump_size: 2
ControlCouple:
exclude: []
enabled: true
LongYieldList:
max_params: 1
exclude: []
enabled: true

2
config/yardstick.yml Normal file
View file

@ -0,0 +1,2 @@
---
threshold: 100

3
lib/mutant/version.rb Normal file
View file

@ -0,0 +1,3 @@
module Mutant
VERSION = '0.0.1'.freeze
end

20
mutant.gemspec Normal file
View file

@ -0,0 +1,20 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/mutant/version.rb', __FILE__)
Gem::Specification.new do |gem|
gem.name = 'mutant'
gem.version = Mutant::VERSION.dup
gem.authors = [ 'Markus Schirp' ]
gem.email = [ 'mbj@seonic.net' ]
gem.description = 'Mutation testing for ruby under rubinius'
gem.summary = gem.description
gem.homepage = 'https://github.com/mbj/mutant'
gem.require_paths = [ 'lib' ]
gem.files = `git ls-files`.split("\n")
gem.test_files = `git ls-files -- spec`.split("\n")
gem.extra_rdoc_files = %w[TODO]
gem.add_runtime_dependency('backports', '~> 2.6.1')
end

3
spec/spec.opts Normal file
View file

@ -0,0 +1,3 @@
--color
--loadby random
--format profile

12
spec/spec_helper.rb Normal file
View file

@ -0,0 +1,12 @@
# encoding: utf-8
require 'mutant'
require 'spec'
require 'spec/autorun'
# require spec support files and shared behavior
Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each { |f| require f }
Spec::Runner.configure do |config|
config.extend Spec::ExampleGroupMethods
end

7
tasks/metrics/ci.rake Normal file
View file

@ -0,0 +1,7 @@
desc 'Run metrics with Heckle'
task :ci => %w[ ci:metrics heckle ]
namespace :ci do
desc 'Run metrics'
task :metrics => %w[ verify_measurements flog flay reek roodi metrics:all ]
end

47
tasks/metrics/flay.rake Normal file
View file

@ -0,0 +1,47 @@
begin
if RUBY_VERSION == '1.8.7' and !defined?(RUBY_ENGINE)
require 'flay'
require 'yaml'
config = YAML.load_file(File.expand_path('../../../config/flay.yml', __FILE__)).freeze
threshold = config.fetch('threshold').to_i
total_score = config.fetch('total_score').to_f
files = Flay.expand_dirs_to_files(config.fetch('path', 'lib'))
# original code by Marty Andrews:
# http://blog.martyandrews.net/2009/05/enforcing-ruby-code-quality.html
desc 'Analyze for code duplication'
task :flay do
# run flay once without a threshold to ensure the max mass matches the threshold
flay = Flay.new(:fuzzy => false, :verbose => false, :mass => 0)
flay.process(*files)
max = flay.masses.map { |hash, mass| mass.to_f / flay.hashes[hash].size }.max
unless max >= threshold
raise "Adjust flay threshold down to #{max}"
end
total = flay.masses.reduce(0.0) { |total, (hash, mass)| total + (mass.to_f / flay.hashes[hash].size) }
unless total == total_score
raise "Flay total is now #{total}, but expected #{total_score}"
end
# run flay a second time with the threshold set
flay = Flay.new(:fuzzy => false, :verbose => false, :mass => threshold.succ)
flay.process(*files)
if flay.masses.any?
flay.report
raise "#{flay.masses.size} chunks of code have a duplicate mass > #{threshold}"
end
end
else
task :flay do
$stderr.puts 'Flay has inconsistend results accros ruby implementations. It is only enabled on 1.8.7, fix and remove guard'
end
end
rescue LoadError
task :flay do
abort 'Flay is not available. In order to run flay, you must: gem install flay'
end
end

43
tasks/metrics/flog.rake Normal file
View file

@ -0,0 +1,43 @@
begin
require 'flog'
require 'yaml'
class Float
def round_to(n)
(self * 10**n).round.to_f * 10**-n
end
end
config = YAML.load_file(File.expand_path('../../../config/flog.yml', __FILE__)).freeze
threshold = config.fetch('threshold').to_f.round_to(1)
# original code by Marty Andrews:
# http://blog.martyandrews.net/2009/05/enforcing-ruby-code-quality.html
desc 'Analyze for code complexity'
task :flog do
flog = Flog.new
flog.flog Array(config.fetch('path', 'lib'))
totals = flog.totals.select { |name, score| name[-5, 5] != '#none' }.
map { |name, score| [ name, score.round_to(1) ] }.
sort_by { |name, score| score }
max = totals.last[1]
unless max >= threshold
raise "Adjust flog score down to #{max}"
end
bad_methods = totals.select { |name, score| score > threshold }
if bad_methods.any?
bad_methods.reverse_each do |name, score|
puts '%8.1f: %s' % [ score, name ]
end
raise "#{bad_methods.size} methods have a flog complexity > #{threshold}"
end
end
rescue LoadError
task :flog do
abort 'Flog is not available. In order to run flog, you must: gem install flog'
end
end

208
tasks/metrics/heckle.rake Normal file
View file

@ -0,0 +1,208 @@
$LOAD_PATH.unshift(File.expand_path('../../../lib', __FILE__))
# original code by Ashley Moran:
# http://aviewfromafar.net/2007/11/1/rake-task-for-heckling-your-specs
begin
require 'pathname'
require 'backports'
require 'active_support/inflector'
require 'heckle'
require 'mspec'
require 'mspec/utils/name_map'
SKIP_METHODS = %w[ blank_slate_method_added ].freeze
class NameMap
def file_name(method, constant)
map = MAP[method]
name = if map
map[constant] || map[:default]
else
method.
gsub('?','_ques').
gsub('!','_bang').
gsub('=','_assign')
end
"#{name}_spec.rb"
end
end
desc 'Heckle each module and class'
task :heckle => :rcov do
unless Ruby2Ruby::VERSION == '1.2.2'
raise "ruby2ruby version #{Ruby2Ruby::VERSION} may not work properly, 1.2.2 *only* is recommended for use with heckle"
end
require 'veritas-mongo-adapter'
root_module_regexp = Regexp.union('Mutant')
spec_dir = Pathname('spec/unit')
NameMap::MAP.each do |op, method|
next if method.kind_of?(Hash)
NameMap::MAP[op] = { :default => method }
end
aliases = Hash.new { |h,mod| h[mod] = Hash.new { |h,method| h[method] = method } }
map = NameMap.new
heckle_caught_modules = Hash.new { |hash, key| hash[key] = [] }
unhandled_mutations = 0
ObjectSpace.each_object(Module) do |mod|
next unless mod.name =~ /\A#{root_module_regexp}(?::|\z)/
spec_prefix = spec_dir.join(mod.name.underscore)
specs = []
# get the public class methods
metaclass = class << mod; self end
ancestors = metaclass.ancestors
spec_class_methods = mod.singleton_methods(false)
spec_class_methods.reject! do |method|
%w[ yaml_new yaml_tag_subclasses? included nesting constants ].include?(method.to_s)
end
if mod.ancestors.include?(Singleton)
spec_class_methods.reject! { |method| method.to_s == 'instance' }
end
# get the protected and private class methods
other_class_methods = metaclass.protected_instance_methods(false) |
metaclass.private_instance_methods(false)
ancestors.each do |ancestor|
other_class_methods -= ancestor.protected_instance_methods(false) |
ancestor.private_instance_methods(false)
end
other_class_methods.reject! do |method|
method.to_s == 'allocate' || SKIP_METHODS.include?(method.to_s)
end
other_class_methods.reject! do |method|
next unless spec_class_methods.any? { |specced| specced.to_s == $1 }
spec_class_methods << method
end
# get the instances methods
spec_methods = mod.public_instance_methods(false)
other_methods = mod.protected_instance_methods(false) |
mod.private_instance_methods(false)
other_methods.reject! do |method|
next unless spec_methods.any? { |specced| specced.to_s == $1 }
spec_methods << method
end
# map the class methods to spec files
spec_class_methods.each do |method|
method = aliases[mod.name][method]
next if SKIP_METHODS.include?(method.to_s)
spec_file = spec_prefix.join('class_methods').join(map.file_name(method, mod.name))
unless spec_file.file?
raise "No spec file #{spec_file} for #{mod}.#{method}"
next
end
specs << [ ".#{method}", [ spec_file ] ]
end
# map the instance methods to spec files
spec_methods.each do |method|
method = aliases[mod.name][method]
next if SKIP_METHODS.include?(method.to_s)
spec_file = spec_prefix.join(map.file_name(method, mod.name))
unless spec_file.file?
raise "No spec file #{spec_file} for #{mod}##{method}"
next
end
specs << [ "##{method}", [ spec_file ] ]
end
# non-public methods are considered covered if they can be mutated
# and any spec fails for the current or descendant modules
other_methods.each do |method|
descedant_specs = []
ObjectSpace.each_object(Module) do |descedant|
next unless descedant.name =~ /\A#{root_module_regexp}(?::|\z)/ && mod >= descedant
descedant_spec_prefix = spec_dir.join(descedant.name.underscore)
descedant_specs << descedant_spec_prefix
if method.to_s == 'initialize'
descedant_specs.concat(Pathname.glob(descedant_spec_prefix.join('class_methods/new_spec.rb')))
end
end
specs << [ "##{method}", descedant_specs ]
end
other_class_methods.each do |method|
descedant_specs = []
ObjectSpace.each_object(Module) do |descedant|
next unless descedant.name =~ /\A#{root_module_regexp}(?::|\z)/ && mod >= descedant
descedant_specs << spec_dir.join(descedant.name.underscore).join('class_methods')
end
specs << [ ".#{method}", descedant_specs ]
end
specs.sort.each do |(method, spec_files)|
puts "Heckling #{mod}#{method}"
IO.popen("spec #{spec_files.join(' ')} --heckle '#{mod}#{method}'") do |pipe|
while line = pipe.gets
case line = line.chomp
when "The following mutations didn't cause test failures:"
heckle_caught_modules[mod.name] << method
when '+++ mutation'
unhandled_mutations += 1
end
end
end
end
end
if unhandled_mutations > 0
error_message_lines = [ "*************\n" ]
error_message_lines << "Heckle found #{unhandled_mutations} " \
"mutation#{"s" unless unhandled_mutations == 1} " \
"that didn't cause spec violations\n"
heckle_caught_modules.each do |mod, methods|
error_message_lines << "#{mod} contains the following " \
'poorly-specified methods:'
methods.each do |method|
error_message_lines << " - #{method}"
end
error_message_lines << ''
end
error_message_lines << 'Get your act together and come back ' \
'when your specs are doing their job!'
raise error_message_lines.join("\n")
else
puts 'Well done! Your code withstood a heckling.'
end
end
rescue LoadError
task :heckle => :spec do
$stderr.puts 'Heckle or mspec is not available. In order to run heckle, you must: gem install heckle mspec'
end
end

View file

@ -0,0 +1,29 @@
begin
require 'metric_fu'
require 'json'
# XXX: temporary hack until metric_fu is fixed
MetricFu::Saikuro.class_eval { include FileUtils }
MetricFu::Configuration.run do |config|
config.rcov = {
:environment => 'test',
:test_files => %w[ spec/**/*_spec.rb ],
:rcov_opts => %w[
--sort coverage
--no-html
--text-coverage
--no-color
--profile
--exclude spec/,^/
--include lib:spec
],
}
end
rescue LoadError
namespace :metrics do
task :all do
$stderr.puts 'metric_fu is not available. In order to run metrics:all, you must: gem install metric_fu'
end
end
end

15
tasks/metrics/reek.rake Normal file
View file

@ -0,0 +1,15 @@
begin
require 'reek/rake/task'
if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'rbx'
task :reek do
$stderr.puts 'Reek fails under rubinius, fix rubinius and remove guard'
end
else
Reek::Rake::Task.new
end
rescue LoadError
task :reek do
$stderr.puts 'Reek is not available. In order to run reek, you must: gem install reek'
end
end

15
tasks/metrics/roodi.rake Normal file
View file

@ -0,0 +1,15 @@
begin
require 'roodi'
require 'rake/tasklib'
require 'roodi_task'
RoodiTask.new do |t|
t.verbose = false
t.config = File.expand_path('../../../config/roodi.yml', __FILE__)
t.patterns = %w[ lib/**/*.rb ]
end
rescue LoadError
task :roodi do
abort 'Roodi is not available. In order to run roodi, you must: gem install roodi'
end
end

View file

@ -0,0 +1,23 @@
begin
require 'pathname'
require 'yardstick'
require 'yardstick/rake/measurement'
require 'yardstick/rake/verify'
require 'yaml'
config = YAML.load_file(File.expand_path('../../../config/yardstick.yml', __FILE__))
# yardstick_measure task
Yardstick::Rake::Measurement.new
# verify_measurements task
Yardstick::Rake::Verify.new do |verify|
verify.threshold = config.fetch('threshold')
end
rescue LoadError
%w[ yardstick_measure verify_measurements ].each do |name|
task name.to_s do
abort "Yardstick is not available. In order to run #{name}, you must: gem install yardstick"
end
end
end

45
tasks/spec.rake Normal file
View file

@ -0,0 +1,45 @@
begin
begin
require 'rspec/core/rake_task'
rescue LoadError
require 'spec/rake/spectask'
module RSpec
module Core
RakeTask = Spec::Rake::SpecTask
end
end
end
desc 'run all specs'
task :spec => %w[ spec:unit spec:integration ]
namespace :spec do
RSpec::Core::RakeTask.new(:integration) do |t|
t.pattern = 'spec/integration/**/*_spec.rb'
end
RSpec::Core::RakeTask.new(:unit) do |t|
t.pattern = 'spec/unit/**/*_spec.rb'
end
end
rescue LoadError
task :spec do
abort 'rspec is not available. In order to run spec, you must: gem install rspec'
end
end
begin
desc "Generate code coverage"
RSpec::Core::RakeTask.new(:rcov) do |t|
t.rcov = true
t.rcov_opts = File.read('spec/rcov.opts').split(/\s+/)
end
rescue LoadError
task :rcov do
abort 'rcov is not available. In order to run rcov, you must: gem install rcov'
end
end
task :test => 'spec'

9
tasks/yard.rake Normal file
View file

@ -0,0 +1,9 @@
begin
require 'yard'
YARD::Rake::YardocTask.new
rescue LoadError
task :yard do
abort 'YARD is not available. In order to run yard, you must: gem install yard'
end
end