Class: RBS::EnvironmentLoader

Inherits:
Object
  • Object
show all
Includes:
FileFinder
Defined in:
sig/environment_loader.rbs,
lib/rbs/environment_loader.rb

Overview

EnvironmentLoader is an object to load RBS files from filesystem.

Set up your configuration through repository and #add method.

# Set up the repository to load library RBSs from.
repo = RBS::Repository.default
repo.add(Pathname("vendor/rbs/gem-rbs"))
repo.add(Pathname("vendor/rbs/internal-rbs"))

loader = RBS::EnvironmentLoader.new(repository: repo)

# Add libraries to load RBS files.
loader.add(library: "minitest")
loader.add(library: "rbs", version: "1.0.0")

# Add dirs to load RBS files from.
loader.add(path: Pathname("sig"))

# Load RBSs into an environment.
environment = RBS::Environment.new()
loader.load(env: environment)

Defined Under Namespace

Classes: Library, UnknownLibraryError

Constant Summary collapse

DEFAULT_CORE_ROOT =

Returns:

  • (Pathname)
Pathname(_ = __dir__) + "../../core"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from FileFinder

each_file, #self?.each_file

Constructor Details

#initialize(core_root: DEFAULT_CORE_ROOT, repository: Repository.new) ⇒ EnvironmentLoader

Accepts two optional keyword arguments.

core_root is the path to the directory with RBSs for core classes. The default value is the core library included in RBS gem. (EnvironmentLoader::DEFAULT_CORE_ROOT) Passing nil means it skips loading core class definitions.

repository is the repository for library classes. The default value is repository only with stdlib classes. (Repository.new)

Parameters:

  • core_root: (Pathname, nil) (defaults to: DEFAULT_CORE_ROOT)
  • repository: (Repository) (defaults to: Repository.new)


69
70
71
72
73
74
75
# File 'sig/environment_loader.rbs', line 69

def initialize(core_root: DEFAULT_CORE_ROOT, repository: Repository.new)
  @core_root = core_root
  @repository = repository

  @libs = Set.new
  @dirs = []
end

Instance Attribute Details

#core_rootPathname? (readonly)

Returns the value of attribute core_root.

Returns:

  • (Pathname, nil)


20
21
22
# File 'lib/rbs/environment_loader.rb', line 20

def core_root
  @core_root
end

#dirsArray[Pathname] (readonly)

Returns the value of attribute dirs.

Returns:

  • (Array[Pathname])


24
25
26
# File 'lib/rbs/environment_loader.rb', line 24

def dirs
  @dirs
end

#libsSet[Library] (readonly)

Returns the value of attribute libs.

Returns:



23
24
25
# File 'lib/rbs/environment_loader.rb', line 23

def libs
  @libs
end

#repositoryRepository (readonly)

Returns the value of attribute repository.

Returns:



21
22
23
# File 'lib/rbs/environment_loader.rb', line 21

def repository
  @repository
end

Class Method Details

.gem_sig_path(name, version) ⇒ [Gem::Specification, Pathname]?

Returns a pair of spec and path for a gem with RBS. Returns nil if the gem is not installed, or the gem doesn't provide RBS.

Parameters:

  • name (String)
  • version (String, nil)

Returns:



105
106
107
108
109
110
111
112
113
114
115
# File 'sig/environment_loader.rbs', line 105

def self.gem_sig_path(name, version)
  requirements = [] #: Array[String]
  requirements << version if version
  spec = Gem::Specification.find_by_name(name, *requirements)
  path = Pathname(spec.gem_dir) + "sig"
  if path.directory?
    [spec, path]
  end
rescue Gem::MissingSpecError
  nil
end

Instance Method Details

#add(path:) ⇒ void #add(library:, version:, resolve_dependencies:) ⇒ void

Add a path or library to load RBSs from.

path can be a file or a directory. All .rbs files from the given directory will be loaded. Specifying a file will load the file regardless the extension of the file is.

library can be a name of a gem. Specifying nil to version will load any version available. It first tries to load RBS files from gem with specified version. If RBS files cannot be found in the gem, it tries to load RBSs from repository.

Overloads:

  • #add(path:) ⇒ void

    This method returns an undefined value.

    Parameters:

    • path: (Pathname)
  • #add(library:, version:, resolve_dependencies:) ⇒ void

    This method returns an undefined value.

    Parameters:

    • library: (String)
    • version: (String, nil)
    • resolve_dependencies: (boolish)


82
83
84
85
86
87
88
89
90
91
# File 'sig/environment_loader.rbs', line 82

