Class: RuboCop::Cop::Layout::ClassStructure
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
| Class Chain: 
          self,
           ::RuboCop::Cop::AutoCorrector,::RuboCop::Cop::Base,::RuboCop::ExcludeLimit,
          NodePattern::Macros,
          RuboCop::AST::Sexp | |
| Instance Chain: | |
| Inherits: | RuboCop::Cop::Base 
 | 
| 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: truewill not use
the example order shown below.
To enforce the order of macros likeattr_reader,
you must define bothExpectedOrderandCategories. | 
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_methodsInstead 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
       - extendIf 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:
Layout/ClassStructure:
  Enabled: true
  ExpectedOrder:
    - public_attribute_macros
    - initializerTo make it work as expected, you must also specify Categories like this:
Layout/ClassStructure:
  ExpectedOrder:
    - public_attribute_macros
    - initializer
  Categories:
    attribute_macros:
      - attr_reader
      - attr_writer
      - attr_accessorConstant Summary
- 
    HUMANIZED_NODE_TYPE =
    
 # File 'lib/rubocop/cop/layout/class_structure.rb', line 182{ casgn: :constants, defs: :public_class_methods, def: :public_methods, sclass: :class_singleton }.freeze
- 
    MSG =
    
 # File 'lib/rubocop/cop/layout/class_structure.rb', line 189'`%<category>s` is supposed to appear before `%<previous>s`.'
::RuboCop::Cop::Base - Inherited
  EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::VisibilityHelp - Included
  
