As part of our adoption process of Sorbet at Shopify, we needed an automated way to teach Sorbet about our ~400 gem dependencies. We decided to tackle this problem by generating interface files (RBI) for each gem via runtime reflection.
However, this turns out to not be as simple as it sounds; the flexible nature of Ruby allows gem authors to do many wild things that make this Hard. Come and hear about all the lessons that we learnt about runtime reflection in Ruby while building tapioca
.
Module#const_get
, Module#constants
, Module#instance_methods
and friendsMethod#source_location
const_source_location
)Module#name
, Kernel#class
, etc.bind(obj).call
idiom (Ruby 2.7 adds bind_call
)define_method(“123”) {}
)Forwardable
)Ruby has one of the nicest reflection APIs across all languages, but the dynamic nature of the Ruby language allows gem authors to do wild things. This makes proper reflection and, in turn, interface generation very hard.
This talk will present an overview of the Ruby reflection API and talk about what makes it so hard to do reflection correctly in the wild for arbitrary types exported from gems. For every challenge, solutions will be presented and discussed. The talk will also mention how Ruby is improving its APIs to make doing this kind of work easier, with the addition of const_source_location
and bind_call
in 2.7, for example.
The speaker is the primary author of the open-source tapioca
gem at Shopify and has had to deal with all this complexity to get everything working both correctly and with reasonable coverage.