Skip to content

Chef Alternatives for Debian/Ubuntu

2012 February 5
by James Kyle

An update-alternatives script for chef. Currently only sets up a version installed in the 1.9.1 gems directory, but extendable to any version easily enough.

#!/bin/bash
RUBY_VERSION=1.9.1
CHEF_VERSION=0.10.8
GEM_ROOT=/var/lib/gems/${RUBY_VERSION}/gems/chef-${CHEF_VERSION}
 
update-alternatives \
   --install /usr/bin/chef-client chef ${GEM_ROOT}/bin/chef-client 500 \
   --slave /usr/bin/chef-solo chef-solo ${GEM_ROOT}/bin/chef-solo \
   --slave /usr/bin/knife knife ${GEM_ROOT}/bin/knife \
   --slave /usr/bin/shef shef  ${GEM_ROOT}/bin/shef

Ruby Alternatives for Debian/Ubuntu

2012 February 5
by James Kyle

A quick update-alternatives script for ruby versions on debian based systems. It only includes the ruby binaries, man pages, etc. included in the ruby1.8 and ruby1.9.1 debs. It’s defaults to ’1.9′ when in auto mode.

After running the script, you can switch between ruby version via

% update-alternatives --config ruby
#!/bin/bash
RUBY_VERSION=1.9.1
 
