Software Escape Archives

Developing virtualhost-aware PSGI applications: Plack::Middleware::MockProxyFrontend

Let’s say you work on a team that runs a web content management system for various different customers. It is hosted at ourcms.com, but each customer’s public content is published on a different domain, which is determined by a setting in the interface, which they can change at will. When a customer is logged into ourcms.com they see links to their public content in various places, and some of the public content has “edit this”-type links back into ourcms.com. All of this runs as a single PSGI application. A not unfamiliar scenario, presumably.

How do you spin up a development server where you can test this?

Plack::Middleware::SignedCookies

I released Plack::Middleware::SignedCookies some time ago because I went looking for it and came up empty. This is a middleware that signs outgoing cookies on the server with a HMAC digest and verifies the digest on incoming cookies. If a cookie doesn’t pass the signature test, it is dropped on the floor and your application never gets to see it.

There are several framework-specific plugins that do the same job, but I wanted to get rid of as much framework-specific code as possible.

One explicit design choice was to not handle expiration. Several of the plugins I saw do handle that themselves, and it certainly is useful for centralising all cookie policy in one place. However, expiration is almost certainly something you’ll want to handle at the application level, if only to vary the “not authorised” message you show the user. So there has to be a protocol to signal that a cookie was present but rejected because it was expired, and the application needs code implementing that protocol in order to react to expirations. This couples the application to the cookie policy code. Now that is fine when that is part of the framework, or a framework-specific component; it is not fine in a framework-agnostic Plack middleware. Leaving the expiration policy to the application means the application only needs to deal with the cookie interface provided by its framework: it is not coupled to the middleware.

Another choice I made in the spirit of “maybe YAGNI” – but which is likely a limitation – is that as of 1.103, SignedCookies doesn’t provide a way to pick which cookies to sign/verify. In a pinch, you can always use a middleware wrapped around it (such as an inline Rewrite rule, for convenience) to intercept and/or inject non-signed cookies outside of its purview. There is a likelihood that this will change in the future.

Anyway, have at it. Share and enjoy.

Buftabline – forget Vim tabs, now you can have buffer tabs

I just released Buftabline, a Vim plugin that takes over the tabline and renders the buffer list in it instead of a tab list. It is designed with the ideal that it should Just Work, and has no configurable behaviour: drop it into your configuration and you’re done.

There are comparisons with several alternative plugins in the README, as well as an explanation of why you probably want this rather than using Vim’s built-in tabs.

Share and enjoy.

MetaCPAN-on-search.cpan.org update

I’ve uploaded a new version of the GreaseMonkey script I introduced in my last entry (to let you use search.cpan.org for searching but with links to MetaCPAN in the search results).

This version is updated for GreaseMonkey 1.0, and also includes a small link at the top right of a search results page which links to the same search on MetaCPAN:

var query = document.querySelector('input[type=text]').value;
if (query) {
    document.querySelector('.t4 small').insertAdjacentHTML('beforeend',' <a id="goto-metacpan">&#x2192; MetaCPAN</a>');
    var link = document.getElementById('goto-metacpan');
    var href = 'https://metacpan.org/search?q=' + encodeURIComponent(query);
    link.href = href.replace(/%20/g,'+');
    link.style.setProperty('float','right');
    link.style.setProperty('padding-right','.4em');
    link.style.setProperty('color','inherit');
}

Easily installable from userscripts.org. Share and enjoy.

Marrying MetaCPAN to the search.cpan.org search engine

I generally like MetaCPAN better than search.cpan.org, but as fREW mentioned in Using search.cpan.org AND metacpan, the latter’s search result ranking algorithm is far superior to the former’s. So fREW’s idea was to use GreaseMonkey – or actually not GreaseMonkey – to rewrite the links in search.cpan.org search results so that he could use that site for searching, but go to MetaCPAN for everything else. In fact though, he used dotjs and took advantage of the built-in jQuery it ships with. Thus his script does not work with vanilla GreaseMonkey.

Here is a version that does. As a bonus, above and beyond fREW’s version, it also only rewrites the distribution and author links rather than just the module links.

// ==UserScript==
// @name        Marry MetaCPAN to the search.cpan.org search engine
// @namespace   http://plasmasturm.org/
// @include     http://search.cpan.org/search?*
// @version     1
// ==/UserScript==

