You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is request to allow bundler to be able to support optional dependencies with an opinionated syntax and behavior.
Use case
A creator of a gem might want to declare that their package has some additional functionality if the client wants to include an additional dependency. For example, a string formatter gem might want to optionally add string coloring functionality if the client is okay with including the rainbow gem.
What we'd like is for a gem to include sorbet-runtime (method type annotations for sorbet). However, we do not want to burden the client with this gem if they do not want to install it. Instead, we'd like to fall back to a separate gem, which is a shim of this runtime.
Desired syntax
This is the desired syntax for this feature. Very open to other options here.
Take no action of the primary dependency is unavailable
Case 1: The user does not include the optional dependency
Behavior: If the gem uses else_add, it will include the given argument (sorbet-runtime-stub above) and reconcile the fall back against the client's other dependencies, and include it in Gemfile.lock of the client (i.e. the fallback is required and behaves as if its any other required gem).
Case 2: The user does include the optional dependency
Behavior: The fallback is ignored, and the optional dependency is required, and the versions are reconciled against the client's version, and the standard reconciliation process applies (i.e. bundler errors if there are irreconcilable dependencies).
Optional additions
Allow the client of the gem to specify the optionality inline.
This would allow the client to explicitly tell the gem to use that dependency. This is essentially syntactical sugar over:
gem'sorbet'gem'my_special_gem'
It makes it explicit that the dependency is specifically to satisfy the optionality of the sorbet dependency.
If the client uses include_optional_dependencies, it uses the version specified by the gem. If the client wants to specify the version of sorbet, they use the second approach (i.e. simply declare the two separate gems with the desired version numbers).
The client's Gemfile.lock will not reflect that the optional dependency is actually a dependency of the gem.
Bundler will not reconcile versions at the time the user executes bundle install. Instead errors come only during runtime, which conflicts with the overarching philosophy of bundler of being able to reconcile dependencies statically in a separate process (i.e. before runtime).
This relies on possibly unstable API (specific errors raised by the gem command and the specific messages in those errors).
Depending on the load order, it might be possible that the fallback overrides the optional dependency in an unexpected way.
In general, we are moving dependency management away from bundler and into the client. This can potentially have a lot of undesirable downstream effects. One simple example is if you are constructing a dependency tree of your application, it would not reflect these optional dependencies using this workaround.
The text was updated successfully, but these errors were encountered:
I just nailed by this from Dependabot suggesting sqlite < 3 but my rails version requires ~> 1.4.
I can run the tests using pg, but once I use sqlite3 it blows up.
Declaring the optional dependencies
As a starter just declaring the version number of both would be great:
This can be posted on rubygems and provide documentation if nothing else.
Reference optional dependencies
I do like the ability to declare that an optional dependency is used:
gem'my_special_gem',include_optional_dependencies: 'sorbet-runtime'gem'activerecord',include_optional_dependencies: 'sqlite'# Shorter key (name for the group to decide)gem'my_special_gem',uses: 'sorbet-runtime'gem'activerecord',uses: 'sqlite'
Since this is opt-in behavior, there is no behavior change without the developer actively seeking it. So no surprises.
It is similar to just including the sqlite gem, but you are deferring to activerecord to set the guidelines that works for them.
I feel the fallback dependency makes this a bit complicated.
For activerecord, it declares optional dependencies as pg, sqlite, and mysql.
If there were a fallback, the developer may be inclided to fallback to sqlite, since activerecord is only useful with a database. But then when someone wants to use oracle, which is not on the gem developer's radar, there is that outstanding sqlite dependency.
I appreciate what we gain by having the fallback, but this adds a layer of complexity that may be better handled in a v2.
Summary
This is request to allow
bundler
to be able to support optional dependencies with an opinionated syntax and behavior.Use case
A creator of a gem might want to declare that their package has some additional functionality if the client wants to include an additional dependency. For example, a string formatter gem might want to optionally add string coloring functionality if the client is okay with including the
rainbow
gem.Another use case is
sorbet
, a type-checker for ruby. More info on my specific use case here.What we'd like is for a gem to include
sorbet-runtime
(method type annotations for sorbet). However, we do not want to burden the client with this gem if they do not want to install it. Instead, we'd like to fall back to a separate gem, which is a shim of this runtime.Desired syntax
This is the desired syntax for this feature. Very open to other options here.
Take no action of the primary dependency is unavailable
Sub in a separate dependency if the primary dependency is unavailable
Desired behavior
Case 1: The user does not include the optional dependency
Behavior: If the gem uses
else_add
, it will include the given argument (sorbet-runtime-stub
above) and reconcile the fall back against the client's other dependencies, and include it inGemfile.lock
of the client (i.e. the fallback is required and behaves as if its any other required gem).Case 2: The user does include the optional dependency
Behavior: The fallback is ignored, and the optional dependency is required, and the versions are reconciled against the client's version, and the standard reconciliation process applies (i.e.
bundler
errors if there are irreconcilable dependencies).Optional additions
Allow the client of the gem to specify the optionality inline.
For example, the client might have:
This would allow the client to explicitly tell the gem to use that dependency. This is essentially syntactical sugar over:
It makes it explicit that the dependency is specifically to satisfy the optionality of the
sorbet
dependency.If the client uses
include_optional_dependencies
, it uses the version specified by the gem. If the client wants to specify the version ofsorbet
, they use the second approach (i.e. simply declare the two separate gems with the desired version numbers).Allow the gem to declare a fallback require
This would allow us to explicitly handle the case where the client did not include the optional dependency.
Other options
This behavior is today able to be emulated, but the emulation is fraught with concerns.
Here is my best attempt at emulating this behavior.
There are many issues with this:
Gemfile.lock
will not reflect that the optional dependency is actually a dependency of the gem.bundle install
. Instead errors come only during runtime, which conflicts with the overarching philosophy of bundler of being able to reconcile dependencies statically in a separate process (i.e. before runtime).gem
command and the specific messages in those errors).bundler
and into the client. This can potentially have a lot of undesirable downstream effects. One simple example is if you are constructing a dependency tree of your application, it would not reflect these optional dependencies using this workaround.The text was updated successfully, but these errors were encountered: