One of the things that bothers me the most in software development is when you come across a problem for which there simply is no nice solution to be found. You come up with some ugly hack and get the job done, but the nagging feeling of having fallen short lingers for days.

Problem in question: A project I’m working on sells stuff and uses a payment gateway for this. API access is expensive so we’re opting for the proxy method, meaning that the page that reads credit card information is actually served by the payment provider. It looks something like this:

http://epay.dk/proxy.cgi/http://myshop.com/payment

This means that our /payment page is a very special case. All asset paths must be relative, ie. images/foo.png instead of /images/foo.png. In contrast, links must be absolute, http://shop.com/about instead of /about.

I want the rest of the site to follow normal Rails conventions, so I need to special case this very page. This in itself is a challenge, because as it turns out my ugly hack will affect the application layout as well.

I first tried a few different routes:

  1. Override url_for and set :path_only => false. This solves only half of the problem (asset paths would still be wrong) and turned out to be painful because of the two different url_for implementations. Figuring out why is left as an exercise for the reader because frankly, I can’t be bothered to explain why. Leave a comment if you want to know.
  2. Define some default_url_options. For some reason, I never got this to work reliably, and after some time of fruitless digging trough the Rails source, I abandoned the idea.
  3. Create middleware that kicks in only if controller == Xxx and action == Yyy. It would then process the HTML and change href=”/…” instances to be relative etc. You may object that this is a terrible way of abusing the middleware system, but if it had worked, it would actually have been the least intrusive option. The reason it broke down was unexpected and enlightening: I was able to rewrite the HTML, but afterwards the Apache module pagespeed rewrote it again to optimize CSS includes etc. Obviously this only happened in our production environment, so it took me a couple of hours to track down.
So it would seem that my quest to solve this (somewhat) elegantly had failed. So be it. Time to resort to the nasty hacks.

The solution

Warning: You will be appalled.

What I ended up doing was:

  • In views/layouts/application.html.haml, I only include JS and CSS if we’re not on the payment page.
  • In the payment view, I include all JS/CSS like this (notice that the path does not begin with a slash): %script{ :src => “javascripts/jquery.min.js”, :type => “text/javascript” }
  • Also in the payment view, I added some JS that absolutifies all links. It looks like this:
  $('a[href^="/"]').each(function (idx) {
    fullUrl = "http://myshop.com" + $(this).attr("href");
    $(this).attr("href", fullUrl);
  });

Conclusion

I have created some code that works, but which I’m really, really unhappy about. I hope that some day I’ll come up with a better idea and fix this, but I doubt it. Suggestions are obviously welcome – please leave a comment.

Follow

Get every new post delivered to your Inbox.