This Is A Reminder For Me

Recently, I tweeted:

 

which prompted:

 

Unfortunately, the code I was referring to was client code, but how about a blog post?

The Widget Problem

For whatever reason, I have been writing lots of Javascript widgets and bookmarklets.  This type of 3rd-party code is usually copied and pasted into a website or initiated via a boorkmark (both work in similar fashion). The tricky party about widgets is that the code must be lean and fast, and must be robust to hostile environments.  By hostile environments, I mean that you can’t make any assumptions about a particular website these days.  If you’ve ever tried to scrape arbitrary webpages, you know what I’m talking about.

The problem with jQuery is that sometimes it is neither lean nor fast, and you can’t assume that the website hosting your widget has jQuery already, or has a particular version of jQuery.  But we need jQuery.  We need its selectors, and Ajax helpers, and CSS helpers.  The solution is to load our own jQuery.

I Got Mine (jQuery)

Start with the typical function expression module that isolates your code from any other Javascript.

(function(document, undefined) {
 
  // ... your isolated code
 
})(document);

Load jQuery asynchronously, and prepare a callback that runs when jQuery is loaded.

(function(document, undefined) {
 
  // Load our own jQuery
  s = document.createElement('script');
  s.src='//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js';
  s.onload = function() {
    // ... 
  });
 
  d.getElementsByTagName('head')[0].appendChild(s);
 
})(document);

When jQuery loads, it will assign itself to the $ variable, overwriting whatever was using it beforehand, such as the host site’s copy of jQuery.  Fortunately, jQuery keeps a copy of the original value, and running jQuery.noConflict() restores the original value of $ and returns the latest value.  Run $.noConflict() to get our own, local copy of jQuery in the $ variable.

(function(document, undefined) {
 
  // Load our own jQuery
  s = document.createElement('script');
  s.src='//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js';
  s.onload = function() {
    var $ = jQuery.noConflict();
    // ... $ now safe to use
  });
 
  d.getElementsByTagName('head')[0].appendChild(s);
 
})(document);

Can We Do Better?

This works well. However, if the thought of a whole network request (even if cached) just to run your widget bothers you, there’s a surprisingly simple alternative: inline jQuery.  That is, copy and paste the entire jQuery library into your widget code.  This may seem unusual, but despite it’s size, it’s still cheaper than a network request.

