class Tapioca::Gemfile::GemSpec
def ==(other)
def ==(other) GemSpec === other && other.name == name && other.version == version end
def collect_files
def collect_files if default_gem? # `Bundler::RemoteSpecification` delegates missing methods to # `Gem::Specification`, so `files` actually always exists on spec. T.unsafe(@spec).files.map do |file| resolve_to_ruby_lib_dir(file) end else @spec.full_require_paths.flat_map do |path| Pathname.glob((Pathname.new(path) / "**/*.rb").to_s) end end end
def contains_path?(path)
def contains_path?(path) if default_gem? files.any? { |file| file.to_s == to_realpath(path) } else path_in_dir?(to_realpath(path), full_gem_path) || has_parent_gemspec?(path) end end
def default_gem?
def default_gem? @spec.respond_to?(:default_gem?) && @spec.default_gem? end
def dependencies
def dependencies @spec.dependencies end
def export_rbi_files?
def export_rbi_files? exported_rbi_files.any? end
def exported_rbi_files
def exported_rbi_files @exported_rbi_files ||= Dir.glob("#{full_gem_path}/rbi/**/*.rbi").sort end
def exported_rbi_tree
def exported_rbi_tree rewriter = RBI::Rewriters::Merge.new(keep: RBI::Rewriters::Merge::Keep::NONE) exported_rbi_files.each do |file| rbi = RBI::Parser.parse_file(file) rewriter.merge(rbi) end rewriter.tree end
def gem_ignored?
def gem_ignored? IGNORED_GEMS.include?(name) end
def has_parent_gemspec?(path)
def has_parent_gemspec?(path) # For some Git installed gems the location of the loaded file can # be different from the gem path as indicated by the spec file # # To compensate for these cases, we walk up the directory hierarchy # from the given file and try to match a <gem-name.gemspec> file in # one of those folders to see if the path really belongs in the given gem # or not. return false unless Bundler::Source::Git === @spec.source parent = Pathname.new(path) until parent.root? parent = parent.parent.expand_path return true if parent.join("#{name}.gemspec").file? end false end
def ignore?(gemfile_dir)
def ignore?(gemfile_dir) gem_ignored? || gem_in_app_dir?(gemfile_dir, full_gem_path) end
def initialize(spec)
def initialize(spec) @spec = T.let(spec, Tapioca::Gemfile::Spec) real_gem_path = to_realpath(@spec.full_gem_path) @full_gem_path = T.let(real_gem_path, String) @version = T.let(version_string, String) @exported_rbi_files = T.let(nil, T.nilable(T::Array[String])) @files = T.let(collect_files, T::Array[Pathname]) end
def name
def name @spec.name end
def parse_yard_docs
def parse_yard_docs files.each do |path| YARD.parse(path.to_s, [], Logger::Severity::FATAL) rescue RangeError # In some circumstances, YARD will raise an error when parsing a file # that is actually valid Ruby. We don't want tapioca to halt in these # cases, so we'll rescue the error, pretend like there was no # documentation, and move on. # # This can be removed when https://github.com/lsegal/yard/issues/1536 # is resolved and released. [] end end
def rbi_file_name
def rbi_file_name "#{name}@#{version}.rbi" end
def relative_path_for(file)
def relative_path_for(file) if default_gem? file.realpath.relative_path_from(RbConfig::CONFIG["rubylibdir"]) else file.realpath.relative_path_from(full_gem_path) end end
def require_paths_prefix_matcher
def require_paths_prefix_matcher @require_paths_prefix_matcher ||= T.let( begin require_paths = T.unsafe(@spec).require_paths prefix_matchers = require_paths.map { |rp| Regexp.new("^#{rp}/") } Regexp.union(prefix_matchers) end, T.nilable(Regexp), ) end
def resolve_to_ruby_lib_dir(file)
def resolve_to_ruby_lib_dir(file) # We want to match require prefixes but fallback to an empty match # if none of the require prefixes actually match. This is so that # we can always replace the match with the Ruby lib directory and # we would have properly resolved the file under the Ruby lib dir. prefix_matcher = Regexp.union(require_paths_prefix_matcher, //) ruby_lib_dir = RbConfig::CONFIG["rubylibdir"] file = file.sub(prefix_matcher, "#{ruby_lib_dir}/") Pathname.new(file).expand_path end
def spec_lookup_by_file_path
def spec_lookup_by_file_path @lookup ||= T.let( [*::Gem::Specification.default_stubs, *::Gem::Specification.stubs] .map! { |spec| new(spec.to_spec) } .flat_map do |spec| spec.files.filter_map { |file| [file.realpath.to_s, spec] if file.exist? } end.to_h, T.nilable(T::Hash[String, Gemfile::GemSpec]), ) end
def version_string
def version_string version = @spec.version.to_s version += "-#{@spec.source.revision}" if Bundler::Source::Git === @spec.source version end