class RuboCop::Cop::Rails::DuplicateAssociation


has_one :foo
has_many :bar, class_name: ‘Foo’
# good
has_one :baz
has_many :bar, class_name: ‘Foo’
has_many :foo, class_name: ‘Foo’
# bad
has_one :foo
belongs_to :bar
# good
has_one :foo
belongs_to :bar
belongs_to :foo
# bad
@example
simply keeps the last of any duplicates and discards the rest.
previously defined association with the new one. Because of this, this cop’s autocorrection
When an association is defined multiple times on a model, Active Record overrides the
Looks for associations that have been defined multiple times in the same file.

def association_nodes(class_node)

def association_nodes(class_node)
  class_send_nodes(class_node).select do |node|
    association(node)&.first
  end
end

def duplicated_association_name_nodes(association_nodes)

def duplicated_association_name_nodes(association_nodes)
  grouped_associations = association_nodes.group_by do |node|
    association(node).first.to_sym
  end
  leave_duplicated_association(grouped_associations)
end

def duplicated_class_name_nodes(association_nodes)

def duplicated_class_name_nodes(association_nodes)
  filtered_nodes = association_nodes.reject { |node| node.method?(:belongs_to) }
  grouped_associations = filtered_nodes.group_by do |node|
    arguments = association(node).last
    next unless arguments.count == 1
    if (class_name = class_name(arguments.first))
      class_name.source
    end
  end
  grouped_associations.delete(nil)
  leave_duplicated_association(grouped_associations)
end

def leave_duplicated_association(grouped_associations)

def leave_duplicated_association(grouped_associations)
  grouped_associations.select do |_, nodes|
    nodes.length > 1
  end
end

def on_class(class_node)

def on_class(class_node)
  return unless active_record?(class_node.parent_class)
  association_nodes = association_nodes(class_node)
  duplicated_association_name_nodes(association_nodes).each do |name, nodes|
    register_offense(name, nodes, MSG)
  end
  duplicated_class_name_nodes(association_nodes).each do |class_name, nodes|
    register_offense(class_name, nodes, MSG_CLASS_NAME)
  end
end

def register_offense(name, nodes, message_template)

def register_offense(name, nodes, message_template)
  nodes.each do |node|
    add_offense(node, message: format(message_template, name: name)) do |corrector|
      next if same_line?(nodes.last, node)
      corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
    end
  end
end