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