update-alternatives \
   --install /usr/bin/ruby ruby /usr/bin/ruby${RUBY_VERSION} 500 \
   --slave /usr/bin/erb erb /usr/bin/erb${RUBY_VERSION} \
   --slave /usr/bin/rake rake /usr/bin/rake${RUBY_VERSION} \
   --slave /usr/bin/irb irb /usr/bin/irb${RUBY_VERSION} \
   --slave /usr/bin/gem gem /usr/bin/gem${RUBY_VERSION} \
   --slave /usr/bin/rdoc rdoc /usr/bin/rdoc${RUBY_VERSION} \
   --slave /usr/bin/testrb testrb /usr/bin/testrb${RUBY_VERSION} \
   --slave /usr/share/man/man1/rdoc.1.gz rdoc.1.gz \
	   /usr/share/man/man1/rdoc${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/testrb.1.gz testrb.1.gz \
	   /usr/share/man/man1/testrb${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/rake.1.gz rake.1.gz \
	   /usr/share/man/man1/rake${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/irb.1.gz irb.1.gz \
	   /usr/share/man/man1/irb${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/erb.1.gz erb.1.gz \
	   /usr/share/man/man1/erb${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/gem.1.gz gem.1.gz \
	   /usr/share/man/man1/gem${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/ruby.1.gz ruby.1.gz \
      	   /usr/share/man/man1/ruby${RUBY_VERSION}.1.gz \
   --slave /usr/share/menu/ruby ruby_menu /usr/share/menu/ruby${RUBY_VERSION} 
 
RUBY_VERSION=1.8
 
update-alternatives \
   --install /usr/bin/ruby ruby /usr/bin/ruby${RUBY_VERSION} 400 \
   --slave /usr/bin/erb erb /usr/bin/erb${RUBY_VERSION} \
   --slave /usr/bin/irb irb /usr/bin/irb${RUBY_VERSION} \
   --slave /usr/bin/rdoc rdoc /usr/bin/rdoc${RUBY_VERSION} \
   --slave /usr/bin/testrb testrb /usr/bin/testrb${RUBY_VERSION} \
   --slave /usr/share/man/man1/rdoc.1.gz rdoc.1.gz \
	   /usr/share/man/man1/rdoc${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/testrb.1.gz testrb.1.gz \
	   /usr/share/man/man1/testrb${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/irb.1.gz irb.1.gz \
	   /usr/share/man/man1/irb${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/erb.1.gz erb.1.gz \
	   /usr/share/man/man1/erb${RUBY_VERSION}.1.gz \
   --slave /usr/share/man/man1/ruby.1.gz ruby.1.gz \
	   /usr/share/man/man1/ruby${RUBY_VERSION}.1.gz \
   --slave /usr/share/menu/ruby ruby_menu \
	   /usr/share/menu/ruby${RUBY_VERSION}

Adding Syntactic Sugar to Python dicts

2012 January 28
by James Kyle

Snippet

This little snippet allows dict keys to be accessed like attributes. For example

d = CustomDict({"foo": "bar", "bah": {"baz": "bot"}})

Can be accessed like

d.foo # returns "bar"
d.foo.bah.baz # returns "bot"

In addition to the hash key lookups.

class CustomDict(dict):
    def __init__(self, data):
        d = data.copy()
        self.__dict__ = self
 
        for key, value in data.iteritems():
            if isinstance(value, dict):
                d[key] = CustomDict(value)
 
        map(self.__setitem__, d.keys(), d.values())

Improving ScriptingBridge Performance Using NSProxy & NSCache

2011 September 10
by James Kyle

The Problem

The ScriptingBridge API is an excellent way to tap into the internals of OS X applications with little effort. However, it does have its drawbacks. Not the least of which being lazy evaluation of SBObject's attributes. Lazy evaluation makes the retrieval of SBObject's very efficient since all of the objects attributes are not retrieved at the same time. However, if you need to sort those objects by a particular key (say the name of a movie) the latency of ScriptingBridge becomes glaringly obvious to the point of becoming unusable.

The Solution

We can solve this problem using a combination of NSCache and NSProxy. NSProxy provides a relatively simple API for wrapping NSObject's and intercepting messages sent to them. By doing so, you can interecept the message and if it's been previously called, retrieve the value from an NSCache.

The Implementation

For our example, we’ll use iTunes. To do so we first have to generate an iTunes objc header file using sdef and sdp.

    % sdef /Applications/iTunes.app | sdp -fh --basename iTunes

This produces a header file called "iTunes.h" that we include in our project to instantiate the iTunes SBApplication object.

This is what our source looks like so far:

    NSArray* getTracks()
    {
      iTunesApplication *itunes;
      itunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
 
      // Retrieve a list of movies in our iTunes library.
      NSArray *sources = [[itunes sources] get];
      NSPredicate *predicate = [NSPredicate 
                          predicateWithFormat:@"name == 'Library' && kind == %i", 
                          iTunesESrcLibrary];
 
      NSArray *libs = [sources filteredArrayUsingPredicate:predicate];
 
      NSMutableArray *theMovies = [NSMutableArray array];
      NSArray *playlists;
      NSArray *movieLists;
 
      for (iTunesSource *source in libs) {
        playlists = [source playlists];
        movieLists = [playlists filteredArrayUsingPredicate:
                      [NSPredicate predicateWithFormat:@"name == 'Movies'"]];
 
        for (iTunesPlaylist *playlist in movieLists) {
          for (iTunesTrack *track in [playlist tracks]) {
            [theMovies addObject:track];
          } 
        }
      }
 
      return theMovies;
    };

The iTunesTrackProxy Object

Not terribly interesting, but now to the fun part. We create a proxy object to wrap the tracks we retrieved in our getTracks method and act as an intermediary to any requests for track attributes.

For our purposes we need to override three of the NSProxy objects methods to handle NSInvocations sent to the track object.

  • - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
  • - (void)forwardInvocation:(NSInvocation *)invocation
  • - (BOOL)respondsToSelector:(SEL)aSelector
We want to respond to all the same methods that the original iTunesTrack object does, so we simply pass the method to the proxies track object.

    - (BOOL)respondsToSelector:(SEL)aSelector
    {
      return [self.track respondsToSelector:aSelector];
    }
We also want our object to transparantly behave just like a iTunesTrack for method returns. So all method signatures will also be identical.
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
    {
      NSMethodSignature *sig;
      sig = [self.track methodSignatureForSelector:sel];
      return sig;
    }

The real action

The real action happens in the forwardInvocation. This is where we catch message invocations intended for the iTunesTrack object and return the values we want from the sources we want (e.g. NSCache). Since I’m not covering the uninteresting boiler plate code in detail, here's our iTunesTrackProxy header and implementation source so far:

    #import <Foundation/Foundation.h>
 
    @class iTunesTrack;
 
    @interface iTunesTrackProxy : NSProxy {
      @private
      iTunesTrack *_track;
      NSCache *_cache;  
    }
 
    - (id)initWithTrack:(iTunesTrack *)track;
 
    @property(readonly) iTunesTrack *track;
    @end
    #import "iTunesTrackProxy.h"
    #import "iTunes.h"
 
    @implementation iTunesTrackProxy
 
    - (id)initWithTrack:(iTunesTrack *)track
    {  
      _track = [track retain];
      _cache = [[NSCache alloc] init];
      return self;
    }
 
    - (void)dealloc
    {
      [_track release];
      [_cache release];
      [super dealloc];
    }
 
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
    {
      NSMethodSignature *sig;
      sig = [self.track methodSignatureForSelector:sel];
      return sig;
    }
 
    - (BOOL)respondsToSelector:(SEL)aSelector
    {
      return [self.track respondsToSelector:aSelector];
    }
 
    - (void)forwardInvocation:(NSInvocation *)invocation 
    {
 
    }
 
    @synthesize track = _track;
    @end

First attempt at the forwardInvocation method

A naive first attempt might look similar to this:

    - (void)forwardInvocation:(NSInvocation *)invocation 
    {
      // Using the string representation of the selector as the NSCache key.
      NSString *key = NSStringFromSelector([invocation selector]);
 
      // First check and see if we've already cached the object
      id result = [_cache objectForKey:key];
 
      // If the object is cached, use it as the returnValue
      if (result) {
        [invocation setReturnValue:&amp;result]; 
 
      } else {
        // if not cached, forward it to the track object, then cache the return
        [invocation invokeWithTarget:self.track];
        [invocation getReturnValue:&amp;result];
        [_cache setObject:result forKey:key];
      }
    }
Simple enough. However, it makes a critically flawed assumption.Specifically that all method invocations to the iTunesTrack object return NSObjects. A quick glance at the iTunes.h header proves otherwise and many return primitive types. Just to keep everything interesting, NSCache only stores NSObjects, thus we have to transform the primitive return types to an NSObject when storing and retrieving these values.

To resolve this oversight we create a few more methods.

  • - (void)setValueFromTrack:(NSInvocation *)invocation
  • - (void)setValueFromCacheObj:(id)obj invocation:(NSInvocation *)inv
  • - (id)mapBuffer:(voidPtr)buffer type:(NSString *)type
  • - (void *)mapObject:(id)obj key:(NSString *)key

Final implementation

Let's walk through each of our new methods and then the final implementation of the forwardInvocation method.

setValueFromTrack

If the result is not found in the cache, this method retrieves it from track itself. If it's an NSObject, it stores the value directly in the NSCache. If it's a primitive, it passes the value to the mapBuffer:type method to retrieve an appropriate object for storing in the NSCache.

    - (void)setValueFromTrack:(NSInvocation *)invocation
    {
      NSString *key = NSStringFromSelector([invocation selector]);
 
      NSString *returnType = [NSString 
                              stringWithUTF8String:[[invocation methodSignature] methodReturnType]];
      id result;
 
      // retrieve value from track object
      [invocation invokeWithTarget:self.track];
 
      // if object is of type NSObject.
      if ([returnType isEqualToString:@"@"]) {
        [invocation getReturnValue:&amp;result];
 
        if (!result) {
          // retrieved value does not exist for track
          // set to default
          result = @"None";
          [invocation setReturnValue:&amp;result];
        }
 
        [_cache setObject:result forKey:key];
 
      } else {
        void* buffer;
        NSUInteger length = [[invocation methodSignature] 
                             methodReturnLength];
        buffer = (void*)malloc(length);
        [invocation getReturnValue:buffer];
 
        id obj = [self mapBuffer:buffer type:returnType];
        // done with buffer
        free(buffer);
 
        // cache value
        [_cache setObject:obj forKey:key];
      }
 
    }
In this implementation we see a new object in play, specifically the NSMessageSignature and its methodReturnType. The methodReturnType is an objc type encoding. Here, we only check for the "@", or NSObject, type. In our mapBuffer:type: method we'll need to handle all the other types we care about.

mapBuffer:type:

For iTunesTrack objects, NSNumber is perfectly adequate at storing all the primitives we're interested in storing. Also, to make mapping of buffers to void pointers easier, we create a union.

    typedef union {
      char               *c;
      int                *i;
      short              *s;
      long               *l;
      long long          *q;
      unsigned char      *C;
      unsigned int       *I;
      unsigned short     *S;
      unsigned long      *L;
      unsigned long long *Q;
      float              *f;
      double             *d;
      _Bool              *B;
      void               *v;
    } TRACKDATA;
The name of each union item is the same as its type encoding key. We use the union and the appropriate type encoding to store the primitive data type in an NSNumber in the mapBuffer:type: method.
    - (id)mapBuffer:(voidPtr)buffer type:(NSString *)type
    {
      id obj;
 
      TRACKDATA data;
      data.v = buffer;
 
      if ([type isEqualToString:@"c"]) {
        obj = [NSNumber numberWithChar:*data.c];
 
      } else if ([type isEqualToString:@"i"]) {
        obj = [NSNumber numberWithInt:*data.i];
 
      } else if ([type isEqualToString:@"s"]) {
        obj = [NSNumber numberWithShort:*data.s];
 
      } else if ([type isEqualToString:@"l"]) {
        obj = [NSNumber numberWithLong:*data.l];
 
      } else if ([type isEqualToString:@"q"]) {
        obj = [NSNumber numberWithLongLong:*data.q];
 
      } else if ([type isEqualToString:@"C"]) {
        obj = [NSNumber numberWithUnsignedChar:*data.C];
 
      } else if ([type isEqualToString:@"I"]) {
        obj = [NSNumber numberWithUnsignedInt:*data.I];
 
      } else if ([type isEqualToString:@"S"]) {
        obj = [NSNumber numberWithUnsignedShort:*data.S];
 
      } else if ([type isEqualToString:@"L"]) {
        obj = [NSNumber numberWithUnsignedLong:*data.L];
 
      } else if ([type isEqualToString:@"Q"]) {
        obj = [NSNumber numberWithUnsignedLongLong:*data.Q];
 
      } else if ([type isEqualToString:@"f"]) {
        obj = [NSNumber numberWithFloat:*data.f];
 
      } else if ([type isEqualToString:@"d"]) {
        obj = [NSNumber numberWithDouble:*data.d];
 
      } else if ([type isEqualToString:@"B"]) {
        // The BOOL type is a special case, so we do a little dance here.
        BOOL val;
        if (*data.B) {
          val = YES;
        } else {
          val = NO;
        }
        obj = [NSNumber numberWithBool:val];
      } else {
        // Raise an exception if we receive a data type we&#39;re not prepared
        // for...
        [NSException 
         raise:@"Unhandled NSMethodSignature:methodReturnType:" 
         format:@"NSMethodSignature:methodReturnType: %@", type];
      }
 
      return obj;
    }

setValueFromCacheObj:invocation:

The reverse case is retrieving an object from the cache. We accomplish that by first checking if the invocation's returnType is an object and if not transforming our cache object into a primitive type with mapObject:type.

     - (void)setValueFromCacheObj:(id)obj invocation:(NSInvocation *)inv
     {  
       NSString *returnType = [NSString 
                               stringWithUTF8String:[[inv methodSignature] methodReturnType]];
 
       if ([returnType isEqualToString:@"@"]) {
         [inv setReturnValue:&amp;obj]; 
 
       } else {
         void *buffer = [self mapObject:obj type:returnType];
         [inv setReturnValue:buffer];
         // done with buffer
         free(buffer);
       }
 
     }

mapObject:type:

The reverse mapping from object to buffer is done by the mapObject:type: method. It's pretty much just the inverse of the mapBuffer:type: method.

    - (void *)mapObject:(id)obj type:(NSString *)type
    {
      void *buffer;
 
      if ([type isEqualToString:@"c"]) {
        buffer = (char *)malloc(sizeof(char));
        *(char *)buffer = [obj charValue];
 
      } else if ([type isEqualToString:@"i"]) {
        buffer = (int *)malloc(sizeof(int));
        *(int *)buffer = [obj intValue];
 
      } else if ([type isEqualToString:@"s"]) {
        buffer = (short *)malloc(sizeof(short));
        *(short *)buffer = [obj shortValue];
 
      } else if ([type isEqualToString:@"l"]) {
        buffer = (long *)malloc(sizeof(long));
        *(long *)buffer = [obj longValue];
 
      } else if ([type isEqualToString:@"q"]) {
        buffer = (long long *)malloc(sizeof(long long));
        *(long long *)buffer = [obj longLongValue];
 
      } else if ([type isEqualToString:@"C"]) {
        buffer = (unsigned char *)malloc(sizeof(unsigned char));
        *(unsigned char *)buffer = [obj unsignedCharValue];
 
      } else if ([type isEqualToString:@"I"]) {
        buffer = (unsigned int *)malloc(sizeof(unsigned int));
        *(unsigned int *)buffer = [obj unsignedIntValue];
 
      } else if ([type isEqualToString:@"S"]) {
        buffer = (unsigned short *)malloc(sizeof(unsigned short));
        *(unsigned short *)buffer = [obj unsignedShortValue];
 
      } else if ([type isEqualToString:@"L"]) {
        buffer = (unsigned long *)malloc(sizeof(unsigned long));
        *(unsigned long *)buffer = [obj unsignedLongValue];
 
      } else if ([type isEqualToString:@"Q"]) {
        buffer = (unsigned long long *)malloc(sizeof(unsigned long long));
        *(unsigned long long *)buffer = [obj unsignedLongLongValue];
 
      } else if ([type isEqualToString:@"f"]) {
        buffer = (float *)malloc(sizeof(float));
        *(float *)buffer = [obj floatValue];
 
      } else if ([type isEqualToString:@"d"]) {
        buffer = (double *)malloc(sizeof(double));
        *(double *)buffer = [obj doubleValue];
 
      } else if ([type isEqualToString:@"B"]) {
        _Bool val;
        if ([obj boolValue]) {
          val = true;
        } else {
          val = false;
        }
        buffer = (_Bool *)malloc(sizeof(_Bool));
        *(_Bool *)buffer = val;
 
      } else {
        [NSException 
         raise:@"Unhandled NSMethodSignature:methodReturnType:" 
         format:@"NSMethodSignature:methodReturnType: %@", type];
      }
      return buffer;
    }

Summary & Benchmarks

That pretty much wraps it up. We can use our iTunesProxy object as a drop in replacement for the iTunesTrack object and it will cache all values for that track. This vastly improves query intensive operations such as sorting.

In fact, we'll use sorting as a demonstration on what we've gained from our work. The following output is from the main function attached to the article.

   We retrieved 254 unproxied movies
   We retrieved 254 proxied movies
   Time elapsed for track sort: -7.293006
   Time elapsed for proxy sort: -0.567359
  
Not the fastest sort in the world, but we did get a 13x improvement bringing the performance from "application breaking" to perfectly workable. Of course, most of the overhead in that second run is from the initial caching of the track values. Let’s see what we get on a second run of our proxy array:
    Time elapsed for proxy sort second run: -0.017478
  
That's more like it! After the initial hit when caching our variables, we are rewarded with a 365x increase in sorting speed.

It's easy to see how this pattern is extendible outside the scope of iTunes tracks or even ScriptingBridge to any situation where the cost of retrieval far out weighs a minor hit to memory.

Full Source Files

Unboxing, first impressions, and 300 mile followup for Rol Wheel’s Volant/RT

2011 June 6
by James Kyle

For the write up on why I chose Rol Wheels over the many other custom and commercial options for wheels, see my original post My experience as a newbie cyclist buying his first set of non stock wheels. .

The Unboxing

Rol Wheels 1

Though packed in nothing but cardboard, the wheels were well protected and secure. It couldn’t have withstood a pallet of cinder blocks being dropped on them or anything, but appeared perfectly suitable for normal shipping bumps and bangs.

Rol Wheels 2

The wheels themselves looked absolutely sexy. Polished with nice looking decals. A quick ping test on spoke tension gave an equal tone for all spokes. I didn’t put them up on a truing rack right off the bat, but a visual check on the mounted wheels didn’t indicate any warping. In hindsight, I should have driven them up to the shop if only for a reference on the 300 mile followup. As others have mentioned on many of the Rol Wheels reviews on roadbikereview.com , the only negative as far as construction might be the plastic pokers for the hubs. But, in my book, this is a minor detail.

The Ride

Well, of course the real test of a new wheel set is when they’re doing what their meant to do…rolling! And I couldn’t have had a better impression of the Volants at that task. They spin up well and corner great. Now, as I mentioned in my original post I’m a newbie so I’m likely not pushing the absolute limits of my bike. However, I did seem to notice a difference between the Volants and my stock Mavic Open Sport’s with shimano 105 hubs. They seemed to hold the line a bit better without flex on tight turns. It could all be in my head or maybe I was just really trying to push the new wheels and just got more out of them because of that. Bottom line is, no dissappointment on that front.

The only possible negative to the wheels is the hubs are of the noisy variety when freewheeling. It doesn’t bother me, but if such things

do

bother you it’s something to take into consideration. Personally, in some ways I even like it. The only time I freewheel is when I’m slowing for other riders or pedestrians on bike paths and the audible click usually lets them know I’ve rolled up behind them. However, the noise is low enough to be drowned out by the wind for the most part.

The 300 Mile Followup

To make one thing clear off the bat, after 300+ miles of riding I don’t expect a new set of wheels to remain true. . . even if they’ve been pre-stressed. You should always do a followup check on your new wheels to make sure they held tru after teh first few weeks of riding. As such, none of the adjustments I note below in the followup should be taken as a slight against the quality of Rol Wheels.

After approximately 300 to 400 miles of road time, I brought the wheels into my local bike coop Bikerowave, free to all Santa Monica College students! While there I threw the wheels up on a truing stand.

The rear wheel had become dished toward the side with the shorter spoke angle. One thing I found odd is that it was perfectly dished, by which I mean they were evenly dished arround the entire wheel. This is something that the naked eye likely wouldn’t catch on a quick spin check unless you were paying close attention to the spacing between the wheel and the rear fork near the bottom bracket. This is why I wished I’d taken the time to bring them to the shop out of the box to see if they came from the factory like that. If they had, it may be an indication that one of Sean’s truing stands is dished. However, it’s not a big deal to correct which is exactly what I did with a few 1/4 turns around the circumference of the wheel.

The front wheel was still very, very close to tru. I tightened it up a bit bringing it to within 1mm which is good enough for government work.

Final Verdict

So, the final verdict is I’m very happy with the new wheel set. If given the choice to do over I’d make the same decision. The weight to price to performance ratio is stellar and when I called with questions Sean, the owner, was very responsive (picked up on the first ring).