lib/ivar/project_root.rb
# frozen_string_literal: true require "pathname" module Ivar # Handles project root detection and caching class ProjectRoot # Project root indicator files, in order of precedence INDICATORS = %w[Gemfile .git .ruby-version Rakefile].freeze def initialize @cache = {} @mutex = Mutex.new end # Determines the project root directory based on the caller's location # @param caller_location [String, nil] Optional file path to start from (defaults to caller's location) # @return [String] The absolute path to the project root directory def find(caller_location = nil) file_path = caller_location || caller_locations(2, 1).first&.path return Dir.pwd unless file_path @mutex.synchronize do return @cache[file_path] if @cache.key?(file_path) end dir = File.dirname(File.expand_path(file_path)) root = find_project_root(dir) @mutex.synchronize do @cache[file_path] = root end root end # Clear the cache (mainly for testing) def clear_cache @mutex.synchronize { @cache.clear } end private # Find the project root by walking up the directory tree # @param start_dir [String] Directory to start the search from # @return [String] The project root directory def find_project_root(start_dir) path = Pathname.new(start_dir) path.ascend do |dir| INDICATORS.each do |indicator| return dir.to_s if dir.join(indicator).exist? end end start_dir end end end