ActiveSupport: Self Deprecation and Deprecations
As I write today’s journal, I am struck with how I’m not the ideal tour guide of the Rails source. Having never contributed to the code and coming at it with fresh eyes, I’m bound to get things wrong in my notes here. Hopefully when I do, you fabulous readers out there can provide some corrections in the comments section which I can fold back into the body of the post.
Ok, back to the source!
I know you have seen the output of this code before…you’re booting your app when all of the sudden you see something like:
This helpful (though generally frustrating) warning message is emitted by some pretty cool code, code that uses a technique we haven’t seen yet in this series. Let’s take a look.
To simplify to the part I’m interested in (though the
$stderr global variable is interesting too):
I believe this is a use of the Strategy Pattern for swapping out the behaviour of the deprecation warnings at runtime, though I’m certainly not an expert at Patterns. It is implemented via a constant hash where the values are Procs.
As a neat bonus, the assignment operator is pretty clever, enabling single strategies, multiple strategies, a custom handler, or an inline Proc:
So you can call it in any of the following ways:
Very cool, this is super extensible and adaptable to whatever kinds of behaviors a developer might want.
I should take a brief aside to talk about Procs since they may or may not be used a lot in the code you’ve come across (but will be everywhere in the Rails source).
You know how we like to deal with objects, bundling up data and code into a package that represents a conceptual unit of some kind? Well, a
Lambda gives us the ability to make an object out of some code which we can then pass around to other methods. This is why Ruby is described as having “first-class functions” (meaning it doesn’t treat them as second-class citizens, but they can be arguments to other functions). Those functions which take functions for arguments are then called “higher order functions”, which Ruby has a metric ton of (think
inject, and on and on…). For a thorough overview of the ways to pass around code in Ruby, check out this article on Skorks.
Bottom line, they are awesome and come in handy.
Alias Method Chaining
Another thing I found interesting in my study of the Deprecation subsystem was the convenient tools provided for deprecating methods. It gives a chance to look into some metaprogramming, which is always fun.
Let’s say we were the designers of a module called Fred which we declared as follows (example from source comments):
Pretty awesome module right?
As time went on, we realized that we wanted to get users of our module to move away from
Fred#foo. With active_support, we could just:
Now when someone tries to use our method, they will be encouraged to make the switch:
But, how does it do that? It turns out it uses a very common pattern in Ruby metaprogramming:
- alias the original method to another name
- insert a method into the module that wraps…
- call whatever new logic you want
- call the original method, now aliased
Here’s the code that does it:
We see our familiar friend
extract_options! doing it’s job, and then a default for the deprecator that is going to do the work. The real bulk of the work is in the enumeration through the method_names. Matching up the logic there with my description of the pattern above we see:
alias_method_chaincreates both aliases for us, it’s found in core_ext/module/aliasing.rb
.send(:define_method, ...with_deprecation)makes the wrapper method
deprecation_warningactually generates the event for whatever behavior handler picks it up
.send(:...without_deprecation)then calls the original method
We will see this quite a bit I’m sure, as it’s a really handy way to put a wrapper around a method.
I may not have mentioned this, but all of this series is on the Rails 4 source. I’m sure we will come across areas where it will behave differently than your installed Rails. In that case, use the Rails-Dev-Box that we setup in Part 1. I have been using it a lot thoughout this exploration.
Voila, Rails 4 source console all loaded and ready for experimentation.
Have fun reading the source!