(function(document, undefined) {
 
  // Load our own jQuery
  /*! jQuery v@1.8.1 jquery.com | jquery.org/license */
  (function(a,b){function G(a){var b=F[a]={};return ...
 
  var $ = jQuery.noConflict();
  // ... $ now safe to use
 
})(document);

Can We (Still) Do Better?

If inlining all 91k (as of 1.8.2) of jQuery also bothers you: consider Zepto (23k for 1.0RC1). Zepto is billed as a minimalist JavaScript library for modern browsers with a largely jQuery-compatible API.”

(function(document, undefined) {
 
  // Load our own Zepto
  /* Zepto v1.0rc1 - polyfill zepto event detect fx ajax form touch - zeptojs.com/license */
  (function(a){String.prototype.trim===a&&(String.prototype ...
 
  var $ = window.Zepto;
  // ... $ now safe to use
 
})(document);

Zepto, unlike jQuery, will not assign itself to $ if it is already defined, so no issue there.

Now, don’t make the mistake I did in thinking Zepto is a drop-in replacement as a “lightweight” jQuery because it certainly is not, especially in CSS manipulations.  Support for CSS effects in IE is sketchy, and its Ajax helpers are a bit different.  However, Zepto gives you all the selectors and DOM manipulators you’re used to, has all the Ajax helpers you need, and offers useful utilities such as $.proxy().

So if you need visual effects in your widget, Zepto will probably not do. But if all you need is the ability to add/remove/select elements, then Zepto works great as a jQuery replacement.

If you’re like me, you have a number of offline jobs running in your application.  These jobs process images, send emails, warm up caches, and sync up data with 3rd-party APIs.

Also, if you’re like me, you run into the same problems over and over:

  • Manually maintaining crontab files is a burden.
  • Frequent jobs that stall or delay may start running multiple copies.
  • Knowing when a job fails automatically is tough because honestly who has time to look at log files?
  • If you have a webfarm, you have to decide which jobs run on which servers, or all of them.

Enter jobby, the PHP cron job manager.

Install a single crontab entry on each of your servers, and never edit crontab again.  The master cron job runs a PHP file which contains all of your real jobs and executes them according to their individual schedules (using crontab syntax) along with other configs. The nice thing is that since jobby is just PHP, you can decided what jobs to run and when programmatically. Now, I know there are more expansive server configuration tools out there, but those felt too heavy for this kind of use.

More features: jobby will ensure only 1 copy of a job runs at a time, can email one or more recipients if a job fails, and can run as different users on select hosts.

I developed a version of jobby years ago, and have brought it along with me on various projects, slowly tweaking it.  It’s helped me immensely and if you’re interested, check out the README and composer install away!

So we took on some vendors to help us out. I wanted to provide these individuals authenticated, read-only access to our git repos so they could stay current with the project, but not commit code directly (they’ll have their own repo). Google yielded these excellent results pages.

When read altogether, I had a few options.

  1. Run the git-daemon. The repo would be publicy available over the internet via the git (git://) protocol.  This provides read-only access, but it provides it to the whole world.  Not a solution.
  2. Run a GitHub-like server, like Gitosis or Gitolite.  This was the sledgehammer solution.  Fine-grained access control, plus all the other bells and whistles. But I was not interested in installing and understanding a whole git-hosting server application.
  3. SSH via git-shell.  Each user gets a regular SSH account on the server, but each user gets a git-shell instead of a regular login shell like bash. The git-shell restricts the user to git operations.  Write permissions are restricted using standard Linux permissions on the repo itself.

So #3 was what I needed. I created an SSH login for each user that needed access to the repo. I set the login shell for each user to /usr/bin/git-shell. I put each user in a group that had read-only file system permissions to the repo.  Testing it out worked well.  Users could git clone and pull, but pushing failed and attempting to SSH directly failed.

One last note: as the man page mentions, you can create a directory called git-shell-commands in home directories of git-shell usersgit-shell users will be able to run any command in this directory.  If there is a help program in the directory, it is run when a git-shell user logs in.  More details on git-shell-commands here, including the location of sample git-shell commands on your server.

I had an existing site I created using Blueprint CSS a while ago. I wanted to make it mobile-friendly. There are responsive CSS grid frameworks out there now, but I didn’t feel like replacing Blueprint, and I figured all I had to do was, using media queries, make every column span a fixed width. That was mostly correct. In the end, I used 100% widths, plus a few other alterations.  Here is how I ended up doing it.

Normally, you will link to your Blueprint screen.css file like so:

	<link href="/css/screen.css" rel="stylesheet" media="screen, projection" type="text/css" />

The following lines tell the browser to link first to a mobile-friendly CSS file. Then, if and when the width is 600px, link to the normal, desktop CSS file. The idea is that bandwidth is scarcer on mobile, so we don’t mind downloading two files on a desktop machine if we have to.

<!-- For mobile displays -->
	<link href="/css/mobile.css" rel="stylesheet" media="screen, projection" type="text/css" />
	<link href="/css/print.css" rel="stylesheet" media="print" type="text/css" />
 
<!-- For desktop displays -->
	<link href="/css/screen.css" rel="stylesheet" media="only screen and (min-width: 600px)" />

To create my mobile.css, I copied screen.css, and made the following changes.

  1. Change table elements padding to 2px 
    th, td, caption {padding: 2px;}
  2. Resize all image widths to fit on screen 
    a img {border:none;max-width:90%}
  3. Change input text fields to 67% width. 
    input.text, input.title {width: 67%; padding: 5px;}
  4. Change textarea inputs to 80% width, and reduce height to 150px
    textarea {width: 80%; height: 150px; padding: 5px;}
  5. Change the overall container to 90% width. 
    .container {width: 90%; margin: 0 auto;}
  6. Change all span grid classes to 100% width. 
    .span-1 {width: 100%;}
    ...
    .span-24 {width: 100%; margin-right: 0;}
  7. For some reason, I ended up deleting all input.span and textarea.span classes, though I’m not sure why now.
  8. Change all append grid classes to 0px right padding. 
    .append-1 {padding-right: 0;}
    ...
    .append-24 {padding-right: 0;}
  9. Change all prepend grid classes to 0px left padding. 
    .prepend-1 {padding-left: 0;}
    ...
    .prepend-24 {padding-left: 0;}
  10. Change all pull grid classes to 0px left margin. 
    .pull-1 {margin-left: 0;}
    ...
    .pull-24 {margin-left: 0;}
  11. Change all push grid classes to 0px margin. 
    .push-1 {margin: 0;}
    ...
    .push-24 {margin: 0;}

Not pretty, but it seems to work well.  Happy CSS!