lib/rubocop/cop/rspec/factory_bot/static_attribute_defined_dynamically.rb



# frozen_string_literal: true

module RuboCop
  module Cop
    module RSpec
      module FactoryBot
        # Prefer declaring static attribute values without a block.
        #
        # @see DynamicAttributeDefinedStatically
        #
        # @example
        #   # bad
        #   kind { :static }
        #
        #   # good
        #   kind :static
        #
        #   # bad
        #   comments_count { 0 }
        #
        #   # good
        #   comments_count 0
        #
        #   # bad
        #   type { User::MAGIC }
        #
        #   # good
        #   type User::MAGIC
        class StaticAttributeDefinedDynamically < Cop
          MSG = 'Do not use a block to set a static value ' \
                'to an attribute.'.freeze

          def_node_matcher :block_value_matcher, <<-PATTERN
            (block (send nil? _) _ $...)
          PATTERN

          def_node_search :factory_attributes, <<-PATTERN
            (block (send nil? { :factory :trait } ...) _ { (begin $...) $(send ...) $(block ...) } )
          PATTERN

          def on_block(node)
            factory_attributes(node).to_a.flatten.each do |attribute|
              values = block_value_matcher(attribute)
              next if values.to_a.all? { |v| dynamic?(v) }
              add_offense(attribute, location: :expression)
            end
          end

          def autocorrect(node)
            lambda do |corrector|
              corrector.replace(
                node.loc.expression,
                autocorrected_source(node)
              )
            end
          end

          private

          def dynamic?(node)
            node && !node.recursive_literal_or_const?
          end

          def autocorrected_source(node)
            "#{node.send_node.source}#{autocorrected_attribute(node.body)}"
          end

          def autocorrected_attribute(body)
            if body.nil?
              ' nil'
            elsif body.hash_type?
              "(#{body.source})"
            else
              ' ' + body.source
            end
          end
        end
      end
    end
  end
end