Wednesday, July 4, 2012

Emptying Full Client-Side Browser Storage

This morning I started seeing the following error on all preview pages generated by an app running on my development machine:

QUOTA_EXCEEDED_ERR: DOM EXCEPTION 22
in http://localhost:8080/sites/1341373150.915000/public_html/layout.less

This error message isn't caused by the application, it's because the generated HTML uses client-side LESS to render its CSS. The LESS processing had filled up local storage, because each preview generates new storage keys due to the ever-changing timestamp part of the URL.

The solution: here's how to manually clear your browser's local storage of any data whose keys match a particular value:

// Clears local storage of keys that match a regular expression.
function clear_local_storage(key_regexp, keep_key=null) {
  var r = new RegExp(key_regexp);
  for (var key in localStorage) {
    if (key != keep_key && r.test(key))
      localStorage.removeItem(key);
  }
}

A sample of its use:

clear_local_storage("^http://localhost:8080/sites.*layout.less(:timestamp)?$");

Since I wrote the app that's publishing the pages that generate this error, I'm considering making the app inject this JavaScript code into the pages to make sure that end users never see this error message.

Saturday, June 4, 2011

Identity Recall

io.com is going away. It's funny how upsetting this is to me. I've been jimm@io.com since 1994 or so—maybe a year or two earlier than that. You know what I'm worried about most? All those open source projects, emails, and other digital resources that point to jimm@io.com are going to be pointing nowhere in a month. It feels like my online identity is being stolen. Except it's not being stolen, of course—merely recalled.

A Slashdot commenter also pointed out the potential security risk: "Think about it, among other things whoever owns that domain now will be able to intercept all mail to io.com accounts, and with the quickness and suddenness of the transfer not everyone's who uses those addresses is going to be able to completely transition off them before the transfer happens"

io.com was the ISP run by Steve Jackson Games—the company raided by the Secret Service because they were writing a role playing game called "Hacker". SJ Games fought the Secret Service and won. I think the EFF grew out of that case.

io.com was bought by prismnet.com years ago. PrismNet changed hands a few times. The last guy who sold it to the current owner (for $20) didn't sell the io.com domain. He kept it but let them use it until July 1, 2011. My guess is he wants to sell it to I/O Digital for a skillion dollars.

From now on, I'm jim@jimmenard.com. Also jim.menard@gmail.com, or jimm@prismnet.com. Time to start wrapping my head around being jim@jimmenard.com instead of jimm@io.com. Bye, jimm@io.com.

io.com is dead. Long live io.com.

Sunday, July 25, 2010

Play Framework Configuration

I've been using the Play framework for a big project at work. It's fantastic. I almost hesitate to say it, but it's like Ruby on Rails for Java. Watch the video that's on the home page; it's a good quick introduction.

The Tech Ops team at work wants control over configuration of the app. This makes sense for a few reasons. First, they're responsible for running it so they should be able to tweak things like memory settings. Second, they need to tell the app where the database lives, what the password is, etc. Tech Ops also wants the app to run as a WAR inside an app server. That's fine: Play comes with a war command that bundles your app into either an exploded WAR or a WAR file.

The problem is, Play doesn't know how to read any configuration file other than the one that's in its conf directory. At least, that's what I thought. It turns out there is an undocumented feature of Play configuration files: If any configuration key is named@include.foo, then the value is used as a path and that file is read, too.

