-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support inline RBI versioning #180
Conversation
return true unless is_a?(NodeWithComments) | ||
|
||
requirements = version_requirements | ||
requirements.empty? || requirements.any? { |req| req.satisfied_by?(version) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: any node with no requirements is treated as satisfying the requirements.
|
||
def test_filter_versions_and | ||
rbi = <<~RBI | ||
# @version > 0.3.0, < 1.0.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this implementation supports and/or logic. The line above is an example of "and" -- the version must be greater than 0.3.0 AND less than 1.0.0. Line 218 is an example of "or" logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it's not quite as flexible, I wonder if only permitting the same syntax as a .gemspec
would be feasible, so there's not a new thing to learn.
The downside is that in some situations it could be very verbose, e.g. instead of:
# @version < 0.3.0
# @version > 1.0.0
You would need to explicitly exclude versions, e.g.:
!= 0.4.0, != 0.5.0, != 0.6.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean the syntax for adding for adding a dependency, e.g. https://guides.rubygems.org/specification-reference/#add_runtime_dependency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this comment! As far as I know, the syntax I'm using here is pretty much identical to the gemspec syntax, at least per the semantic versioning guide on RubyGems. Can you let me know if there's a particular difference you've spotted?
03c4000
to
aad3598
Compare
aad3598
to
f3d809b
Compare
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]>
f3d809b
to
03da59f
Compare
This is a proof of concept for RBI versioning, which @Morriar, @bitwise-aiden, and I had been working on for fun a while back.
Problem
This proof of concept aims to solve the problem of versioning RBIs. Up until now, there is no default way to write RBI for different versions of the same gem. This can lead to confusing issues for Sorbet users where RBIs don't match the version of a gem they're using, and they have to spend time figuring out whether their gem is out of date, whether the RBI is wrong, or if it's even both! Having a way to specify RBI for multiple versions of the same gem would allow people to adopt and use Sorbet without having to go through this hassle.
Approach
To implement this behavior, we decided to create a system of versioning RBI within the same file using
@version
annotations within comments. This is an example of using such an annotation:In this example, the class
Foo
has a methodbar
that takes one argument in versions 0.3.0 and earlier, but that argument is removed in later versions.There are a few benefits to this approach:
There are also some drawbacks, namely that RBI files could potentially become cluttered if many gem versions are supported. We plan to address this using some automated linting (see "Future Work" section), and in practice we don't believe that most gem authors actually support that many versions at a time. Another potential drawback is that parsing out irrelevant parts of the RBI can slow down parsing, but in practice, we see that most RBI files are relatively small and that speed is not currently a big issue; however, this can be revisited if performance becomes a bigger priority.
For more information about the other approaches we considered, see this document.
Implementation
This change was implemented by creating a new rewriter called
FilterVersions
. This takes the current RBI tree as well as a gem version to filter on. For each node in the RBI, the rewriter determines if the node satisfies the gem version by examining any@version
annotations in the comments and relying on logic from Ruby'sGem::Requirement
class to perform comparisons.RBIs with no versions remain unaffected.
Future Work
Documentation
If the team is supportive of this approach, I'll write up documentation for this behavior before merging the PR.
Linting
While we believe the inline versioning approach makes the most sense for RBI, it still leaves the possibility of developers writing disorganized RBIs (e.g. different versions of the same method are far away from each other) or invalid RBI files (e.g. writing versions that are somehow incompatible with each other). To address these issues, we would implement some sort of linting that prevents developers from falling into these mistakes so that this feature is as easy to use as possible.