Velocity Labs

Pessimistic Prerelease Peculiarities

The assumption

There seems to be an assumption that the pessimistic requirement operator (aka ~>, aka the twiddle-wakka) is short hand for a pairing of >= and <.

e.g: in a Gemfile we see ~> 1.1 as equivalent to: '>= 1.1' '< 2.0'.

That seems reasonable, but be warned, it breaks down when prerelease gems are involved.

The code

Firstly, here’s a method to check a gem’s version against some requirements:

1
2
3
def check(version, *requirements)
  requirements.map { |v| Gem::Requirement.new(v).satisfied_by? Gem::Version.new(version) }.all?
end

We can use this to check if ~> 1.2 does behave indeed the same as '>= 1.1' '< 2.0'. Let’s check both a good version (which meets the requirements) and a bad version (which does not):

> good_version = '1.8'
> [check(good_version, '~> 1.1'), check(good_version, '>= 1.1', '< 2.0')
 => [true, true]
> bad_version = '2.0'
> [check(bad_version, '~> 1.1'), check(bad_version, '>= 1.1', '< 2.0')]
 => [false, false]

So far, so good.

The peculiarity with prerelease

However, this equivalence doesn’t hold when the version being checked is a prerelease:

> pre_version = '2.0.pre'
> [check(pre_version, '~> 1.1'), check(pre_version, '>= 1.1', '< 2.0')]
 => [false, true]

We can see that the pessimistic operator doesn’t think this prerelease gem meets the requirement, but our supposedly ‘equivalent’ version does. What gives?

It happens because a less than 2.0 requirement is true if the version is a prerelease of 2.0:

> check '2.0.pre', '< 2.0'
 => true

However a pessimistic 1.1 requirement is false if the version is a prerelease of 2.0:

> check '2.0.pre', '~> 1.1'
 => false

Making sense of it

Rephrasing those two versions as questions, it’s clear that both these answers make sense:

  • Should a prerelease of N be considered a lower version N? Yes.
  • Should a prerelease of N+1 be pessimistically compatible with version N? No.

It is in fact our initial assumption that ~> 1.1 is equivalent to: '>= 1.1', '< 2.0' that is incorrect.

So it seems in the absence of anyway to express “less than version 2.0 and any prerelease thereof” that the pessimistic operator is more than just a convenient short hand, it’s the only correct way to specify a requirement pessimistically.

Need help with your app?

We build custom software, rescue apps that are stuck, and keep them running. Tell us what you're dealing with.