Skip to content

Latest commit

 

History

History
163 lines (122 loc) · 2.79 KB

Instance-Variable-Assumption.md

File metadata and controls

163 lines (122 loc) · 2.79 KB

Instance Variable Assumption

Introduction

Classes should not assume that instance variables are set or present outside of the current class definition.

Good:

class Foo
  def initialize
    @bar = :foo
  end

  def foo?
    @bar == :foo
  end
end

Good as well:

class Foo
  def foo?
    bar == :foo
  end

  def bar
    @bar ||= :foo
  end
end

Bad:

class Foo
  def go_foo!
    @bar = :foo
  end

  def foo?
    @bar == :foo
  end
end

Example

Running Reek on:

class Dummy
  def test
    @ivar
  end
end

would report:

  [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar

Note that this example would trigger this smell warning as well:

class Parent
  def initialize(omg)
    @omg = omg
  end
end

class Child < Parent
  def foo
    @omg
  end
end

The way to address the smell warning is that you should create an attr_reader to use @omg in the subclass and not access @omg directly like this:

class Parent
  attr_reader :omg

  def initialize(omg)
    @omg = omg
  end
end

class Child < Parent
  def foo
    omg
  end
end

Directly accessing instance variables is considered a smell because it breaks encapsulation and makes it harder to reason about code.

If you don't want to expose those methods as public API just make them private like this:

class Parent
  def initialize(omg)
    @omg = omg
  end

  private
  attr_reader :omg
end

class Child < Parent
  def foo
    omg
  end
end

Current Support in Reek

An instance variable must:

  • be set in the constructor
  • or be accessed through a method with lazy initialization / memoization.

If not, Instance Variable Assumption will be reported.

Using Instance Variable Assumption in a Rails context

In ActiveRecord it seems common to use callbacks like after_initialize to initialize instance variables as outlined here or here instead of overriding the initialize method. If an instance variable is initialized in such a callback Reek will report it correspondingly.

This would smell for instance:

class Sample < ApplicationRecord
  after_initialize do
    @my_var = false
  end
end

Since Reek cannot reliably detect that is used in a Rails context we recommend to disable this detector for "app/models" like this:

directories:
  # Your other configuration....
  "app/models":
    InstanceVariableAssumption:
      enabled: false

Configuration

Instance Variable Assumption supports the Basic Smell Options.