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
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
Constant Summary
-
HUMANIZED_NODE_TYPE =
# File 'lib/rubocop/cop/layout/class_structure.rb', line 147{ casgn: :constants, defs: :public_class_methods, def: :public_methods, sclass: :class_singleton }.freeze
-
MSG =
# File 'lib/rubocop/cop/layout/class_structure.rb', line 154'`%<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
ExpectedOrder
config. -
#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
#disable_offense, #disable_offense_at_end_of_line, #disable_offense_before_and_after, #disable_offense_with_eol_or_surround_comment, #heredoc_range, #max_line_length, #multiline_ranges, #multiline_string?, | |
#range_by_lines | Expand the given range to include all of any lines it covers. |
#range_of_first_line, #range_overlaps_offense?, #string_continuation?, #surrounding_heredoc?, #surrounding_percent_array? |
::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 174
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 305
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 328
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 340
def categories cop_config['Categories'] end
#class_elements(class_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 234
def class_elements(class_node) class_def = class_node.body return [] unless class_def if class_def.def_type? || class_def.send_type? [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 194
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 271
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 295
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 334
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 212
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 324
def find_heredoc(node) node.each_node(:str, :dstr, :xstr).find(&:heredoc?) end
#humanize_node(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/layout/class_structure.rb', line 262
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 246
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 253
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 289
def marked_as_private_constant?(node, name) return false unless node.method?(:private_constant) node.arguments.any? { |arg| (arg.sym_type? || arg.str_type?) && 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 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 = 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 169
alias on_sclass on_class
#private_constant?(node) ⇒ Boolean
(private)
# File 'lib/rubocop/cop/layout/class_structure.rb', line 279
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 320
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 225
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 316
def whole_line_comment_at_line?(line) /\A\s*#/.match?(processed_source.lines[line - 1]) end