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

NOTE: Simply enabling the cop with Enabled: true will not use the example order shown below. To enforce the order of macros like attr_reader, you must define both ExpectedOrder and Categories.

You can configure the following order:

[source,yaml]

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, it is also possible to group categories of macros. Visibility levels are handled automatically.

[source,yaml]

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

If you only set ExpectedOrder without defining Categories, macros such as attr_reader or has_many will not be recognized as part of a category, and their order will not be validated. For example, the following will NOT raise any offenses, even if the order is incorrect:

[source,yaml]

Layout/ClassStructure: Enabled: true ExpectedOrder: - public_attribute_macros - initializer

To make it work as expected, you must also specify Categories like this:

[source,yaml]

Layout/ClassStructure: ExpectedOrder: - public_attribute_macros - initializer Categories: attribute_macros: - attr_reader - attr_writer - attr_accessor

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

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, #project_index

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, #always_autocorrect?, autocorrect_incompatible_with, badge, #begin_investigation, #callbacks_needed, callbacks_needed, #config_to_allow_offenses, #config_to_allow_offenses=, #contextual_autocorrect?, #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, #parser_engine, #ready, #relevant_file?, requires_gem, #string_literals_frozen_by_default?, support_autocorrect?, support_multiple_source?, #target_gem_version, #target_rails_version, #target_ruby_version

Methods included from ExcludeLimit

cop_dir_for, #exclude_limit, read_limits

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.



193
194
195
196
197
198
199
200
201
202
203
# File 'lib/rubocop/cop/layout/class_structure.rb', line 193

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