Skip to content

Commit

Permalink
Perform initial indexing and synchronization
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Aug 15, 2023
1 parent 5a24001 commit 32c2048
Show file tree
Hide file tree
Showing 12 changed files with 232 additions and 64 deletions.
2 changes: 2 additions & 0 deletions .index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
excluded_patterns:
- "**/test/fixtures/**/*.rb"
36 changes: 19 additions & 17 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@ gemspec
# sorbet-static is not available on Windows. We also skip Tapioca since it depends on sorbet-static-and-runtime
NON_WINDOWS_PLATFORMS = [:ruby] # C Ruby (MRI), Rubinius or TruffleRuby, but NOT Windows

gem "bundler", "~> 2.4.2"
gem "debug", "~> 1.8", require: false
gem "minitest", "~> 5.19"
gem "minitest-reporters", "~> 1.6"
gem "mocha", "~> 2.1"
gem "rake", "~> 13.0"
gem "rubocop", "~> 1.56"
gem "rubocop-shopify", "~> 2.14", require: false
gem "rubocop-minitest", "~> 0.31.0", require: false
gem "rubocop-rake", "~> 0.6.0", require: false
gem "rubocop-sorbet", "~> 0.7", require: false
gem "sorbet-static-and-runtime", platforms: NON_WINDOWS_PLATFORMS
gem "tapioca", "~> 0.11", require: false, platforms: NON_WINDOWS_PLATFORMS
gem "rdoc", require: false
gem "psych", "~> 5.1", require: false
group :development do
gem "bundler", "~> 2.4.2"
gem "debug", "~> 1.8", require: false
gem "minitest", "~> 5.19"
gem "minitest-reporters", "~> 1.6"
gem "mocha", "~> 2.1"
gem "rake", "~> 13.0"
gem "rubocop", "~> 1.56"
gem "rubocop-shopify", "~> 2.14", require: false
gem "rubocop-minitest", "~> 0.31.0", require: false
gem "rubocop-rake", "~> 0.6.0", require: false
gem "rubocop-sorbet", "~> 0.7", require: false
gem "sorbet-static-and-runtime", platforms: NON_WINDOWS_PLATFORMS
gem "tapioca", "~> 0.11", require: false, platforms: NON_WINDOWS_PLATFORMS
gem "rdoc", require: false
gem "psych", "~> 5.1", require: false

# The Rails documentation link only activates when railties is detected.
gem "railties", "~> 7.0", require: false
# The Rails documentation link only activates when railties is detected.
gem "railties", "~> 7.0", require: false
end
51 changes: 45 additions & 6 deletions lib/ruby_indexer/lib/ruby_indexer/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,55 @@ module RubyIndexer
class Configuration
extend T::Sig

sig { returns(T::Array[String]) }
attr_accessor :files_to_index

sig { void }
def initialize
development_only_dependencies = Bundler.definition.dependencies.filter_map do |dependency|
dependency.name if dependency.groups == [:development]
end

@excluded_gems = T.let(development_only_dependencies, T::Array[String])
@included_gems = T.let([], T::Array[String])
@excluded_patterns = T.let(["*_test.rb"], T::Array[String])
@included_patterns = T.let(["#{Dir.pwd}/**/*.rb"], T::Array[String])
end

sig { params(config: T::Hash[String, T.untyped]).void }
def apply_config(config)
excluded_gems = config.delete("excluded_gems")
@excluded_gems.concat(excluded_gems) if excluded_gems

included_gems = config.delete("included_gems")
@included_gems.concat(included_gems) if included_gems

excluded_patterns = config.delete("excluded_patterns")
@excluded_patterns.concat(excluded_patterns) if excluded_patterns

included_patterns = config.delete("included_patterns")
@included_patterns.concat(included_patterns) if included_patterns

raise ArgumentError, "Unknown configuration options: #{config.keys.join(", ")}" if config.any?
end

sig { returns(T::Array[String]) }
def files_to_index
files_to_index = $LOAD_PATH.flat_map { |p| Dir.glob("#{p}/**/*.rb", base: p) }
files_to_index.concat(Dir.glob("#{Dir.pwd}/**/*.rb"))
files_to_index.reject! { |path| path.end_with?("_test.rb") }

@files_to_index = T.let(files_to_index, T::Array[String])
@included_patterns.each do |pattern|
files_to_index.concat(Dir.glob(pattern, File::FNM_PATHNAME | File::FNM_EXTGLOB))
end

excluded_gem_paths = (@excluded_gems - @included_gems).filter_map do |gem_name|
Gem::Specification.find_by_name(gem_name).full_gem_path
rescue Gem::MissingSpecError
warn("Gem #{gem_name} is excluded in .index.yml, but that gem was not found in the bundle")
end

files_to_index.reject! do |path|
@excluded_patterns.any? { |pattern| File.fnmatch?(pattern, path, File::FNM_PATHNAME | File::FNM_EXTGLOB) } ||
excluded_gem_paths.any? { |gem_path| File.fnmatch?("#{gem_path}/**/*.rb", path) }
end
files_to_index.uniq!
files_to_index
end
end
end
16 changes: 10 additions & 6 deletions lib/ruby_indexer/lib/ruby_indexer/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ module RubyIndexer
class Index
extend T::Sig

