Ruby and Sorbet Module Loading Order

Corey Alexander - Jul 30 '23 - - Dev Community

Today I learned that Sorbet interfaces do some magic to override how Ruby module and method overloading usually works. This is great, lets take a look!

We had a base class lets call it SomeBaseClass and it implemented a method like api_options. So we had the following and OurClass#api_options was 'correctly' using the implementation from SomeBaseClass

class OurClass < SomeBaseClass
  include ApiInterface
end
Enter fullscreen mode Exit fullscreen mode

But what was interesting is ApiInterface was defined to need aapi_options method, which looks like this

sig { abstract.returns(ApiOptions) }
def api_options; end
Enter fullscreen mode Exit fullscreen mode

Without all the Sorbet stuff in the sig that's an empty method definition which would just return nil everytime. Without Sorbet we'd expect OurClass#api_options to return nil since this version would be the last one defined.

However, Sorbet is doing the 'right' thing and calling the actual implementation from our base class! It kind of has to, or else it's interfaces would be hard to work with. But its good to confirm, and write down for next time I run into this!

I dug a bit and found some code to replace a method and some to call the original implementation, so I assume some combination of these is how Sorbet pulls off this trick. Maybe I'll dig deeper and turn this into a full post in the future.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .