SHARE:
Uncategorized

Monkeypatching Ruby Core

Flatiron School / 25 July 2013

The following is a guest post by George Lin and originally appeared on his blog. George is currently a student at The Flatiron School. You can follow him on Twitter here.

A general disclaimer: I just learned Ruby ~3 months ago, so forgive me (and let me know!) if any the following is not best practice or a great idea or whatnot

As I’ve been learning and becoming familiar with Ruby and its various libraries / gems, one thing I’ve found myself doing often is opening up irb or pry and playing around all different kinds of methods, classes, and modules. However, I’ve found that it’s hard to explore this consistently without having to constantly refer back to the documentation for what methods exist for each class / module or what the structure of the classes or modules are. I just want to play around and avoid constantly going back online or to the source code! The solution: I ended up modifying the core ~/.irbrc and ~/.pryrc files to monkeypatch Ruby’s core classes, so that everytime I start up irbpry, or rails console I get access to my custom methods. Here’s what my file looks like:

Now, I can much more easily explore methods, classes, modules and other Ruby constructs:

Exploring Methods

  • any method which ends with the word "methods", i.e. returns an array of the defined methods for an object, whether it is all methods, instance methods, private methods, or whatever else, can now be prefixed with "local_" to return a much shorter array of just methods which aren’t already defined for all Ruby Objects (though you can modify this default to return the difference with any class).
    • Ruby does have a built-in way to retrieve only methods defined in the immediate scope, not any mixed-in modules or superclasses, by setting the argument in xxx_methods(arg) so that arg = false. However, having a local_xxx_methods is more customizable, and may be more telling in many cases, such as where you are totally unfamiliar with a class or module and want to see not just how it is different from its immediate ancestors, but overall, i.e. to see the methods it inherited from everything but the Object class.

Exploring Namespaces

  • When I call ancestors on a module, the list of results returned is a mix of the modules mixed in and the superclasses. Breaking these into two methods, module_ancestors and class_ancestors, helps me to better understand the inheritance chain of a given module or class. For example, it’s much clearer now that the class inheritance chain for Fixnum is [Fixnum, Integer, Numeric, Object, BasicObject], while the modules [Comparable, Kernel]are only mixed in.

  • For a given namespace, such as ActiveRecord::Base, I was curious to see what the various modules and classes that exist under that namespace are, as there is (1) no easy way to see all of these without consulting the source code, which is massive for something like ActiveRecord, and not trivial to find and explore, and (2) no easy way to tell the difference between modules, classes, and other constants under that namespace.

    • I initially consulted a StackOverflow post that explains how to do this, and wrote up the methods subconstruct and submodule. Unfortunately, these only look for subconstructs within subconstructs and submodules within submodules…
    • There are many cases where in the nested namespaces, you could have a construct in a module in a construct in a module, or anything else. Although more complicated, I eventually came up with a way to do this using a “subthing” helper method, which is a ~20 line monstrosity I’m not happy with. Regardless, the methods subconstruct_classes and subconstruct_methods will look into all nested namespaces, no matter how deep, and retrieve all classes or modules.
  • Modifying the above code slightly allows for retrieval of non-module, non-class constants for a given module – for example, finding that the only constants that exist for the Math module are :PI and :E using subconstants and subconstant_names

Exploring Class Inheritance

  • Finally, for classes, there is an easy built-in way to look up the inheritance chain and find the parent of a class using the superclass method. Unfortunately the opposite isn’t true – there’s no built-in way to find all children of a parent class. Fortunately Ruby has module called ObjectSpacewhich allows for traversal of all living objects in memory, allowing for a simple child_classes (& alias children) method to be defined. Note that this is NOT the same thing as the subclassesmethod I mentioned earlier – the latter only cares about namespaces, i.e. BaseModule::AnotherModule::SubClass, while child_classes is concerned with class inheritance, i.e. ChildClass < ParentClass.

Not finding giants to stand on the shoulders of, aka It’s been done before

Of course, it was only after I had written all of these that I found there’s much more experienced coders who have done a lot of this already! Here are some (probably better) ways to accomplish what I monkeypatched:

  • There is an entire gem which consists of libraries which extend the core capabilities of Ruby’s built-in constructs: Facets. A lot of the methods from Facets do the above, probably in a better way. Updates to Facets have been infrequent in the last year, but nevertheless it seems to have tons of potentially useful additions to core Ruby.

  • There are in gems which make it much easier to print readable, colorful Ruby – for example, the “y” method in YAML, “pp” for prettyprint, “pretty_generate” in JSON. Among these is a great little gem called Awesome Print, which not only formats and colorizes the output, but also adds additional helpful info, such as the superclass of the printed object, the index of each element in an array, and vertically aligning the hash rocket in a hash so that the keys and values are easier to read. To always include awesome print, I added this to my .pryrc as per instructions:

Now, this solves the problem of being able to tell the difference between class and module ancestors. For example, with ap enabled, CodeRay::WordList::CaseIgnoring.ancestors returns the following:

This is clearly much easier to read and tells you not only which ancestors are classes, but what each ancestor’s superclass is.

Regardless, by doing these monkeypatches, I’ve found it much easier to navigate around all kinds of objects and methods in Ruby to learn more about it, without necessarily having to go back to the source code – for example, find a specific class within ActiveRecord::Base and then find all the local_methods for that class. Even if there is still a lot of code I could change / clean up, this was a great learning experience to understand Ruby more deeply. As they say, it’s about the journey, not just the destination.

Thanks to the following posts on StackOverflow for guidance:

  1. http://www.natontesting.com/2010/06/30/how-to-get-the-submodules-of-a-ruby-module/
  2. http://stackoverflow.com/questions/2393697/look-up-all-descendants-of-a-class-in-ruby
Revealing Mass Assignment Previous Post Rails Console: Useful Tricks Next Post