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;
}

Wednesday, February 25, 2009

Time Keeps On Slipping, So Freeze It

A friend of a friend has trouble testing timestamps. I tried replying there, but don't think my answer got through so here it is.

The short answer: all time operations should not use the wall clock, but a system time object that you create. In production, the system time object returns wall clock time. In testing, you replace/mock/modify it so that it uses the time you give it. In other words, it becomes a clock that you can freeze and re-set whenever you need to.

Here is an example SystemClock class in Ruby. All of your classes should use this class to get the current date and time instead of using the Time or Date classes directly.

# Keeper of the current system time. This class is sometimes overridden during
# testing to return different times.
class SystemClock

  def self.date
    Date.today
  end

  def self.time
    Time.new
  end

end

Here is the mock used during testing. You can set the time using this mock object and it will not change until it is set again.

# This mock lets you set the system date and time during testing. For example,
# to set the date to tomorrow,
#
# A few examples of use:
#
#   SystemClock.date = Date.today + 1 # Time also set: to tomorrow 9:10:11 am
#   SystemClock.date = nil            # Go back to using system date and time
#   SystemClock.date = Date.civil(2006, 4, 15)
#   SystemClock.time = Time.local(2006, 3, 2, 8, 42, 42) # Date changed, too
#
# When you set the date, the time is set to 9:10:11 am of the same day, local
# time. When you set the time, the date is set to the same day.

require 'models/system_clock'

class SystemClock

  @mock_date = nil
  @mock_time = nil

  def self.reset
    @mock_date = nil
    @mock_time = nil
  end

  def self.date=(date)
    @mock_date = date
    @mock_time = date == nil ? nil :
      Time.local(date.year, date.month, date.day, 9, 10, 11)
  end

  def self.time=(time)
    @mock_time = time
    @mock_date = time == nil ? nil :
      Date.civil(time.year, time.month, time.day)
  end

  def self.date
    @mock_date || Date.today
  end

  def self.time
    @mock_time || Time.new
  end

end

So in your normal production code you get the current time by calling SystemClock.time instead of Time.now. In your test code, you'd do something like this:

# Make sure to include the mock class
SystemClock.time = Time.now # Or any arbitrary time
thing = Thing.new           # Uses SystemClock to set created_at attribute
assert_equal SystemClock.time, thing.created_at