Make your stacktraces wack! Insert arbitrary content into the call stack if that is, for some reason, something you want to do. Who am I to judge?
# in example.rb:
Wacktrace.add_to_stack([
['Fire in the disco!', 1, 'š„šŗ '],
['Fire in the Taco Bell!', 2, 'š„š® '],
['Fire in the disco!', 3, 'š„šŗ '],
['Fire in the gates of hell!', 4, 'š„š '],
]) do
raise "High Voltage!"
end
$ ruby example.rb
example.rb:25:in `block in <main>': High Voltage! (RuntimeError)
from /Users/kevin/.rvm/gems/ruby-3.0.0/gems/wacktrace-0.1.0/lib/wacktrace.rb:49:in `block in add_to_stack'
from š„šŗ :1:in `Ā FireĀ inĀ theĀ discoļøĀ '
from š„š® :2:in `Ā FireĀ inĀ theĀ TacoĀ Bellļø'
from š„šŗ :3:in `Ā FireĀ inĀ theĀ discoļø'
from š„š :4:in `Ā FireĀ inĀ theĀ gatesĀ ofĀ hellļø'
from /Users/kevin/.rvm/gems/ruby-3.0.0/gems/wacktrace-0.1.0/lib/wacktrace.rb:50:in `add_to_stack'
from example.rb:19:in `<main>'
Amaze your friends! Annoy your coworkers! Ok, it's mostly that second one! But maybe someone has a good use for this, like inserting warnings into stack traces:
Traceback (most recent call last):
14: from example.rb:47:in `<main>'
13: from example.rb:35:in `dangerous_method'
12: from /Users/kevin/.rvm/gems/ruby-2.7.2/gems/wacktrace-0.1.0/lib/wacktrace.rb:60:in `add_to_stack_from_lyrics'
11: from /Users/kevin/.rvm/gems/ruby-2.7.2/gems/wacktrace-0.1.0/lib/wacktrace.rb:50:in `add_to_stack'
10: from !:in `Ā '
9: from !:1:in `Ā Ā āāāāāāāāāāāāāāāāāĀ WARNINGļøĀ āāāāāāāāāāāāāāāāāāāāāā'
8: from !:2:in `Ā ļ½Ā IfĀ youĀ hitĀ anĀ errorĀ hereļ¼Ā youĀ ļ¼reallyļ¼Ā needĀ ļ½'
7: from !:3:in `Ā ļ½Ā toĀ goĀ overĀ toĀ theĀ FooBarBazĀ adminĀ panelĀ andĀ Ā Ā Ā ļ½'
6: from !:4:in `Ā ļ½Ā cleanĀ upĀ theĀ SpleemĀ thatĀ wereĀ erroniouslyĀ Ā Ā Ā Ā Ā ļ½'
5: from !:5:in `Ā ļ½Ā createdā¤Ā Ā IfĀ youĀ donātĀ doĀ thatļ¼Ā badĀ thingsĀ Ā Ā Ā ļ½'
4: from !:6:in `Ā ļ½Ā willĀ happenļøĀ Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā ļ½'
3: from !:7:in `Ā Ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'
2: from /Users/kevin/.rvm/gems/ruby-2.7.2/gems/wacktrace-0.1.0/lib/wacktrace.rb:49:in `block in add_to_stack'
1: from example.rb:44:in `block in dangerous_method'
example.rb:30:in `real_dangerous_method': terrible error (RuntimeError)
Sketchily! It's a set of wack stack hacks. The basic idea is that we just set up a series of methods like:
def a; b; end
def b; c; end
def c; actual_function; end
But to dynamically generate methods, you'd usually reach for define_method
. Unfortunately, methods defined with define_method
don't actually show up in call stacks because that'd be too easy. Instead we'll use eval("def #{method_name};#{next_method_name};end")
. And, as a bonus, eval
lets us pass in a filename and line number for stack trace purposes!
But to avoid having a hundred methods in the global namespace like baby_shark_do_do_do_do_do_do_do
, we'll Class.new
-up a temporary class and then instance_eval
on that.
But now we're stuck with underscore-laden method names. We're not gonna stand for that kinda limitation. We're gonna reach for the sun and slap the face of god. We'll take arbitrary text and replace all the invalid characters like "
and @
with unicode near-equivalents like ļ¼
and ļ¼
. Because def Cthulhu R'lyeh wgah'nagl fhtagn
isn't a valid ruby method, but the visually-identical def Cthulhu Rālyeh wgahānagl fhtagn
is!
Other fun tricks:
- Detect which direction ruby is printing stack traces today (it's newest-first on a gibbous or waxing moon, and newest-last the rest of the month)
- Deal with duplicate method names by padding them with non-breaking-spaces
- Prepend all methods with non-breaking spaces so ruby doesn't get confused by methods that start with capital letters
Add this line to your application's Gemfile:
gem 'wacktrace'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install wacktrace
To have full(ish) control over the stack trace contents, call add_to_stack
and give it a block. That block will be run with the lines you provide in its call stack:
Wacktrace.add_to_stack([
['method name 1', 123, 'file name 1'],
['method name 2', 123, 'file name 2'],
['method name 3', 123, 'file name 3'],
]) do
puts caller_locations
# Prints out:
# file name 1:123:in `Ā methodĀ nameĀ 1'
# file name 2:456:in `Ā methodĀ nameĀ 2'
# file name 3:789:in `Ā methodĀ nameĀ 3'
end
If you want a quicker way to shove a bunch of text (like a warning, comment, or classic poem on the fleeting nature of mankind's greatest achievements), you can use add_to_stack_from_lyrics
with a single newline-delimited string:
lyrics = "I met a traveller from an antique land,
Who saidāāTwo vast and trunkless legs of stone
Stand in the desert... Near them, on the sand,
Half sunk a shattered visage lies, whose frown,
And wrinkled lip, and sneer of cold command,
Tell that its sculptor well those passions read
Which yet survive, stamped on these lifeless things,
The hand that mocked them, and the heart that fed;
And on the pedestal!, these words appear:
My name is Ozymandias, King of Kings;
Look on my Works, ye Mighty, and despair!
Nothing beside remains. Round the decay
Of that colossal Wreck, boundless and bare
The lone and level sands stretch far away.ā"
Wacktrace.add_to_stack_from_lyrics(lyrics, 'Percy Bysshe Shelley') { raise "Suck it, Horace Smith." }
...which will result in...
ruby example.rb
example.rb:17:in `block in <main>': Suck it, Horace Smith. (RuntimeError)
from /Users/kevin/.rvm/gems/ruby-3.0.0/gems/wacktrace-0.1.0/lib/wacktrace.rb:56:in `block in add_to_stack'
from Percy Bysshe Shelley:in `Ā IĀ metĀ aĀ travellerĀ fromĀ anĀ antiqueĀ landļ¼'
from Percy Bysshe Shelley:1:in `Ā WhoĀ saidāāTwoĀ vastĀ andĀ trunklessĀ legsĀ ofĀ stone'
from Percy Bysshe Shelley:2:in `Ā StandĀ inĀ theĀ desertā¤ā¤ā¤Ā NearĀ themļ¼Ā onĀ theĀ sandļ¼'
from Percy Bysshe Shelley:3:in `Ā HalfĀ sunkĀ aĀ shatteredĀ visageĀ liesļ¼Ā whoseĀ frownļ¼'
from Percy Bysshe Shelley:4:in `Ā AndĀ wrinkledĀ lipļ¼Ā andĀ sneerĀ ofĀ coldĀ commandļ¼'
from Percy Bysshe Shelley:5:in `Ā TellĀ thatĀ itsĀ sculptorĀ wellĀ thoseĀ passionsĀ read'
from Percy Bysshe Shelley:6:in `Ā WhichĀ yetĀ surviveļ¼Ā stampedĀ onĀ theseĀ lifelessĀ thingsļ¼'
from Percy Bysshe Shelley:7:in `Ā TheĀ handĀ thatĀ mockedĀ themļ¼Ā andĀ theĀ heartĀ thatĀ fedĶ¾'
from Percy Bysshe Shelley:8:in `Ā AndĀ onĀ theĀ pedestalļøļ¼Ā theseĀ wordsĀ appearļ¼'
from Percy Bysshe Shelley:9:in `Ā MyĀ nameĀ isĀ Ozymandiasļ¼Ā KingĀ ofĀ KingsĶ¾'
from Percy Bysshe Shelley:10:in `Ā LookĀ onĀ myĀ Worksļ¼Ā yeĀ Mightyļ¼Ā andĀ despairļø'
from Percy Bysshe Shelley:11:in `Ā NothingĀ besideĀ remainsā¤Ā RoundĀ theĀ decay'
from Percy Bysshe Shelley:12:in `Ā OfĀ thatĀ colossalĀ Wreckļ¼Ā boundlessĀ andĀ bare'
from Percy Bysshe Shelley:13:in `Ā TheĀ loneĀ andĀ levelĀ sandsĀ stretchĀ farĀ awayā¤ā'
from /Users/kevin/.rvm/gems/ruby-3.0.0/gems/wacktrace-0.1.0/lib/wacktrace.rb:57:in `add_to_stack'
from /Users/kevin/.rvm/gems/ruby-3.0.0/gems/wacktrace-0.1.0/lib/wacktrace.rb:67:in `add_to_stack_from_lyrics'
from example.rb:17:in `<main>'
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/wacktrace. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Wacktrace project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.