Ruby 3: RSpec table syntax broken for Integer tables
Problem
While working towards a working CI build with Ruby 3, I ran into an issue where RSpec tests that use rspec-parameterized
table syntax were breaking whenever they consisted of Integer operands, e.g.:
where(:index, :percentage, :is_enabled) do
50 | 40 | false
40 | 50 | true
nil | 50 | false
end
This is currently failing because the |
-operator refinement added by this gem is not going into effect, thus leading to Ruby interpreting the pipe verbatim i.e. as the bit-wise OR. This then leads to the block returning integer values instead of Table
instances.
Example test failure: https://gitlab.com/gitlab-org/gitlab/-/jobs/1470020798
Analysis
I have already spent 1day+ on trying to get to the bottom of this. I have a strong suspicion that the Ruby method cache is polluted somehow, since adding this snippet to the top of the spec file will turn the test green:
module M
refine Integer do
def |
# nop
end
end
end
I suspect what's happening is that this (unused) refinement triggers a rebuild of the method cache, thus leading to the RSpec refinement being correctly picked up. But I am not sure.
I am fairly certain it is not other Integer refinements shadowing this one since none that I found also redefine |
:
git@24d3a948733e:~/gitlab$ grep -r 'refine Integer' /data/cache/bundle-2.7.2/ruby/3.0.0/gems/**/*
/data/cache/bundle-2.7.2/ruby/3.0.0/gems/rexml-3.2.5/lib/rexml/xpath_parser.rb: refine Integer do
/data/cache/bundle-2.7.2/ruby/3.0.0/gems/rspec-parameterized-0.4.2/lib/rspec/parameterized/table_syntax.rb: refine Integer do
/data/cache/bundle-2.7.2/ruby/3.0.0/gems/rspec-parameterized-0.5.0/lib/rspec/parameterized/table_syntax.rb: refine Integer do
I was also able to fix this issue by running the test with a reduced dependency footprint e.g. by not using spec_helper
and/or spring
, since those will pull in the entire application dependency stack. When comparing Integer.ancestors
between the minimal dependency stack where the test passes and the full stack, I get this diff in code added to the Integer
class:
[Net::BER::Extensions::Integer,
RQRCode::CoreExtensions::Integer::Bitwise,
MessagePack::CoreExt,
ActiveSupport::ForkTracker::CoreExtPrivate,
ActiveSupport::ForkTracker::CoreExt,
Pry::Shell::Patches::Object,
GettextI18nRails::HtmlSafeTranslations,
FastGettext::Translation,
ERB::Util,
ActiveSupport::ForkTracker::CoreExtPrivate,
ActiveSupport::ForkTracker::CoreExt]
So another possibility is that one of these extensions collides with the table-syntax refinement. Or maybe it is merely the structure of the ancestor chain that triggers it, which I think is more likely than it being any of these particular extensions. (I already looked at most of them, and I have found no evidence as to why they would shadow |
.)
Related links
- Ruby 3 build MR: !50640 (closed)
- https://bugs.ruby-lang.org/issues/17806