class RuboCop::Cop::Highlands::FactoryAttrReferencesClass

Cop to enforce “attr { CONST }” instead of “attr CONST” in factories, because the latter forces autoload, which slows down spec startup time and Zeus reload time after touching a model.

Constants

MSG

Public Instance Methods

on_send(node) click to toggle source

Look for “attr CONST” expressions in factories or traits. In RuboCop, this is a `send` node, sending the attr method.

# File lib/rubocop/cop/highlands/factory_attr_references_class.rb, line 17
def on_send(node)
  return unless in_factory_file?(node)
  return unless in_factory_or_trait?(node)

  add_const_offenses(node)
end

Private Instance Methods

add_const_offenses(node) click to toggle source
# File lib/rubocop/cop/highlands/factory_attr_references_class.rb, line 59
def add_const_offenses(node)
  # Add an offense for any const reference
  node.each_child_node(:const) do |const_node|
    add_offense(const_node)
  end

  # Recurse into arrays, hashes, and method calls such as ConstName[:symbol],
  # adding offenses for any const reference inside them.
  node.each_child_node(:array, :hash, :pair, :send) do |array_node|
    add_const_offenses(array_node)
  end
end
in_factory_file?(node) click to toggle source
# File lib/rubocop/cop/highlands/factory_attr_references_class.rb, line 26
def in_factory_file?(node)
  filename = node.location.expression.source_buffer.name

  # For tests, the input is a string
  filename.include?("spec/factories/") || filename == "(string)"
end
in_factory_or_trait?(node) click to toggle source

Is this node in a factory or trait, but not inside a nested block in a factory or trait?

# File lib/rubocop/cop/highlands/factory_attr_references_class.rb, line 34
def in_factory_or_trait?(node)
  return false unless node

  # Bail out if this IS the factory or trait node.
  return false unless factory_attributes(node)
  return false unless node.parent

  # Is this node in a block that was passed to the factory or trait method?
  if node.parent.is_a?(RuboCop::AST::Node) && node.parent.block_type?
    send_node = node.parent.children.first
    return false unless send_node
    return false unless send_node.send_type?

    # Const is referenced in the block passed to a factory or trait.
    return true if send_node.command?(:factory)
    return true if send_node.command?(:trait)

    # Const is a block that's nested deeper inside a factory or trait. This is what we want
    # developers to do.
    return false
  end

  in_factory_or_trait?(node.parent)
end