sig { returns(T::Hash[String, T::Array[Entry]]) }
attr_reader :entries

sig { void }
def initialize
# Holds all entries in the index using the following format:
Expand Down Expand Up @@ -70,9 +67,16 @@ def resolve(name, nesting)
nil
end

sig { void }
def clear
@entries.clear
sig { params(paths: T::Array[String]).void }
def index_all(paths: RubyIndexer.configuration.files_to_index)
paths.each { |path| index_single(path) }
end

sig { params(path: String, source: T.nilable(String)).void }
def index_single(path, source = nil)
content = source || File.read(path)
visitor = IndexVisitor.new(self, YARP.parse(content), path)
visitor.run
end

class Entry
Expand Down
25 changes: 8 additions & 17 deletions lib/ruby_indexer/ruby_indexer.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# typed: strict
# frozen_string_literal: true

require "yaml"

require "ruby_indexer/lib/ruby_indexer/visitor"
require "ruby_indexer/lib/ruby_indexer/index"
require "ruby_indexer/lib/ruby_indexer/configuration"
Expand All @@ -9,28 +11,17 @@ module RubyIndexer
class << self
extend T::Sig

sig { params(block: T.proc.params(configuration: Configuration).void).void }
def configure(&block)
block.call(configuration)
sig { void }
def load_configuration_file
return unless File.exist?(".index.yml")

config = YAML.parse_file(".index.yml")
configuration.apply_config(config.to_ruby) if config
end

sig { returns(Configuration) }
def configuration
@configuration ||= T.let(Configuration.new, T.nilable(Configuration))
end

sig { params(paths: T::Array[String]).returns(Index) }
def index(paths = configuration.files_to_index)
index = Index.new
paths.each { |path| index_single(index, path) }
index
end

sig { params(index: Index, path: String, source: T.nilable(String)).void }
def index_single(index, path, source = nil)
content = source || File.read(path)
visitor = IndexVisitor.new(index, YARP.parse(content), path)
visitor.run
end
end
end
6 changes: 1 addition & 5 deletions lib/ruby_indexer/test/classes_and_modules_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ def setup
@index = Index.new
end

def teardown
@index.clear
end

def test_empty_statements_class
index(<<~RUBY)
class Foo
Expand Down Expand Up @@ -212,7 +208,7 @@ class Foo; end
private

def index(source)
RubyIndexer.index_single(@index, "/fake/path/foo.rb", source)
@index.index_single("/fake/path/foo.rb", source)
end

def assert_entry(expected_name, type, expected_location)
Expand Down
14 changes: 5 additions & 9 deletions lib/ruby_indexer/test/index_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,12 @@ def setup
@index = Index.new
end

def teardown
@index.clear
end

def test_deleting_one_entry_for_a_class
RubyIndexer.index_single(@index, "/fake/path/foo.rb", <<~RUBY)
@index.index_single("/fake/path/foo.rb", <<~RUBY)
class Foo
end
RUBY
RubyIndexer.index_single(@index, "/fake/path/other_foo.rb", <<~RUBY)
@index.index_single("/fake/path/other_foo.rb", <<~RUBY)
class Foo
end
RUBY
Expand All @@ -32,7 +28,7 @@ class Foo
end

def test_deleting_all_entries_for_a_class
RubyIndexer.index_single(@index, "/fake/path/foo.rb", <<~RUBY)
@index.index_single("/fake/path/foo.rb", <<~RUBY)
class Foo
end
RUBY
Expand All @@ -46,7 +42,7 @@ class Foo
end

def test_index_resolve
RubyIndexer.index_single(@index, "/fake/path/foo.rb", <<~RUBY)
@index.index_single("/fake/path/foo.rb", <<~RUBY)
class Bar; end
module Foo
Expand Down Expand Up @@ -80,7 +76,7 @@ class Something
end

def test_accessing_with_colon_colon_prefix
RubyIndexer.index_single(@index, "/fake/path/foo.rb", <<~RUBY)
@index.index_single("/fake/path/foo.rb", <<~RUBY)
class Bar; end
module Foo
Expand Down
29 changes: 29 additions & 0 deletions lib/ruby_indexer/test/ruby_indexer_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# typed: true
# frozen_string_literal: true

require "test_helper"

module RubyIndexer
class RubyIndexerTest < Minitest::Test
def test_load_configuration_executes_configure_block
RubyIndexer.load_configuration_file
files_to_index = RubyIndexer.configuration.files_to_index

assert(files_to_index.none? { |path| path.include?("test/fixtures") })
assert(files_to_index.none? { |path| path.include?("minitest-reporters") })
end

def test_paths_are_unique
RubyIndexer.load_configuration_file
files_to_index = RubyIndexer.configuration.files_to_index

assert_equal(files_to_index.uniq.length, files_to_index.length)
end

def test_configuration_raises_for_unknown_keys
assert_raises(ArgumentError) do
RubyIndexer.configuration.apply_config({ "unknown" => 123 })
end
end
end
end
Loading

0 comments on commit 32c2048

Please sign in to comment.