A general disclaimer: I just learned Ruby ~3 months ago, so please let me know if any of this can be improved upon
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 every time I start up irb
, pry
, or rails console
I get access to my custom methods. Here’s a link to what my .pryrc file looks like:
https://gist.github.com/gglin/5930277
If you want to see an example of all this in action, go to the end of this post
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 thatarg = false
. However, having alocal_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.
- 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
See code here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
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
andclass_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 forFixnum
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
subclasses
andsubmodules
. Unfortunately, these only look for subclasses within subclasses and submodules within submodules… - There are many cases where in the nested namespaces, you could have a class in a module in a class 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
andsubconstruct_methods
will look into all nested namespaces, no matter how deep, and retrieve all classes or modules.
- I initially consulted a StackOverflow post that explains how to do this, and wrote up the methods
- 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
&:E
, and that their values are 3.14159… & 2.71828… usingsubconstants
andsubconstant_names
See code here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
|
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 calledObjectSpace
which allows for traversal of all living objects in memory, allowing for a simplechild_classes
(& aliaschildren
) method to be defined. Note that this is NOT the same thing as thesubclasses
method I mentioned earlier – the latter only cares about namespaces, i.e.BaseModule::AnotherModule::SubClass
, whilechild_classes
is concerned with class inheritance, i.e.ChildClass < ParentClass
.
See code here:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
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:
1 2 |
|
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.
Example of Usage
As an example of how I can use the code I wrote, I started pry and typed in:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Now if I want to know the local class methods available for CodeRay::WordList
and local instance methods available for CodeRay::TokensProxy
, I can type:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Some other fun things to try out:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
|
Thanks to the following posts on StackOverflow for guidance: