class RuboCop::Cop::Style::ClassAndModuleChildren
The compact style is only forced for classes/modules with one child.
end
class Foo::Bar
# combine definitions as much as possible
# good
@example EnforcedStyle: compact
end
end
class Bar
class Foo
# have each child on its own line
# good
@example EnforcedStyle: nested (default)
manual oversight.
have the knowledge to perform either operation safely and thus requires
verification that the outer parent is defined elsewhere. RuboCop does not
outer parent is a module or a class. Moving from ‘nested` to `compact` requires
Moving from `compact` to `nested` children requires knowledge of whether the
Autocorrection is unsafe.
@safety
value will be used.
`EnforcedStyleForModules` respectively. If not set, or set to nil, the `EnforcedStyle`
styles can be defined for classes and modules by using `EnforcedStyleForClasses` and
By default, `EnforcedStyle` applies to both classes and modules. If desired, separate
only include the fully qualified class or module name.
`nested` style will result in each level being added, whereas `compact` style will
NOTE: The style chosen will affect `Module.nesting` for the class or module. Using
defined with fully qualified names (using `::` for namespaces).
on each line, without `::`). With `compact` style, classes and modules should be
With `nested` style, classes and modules should be defined separately (one constant
Checks that namespaced classes and modules are defined with a consistent style.
def add_trailing_end(corrector, node, padding)
def add_trailing_end(corrector, node, padding) replacement = "#{padding}end\n#{leading_spaces(node)}end" corrector.replace(node.loc.end, replacement) end
def autocorrect(corrector, node)
def autocorrect(corrector, node) return if node.class_type? && node.parent_class && style != :nested nest_or_compact(corrector, node) end
def check_compact_style(node, body)
def check_compact_style(node, body) parent = node.parent return if parent&.type?(:class, :module) return unless needs_compacting?(body) add_offense(node.loc.name, message: COMPACT_MSG) do |corrector| autocorrect(corrector, node) end end
def check_nested_style(node)
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
def check_style(node, body, style)
def check_style(node, body, style) return if node.identifier.namespace&.cbase_type? if style == :nested check_nested_style(node) else check_compact_style(node, body) end end
def compact_definition(corrector, node)
def compact_definition(corrector, node) compact_node(corrector, node) remove_end(corrector, node.body) unindent(corrector, node) end
def compact_identifier_name(node)
def compact_identifier_name(node) "#{node.identifier.const_name}::" \ "#{node.body.children.first.const_name}" end
def compact_node(corrector, node)
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
def compact_node_name?(node)
def compact_node_name?(node) node.identifier.source.include?('::') end
def compact_replacement(node)
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
def leading_spaces(node)
def leading_spaces(node) node.source_range.source_line[/\A\s*/] end
def needs_compacting?(body)
def needs_compacting?(body) body && %i[module class].include?(body.type) end
def nest_definition(corrector, node)
def nest_definition(corrector, node) padding = indentation(node) + leading_spaces(node) 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
def nest_or_compact(corrector, node)
def nest_or_compact(corrector, node) style = node.class_type? ? style_for_classes : style_for_modules if style == :nested nest_definition(corrector, node) else compact_definition(corrector, node) end end
def on_class(node)
def on_class(node) return if node.parent_class && style != :nested check_style(node, node.body, style_for_classes) end
def on_module(node)
def on_module(node) check_style(node, node.body, style_for_modules) end
def remove_end(corrector, body)
def remove_end(corrector, body) remove_begin_pos = if same_line?(body.loc.name, body.loc.end) body.loc.name.end_pos else body.loc.end.begin_pos - leading_spaces(body).size end 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
def replace_namespace_keyword(corrector, node)
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
def spaces_size(spaces_string)
def spaces_size(spaces_string) mapping = { "\t" => tab_indentation_width } spaces_string.chars.sum { |character| mapping.fetch(character, 1) } end
def split_on_double_colon(corrector, node, padding)
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
def style_for_classes
def style_for_classes cop_config['EnforcedStyleForClasses'] || style end
def style_for_modules
def style_for_modules cop_config['EnforcedStyleForModules'] || style end
def tab_indentation_width
def tab_indentation_width config.for_cop('Layout/IndentationStyle')['IndentationWidth'] || configured_indentation_width end
def unindent(corrector, node)
def unindent(corrector, node) return unless node.body.children.last last_child_leading_spaces = leading_spaces(node.body.children.last) return if spaces_size(leading_spaces(node)) == spaces_size(last_child_leading_spaces) column_delta = configured_indentation_width - spaces_size(last_child_leading_spaces) return if column_delta.zero? AlignmentCorrector.correct(corrector, processed_source, node, column_delta) end