class RuboCop::Cop::Style::ClassAndModuleChildren
Checks the style of children definitions at classes and modules. Basically there are two different styles:
@safety
Autocorrection is unsafe. Moving from compact to nested children requires knowledge of whether the outer parent is a module or a class. Moving from nested to compact requires verification that the outer parent is defined elsewhere. Rubocop does not have the knowledge to perform either operation safely and thus requires manual oversight.
@example EnforcedStyle: nested (default)
# good # have each child on its own line class Foo class Bar end end
@example EnforcedStyle: compact
# good # combine definitions as much as possible class Foo::Bar end
The compact style is only forced for classes/modules with one child.
Constants
- COMPACT_MSG
- NESTED_MSG
Public Instance Methods
on_class(node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 41 def on_class(node) return if node.parent_class && style != :nested check_style(node, node.body) end
on_module(node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 47 def on_module(node) check_style(node, node.body) end
Private Instance Methods
add_trailing_end(corrector, node, padding)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 88 def add_trailing_end(corrector, node, padding) replacement = "#{padding}end\n#{leading_spaces(node)}end" corrector.replace(node.loc.end, replacement) end
autocorrect(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 177 def autocorrect(corrector, node) return if node.class_type? && node.parent_class && style != :nested nest_or_compact(corrector, node) end
check_compact_style(node, body)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 166 def check_compact_style(node, body) parent = node.parent return if parent&.class_type? || parent&.module_type? return unless needs_compacting?(body) add_offense(node.loc.name, message: COMPACT_MSG) do |corrector| autocorrect(corrector, node) end end
check_nested_style(node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 158 def check_nested_style(node) return unless compact_node_name?(node) add_offense(node.loc.name, message: NESTED_MSG) do |corrector| autocorrect(corrector, node) end end
check_style(node, body)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 148 def check_style(node, body) return if node.identifier.children[0]&.cbase_type? if style == :nested check_nested_style(node) else check_compact_style(node, body) end end
compact_definition(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 93 def compact_definition(corrector, node) compact_node(corrector, node) remove_end(corrector, node.body) unindent(corrector, node) end
compact_identifier_name(node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 114 def compact_identifier_name(node) "#{node.identifier.const_name}::" \ "#{node.body.children.first.const_name}" end
compact_node(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 99 def compact_node(corrector, node) range = range_between(node.loc.keyword.begin_pos, node.body.loc.name.end_pos) corrector.replace(range, compact_replacement(node)) end
compact_node_name?(node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 187 def compact_node_name?(node) /::/.match?(node.identifier.source) end
compact_replacement(node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 104 def compact_replacement(node) replacement = "#{node.body.type} #{compact_identifier_name(node)}" body_comments = processed_source.ast_with_comments[node.body] unless body_comments.empty? replacement = body_comments.map(&:text).push(replacement).join("\n") end replacement end
configured_indentation_width()
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 127 def configured_indentation_width config.for_badge(Layout::IndentationWidth.badge).fetch('Width', 2) end
indent_width()
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 144 def indent_width @config.for_cop('Layout/IndentationWidth')['Width'] || 2 end
leading_spaces(node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 140 def leading_spaces(node) node.source_range.source_line[/\A\s*/] end
needs_compacting?(body)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 183 def needs_compacting?(body) body && %i[module class].include?(body.type) end
nest_definition(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 61 def nest_definition(corrector, node) padding = ((' ' * indent_width) + leading_spaces(node)).to_s padding_for_trailing_end = padding.sub(' ' * node.loc.end.column, '') replace_namespace_keyword(corrector, node) split_on_double_colon(corrector, node, padding) add_trailing_end(corrector, node, padding_for_trailing_end) end
nest_or_compact(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 53 def nest_or_compact(corrector, node) if style == :nested nest_definition(corrector, node) else compact_definition(corrector, node) end end
remove_end(corrector, body)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 119 def remove_end(corrector, body) remove_begin_pos = body.loc.end.begin_pos - leading_spaces(body).size adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1 range = range_between(remove_begin_pos, body.loc.end.end_pos + adjustment) corrector.remove(range) end
replace_namespace_keyword(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 70 def replace_namespace_keyword(corrector, node) class_definition = node.left_sibling&.each_node(:class)&.find do |class_node| class_node.identifier == node.identifier.namespace end namespace_keyword = class_definition ? 'class' : 'module' corrector.replace(node.loc.keyword, namespace_keyword) end
split_on_double_colon(corrector, node, padding)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 79 def split_on_double_colon(corrector, node, padding) children_definition = node.children.first range = range_between(children_definition.loc.double_colon.begin_pos, children_definition.loc.double_colon.end_pos) replacement = "\n#{padding}#{node.loc.keyword.source} " corrector.replace(range, replacement) end
unindent(corrector, node)
click to toggle source
# File lib/rubocop/cop/style/class_and_module_children.rb, line 131 def unindent(corrector, node) return if node.body.children.last.nil? column_delta = configured_indentation_width - leading_spaces(node.body.children.last).size return if column_delta.zero? AlignmentCorrector.correct(corrector, processed_source, node, column_delta) end