A few caveats.

  • The path is relative to the application directory within the WAR. So, for example, to find a file in Tomcat's conf directory, the path will have to be something like ../../../../../conf/play.conf.
  • The file that gets included isn't treated exactly like the normal configuration file. Magic values like ${play.path} and ${application.path} are not interpreted,.
  • The current Play framework id (similar to Rails' environment) is ignored. However, since the include code is run after everything else is loaded, this isn't really a problem since the external configuration file you're loading is presumably for the environment you're running.

There is also a plugin architecture, and a hook that runs after the configuration file is read. This means that, even if @include.foo didn't exist, you can write a plugin to do anything you want to alter the app's configuration.

Update: Core Play developer Guillaume Bort wrote on the email list, "This feature is not yet documented because it is not ready: as you observed it the placeholders and the framework id stuff are not handled properly." Caveat emptor.

Saturday, September 12, 2009

Up, Up, and Away

Let's say you are in a subdirectory, and there's a Makefile in a parent directory. How do you run make? You climb back up to the parent directory and type "make", or use "make -C parent_dir". In either case, you have to know what the parent dir is. Here are a pair of scripts that do that for you.

The findup script's job is to find a file in a parent directory. It prints the directory name if found, else it exits with status 1.


#! /bin/sh
#
# usage: findup file [file...]
#
# Finds any of the files in the current directory or any directory above and
# prints the directory name. If no such directory is found (we hit the root
# directory), exit 1. Note: will not find files in the '/' directory.

start_dir=`pwd`

while [ `pwd` != '/' ] ; do
    for f in $* ; do
        if [ -f $f ] ; then
            echo `pwd`
            exit 0
        fi
    done
    # Keep swimming...keep swimming...swimming, swimming, swimming...
    cd ..
done

# No parent directory
exit 1

The makeup script uses findup.


#! /bin/sh
#
# usage: makeup [args...]
#
# Finds the first makefile in the current directory or any directory above and
# then runs make, passing on any args given to this script.
#
# Relies on the "findup" command, which is found in this directory.

mfdir=`findup makefile Makefile`
if [ -z "$mfdir" ] ; then
    echo no makefile found
    exit 1
fi

# We've found a makefile. Use the -C flag to tell make to run from the
# directory where we found the makefile. We do this so that error messages
# produced during the make process are relative to the current directory. I
# think.
make -C $mfdir $*

Monday, August 3, 2009

ChucK Hack Fork

I've created a GitHub fork of ChucK, the "strongly-timed, concurrent, and on-the-fly audio programming language." In my fork I've started to add missing features like string methods, and hope to add many more such as raw file I/O and perhaps some form of MIDI file I/O.

So far, I've added string.ch which is a getter and setter for string characters (really one-character length substrings) and string.substr. This allowed me to write a function in ChucK that converts strings like "c#4" to MIDI note numbers.

It looks like ChucK's code was last updated in 2006, so I don't feel to bad about forking it.

Update: I just read in the chuck email list archive that the team is planning to start active development again, and they will be adding file I/O. If they get it done before I do, that would be very nice.

Monday, June 22, 2009

A Weak Man Begs Forgiveness

Dearest Colleagues,

I write this electronically-delivered letter to you in the hopes that you will forgive me. A terrible fate has befallen my lovely daughter V_______, and I am afrid that I now share in her misfortune. To protect you all from similarly horrible destinies, I must eschew your company this day.

It all started innocently enough, with a persistent cough. My eldest daughter would delicately hack to "clear her throat". The coughs became more frequent and urgent. She developed a fever which burned her pure, innocent forehead. As time passed, she begain to writhe in agony, delirious with dreams of God knows not what Heavenly retributions and Hellish tortures.

My dearest wife E____ conveyed her to the pediatrician's office on Sunday. Her doctor (a woman, by God! Ah, well. I know not what muse guides Science, but she must hold in favor the fairer sex) diagnosed V_______ with Swine Flu. The woman practiced her Art with confidence, perscribing the most modern treatments available.

We repaired to our home, powders and poultices in hand. The cure had barely taken hold of my dear daughter when I myself began to cough. At first, I denied the obvious. What strong man would not? Having fought in The War, my thoughts were not for my own safety or bodily condition but for that of my child.

I tell you now: Mr. Poe never wrote of horrors so small. The tiny demons that ravage my body even now defy all description, and to burden you with reports of the agony they cause does not merit further thought.

The submarine-like conditions of the Juicyorange office being the breeding grounds for animalcules it is, it is best that I remain far, far away. This is why I have decided to remain in an entirely different State of the Union. My sorrow at not joining you is only mitigated by the knowledge that you will not be exposed to the fiends that wrack my body.

In the fervent hope that this missive is not rerouted to your Spam folder,

With Deepest Respect,
James J. Menard
Babbage Engine Servitor

Saturday, April 18, 2009

Using will_paginate in Rails to Submit an AJAX Form

I'm using will_paginate for a Rails project. It works great, but I wanted to use it for a sidebar that performs searches and displays the results. It's not too hard to write a custom link renderer that will submit the will_paginate link using Ajax, but my situation was a bit different: I wanted will_paginate to inject the page number into the search form and submit the form using Ajax.

Here's what I ended up doing: writing a link renderer that calls a JavaScript function that injects the page number into the form and calls the form's onsubmit function (which then submits the form using Ajax).

The code. First the Haml, which is in a partial that gets rendered on a number of pages:

%h1 Asset Search
- form_remote_tag(:html => {:id => 'ssform', :name => 'ssform'}, ...) do
  %input{:type => 'hidden', :id => 'sspage', :name => 'sspage'}
  / ...
  %input{:type => 'submit', :value => 'Search'}

  - paginated_section(sidebar_assets, :class => 'rhs_pagination', :renderer => SidebarSearchLinkRenderer) do
    %table.list
    / ...

Next, the will_paginate renderer, in the file app/helpers/sidebar_search_link_renderer.rb. The "#" is the href value, and the onclick attribute calls a JavaScript funtion.

class SidebarSearchLinkRenderer < WillPaginate::LinkRenderer

  def page_link(page, text, attributes = {})
    @template.link_to text, '#', attributes.merge(:onclick => "return sidebar_search(#{page});")
  end

end

Finally, the JavaScript function which I put in public/javascripts/application.js:

function sidebar_search(page) {
  $('sspage').value = page;
  document.forms['ssform'].onsubmit();
  return false;
}