Class Attribute Summary
::RuboCop::Cop::AutoCorrector - Extended
::RuboCop::Cop::Base - Inherited
| .gem_requirements, .lint?, | |
| .support_autocorrect? | Returns if class supports autocorrect. | 
| .support_multiple_source? | Override if your cop should be called repeatedly for multiple investigations Between calls to  | 
Class Method Summary
::RuboCop::Cop::Base - Inherited
| .autocorrect_incompatible_with | List of cops that should not try to autocorrect at the same time as this cop. | 
| .badge | Naming. | 
| .callbacks_needed, .cop_name, .department, | |
| .documentation_url | Returns a url to view this cops documentation online. | 
| .exclude_from_registry | Call for abstract Cop classes. | 
| .inherited, | |
| .joining_forces | Override and return the Force class(es) you need to join. | 
| .match? | Returns true if the cop name or the cop namespace matches any of the given names. | 
| .new, | |
| .requires_gem | Register a version requirement for the given gem name. | 
| .restrict_on_send | |
::RuboCop::ExcludeLimit - Extended
| exclude_limit | Sets up a configuration option to have an exclude limit tracked. | 
| transform | |
Instance Attribute Summary
::RuboCop::Cop::Base - Inherited
::RuboCop::Cop::AutocorrectLogic - Included
Instance Method Summary
- 
    
      #on_class(class_node)  
      (also: #on_sclass)
    
    Validates code style on class declaration. 
- 
    
      #on_sclass(class_node)  
    
    Alias for #on_class. 
- 
    
      #autocorrect(corrector, node)  
    
    private
    Autocorrect by swapping between two nodes autocorrecting them. 
- #begin_pos_with_comment(node) private
- #buffer private
- 
    
      #categories  
    
    private
    Setting categories hash allow you to group methods in group to match in the #expected_order. 
- #class_elements(class_node) private
- 
    
      #classify(node)  ⇒ Object 
    
    private
    Classifies a node to match with something in the #expected_order 
- #dynamic_constant?(node) ⇒ Boolean private
- #end_position_for(node) private
- 
    
      #expected_order  
    
    private
    Load expected order from ExpectedOrderconfig.
- 
    
      #find_category(node)  ⇒ String 
    
    private
    Categorize a node according to the #expected_order Try to match #categories values against the node’s method_name given also its visibility. 
- #find_heredoc(node) private
- #humanize_node(node) private
- #ignore?(node, classification) ⇒ Boolean private
- #ignore_for_autocorrect?(node, sibling) ⇒ Boolean private
- #marked_as_private_constant?(node, name) ⇒ Boolean private
- #private_constant?(node) ⇒ Boolean private
- #start_line_position(node) private
- #walk_over_nested_class_definition(class_node) private
- #whole_line_comment_at_line?(line) ⇒ Boolean private
::RuboCop::Cop::CommentsHelp - Included
| #comments_contain_disables?, #comments_in_range, #contains_comments?, #source_range_with_comment, #begin_pos_with_comment, #buffer, #end_position_for, | |
| #find_end_line | Returns the end line of a node, which might be a comment and not part of the AST End line is considered either the line at which another node starts, or the line at which the parent node ends. | 
| #start_line_position | |
::RuboCop::Cop::VisibilityHelp - Included
::RuboCop::Cop::Base - Inherited
| #add_global_offense | Adds an offense that has no particular location. | 
| #add_offense | Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense. | 
| #begin_investigation | Called before any investigation. | 
| #callbacks_needed, | |
| #cop_config | Configuration Helpers. | 
| #cop_name, #excluded_file?, | |
| #external_dependency_checksum | This method should be overridden when a cop’s behavior depends on state that lives outside of these locations: | 
| #inspect, | |
| #message | Gets called if no message is specified when calling  | 
| #name | Alias for Base#cop_name. | 
| #offenses, | |
| #on_investigation_end | Called after all on_… | 
| #on_new_investigation | Called before all on_… | 
| #on_other_file | Called instead of all on_… | 
| #parse | There should be very limited reasons for a Cop to do it’s own parsing. | 
| #parser_engine, | |
| #ready | Called between investigations. | 
| #relevant_file?, | |
| #target_gem_version | Returns a gems locked versions (i.e. | 
| #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction, | |
| #callback_argument | Reserved for Cop::Cop. | 
| #complete_investigation | Called to complete an investigation. | 
| #correct, #current_corrector, | |
| #current_offense_locations | Reserved for Commissioner: | 
| #current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range, | |
| #reset_investigation | Actually private methods. | 
| #use_corrector | |
::RuboCop::Cop::AutocorrectLogic - Included
::RuboCop::Cop::IgnoredNode - Included
Constructor Details
This class inherits a constructor from RuboCop::Cop::Base
Instance Method Details
#autocorrect(corrector, node) (private)
Autocorrect by swapping between two nodes autocorrecting them
# File 'lib/rubocop/cop/layout/class_structure.rb', line 209
def autocorrect(corrector, node) previous = node.left_siblings.reverse.find do |sibling| !ignore_for_autocorrect?(node, sibling) end return unless previous current_range = source_range_with_comment(node) previous_range = source_range_with_comment(previous) corrector.insert_before(previous_range, current_range.source) corrector.remove(current_range) end
#begin_pos_with_comment(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 340
def begin_pos_with_comment(node) first_comment = nil (node.first_line - 1).downto(1) do |annotation_line| break unless (comment = processed_source.comment_at_line(annotation_line)) first_comment = comment if whole_line_comment_at_line?(annotation_line) end start_line_position(first_comment || node) end
#buffer (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 363
def buffer processed_source.buffer end
#categories (private)
Setting categories hash allow you to group methods in group to match in the #expected_order.
# File 'lib/rubocop/cop/layout/class_structure.rb', line 375
def categories cop_config['Categories'] end
#class_elements(class_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 269
def class_elements(class_node) class_def = class_node.body return [] unless class_def if class_def.type?(:def, :send) [class_def] else class_def.children.compact end end
    #classify(node)  ⇒ Object  (private)
  
Classifies a node to match with something in the #expected_order
# File 'lib/rubocop/cop/layout/class_structure.rb', line 229
def classify(node) return node.to_s unless node.respond_to?(:type) case node.type when :block classify(node.send_node) when :send find_category(node) else humanize_node(node) end.to_s end
    #dynamic_constant?(node)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/layout/class_structure.rb', line 306
def dynamic_constant?(node) return false unless node.casgn_type? && node.namespace.nil? expression = node.expression expression.send_type? && !(expression.method?(:freeze) && expression.receiver&.recursive_basic_literal?) end
#end_position_for(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 330
def end_position_for(node) if node.casgn_type? heredoc = find_heredoc(node) return heredoc.location.heredoc_end.end_pos + 1 if heredoc end end_line = buffer.line_for_position(node.source_range.end_pos) buffer.line_range(end_line).end_pos end
#expected_order (private)
Load expected order from ExpectedOrder config.
Define new terms in the expected order by adding new #categories.
# File 'lib/rubocop/cop/layout/class_structure.rb', line 369
def expected_order cop_config['ExpectedOrder'] end
#find_category(node) ⇒ String (private)
Categorize a node according to the #expected_order Try to match #categories values against the node’s method_name given also its visibility.
# File 'lib/rubocop/cop/layout/class_structure.rb', line 247
def find_category(node) name = node.method_name.to_s category, = categories.find { |_, names| names.include?(name) } key = category || name visibility_key = if node.def_modifier? "#{name}_methods" else "#{node_visibility(node)}_#{key}" end expected_order.include?(visibility_key) ? visibility_key : key end
#find_heredoc(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 359
def find_heredoc(node) node.each_node(:any_str).find(&:heredoc?) end
#humanize_node(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 297
def humanize_node(node) if node.def_type? return :initializer if node.method?(:initialize) return "#{node_visibility(node)}_methods" end HUMANIZED_NODE_TYPE[node.type] || node.type end
    #ignore?(node, classification)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/layout/class_structure.rb', line 281
def ignore?(node, classification) classification.nil? || classification.to_s.end_with?('=') || expected_order.index(classification).nil? || private_constant?(node) end
    #ignore_for_autocorrect?(node, sibling)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/layout/class_structure.rb', line 288
def ignore_for_autocorrect?(node, sibling) classification = classify(node) sibling_class = classify(sibling) ignore?(sibling, sibling_class) || classification == sibling_class || dynamic_constant?(node) end
    #marked_as_private_constant?(node, name)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/layout/class_structure.rb', line 324
def marked_as_private_constant?(node, name) return false unless node.method?(:private_constant) node.arguments.any? { |arg| arg.type?(:sym, :str) && arg.value == name } end
#on_class(class_node) Also known as: #on_sclass
Validates code style on class declaration. Add offense when find a node out of expected order.
# 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 = format(MSG, category: category, previous: expected_order[previous]) add_offense(node, message: ) { |corrector| autocorrect(corrector, node) } end previous = index end end
#on_sclass(class_node)
Alias for #on_class.
# File 'lib/rubocop/cop/layout/class_structure.rb', line 204
alias on_sclass on_class
    #private_constant?(node)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/layout/class_structure.rb', line 314
def private_constant?(node) return false unless node.casgn_type? && node.namespace.nil? return false unless (parent = node.parent) parent.each_child_node(:send) do |child_node| return true if marked_as_private_constant?(child_node, node.name) end false end
#start_line_position(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 355
def start_line_position(node) buffer.line_range(node.loc.line).begin_pos - 1 end
#walk_over_nested_class_definition(class_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 260
def walk_over_nested_class_definition(class_node) class_elements(class_node).each do |node| classification = classify(node) next if ignore?(node, classification) yield node, classification end end
    #whole_line_comment_at_line?(line)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/layout/class_structure.rb', line 351
def whole_line_comment_at_line?(line) /\A\s*#/.match?(processed_source.lines[line - 1]) end