Image for post
Image for post
Source: unsplash.com

Conditional class opening in Ruby

Why the resolv-replace library monkey-patches the SOCKSSocket class under specific conditions.

Mehdi Farsi
Sep 13, 2020 · 3 min read

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 with SOCKSSocket

I’m thrilled to share with you our latest project: Fun Facts about Ruby — Volume 1

Please feel free to spread the word and share this link! 🙏

Thank you for your time!

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 your configure 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 a 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 external library and the optional features provided by the Ruby interpreter, you need to ensure that the class is defined before any modification.

Voilà!

RubyCademy

E-Learning platform for Ruby and Ruby on Rails

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store