lib/reek/smell_detectors/feature_envy.rb
# frozen_string_literal: true
require_relative 'base_detector'
module Reek
module SmellDetectors
#
# Feature Envy occurs when a code fragment references another object
# more often than it references itself, or when several clients do
# the same series of manipulations on a particular type of object.
#
# A simple example would be the following method, which "belongs"
# on the Item class and not on the Cart class:
#
# class Cart
# def price
# @item.price + @item.tax
# end
# end
#
# Feature Envy reduces the code's ability to communicate intent:
# code that "belongs" on one class but which is located in another
# can be hard to find, and may upset the "System of Names"
# in the host class.
#
# Feature Envy also affects the design's flexibility: A code fragment
# that is in the wrong class creates couplings that may not be natural
# within the application's domain, and creates a loss of cohesion
# in the unwilling host class.
#
# Currently +FeatureEnvy+ reports any method that refers to self less
# often than it refers to (ie. send messages to) some other object.
#
# If the method doesn't reference self at all, +UtilityFunction+ is
# reported instead.
#
# See {file:docs/Feature-Envy.md} for details.
class FeatureEnvy < BaseDetector
#
# Checks whether the given +context+ includes any code fragment that
# might "belong" on another class.
#
# @return [Array<SmellWarning>]
#
def sniff
return [] if context.singleton_method? || context.module_function?
return [] unless context.references_self?
envious_receivers.map do |name, lines|
smell_warning(
lines: lines,
message: "refers to '#{name}' more than self (maybe move it to another class?)",
parameters: { name: name.to_s })
end
end
private
def refs
@refs ||= context.refs
end
def envious_receivers
return {} if refs.self_is_max?
refs.most_popular
end
end
end
end