var _rx = new RegExp('^/~([^/]+)(.*)');
var urx = new RegExp('^https://metacpan.org/module/([^/]+)/$');
var rrx = new RegExp('^https://metacpan.org/module/([^/]+/[^/]+)/$');
var anchors = Array.prototype.slice.call(document.querySelectorAll('a[href^="/~"]'));
anchors.forEach(function(anchor){
    var href = anchor.getAttribute('href');
    href = href.replace(_rx, function (m,p1,p2) { return 'https://metacpan.org/module/' + p1.toUpperCase() + p2 });
    href = href.replace(urx, 'https://metacpan.org/author/$1');
    href = href.replace(rrx, 'https://metacpan.org/release/$1');
    anchor.href = href;
});

And here it is ready to install on userscripts.org. Share and enjoy.

Bash perldoc completion tweaks

Remember when I wrote the following?

Completion in bash is hard-wired to understand trailing slashes as “the user might want to do more completion right after this” – we want :: treated that way instead but there is no way to tell bash.

This is still true, but reading the manpage a little less carelessly reveals that passing -o nospace to the complete command tells bash to simply never append a space, which achieves what is desired without hacks.

While I was in there, I found -o default, which means that if the completion generator returned no results, bash should use its default completion generator instead. This is very useful.

Previously, with “.” in your @INC (as it commonly is), all directories directly inside the current working directory would be included in the completions list as suggested namespaces, because perldoc-complete has no way of knowing which of them contain modules and which don’t. (It would have to recurse, which is too expensive.) But since there is a way to fall back to bash’s default filesystem completion, I have added an extra check that removes not only the home directory from @INC, but the current directory as well.

If you want to complete on documentation in modules somewhere below the current directory, simply type perldoc ./<Tab> etc to use standard path completion. Or perldoc /<Tab> to complete on an absolute path.

Share and enjoy.

Summary

  • No more ugly fake suggestions to force ambiguity
  • No more spurious namespace suggestions outside your home directory
  • You can now complete on paths as well as namespaces by starting the path with / or ./
  • Existing users will have to change their .bashrc line to
    complete -C perldoc-complete -o nospace -o default perldoc

More bash completion help for perldoc

Yesterday’s posting got a fair bit of response. Among other feedback, I had some feature requests from Offer Kaye in my email today.

So now perldoc-complete will complete built-in Perl functions if you’ve typed perldoc -f (try it with eg. perldoc -f ch<Tab>) and it will also complete Perl’s documentation pages – although for uncluttering’s sake, it will hide the list if you’ve merely typed perldoc <Tab>.

Share and enjoy.

A bash completion helper for perldoc

A month ago, Yanick Champoux wrote a note about helpers for browsing the POD in your your local perl install.

His first script is for firing up a browser pointed at a local POD web server, including starting one up if it’s not already running – not that useful to me, since I haven’t found myself actually using these servers very much, because of the console↔browser flipping that they entail. Plain old perldoc on a console just feels faster to juggle.

However, he also includes another script: a completion helper for bash. This allows you to type something like perldoc Cata<tab> and have bash turn it into perldoc Catalyst for you. I used this script for mere hours before I realised it’s exactly the one thing I have always missed in Perl: a way to quickly and efficiently browse my local module library – the thing that all the POD web servers promised to give me, but couldn’t deliver in a convenient enough fashion for me to use routinely.

But as presented, his script has one limitation that annoyed me more than perhaps I should have let it: you have to use your system directory separator (ie. slash on Unixoid systems) en lieu of Perl’s :: package separator. Ie.: you cannot type perldoc Catalyst::Re<tab>, it has to be perldoc Catalyst/Re<tab>.

I thought that should be easy to fix. (Famous last words, I know.) It turned out to be harder than expected due the fact that completion in bash is hard-wired to understand trailing slashes as “the user might want to do more completion right after this” – we want :: treated that way instead but there is no way to tell bash. I also ended up rewriting the script to be more (theoretically) portable, to not require non-core dependencies, and to work on much older perls than just 5.10.0, as Yanick’s code requires. Fastidious as I always am, I also spent quite a bit of effort rewriting the code to make it more beautiful and easy to skim and understand.

So without further ado: share and enjoy.

About Aristotle

user-pic Waxing philosophical