Class: RuboCop::Cop::Operandi::StepMethodExists

Inherits:
Base
  • Object
show all
Defined in:
lib/operandi/rubocop/cop/operandi/step_method_exists.rb

Overview

Ensures that all step declarations have a corresponding method defined in the same class.

Note: This cop only checks for methods defined in the same file/class. It cannot detect methods inherited from parent classes. Use parent: true, the ExcludedSteps option, or rubocop:disable comments for inherited steps.

Examples:

# bad
class MyService < ApplicationService
  step :validate
  step :process

  private

  def validate
    # only validate is defined, process is missing
  end
end

# good
class MyService < ApplicationService
  step :validate
  step :process

  private

  def validate
    # validation logic
  end

  def process
    # processing logic
  end
end

parent: true - step method is defined in a parent class

# good - step method is inherited from the parent class
class User::Create < CreateService
  step :initialize_entity, parent: true
  step :assign_attributes, parent: true
  step :send_welcome_email

  private

  def send_welcome_email
    # only this method needs to be defined
  end
end

ExcludedSteps: ['initialize_entity', 'assign_attributes'] (default: [])

# good - these steps are excluded from checking
class User::Create < CreateService
  step :initialize_entity
  step :assign_attributes
  step :send_welcome_email

  private

  def send_welcome_email
    # only this method needs to be defined
  end
end

Constant Summary collapse

MSG =
"Step `%<name>s` has no corresponding method. " \
"For inherited steps, use `parent: true`, disable this line, or add to ExcludedSteps."

Instance Method Summary collapse

Instance Method Details

#after_class(_node) ⇒ Object



94
95
96
97
98
99
100
101
102
103
# File 'lib/operandi/rubocop/cop/operandi/step_method_exists.rb', line 94

def after_class(_node)
  return unless @step_calls&.any?

  @step_calls.each do |step|
    next if @defined_methods&.include?(step[:name])
    next if excluded_step?(step[:name])

    add_offense(step[:node], message: format(MSG, name: step[:name]))
  end
end

#on_class(_node) ⇒ Object



73
74
75
76
# File 'lib/operandi/rubocop/cop/operandi/step_method_exists.rb', line 73

def on_class(_node)
  @step_calls = []
  @defined_methods = []
end

#on_def(node) ⇒ Object



89
90
91
92
# File 'lib/operandi/rubocop/cop/operandi/step_method_exists.rb', line 89

def on_def(node)
  @defined_methods ||= []
  @defined_methods << node.method_name
end

#on_send(node) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/operandi/rubocop/cop/operandi/step_method_exists.rb', line 78

def on_send(node)
  return unless step_call?(node)

  step_name = node.arguments.first&.value
  return unless step_name
  return if parent_step?(node)

  @step_calls ||= []
  @step_calls << { name: step_name, node: node }
end