Class: Reek::Smells::UncommunicativeVariableName

Inherits:
SmellDetector show all
Defined in:
lib/reek/smells/uncommunicative_variable_name.rb

Overview

An Uncommunicative Name is a name that doesn't communicate its intent well enough.

Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.

Currently UncommunicativeName checks for

  • 1-character names

  • names ending with a number

Constant Summary collapse

REJECT_KEY =

The name of the config field that lists the regexps of smelly names to be reported.

'reject'
DEFAULT_REJECT_SET =
[/^.$/, /[0-9]$/, /[A-Z]/]
ACCEPT_KEY =

The name of the config field that lists the specific names that are to be treated as exceptions; these names will not be reported as uncommunicative.

'accept'
DEFAULT_ACCEPT_SET =
['_']

Constants inherited from SmellDetector

SmellDetector::DEFAULT_EXCLUDE_SET, SmellDetector::EXCLUDE_KEY

Instance Attribute Summary

Attributes inherited from SmellDetector

#smell_category, #smell_type, #smells_found, #source

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from SmellDetector

#config_for, #configure_with, default_smell_category, #enabled?, #enabled_for?, #examine, #exception?, #initialize, #register, #report_on, smell_type, #value

Constructor Details

This class inherits a constructor from Reek::Smells::SmellDetector

Class Method Details

.contextsObject

:nodoc:


42
43
44
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 42

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

.default_configObject


35
36
37
38
39
40
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 35

def self.default_config
  super.merge(
    REJECT_KEY => DEFAULT_REJECT_SET,
    ACCEPT_KEY => DEFAULT_ACCEPT_SET
  )
end

.smell_categoryObject


31
32
33
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 31

def self.smell_category
  'UncommunicativeName'
end

Instance Method Details

#bad_name?(name, _ctx) ⇒ Boolean

Returns:

  • (Boolean)

65
66
67
68
69
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 65

def bad_name?(name, _ctx)
  var = name.to_s.gsub(/^[@\*\&]*/, '')
  return false if @accept_names.include?(var)
  @reject_names.find { |patt| patt =~ var }
end

#examine_context(ctx) ⇒ Array<SmellWarning>

Checks the given context for uncommunicative names.

Returns:


51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 51

def examine_context(ctx)
  @reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
  @accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
  variable_names(ctx.exp).select do |name, _lines|
    bad_name?(name, ctx)
  end.map do |name, lines|
    SmellWarning.new(self,
                     context: ctx.full_name,
                     lines: lines,
                     message: "has the variable name '#{name}'",
                     parameters: { name: name.to_s })
  end
end

#find_assignment_variable_names(exp, accumulator) ⇒ Object


78
79
80
81
82
83
84
85
86
87
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 78

def find_assignment_variable_names(exp, accumulator)
  assignment_nodes = exp.each_node(:lvasgn, [:class, :module, :defs, :def])

  case exp.first
  when :class, :module
    assignment_nodes += exp.each_node(:ivasgn, [:class, :module])
  end

  assignment_nodes.each { |asgn| accumulator[asgn[1]].push(asgn.line) }
end

#find_block_argument_variable_names(exp, accumulator) ⇒ Object


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 89

def find_block_argument_variable_names(exp, accumulator)
  arg_search_exp = case exp.first
                   when :class, :module
                     exp
                   when :defs, :def
                     exp.body
                   end

  return unless arg_search_exp
  args_nodes = arg_search_exp.each_node(:args, [:class, :module, :defs, :def])

  args_nodes.each do |args_node|
    recursively_record_variable_names(accumulator, args_node)
  end
end

#record_variable_name(exp, symbol, accumulator) ⇒ Object


116
117
118
119
120
121
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 116

def record_variable_name(exp, symbol, accumulator)
  varname = symbol.to_s.sub(/^\*/, '')
  return if varname == ''
  var = varname.to_sym
  accumulator[var].push(exp.line)
end

#recursively_record_variable_names(accumulator, exp) ⇒ Object


105
106
107
108
109
110
111
112
113
114
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 105

def recursively_record_variable_names(accumulator, exp)
  exp.children.each do |subexp|
    case subexp.type
    when :mlhs
      recursively_record_variable_names(accumulator, subexp)
    else
      record_variable_name(exp, subexp.name, accumulator)
    end
  end
end

#variable_names(exp) ⇒ Object


71
72
73
74
75
76
# File 'lib/reek/smells/uncommunicative_variable_name.rb', line 71

def variable_names(exp)
  result = Hash.new { |hash, key| hash[key] = [] }
  find_assignment_variable_names(exp, result)
  find_block_argument_variable_names(exp, result)
  result.to_a.sort_by { |name, _| name.to_s }
end