class RuboCop::Cop::Style::MixinGrouping

Checks for grouping of mixins in ‘class` and `module` bodies. By default it enforces mixins to be placed in separate declarations, but it can be configured to enforce grouping them in one declaration.

@example EnforcedStyle: separated (default)

# bad
class Foo
  include Bar, Qox
end

# good
class Foo
  include Qox
  include Bar
end

@example EnforcedStyle: grouped

# bad
class Foo
  extend Bar
  extend Qox
end

# good
class Foo
  extend Qox, Bar
end

Constants

MIXIN_METHODS
MSG

Public Instance Methods

on_class(node) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 40
def on_class(node)
  begin_node = node.child_nodes.find(&:begin_type?) || node
  begin_node.each_child_node(:send).select(&:macro?).each do |macro|
    next unless MIXIN_METHODS.include?(macro.method_name)

    check(macro)
  end
end
Also aliased as: on_module
on_module(node)
Alias for: on_class

Private Instance Methods

check(send_node) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 64
def check(send_node)
  if separated_style?
    check_separated_style(send_node)
  else
    check_grouped_style(send_node)
  end
end
check_grouped_style(send_node) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 72
def check_grouped_style(send_node)
  return if sibling_mixins(send_node).size == 1

  message = format(MSG, mixin: send_node.method_name, suffix: 'a single statement')

  add_offense(send_node, message: message) do |corrector|
    range = send_node.loc.expression
    mixins = sibling_mixins(send_node)
    if send_node == mixins.first
      correction = group_mixins(send_node, mixins)
    else
      range = range_to_remove_for_subsequent_mixin(mixins, send_node)
      correction = ''
    end

    corrector.replace(range, correction)
  end
end
check_separated_style(send_node) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 91
def check_separated_style(send_node)
  return if send_node.arguments.one?

  message = format(MSG, mixin: send_node.method_name, suffix: 'separate statements')

  add_offense(send_node, message: message) do |corrector|
    range = send_node.loc.expression
    correction = separate_mixins(send_node)

    corrector.replace(range, correction)
  end
end
group_mixins(node, mixins) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 127
def group_mixins(node, mixins)
  mixin_names = mixins.reverse.flat_map { |mixin| mixin.arguments.map(&:source) }

  "#{node.method_name} #{mixin_names.join(', ')}"
end
grouped_style?() click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 110
def grouped_style?
  style == :grouped
end
range_to_remove_for_subsequent_mixin(mixins, node) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 53
def range_to_remove_for_subsequent_mixin(mixins, node)
  range = node.loc.expression
  prev_mixin = mixins.each_cons(2) { |m, n| break m if n == node }
  between = prev_mixin.loc.expression.end.join(range.begin)
  # if separated from previous mixin with only whitespace?
  unless /\S/.match?(between.source)
    range = range.join(between) # then remove that too
  end
  range
end
separate_mixins(node) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 118
def separate_mixins(node)
  arguments = node.arguments.reverse
  mixins = ["#{node.method_name} #{arguments.first.source}"]

  arguments[1..].inject(mixins) do |replacement, arg|
    replacement << "#{indent(node)}#{node.method_name} #{arg.source}"
  end.join("\n")
end
separated_style?() click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 114
def separated_style?
  style == :separated
end
sibling_mixins(send_node) click to toggle source
# File lib/rubocop/cop/style/mixin_grouping.rb, line 104
def sibling_mixins(send_node)
  siblings = send_node.parent.each_child_node(:send).select(&:macro?)

  siblings.select { |sibling_node| sibling_node.method?(send_node.method_name) }
end