1

I am struggling with Rubocop's Style/ClassVars rule. It wants me to Replace class var @@mutex with a class instance var. Here is the code, it performs some lazy-initialization which is quite slow:

FastGettext.class_eval do
    attr_writer :mutex
    attr_accessor :locales
  
    def mutex
      # Style/ClassVars: Replace class var @@mutex with a class instance var.
      @@mutex ||= Mutex.new
    end
  
    def human_available_locales
      original_locale = FastGettext.locale
      mutex.synchronize do
        return locales if locales
        # perform loading and translating of locale names
        # (removed)
        locales
      end
    ensure
      FastGettext.locale = original_locale
    end
  end

Rails had a nice helper attr_accessor_with_default which allowed to define an accessor with a default value, but it has been deprecated. Use Ruby instead, says the deprecation message. I am stuck, I really don't know how this code should looks like to satisfy Rubocop. Normally, attributes are initialized in the constructor but this is a class context. I need to initialize the mutex, ideally during class loading.

My initial implementation simply had @@mutex and @@locales and I have no idea why Rubocop pushes so hard on this. I know that accessors are convinient for overloading, but I am aware of this. Either I am missing something here or this is a really bad cop to be enabled by default.

Thanks for help

Edit: Here is one solution that still looks weird, but it works:

      FastGettext.class_eval do
        attr_accessor :mutex, :locales
        self.mutex = Mutex.new

        def human_available_locales
          original_locale = FastGettext.locale
          mutex.synchronize do
            return locales if locales
            # ...
            locales
          end
        ensure
          FastGettext.locale = original_locale
        end
      end
lzap
  • 16,417
  • 12
  • 71
  • 108
  • Does this answer your question? [Ruby class instance variable vs. class variable](https://stackoverflow.com/questions/15773552/ruby-class-instance-variable-vs-class-variable) – idmean Jan 27 '21 at 16:11

1 Answers1

1

RuboCop is right. Suppose you have an inherited class like this:

class VeryFastGettext < FastGettext
end

Now VeryFastGettext shares the same mutex lock as the base class. This is rarely a desired behavior.

Here's the code that can appease RuboCop while being reasonable:

FastGettext.class_eval do
  def self.mutex
    @mutex ||= Mutex.new
  end

  def mutex
    self.class.mutex
  end
end
iBug
  • 35,554
  • 7
  • 89
  • 134
  • Can't lazy initialize mutex, that's a non-atomic operation. However I found the solution. Thanks. – lzap Jan 28 '21 at 16:22