Conditional class opening in Ruby
Why the resolv-replace
library monkey-patches the SOCKSSocket class under specific conditions.
In this article, we’re going to explore the following topics:
- How to enable
SOCKSSocket
at Ruby interpreter compilation - how the
resolv-replace
library check if Ruby has been compiled withSOCKSSocket
SOCKS Sockets
SOCKS is an Internet protocol that exchanges network packets between a client and server through a proxy server.
Ruby provides an interface to the SOCKS protocol through SOCKSSocket
class. This class directly inherits from TCPSocket
. SOCKSSocket
is an optional feature of Ruby
irb> require "socket"
=> true
irb> defined? SOCKSSocket
=> nil
Here we can see that SOCKSSocket is not defined within Ruby 2.7.1 by default.
To add this feature to your Ruby interpreter, you must recompile Ruby with the --enable-socks
flag
?> ./configure --enable-socks
...
?> make && make install
..
?> irb
2.7.1 :001> require "socket"
=> true
2.7.1 :002> defined? SOCKSSocket
=> "constant"
In the above example, we recompile our Ruby interpreter by adding --enable-socks
. This flag provides the SOCKS
C Macro that is used to enable the loading of the SOCKSSocket
class at runtime.
Note that if
SOCKSSocket
is not loaded you must probably add this option to yourconfigure
file:enable_option_checking=no
before to restart the compilation process.
Now that we’re more familiar with the fact that the default compiled Ruby interpreter doesn’t load all the classes provided by Ruby and that you must recompile your Ruby interpreter to explicitly add these classes, let’s see how the resolv-replace
library — available in the Ruby Standard Library — handles the case of monkey-patching (or not) the SOCKSSocket
class.
resolv-replace and SOCKSSocket
This library simply monkey-patches (IP|TCP|UDP|SOCKS)Socket
classes provided by the socket
library to use the Resolv
DNS resolver.
In the case of SOCKSSocket
, the monkey-patching only intervenes when the Ruby interpreter is compiled with the --enable-socks
flag. So let’s see how the resolv-replace
library handles this tricky case
Notice the modifier-if
line 10. Indeed, to choose to monkey-patch this class or not, the resolv-replace
library simply checks if the SOCKSSocket
constant is defined using the defined?
keyword. Indeed, as seen in the previous section, SOCKSSocket
is only defined if the Ruby interpreter is compiled using the --enable-flag
.
So, as the class
keyword is only syntactic sugar for class definition and class opening, then you can call a modifier-if to execute this code (or not).
Conclusion
Opening a class only if this class is already defined isn’t a common pattern. But when you deal with an external library and the optional features provided by the Ruby interpreter, you need to ensure that the class is defined before any modification.
Ruby Mastery
We’re currently finalizing our first online course: Ruby Mastery.
Join the list for an exclusive release alert! 🔔
Also, you can follow us on x.com as we’re very active on this platform. Indeed, we post elaborate code examples every day.
💚