Class: Reek::SmellDetectors::MissingSafeMethod

Inherits:
BaseDetector
  • Object
show all
Defined in:
lib/reek/smell_detectors/missing_safe_method.rb

Overview

Excerpt from: dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist since this sums it up really well:

The ! in method names that end with ! means, "This method is dangerous"
-- or, more precisely, this method is the "dangerous" version of an
equivalent method, with the same name minus the !. "Danger" is
relative; the ! doesn't mean anything at all unless the method name
it's in corresponds to a similar but bang-less method name.

Don't add ! to your destructive (receiver-changing) methods' names,
unless you consider the changing to be "dangerous" and you have a
"non-dangerous" equivalent method without the !. If some arbitrary
subset of destructive methods end with !, then the whole point of !
gets distorted and diluted, and ! ceases to convey any information
whatsoever.

Such a method is called MissingSafeMethod and is reported as a smell.

See Missing-Safe-Method for details.

Constant Summary

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, default_config, 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:



28
29
30
# File 'lib/reek/smell_detectors/missing_safe_method.rb', line 28

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

Instance Method Details

#ignore_method?(method_node) ⇒ Boolean (private)

Parameters:

Returns:

  • (Boolean)


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

def ignore_method?(method_node)
  ignore_method_names.include? method_node.name.to_s # method_node.name is e.g.: :bravo!
end

#ignore_method_namesObject (private)

e.g. [“bravo!”]



87
88
89
# File 'lib/reek/smell_detectors/missing_safe_method.rb', line 87

def ignore_method_names
  @ignore_method_names ||= value(EXCLUDE_KEY, context)
end

#missing_safe_method?(method_sexp) ⇒ Boolean (private)

Returns:

  • (Boolean)


63
64
65
66
67
68
69
# File 'lib/reek/smell_detectors/missing_safe_method.rb', line 63

def missing_safe_method?(method_sexp)
  return false unless method_sexp.ends_with_bang?
  return false if ignore_method? method_sexp
  return false if version_without_bang_exists? method_sexp

  true
end

#sniffArray<SmellWarning>

Given this code:

class Alfa

def bravo!
end

end

An example context could look like this:

s(:class,

s(:const, nil, :Alfa), nil,
  s(:def, :bravo!,
    s(:args), nil))

Returns:



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/reek/smell_detectors/missing_safe_method.rb', line 49

def sniff
  context.node_instance_methods.select do |method_sexp|
    missing_safe_method?(method_sexp)
  end.map do |method_sexp|
    name = method_sexp.name
    smell_warning(
      lines: [method_sexp.line],
      message: "has missing safe method '#{name}'",
      parameters: { name: name.to_s })
  end
end

#version_without_bang_exists?(method_sexp) ⇒ Boolean (private)

Returns:

  • (Boolean)


71
72
73
74
75
# File 'lib/reek/smell_detectors/missing_safe_method.rb', line 71

def version_without_bang_exists?(method_sexp)
  context.node_instance_methods.find do |sexp_item|
    sexp_item.name.to_s == method_sexp.name_without_bang
  end
end