How do you track down an obtuse error in your Rails app?
Have you ever tried having an exception raised in your app where the error message is inscrutable? You have no idea where to turn because it barely makes sense. Let alone any clue on how to fix it. To top it off what if the error originates in code you’re not directly calling? That’s what happened to me recently in a middleware. The good news is, there is a way out of that pit.
I was trying to connect to Office 365 using OmniAuth. To use OmniAuth you need to install extra plugins; one provider for every service you want to authenticate with. I was using the
omniauth-office365 gem, which I also needed to tweak for our app. When I did that, however, I was greeted by this bastard over and over:
Faraday::Error::ConnectionFailed, getaddrinfo: nodename nor servname provided, or not known. You’re damn right the connection failed, I have no idea how to connect that failure to anything I’ve done in my code!
After a few rounds of that kind of frustration you’re ready to start pulling hairs out (or let the ones grey already give in to temptation). There’s no other way to go about it than try again. Meanwhile I was doubly confused because the error says it can’t resolve the DNS name. But I had given it a valid hostname: ’https://outlook.office365.com‘.
At times when I’m completely stumped I reach for my debugger and some trusty Ruby tricks.
Some people get great mileage out of
p, but I always end up with my gas tank empty before the interstate. For me though? There’s no tool like byebug. You just pop a
byebug statement in your code and you’re off to the runtime introspection races.
Now for the Ruby trick. How do you get a hold of the middleware in your Rails app? I tried several things but nothing close to a bullseye. Until I remembered sooner or later, the middleware has to be instantiated which means
new is called, which means
initialize is called.
That means I know where to pierce the skin and start poking. Now how do I inject the
byebug statement in there? Ruby has a great answer for that:
I created a module which overrode the method to fire off my debugger and call super. Like this:
module Office365Byebug def initialize(*) byebug super end end OmniAuth::Strategies::Office365.prepend Office365Byebug
This breaks right at
super above, but before it’s executed. Mashing
up once - or
u for short - takes you to where the super class called initialize. That’s where I wanted to be. See, once I was stopping every time the middleware was instantiated, and I could prod the running code, my pace picked up immensely. Nothing really gels what I’m trying to understand quite like seeing the code run in action line by line.
Oh, and that splat trick? I wrote about it and more here.
As for the solution? Well, it’s a real slasher.
omniauth-office365 didn’t check if the base url had a slash or not, so the URL it was using to query user info became
https://outlook.office365.comMe?api_version=1.5. I don’t know about you, but I’ve never heard about a
comMe TLD - and they sure come weird these days.
At least there’s comfort in the simple fix, opposed to the franticness of not knowing how you’ll pull through. Though don’t forget your safety googles, whippersnapper.