Monkeypatching Ruby Core
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Ā irb,Ā pry, 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:
http://www.natontesting.com/2010/06/30/how-to-get-the-submodules-of-a-ruby-module/
http://stackoverflow.com/questions/2393697/look-up-all-descendants-of-a-class-in-ruby










