1

I want to create a DSL which stores various blocks, and then can call them. I want this to be a reusable module that I can include in multiple classes.

I figured out a way to do this using class variables, but rubocop complains and says that I should use class instance variables instead. I can't figure out a way to do this though. Is it possible?

module MyModule
  def self.included(base)
    base.extend(ClassMethods)
  end

  def run_fixers
    ClassMethods.class_variable_get(:@@fixers).each(&:call)
  end

  module ClassMethods
    def fix(_name, &block)
      @@fixers ||= []
      @@fixers << block
    end
  end
end

class MyClass
  include MyModule

  def initialize
    run_fixers
  end

  fix 'test' do
    puts 'testing'
  end
end
Xodarap
  • 11,581
  • 11
  • 56
  • 94

1 Answers1

2

Rubocop complains about @@class_variable style. Class variables usage can be refactored into using instance variable of a class:

class SomeClass
  @some_variable ||= []
end

instead of

class SomeClass
  @@some_variable ||= []
end

You can define instance variable of a class in any place where self equals to the class object. For example, in a class method:

module MyModule
  def self.included(base)
    base.extend(ClassMethods)
  end

  def run_fixers
    ClassMethods.fixers.each(&:call)
  end

  module ClassMethods
    def self.fixers
      @fixers ||= []
    end

    def fix(_name, &block)
      ClassMethods.fixers << block
    end
  end
end
Igor Drozdov
  • 14,690
  • 5
  • 37
  • 53
  • Nice answer. Consider replacing the first sentence with a brief explanation of why Robocop is suggesting the use of a class instance variable rather than a class variable, possibly with a link to a fuller explanation (such as Brent's answer [here](https://stackoverflow.com/questions/15773552/ruby-class-instance-variable-vs-class-variable) or [this blog](http://thoughts.codegram.com/understanding-class-instance-variables-in-ruby/)). – Cary Swoveland Aug 13 '17 at 18:42