Class: RuboCop::Cop::Layout::ClassStructure

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Includes:
CommentsHelp, VisibilityHelp
Defined in:
lib/rubocop/cop/layout/class_structure.rb

Overview

Checks if the code style follows the ExpectedOrder configuration:

Categories allows us to map macro names into a category.

Consider an example of code style that covers the following order:

  • Module inclusion (include, prepend, extend)

  • Constants

  • Associations (has_one, has_many)

  • Public attribute macros (attr_accessor, attr_writer, attr_reader)

  • Other macros (validates, validate)

  • Public class methods

  • Initializer

  • Public instance methods

  • Protected attribute macros (attr_accessor, attr_writer, attr_reader)

  • Protected instance methods

  • Private attribute macros (attr_accessor, attr_writer, attr_reader)

  • Private instance methods

You can configure the following order:

 Layout/ClassStructure:
   ExpectedOrder:
     - module_inclusion
     - constants
     - association
     - public_attribute_macros
     - public_delegate
     - macros
     - public_class_methods
     - initializer
     - public_methods
     - protected_attribute_macros
     - protected_methods
     - private_attribute_macros
     - private_delegate
     - private_methods

Instead of putting all literals in the expected order, is also possible to group categories of macros. Visibility levels are handled automatically.

 Layout/ClassStructure:
   Categories:
     association:
       - has_many
       - has_one
     attribute_macros:
       - attr_accessor
       - attr_reader
       - attr_writer
     macros:
       - validates
       - validate
     module_inclusion:
       - include
       - prepend
       - extend

Examples:

# bad
# Expect extend be before constant
class Person < ApplicationRecord
  has_many :orders
  ANSWER = 42

  extend SomeModule
  include AnotherModule
end

# good
class Person
  # extend and include go first
  extend SomeModule
  include AnotherModule

  # inner classes
  CustomError = Class.new(StandardError)

  # constants are next
  SOME_CONSTANT = 20

  # afterwards we have public attribute macros
  attr_reader :name

  # followed by other macros (if any)
  validates :name

  # then we have public delegate macros
  delegate :to_s, to: :name

  # public class methods are next in line
  def self.some_method
  end

  # initialization goes between class methods and instance methods
  def initialize
  end

  # followed by other public instance methods
  def some_method
  end

  # protected attribute macros and methods go next
  protected

  attr_reader :protected_name

  def some_protected_method
  end

  # private attribute macros, delegate macros and methods
  # are grouped near the end
  private

  attr_reader :private_name

  delegate :some_private_delegate, to: :name

  def some_private_method
  end
end

Cop Safety Information:

  • Autocorrection is unsafe because class methods and module inclusion can behave differently, based on which methods or constants have already been defined.

    Constants will only be moved when they are assigned with literals.

Constant Summary collapse

HUMANIZED_NODE_TYPE =
{
  casgn: :constants,
  defs: :public_class_methods,
  def: :public_methods,
  sclass: :class_singleton
}.freeze
MSG =
'`%<category>s` is supposed to appear before `%<previous>s`.'

Constants included from VisibilityHelp

VisibilityHelp::VISIBILITY_SCOPES

Constants inherited from Base

Base::RESTRICT_ON_SEND

Instance Attribute Summary

Attributes inherited from Base

#config, #processed_source

Instance Method Summary collapse

Methods included from AutoCorrector

support_autocorrect?

Methods included from CommentsHelp

#comments_contain_disables?, #comments_in_range, #contains_comments?, #source_range_with_comment

Methods inherited from Base

#active_support_extensions_enabled?, #add_global_offense, #add_offense, autocorrect_incompatible_with, badge, #begin_investigation, callbacks_needed, #callbacks_needed, #config_to_allow_offenses, #config_to_allow_offenses=, #cop_config, cop_name, #cop_name, department, documentation_url, exclude_from_registry, #excluded_file?, #external_dependency_checksum, inherited, #initialize, #inspect, joining_forces, lint?, match?, #message, #offenses, #on_investigation_end, #on_new_investigation, #on_other_file, #parse, #ready, #relevant_file?, support_autocorrect?, support_multiple_source?, #target_rails_version, #target_ruby_version

Methods included from ExcludeLimit

#exclude_limit

Methods included from AutocorrectLogic

#autocorrect?, #autocorrect_enabled?, #autocorrect_requested?, #autocorrect_with_disable_uncorrectable?, #correctable?, #disable_uncorrectable?, #safe_autocorrect?

Methods included from IgnoredNode

#ignore_node, #ignored_node?, #part_of_ignored_node?

Methods included from Util

silence_warnings

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Method Details

#on_class(class_node) ⇒ Object Also known as: on_sclass

Validates code style on class declaration. Add offense when find a node out of expected order.



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/rubocop/cop/layout/class_structure.rb', line 158

def on_class(class_node)
  previous = -1
  walk_over_nested_class_definition(class_node) do |node, category|
    index = expected_order.index(category)
    if index < previous
      message = format(MSG, category: category, previous: expected_order[previous])
      add_offense(node, message: message) { |corrector| autocorrect(corrector, node) }
    end
    previous = index
  end
end