class RuboCop::Cop::ThreadSafety::RackMiddlewareInstanceVariable

end
end
app.call(env)
def call(env)
end
@app = app
def initialize(app)
class IdentityMiddleware
end
end
@counter.update { |ref| ref + 1 }
ensure
app.call(env)
def call(env)
end
@counter = Concurrent::AtomicReference.new(0)
@app = app
def initialize(app)
class CounterMiddleware
# good
end
end
@counter += 1
ensure
app.call(env)
def call(env)
end
@counter = 0
@app = app
def initialize(app)
class CounterMiddleware
# bad
@example
or to implement proper synchronization mechanisms.
To avoid potential race conditions, it’s recommended to design middlewares to be stateless
Middlewares are initialized once, meaning any instance variables are shared between executor threads.
Avoid instance variables in rack middleware.

def extract_application_variable_from_contructor_method(constructor_method)

def extract_application_variable_from_contructor_method(constructor_method)
  constructor_method
    .then { |node| app_variable(node) }
    .then { |variables| variables.first[1] if variables.first }
end

def extract_safe_variables_from_constructor_method(constructor_method)

def extract_safe_variables_from_constructor_method(constructor_method)
  constructor_method
    .each_node(:ivasgn)
    .select { |ivasgn_node| operation_produces_threadsafe_object?(ivasgn_node.to_a[1]) }
    .map { _1.to_a[0] }
end

def find_constructor_method(class_node)

def find_constructor_method(class_node)
  class_node
    .each_node(:def)
    .find { |node| node.method?(:initialize) && node.arguments.size >= 1 }
end

def on_class(node)

def on_class(node)
  return unless rack_middleware_like_class?(node)
  constructor_method = find_constructor_method(node)
  return unless (application_variable = extract_application_variable_from_contructor_method(constructor_method))
  safe_variables = extract_safe_variables_from_constructor_method(constructor_method)
  node.each_node(:def) do |def_node|
    def_node.each_node(:ivasgn, :ivar) do |ivar_node|
      variable, = ivar_node.to_a
      if variable == application_variable || safe_variables.include?(variable) || allowed_identifier?(variable)
        next
      end
      add_offense ivar_node
    end
  end
end

def on_send(node)

def on_send(node)
  argument = node.first_argument
  return unless argument&.type?(:sym, :str)
  return if allowed_identifier?(argument.value)
  add_offense node
end