Class: Reek::SmellDetectors::DataClump

Inherits:
BaseDetector show all
Defined in:
lib/reek/smell_detectors/data_clump.rb

Overview

A Data Clump occurs when the same two or three items frequently appear together in classes and parameter lists, or when a group of instance variable names start or end with similar substrings.

The recurrence of the items often means there is duplicate code spread around to handle them. There may be an abstraction missing from the code, making the system harder to understand.

Currently Reek looks for a group of two or more parameters with the same names that are expected by three or more methods of a class.

See Data-Clump for details.

Constant Summary collapse

MAX_COPIES_KEY =

The name of the config field that sets the maximum allowed copies of any clump. No group of common parameters will be reported as a DataClump unless there are more than this many methods containing those parameters.

'max_copies'
DEFAULT_MAX_COPIES =
2
MIN_CLUMP_SIZE_KEY =

The name of the config field that sets the minimum clump size. No group of common parameters will be reported as a DataClump unless it contains at least this many parameters.

'min_clump_size'
DEFAULT_MIN_CLUMP_SIZE =
2

Constants inherited from BaseDetector

BaseDetector::DEFAULT_EXCLUDE_SET, BaseDetector::EXCLUDE_KEY

Instance Attribute Summary

Attributes inherited from BaseDetector

#config, #context

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseDetector

#config_for, configuration_keys, descendants, #enabled?, #exception?, #expression, inherited, #initialize, #run, smell_type, #smell_type, #smell_warning, #source_line, to_detector, todo_configuration_for, #value

Constructor Details

This class inherits a constructor from Reek::SmellDetectors::BaseDetector

Class Method Details

.contextsObject

:nodoc:



38
39
40
# File 'lib/reek/smell_detectors/data_clump.rb', line 38

def self.contexts # :nodoc:
  [:class, :module]
end

.default_configObject



42
43
44
45
46
# File 'lib/reek/smell_detectors/data_clump.rb', line 42

def self.default_config
  super.merge(
    MAX_COPIES_KEY => DEFAULT_MAX_COPIES,
    MIN_CLUMP_SIZE_KEY => DEFAULT_MIN_CLUMP_SIZE)
end


68
69
70
# File 'lib/reek/smell_detectors/data_clump.rb', line 68

def self.print_clump(clump)
  "[#{clump.map { |parameter| "'#{parameter}'" }.join(', ')}]"
end

Instance Method Details

#candidate_clumpsObject (private)



86
87
88
89
90
91
92
# File 'lib/reek/smell_detectors/data_clump.rb', line 86

def candidate_clumps
  candidate_methods.combination(max_copies + 1).map do |methods|
    common_argument_names_for(methods)
  end.select do |clump|
    clump.length >= min_clump_size
  end.uniq
end

#candidate_methodsObject (private)



82
83
84
# File 'lib/reek/smell_detectors/data_clump.rb', line 82

def candidate_methods
  @candidate_methods ||= context.node_instance_methods
end

#clumpsObject (private)



103
104
105
106
107
# File 'lib/reek/smell_detectors/data_clump.rb', line 103

def clumps
  candidate_clumps.map do |clump|
    [clump, methods_containing_clump(clump)]
  end
end

#common_argument_names_for(methods) ⇒ Object (private)



95
96
97
# File 'lib/reek/smell_detectors/data_clump.rb', line 95

def common_argument_names_for(methods)
  methods.map(&:arg_names).inject(:&).compact.sort
end

#max_copiesObject (private)



74
75
76
# File 'lib/reek/smell_detectors/data_clump.rb', line 74

def max_copies
  @max_copies ||= value(MAX_COPIES_KEY, context)
end

#methods_containing_clump(clump) ⇒ Object (private)



99
100
101
# File 'lib/reek/smell_detectors/data_clump.rb', line 99

def methods_containing_clump(clump)
  candidate_methods.select { |method| clump & method.arg_names == clump }
end

#min_clump_sizeObject (private)



78
79
80
# File 'lib/reek/smell_detectors/data_clump.rb', line 78

def min_clump_size
  @min_clump_size ||= value(MIN_CLUMP_SIZE_KEY, context)
end

#sniffArray<SmellWarning>

Checks the given class or module for multiple identical parameter sets.

Returns:



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/reek/smell_detectors/data_clump.rb', line 53

def sniff
  clumps.map do |clump, methods|
    methods_length = methods.length
    smell_warning(
      lines: methods.map(&:line),
      message: "takes parameters #{DataClump.print_clump(clump)} " \
               "to #{methods_length} methods",
      parameters: {
        parameters: clump.map(&:to_s),
        count:      methods_length
      })
  end
end