Class: RuboCop::Cop::DevDoc::Style::NoUnscopedMethodDefinitions

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/dev_doc/style/no_unscoped_method_definitions.rb

Overview

Flag def / define_method whose enclosing scope chain does not include a class or module body — the method lands on Object.

Rationale

A def not inside an explicit class or module body defines a method on Object, even when it visually looks scoped. The most common failure mode is inside Rake's namespace block:

❌ Two rake files both define `build_load_plan` — whichever file
 loads second silently wins. Tests for one task call the other
 task's helper logic without warning.
namespace :faqs do
def build_load_plan(fixtures, by_slug)
  ...
end
end

✔️ Wrapped in a real Ruby scope — no collision risk.
module FaqsLoader
extend self

def build_load_plan(fixtures, by_slug)
  ...
end
end

Core's Style/TopLevelMethodDefinition catches literal top-level def (not inside any block) but misses the rake namespace pattern because the def is technically inside a block node. This cop subsumes that case — disable Style/TopLevelMethodDefinition when this cop is enabled to avoid double-flagging.

Allowlist

Some DSLs legitimately define methods inside blocks where the block's receiver is not Object (Struct.new, Class.new, Module.new). Configure SafeDSLReceivers to extend the allowlist.

Examples:

# bad — literal top-level def
def helper_method
end

# bad — inside a rake namespace (lands on Object)
namespace :faqs do
  def build_load_plan(fixtures, by_slug)
  end
end

# good — inside an explicit module
module FaqsLoader
  extend self

  def build_load_plan(fixtures, by_slug)
  end
end

# good — inside an explicit class
class FaqsImporter
  def import
  end
end

# good — Struct.new block (allowlisted by default)
Point = Struct.new(:x, :y) do
  def distance
  end
end

Constant Summary collapse

MSG =
'Define methods inside an explicit `module` or `class`, not at the top level ' \
'or inside a DSL block (e.g. Rake `namespace`). ' \
'Methods defined here land on `Object` and can silently collide across files.'.freeze
DEFAULT_SAFE_DSL_RECEIVERS =
%w[Struct Class Module].freeze

Instance Method Summary collapse

Instance Method Details

#on_def(node) ⇒ Object Also known as: on_defs



79
80
81
# File 'lib/rubocop/cop/dev_doc/style/no_unscoped_method_definitions.rb', line 79

def on_def(node)
  add_offense(node.loc.keyword) unless enclosed_in_class_or_module?(node)
end

#on_send(node) ⇒ Object



84
85
86
87
88
89
# File 'lib/rubocop/cop/dev_doc/style/no_unscoped_method_definitions.rb', line 84

def on_send(node)
  return unless node.method_name == :define_method
  return if enclosed_in_class_or_module?(node)

  add_offense(node.loc.selector)
end