From 079dca3507f575a3c39904c2e6b5932665e10929 Mon Sep 17 00:00:00 2001 From: Kevin Rutherford Date: Mon, 6 Jul 2009 12:36:25 +0100 Subject: [PATCH] Added masked smells to report headers --- Manifest.txt | 2 ++ features/masking_smells.feature | 4 +-- lib/reek/config_file.rb | 31 +++++++++++++++++++++++ lib/reek/detector_stack.rb | 24 ++++++++++++++++++ lib/reek/object_source.rb | 2 +- lib/reek/smells/long_parameter_list.rb | 2 +- lib/reek/smells/long_yield_list.rb | 2 +- lib/reek/smells/smell_detector.rb | 8 ++++-- lib/reek/sniffer.rb | 34 ++++++++++++-------------- 9 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 lib/reek/config_file.rb create mode 100644 lib/reek/detector_stack.rb diff --git a/Manifest.txt b/Manifest.txt index b12e05d3c..4f04fa193 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -16,6 +16,8 @@ lib/reek/block_context.rb lib/reek/class_context.rb lib/reek/code_context.rb lib/reek/code_parser.rb +lib/reek/config_file.rb +lib/reek/detector_stack.rb lib/reek/exceptions.reek lib/reek/if_context.rb lib/reek/method_context.rb diff --git a/features/masking_smells.feature b/features/masking_smells.feature index 634360496..e18a4da4a 100644 --- a/features/masking_smells.feature +++ b/features/masking_smells.feature @@ -22,7 +22,7 @@ Feature: Masking smells using config files Scenario: corrupt config file prevents normal output When I run reek spec/samples/corrupt_config_file/dirty.rb Then it fails with exit status 1 - And it reports the error 'Error: invalid configuration file "corrupt.reek"' + And it reports the error 'Error: Invalid configuration file "corrupt.reek" -- not a Hash' Scenario: missing source file is an error When I run reek no_such_file.rb @@ -34,7 +34,7 @@ Feature: Masking smells using config files Then it fails with exit status 2 And it reports: """ - spec/samples/masked/dirty.rb -- 3 warnings: + spec/samples/masked/dirty.rb -- 3 warnings (+3 masked): Dirty#a calls @s.title multiple times (Duplication) Dirty#a calls puts(@s.title) multiple times (Duplication) Dirty#a/block/block is nested (Nested Iterators) diff --git a/lib/reek/config_file.rb b/lib/reek/config_file.rb new file mode 100644 index 000000000..0e0b9dddb --- /dev/null +++ b/lib/reek/config_file.rb @@ -0,0 +1,31 @@ +require 'yaml' + +module Reek + class ConfigFile + + def initialize(file_path) + @file_path = file_path + @hash = YAML.load_file(@file_path) || {} + problem('not a Hash') unless Hash === @hash + end + + # + # Configure the given sniffer using the contents of the config file. + # + def configure(sniffer) + @hash.each { |klass_name, config| + sniffer.configure(find_class(klass_name), config) + } + end + + def find_class(name) + klass = Reek::Smells.const_get(name) + problem("#{name} is not a code smell") unless klass + klass + end + + def problem(reason) + raise "Invalid configuration file \"#{File.basename(@file_path)}\" -- #{reason}" + end + end +end diff --git a/lib/reek/detector_stack.rb b/lib/reek/detector_stack.rb new file mode 100644 index 000000000..9381a5d1a --- /dev/null +++ b/lib/reek/detector_stack.rb @@ -0,0 +1,24 @@ + +module Reek + class DetectorStack + + def initialize(default_detector) + @detectors = [default_detector] + end + + def push(config) + det = @detectors[0].copy + det.configure_with(config) + @detectors.each {|smell| smell.be_masked} + @detectors << det + end + + def listen_to(hooks) + @detectors.each { |smell| smell.listen_to(hooks) } + end + + def report_on(report) + @detectors.each { |smell| smell.report_on(report) } + end + end +end diff --git a/lib/reek/object_source.rb b/lib/reek/object_source.rb index 78bcd73ea..5fb3e6f58 100644 --- a/lib/reek/object_source.rb +++ b/lib/reek/object_source.rb @@ -22,7 +22,7 @@ def self.unify(sexp) # :nodoc: def initialize(code, desc) # :nodoc: super - @sniffer.disable('LargeClass') + @sniffer.disable(LargeClass) end def can_parse_objects? diff --git a/lib/reek/smells/long_parameter_list.rb b/lib/reek/smells/long_parameter_list.rb index fcf7df3a2..3025f8e4e 100644 --- a/lib/reek/smells/long_parameter_list.rb +++ b/lib/reek/smells/long_parameter_list.rb @@ -22,7 +22,7 @@ def self.default_config super.adopt(MAX_ALLOWED_PARAMS_KEY => 3) end - def initialize(config) + def initialize(config = LongParameterList.default_config) super(config) @action = 'has' end diff --git a/lib/reek/smells/long_yield_list.rb b/lib/reek/smells/long_yield_list.rb index c6ada8c84..ed5521e93 100644 --- a/lib/reek/smells/long_yield_list.rb +++ b/lib/reek/smells/long_yield_list.rb @@ -9,7 +9,7 @@ def self.contexts # :nodoc: [:yield] end - def initialize(config) + def initialize(config = LongYieldList.default_config) super @action = 'yields' end diff --git a/lib/reek/smells/smell_detector.rb b/lib/reek/smells/smell_detector.rb index f78e1cd09..9e3687332 100644 --- a/lib/reek/smells/smell_detector.rb +++ b/lib/reek/smells/smell_detector.rb @@ -40,16 +40,20 @@ def self.create(config) def self.listen(hooks, config) detector = create(config) - contexts.each { |ctx| hooks[ctx] << detector } + detector.listen_to(hooks) detector end - def initialize(config) + def initialize(config = SmellDetector.default_config) @config = config @smells_found = [] @masked = false end + def listen_to(hooks) + self.class.contexts.each { |ctx| hooks[ctx] << self } + end + def be_masked @masked = true end diff --git a/lib/reek/sniffer.rb b/lib/reek/sniffer.rb index c7777c2e2..ffd07422e 100644 --- a/lib/reek/sniffer.rb +++ b/lib/reek/sniffer.rb @@ -1,3 +1,4 @@ +require 'reek/detector_stack' require 'reek/smells/control_couple' require 'reek/smells/duplication' require 'reek/smells/feature_envy' @@ -8,6 +9,7 @@ require 'reek/smells/nested_iterators' require 'reek/smells/uncommunicative_name' require 'reek/smells/utility_function' +require 'reek/config_file' require 'yaml' class Hash @@ -55,7 +57,9 @@ class Sniffer def initialize defaults_file = File.join(File.dirname(__FILE__), '..', '..', 'config', 'defaults.reek') @config = YAML.load_file(defaults_file) - @detectors = nil + @typed_detectors = nil + @detectors = Hash.new + SMELL_CLASSES.each { |klass| @detectors[klass] = DetectorStack.new(klass.new) } @listeners = [] end @@ -67,27 +71,21 @@ def initialize # def configure_along_path(filename) path = File.expand_path(File.dirname(filename)) - all_reekfiles(path).each { |rfile| configure_with(rfile) } + all_reekfiles(path).each { |config_file| ConfigFile.new(config_file).configure(self) } self end - # - # Overrides this sniffer's current settings with those in the named - # +config_file+. - # - def configure_with(config_file) - hash = YAML.load_file(config_file) - return unless hash - raise "invalid configuration file \"#{File.basename(config_file)}\"" unless Hash === hash - hash.push_keys(@config) + def configure(klass, config) + @detectors[klass].push(config) end - def disable(smell) - @config[smell].adopt!({Reek::Smells::SmellDetector::ENABLED_KEY => false}) + def disable(klass) + disabled_config = {Reek::Smells::SmellDetector::ENABLED_KEY => false} + @detectors[klass].push(disabled_config) end def report_on(report) - @listeners.each {|smell| smell.report_on(report)} + @detectors.each_value { |stack| stack.report_on(report) } end def examine(scope, type) @@ -98,11 +96,11 @@ def examine(scope, type) private def smell_listeners() - unless @detectors - @detectors = Hash.new {|hash,key| hash[key] = [] } - SMELL_CLASSES.each { |smell| @listeners << smell.listen(@detectors, @config) } + unless @typed_detectors + @typed_detectors = Hash.new {|hash,key| hash[key] = [] } + @detectors.each_value { |stack| stack.listen_to(@typed_detectors) } end - @detectors + @typed_detectors end def all_reekfiles(path)