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