185 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Ruby
		
	
	
	
# frozen_string_literal: true
 | 
						|
 | 
						|
module RuboCop
 | 
						|
  module CodeReuseHelpers
 | 
						|
    # Returns true for a `(send const ...)` node.
 | 
						|
    def send_to_constant?(node)
 | 
						|
      node.type == :send && node.children&.first&.type == :const
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns `true` if the name of the receiving constant ends with a given
 | 
						|
    # `String`.
 | 
						|
    def send_receiver_name_ends_with?(node, suffix)
 | 
						|
      return false unless send_to_constant?(node)
 | 
						|
 | 
						|
      receiver_name = name_of_receiver(node)
 | 
						|
 | 
						|
      receiver_name != suffix &&
 | 
						|
        receiver_name.end_with?(suffix)
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns the file path (as a `String`) for an AST node.
 | 
						|
    def file_path_for_node(node)
 | 
						|
      node.location.expression.source_buffer.name
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns the name of a constant node.
 | 
						|
    #
 | 
						|
    # Given the AST node `(const nil? :Foo)`, this method will return `:Foo`.
 | 
						|
    def name_of_constant(node)
 | 
						|
      node.children[1]
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/finders or ee/app/finders.
 | 
						|
    def in_finder?(node)
 | 
						|
      in_app_directory?(node, 'finders')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/models or ee/app/models.
 | 
						|
    def in_model?(node)
 | 
						|
      in_app_directory?(node, 'models')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/services or ee/app/services.
 | 
						|
    def in_service_class?(node)
 | 
						|
      in_app_directory?(node, 'services')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/presenters or
 | 
						|
    # ee/app/presenters.
 | 
						|
    def in_presenter?(node)
 | 
						|
      in_app_directory?(node, 'presenters')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/serializers or
 | 
						|
    # ee/app/serializers.
 | 
						|
    def in_serializer?(node)
 | 
						|
      in_app_directory?(node, 'serializers')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/workers or ee/app/workers.
 | 
						|
    def in_worker?(node)
 | 
						|
      in_app_directory?(node, 'workers')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/controllers or
 | 
						|
    # ee/app/controllers.
 | 
						|
    def in_controller?(node)
 | 
						|
      in_app_directory?(node, 'controllers')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in app/graphql/types,
 | 
						|
    # ee/app/graphql/types, or ee/app/graphql/ee/types.
 | 
						|
    def in_graphql_types?(node)
 | 
						|
      in_app_directory?(node, 'graphql/types') || in_app_directory?(node, 'graphql/ee/types')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in lib/api or ee/lib/api.
 | 
						|
    def in_api?(node)
 | 
						|
      in_lib_directory?(node, 'api')
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns true if the given node resides in spec or ee/spec.
 | 
						|
    def in_spec?(node)
 | 
						|
      file_path_for_node(node).start_with?(
 | 
						|
        ce_spec_directory,
 | 
						|
        ee_spec_directory
 | 
						|
      )
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns `true` if the given AST node resides in the given directory,
 | 
						|
    # relative to app and/or ee/app.
 | 
						|
    def in_app_directory?(node, directory)
 | 
						|
      file_path_for_node(node).start_with?(
 | 
						|
        File.join(ce_app_directory, directory),
 | 
						|
        File.join(ee_app_directory, directory)
 | 
						|
      )
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns `true` if the given AST node resides in the given directory,
 | 
						|
    # relative to lib and/or ee/lib.
 | 
						|
    def in_lib_directory?(node, directory)
 | 
						|
      file_path_for_node(node).start_with?(
 | 
						|
        File.join(ce_lib_directory, directory),
 | 
						|
        File.join(ee_lib_directory, directory)
 | 
						|
      )
 | 
						|
    end
 | 
						|
 | 
						|
    # Returns the receiver name of a send node.
 | 
						|
    #
 | 
						|
    # For the AST node `(send (const nil? :Foo) ...)` this would return
 | 
						|
    # `'Foo'`.
 | 
						|
    def name_of_receiver(node)
 | 
						|
      name_of_constant(node.children.first).to_s
 | 
						|
    end
 | 
						|
 | 
						|
    # Yields every defined class method in the given AST node.
 | 
						|
    def each_class_method(node)
 | 
						|
      return to_enum(__method__, node) unless block_given?
 | 
						|
 | 
						|
      # class << self
 | 
						|
      #   def foo
 | 
						|
      #   end
 | 
						|
      # end
 | 
						|
      node.each_descendant(:sclass) do |sclass|
 | 
						|
        sclass.each_descendant(:def) do |def_node|
 | 
						|
          yield def_node
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # def self.foo
 | 
						|
      # end
 | 
						|
      node.each_descendant(:defs) do |defs_node|
 | 
						|
        yield defs_node
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Yields every send node found in the given AST node.
 | 
						|
    def each_send_node(node, &block)
 | 
						|
      node.each_descendant(:send, &block)
 | 
						|
    end
 | 
						|
 | 
						|
    # Registers a RuboCop offense for a `(send)` node with a receiver that ends
 | 
						|
    # with a given suffix.
 | 
						|
    #
 | 
						|
    # node - The AST node to check.
 | 
						|
    # suffix - The suffix of the receiver name, such as "Finder".
 | 
						|
    # message - The message to use for the offense.
 | 
						|
    def disallow_send_to(node, suffix, message)
 | 
						|
      each_send_node(node) do |send_node|
 | 
						|
        next unless send_receiver_name_ends_with?(send_node, suffix)
 | 
						|
 | 
						|
        add_offense(send_node, location: :expression, message: message)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def ce_app_directory
 | 
						|
      File.join(rails_root, 'app')
 | 
						|
    end
 | 
						|
 | 
						|
    def ee_app_directory
 | 
						|
      File.join(rails_root, 'ee', 'app')
 | 
						|
    end
 | 
						|
 | 
						|
    def ce_lib_directory
 | 
						|
      File.join(rails_root, 'lib')
 | 
						|
    end
 | 
						|
 | 
						|
    def ee_lib_directory
 | 
						|
      File.join(rails_root, 'ee', 'lib')
 | 
						|
    end
 | 
						|
 | 
						|
    def ce_spec_directory
 | 
						|
      File.join(rails_root, 'spec')
 | 
						|
    end
 | 
						|
 | 
						|
    def ee_spec_directory
 | 
						|
      File.join(rails_root, 'ee', 'spec')
 | 
						|
    end
 | 
						|
 | 
						|
    def rails_root
 | 
						|
      File.expand_path('..', __dir__)
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 |