def add(path: nil, library: nil, version: nil, resolve_dependencies: true)
  case
  when path
    dirs << path
  when library
    if libs.add?(Library.new(name: library, version: version)) && resolve_dependencies
      resolve_dependencies(library: library, version: version)
    end
  end
end

#add_collection(lockfile) ⇒ void

This method returns an undefined value.

Add repository path and libraries via rbs_collection.lock.yaml.

Parameters:



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'sig/environment_loader.rbs', line 88

def add_collection(lockfile)
  lockfile.check_rbs_availability!

  repository.add(lockfile.fullpath)

  lockfile.gems.each_value do |gem|
    name = gem[:name]
    locked_version = gem[:version]

    if (source = gem[:source]).is_a?(Collection::Sources::Rubygems)
      # allow loading different version of a gem

      unless source.has?(name, locked_version)
        if (spec, _ = self.class.gem_sig_path(name, nil))
          RBS.logger.warn { "Loading type definition from gem `#{name}-#{spec.version}` because locked version `#{locked_version}` is unavailable. Try `rbs collection update` to fix the (potential) issue." }
          locked_version = spec.version.to_s
        end
      end
    end

    add(library: gem[:name], version: locked_version, resolve_dependencies: false)
  end
end

#each_dir {|arg0, arg1| ... } ⇒ void

This method returns an undefined value.

Yields:

Yield Parameters:

  • arg0 (source)
  • arg1 (Pathname)

Yield Returns:

  • (void)


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/rbs/environment_loader.rb', line 125

def each_dir
  if root = core_root
    yield :core, root
  end

  libs.each do |lib|
    unless has_library?(version: lib.version, library: lib.name)
      raise UnknownLibraryError.new(lib: lib)
    end

    case
    when from_gem = self.class.gem_sig_path(lib.name, lib.version)
      yield lib, from_gem[1]
    when from_repo = repository.lookup(lib.name, lib.version)
      yield lib, from_repo
    end
  end

  dirs.each do |dir|
    yield dir, dir
  end
end

#each_signature {|arg0, arg1, arg2, arg3, arg4| ... } ⇒ void

This method returns an undefined value.

Yields:

Yield Parameters:

  • arg0 (source)
  • arg1 (Pathname)
  • arg2 (Buffer)
  • arg3 (Array[AST::Declarations::t])
  • arg4 (Array[AST::Directives::t])

Yield Returns:

  • (void)


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/rbs/environment_loader.rb', line 148

def each_signature
  files = Set[]

  each_dir do |source, dir|
    skip_hidden = !source.is_a?(Pathname)

    FileFinder.each_file(dir, skip_hidden: skip_hidden) do |path|
      next if files.include?(path)

      files << path
      buffer = Buffer.new(name: path, content: path.read(encoding: "UTF-8"))

      _, dirs, decls = Parser.parse_signature(buffer)

      yield source, path, buffer, decls, dirs
    end
  end
end

#has_library?(library:, version:) ⇒ Boolean

This is helper function to test if RBS for a library is available or not.

Parameters:

  • library: (String)
  • version: (String, nil)

Returns:

  • (Boolean)


92
93
94
95
96
97
98
# File 'sig/environment_loader.rbs', line 92

def has_library?(library:, version:)
  if self.class.gem_sig_path(library, version) || repository.lookup(library, version)
    true
  else
    false
  end
end

#load(env:) ⇒ Array[[AST::Declarations::t, Pathname, source]]

Add all declarations to environment.

Raises UnknownLibraryError if RBS cannot be loaded from a library.

Returns an array of tuples of the declaration, path to the file, and the source.

Parameters:

Returns:

  • (Array[[AST::Declarations::t, Pathname, source]])


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'sig/environment_loader.rbs', line 100

def load(env:)
  # @type var loaded: Array[[AST::Declarations::t, Pathname, source]]
  loaded = []

  # For migrating stringio to stdlib
  if @core_root && libs.none? { |lib| lib.name == 'stringio' }
    add(library: 'stringio', version: nil)
  end

  each_signature do |source, path, buffer, decls, dirs|
    decls.each do |decl|
      loaded << [decl, path, source]
    end
    env.add_source(Source::RBS.new(buffer, dirs, decls))
  end

  loaded
end

#resolve_dependencies(library:, version:) ⇒ void

This method returns an undefined value.

Parameters:

  • library: (String)
  • version: (String, nil)


59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rbs/environment_loader.rb', line 59

def resolve_dependencies(library:, version:)
  [Collection::Sources::Rubygems.instance, Collection::Sources::Stdlib.instance].each do |source|
    next unless source.has?(library, version)

    unless version
      version = source.versions(library).last or raise
    end

    source.dependencies_of(library, version)&.each do |dep|
      add(library: dep['name'], version: nil)
    end
    return
  end
end