-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds the notion of versioning to the RBI gem. 1. Users can specify versions for parts of their RBI files using `@version` annotations in comments above any class, module, or method definition. 2. The `FilterVersions` rewriter will take an RBI file, as well as a version number, and rewrite the RBI to only include the portions that are relevant for a specific gem version. 3. RBI without any version annotations will continue to behave as expected. Co-authored-by: Alexandre Terrasa <[email protected]> Co-authored-by: Aiden Storey <[email protected]>
- Loading branch information
1 parent
7bd776c
commit 03c4000
Showing
3 changed files
with
347 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# typed: strict | ||
# frozen_string_literal: true | ||
|
||
module RBI | ||
module Rewriters | ||
class FilterVersions < Visitor | ||
extend T::Sig | ||
|
||
VERSION_PREFIX = "version " | ||
|
||
class << self | ||
extend T::Sig | ||
|
||
sig { params(tree: Tree, version: Gem::Version).void } | ||
def filter(tree, version) | ||
v = new(version) | ||
v.visit(tree) | ||
end | ||
end | ||
|
||
sig { params(version: Gem::Version).void } | ||
def initialize(version) | ||
super() | ||
@version = version | ||
end | ||
|
||
sig { override.params(node: T.nilable(Node)).void } | ||
def visit(node) | ||
return unless node | ||
|
||
unless node.satisfies_version?(@version) | ||
node.detach | ||
return | ||
end | ||
|
||
visit_all(node.nodes.dup) if node.is_a?(Tree) | ||
end | ||
end | ||
end | ||
|
||
class Node | ||
sig { params(version: Gem::Version).returns(T::Boolean) } | ||
def satisfies_version?(version) | ||
return true unless is_a?(NodeWithComments) | ||
|
||
requirements = version_requirements | ||
requirements.empty? || requirements.any? { |req| req.satisfied_by?(version) } | ||
end | ||
end | ||
|
||
class NodeWithComments | ||
sig { returns(T::Array[Gem::Requirement]) } | ||
def version_requirements | ||
annotations.select do |annotation| | ||
annotation.start_with?(Rewriters::FilterVersions::VERSION_PREFIX) | ||
end.map do |annotation| | ||
versions = annotation.delete_prefix(Rewriters::FilterVersions::VERSION_PREFIX).split(/, */) | ||
Gem::Requirement.new(versions) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
# typed: true | ||
# frozen_string_literal: true | ||
|
||
require "test_helper" | ||
|
||
module RBI | ||
class FilterVersions < Minitest::Test | ||
def test_filter_versions_do_nothing | ||
rbi = <<~RBI | ||
class Foo; end | ||
class Bar; end | ||
class Baz; end | ||
class Buzz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
class Foo; end | ||
class Bar; end | ||
class Baz; end | ||
class Buzz; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_less_than | ||
rbi = <<~RBI | ||
# @version < 1.0.0 | ||
class Foo; end | ||
# @version < 0.4.0-prerelease | ||
class Bar; end | ||
# @version < 0.4.0 | ||
class Baz; end | ||
# @version < 0.3.0 | ||
class Buzz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version < 1.0.0 | ||
class Foo; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_greater_than | ||
rbi = <<~RBI | ||
# @version > 1.0.0 | ||
class Foo; end | ||
# @version > 0.4.0-prerelease | ||
class Bar; end | ||
# @version > 0.4.0 | ||
class Baz; end | ||
# @version > 0.3.0 | ||
class Buzz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version > 0.4.0-prerelease | ||
class Bar; end | ||
# @version > 0.3.0 | ||
class Buzz; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_equals | ||
rbi = <<~RBI | ||
# @version = 1.0.0 | ||
class Foo; end | ||
# @version = 0.4.0-prerelease | ||
class Bar; end | ||
# @version = 0.4.0 | ||
class Baz; end | ||
# @version = 0.3.0 | ||
class Buzz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version = 0.4.0 | ||
class Baz; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_greater_than_or_equals | ||
rbi = <<~RBI | ||
# @version >= 1.0.0 | ||
class Foo; end | ||
# @version >= 0.4.0-prerelease | ||
class Bar; end | ||
# @version >= 0.4.0 | ||
class Baz; end | ||
# @version >= 0.3.0 | ||
class Buzz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version >= 0.4.0-prerelease | ||
class Bar; end | ||
# @version >= 0.4.0 | ||
class Baz; end | ||
# @version >= 0.3.0 | ||
class Buzz; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_less_than_or_equals | ||
rbi = <<~RBI | ||
# @version <= 1.0.0 | ||
class Foo; end | ||
# @version <= 0.4.0-prerelease | ||
class Bar; end | ||
# @version <= 0.4.0 | ||
class Baz; end | ||
# @version <= 0.3.0 | ||
class Buzz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version <= 1.0.0 | ||
class Foo; end | ||
# @version <= 0.4.0 | ||
class Baz; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_prerelease | ||
rbi = <<~RBI | ||
# @version <= 1.0.0 | ||
class Foo; end | ||
# @version <= 0.4.0-prerelease | ||
class Bar; end | ||
# @version = 0.4.0 | ||
class Baz; end | ||
# @version >= 0.4.0-prerelease | ||
class Buzz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0-prerelease")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version <= 1.0.0 | ||
class Foo; end | ||
# @version <= 0.4.0-prerelease | ||
class Bar; end | ||
# @version >= 0.4.0-prerelease | ||
class Buzz; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_and | ||
rbi = <<~RBI | ||
# @version > 0.3.0, < 1.0.0 | ||
class Foo; end | ||
# @version >= 1.1.0, < 2.0.0 | ||
class Bar; end | ||
# @version > 0.3.2, <= 0.4.2 | ||
class Baz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version > 0.3.0, < 1.0.0 | ||
class Foo; end | ||
# @version > 0.3.2, <= 0.4.2 | ||
class Baz; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_or | ||
rbi = <<~RBI | ||
# @version < 0.3.0 | ||
# @version > 1.0.0 | ||
class Foo; end | ||
# @version = 0.4.0 | ||
# @version > 0.5.0 | ||
class Bar; end | ||
# @version < 0.3.2 | ||
# @version = 0.4.0-prerelease | ||
# @version > 0.4.0 | ||
class Baz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version = 0.4.0 | ||
# @version > 0.5.0 | ||
class Bar; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_andor | ||
rbi = <<~RBI | ||
# @version > 0.3.0, < 1.0.0 | ||
# @version > 1.5.0 | ||
class Foo; end | ||
# @version >= 0.1.0, < 0.2.3 | ||
# @version = 0.4.0 | ||
class Bar; end | ||
# @version > 0.2.5, < 0.2.7 | ||
# @version > 0.4.0, < 0.5.0 | ||
class Baz; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
assert_equal(<<~RBI, tree.string) | ||
# @version > 0.3.0, < 1.0.0 | ||
# @version > 1.5.0 | ||
class Foo; end | ||
# @version >= 0.1.0, < 0.2.3 | ||
# @version = 0.4.0 | ||
class Bar; end | ||
RBI | ||
end | ||
|
||
def test_filter_versions_parse_errors | ||
rbi = <<~RBI | ||
# @version > | ||
class Foo; end | ||
RBI | ||
|
||
tree = Parser.parse_string(rbi) | ||
|
||
assert_raises(Gem::Requirement::BadRequirementError) do | ||
Rewriters::FilterVersions.filter(tree, Gem::Version.new("0.4.0")) | ||
end | ||
end | ||
end | ||
end |