<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en" xml:base="http://hackety.org/index.atom">
    <title>hackety org</title>
    <link href="http://hackety.org" rel="alternate" type="text/html" />
    
    <updated>2008-06-26T22:47:31Z</updated>
    <subtitle>filling your pockets &amp; hat with code</subtitle>
    <id>tag:hackety.org,2008:blog</id>
    <generator uri="http://hobix.com/" version="0.5">Hobix</generator>
    <rights>None</rights>
<link rel="self" href="http://feeds.feedburner.com/HacketyOrg" type="application/atom+xml" /><feedburner:browserFriendly></feedburner:browserFriendly><entry xml:base="/2008/06/26/gitHooksForDitz.html"><title xml:space="preserve">Git Hooks For Ditz</title><link href="/2008/06/26/gitHooksForDitz.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FgitHooksForDitz</id><published xml:space="preserve">2008-06-26T18:04:23Z</published><updated xml:space="preserve">2008-06-26T18:04:23Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;&lt;code&gt;~/.ditz/hooks/after_add.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;Ditz::HookManager.on :after_add do |project, config, issues|
  issues.each do |issue|
    `git add #{issue.pathname}`
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;~/.ditz/hooks/after_delete.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;Ditz::HookManager.on :after_delete do |project, config, issues|
  issues.each do |issue|
    `git rm #{issue.pathname}`
  end
end
&lt;/code&gt;&lt;/pre&gt;</content></entry><entry xml:base="/2008/06/25/callingCocoaCommandline.html"><title xml:space="preserve">Calling Cocoa Commandline</title><link href="/2008/06/25/callingCocoaCommandline.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FcallingCocoaCommandline</id><published xml:space="preserve">2008-06-25T19:31:14Z</published><updated xml:space="preserve">2008-06-25T19:31:14Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">
&lt;pre&gt;&lt;code class="objc"&gt;[win center];
[win setLevel: NSFloatingWindowLevel];
[win makeKeyAndOrderFront: nil];
&lt;/code&gt;&lt;/pre&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;The euphoria.  All of Shoes&amp;#8217; Carbon code is gone, rewritten using entirely Cocoa native code.  Not only did this shrink the native code to nearly half of its previous size, but things actually work now.  The euphoria.  And also: the elation.&lt;/p&gt;
&lt;p&gt;The only painful part was figuring out how to get started with only gcc and get it all linked with the C code.  To get away without an &lt;span class="caps"&gt;IDE&lt;/span&gt;.  It&amp;#8217;s been done in lots of other cross-platform libs, sure.  Buried under autotools and lost in deep directories.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a summary of using Cocoa from the commandline.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;A truly trivial Cocoa program reads thusly:&lt;/p&gt;

&lt;pre&gt;&lt;code class="objc"&gt;#import &amp;lt;Cocoa/Cocoa.h&amp;gt;

#define INIT    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#define RELEASE [pool release];

int
main(int argc, char *argv[])
{
  NSApplication *app = [NSApplication sharedApplication];
  INIT;

  NSWindow *win = [[NSWindow alloc] initWithContentRect: NSMakeRect(0, 0, 340, 140)
    styleMask: (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)
    backing: NSBackingStoreBuffered defer: NO];
  NSTextField *text = [[[NSTextField alloc] initWithFrame: NSMakeRect(40, 90, 260, 18)]
    autorelease];

  [[win contentView] addSubview: text];
  [text setBezeled: NO];
  [text setStringValue: @"Drink Coke-O"];
  [text setBackgroundColor: [NSColor windowBackgroundColor]];
  [text setEditable: NO];
  [text setSelectable: NO];

  [win center];
  [win orderFront: nil];

  RELEASE;
  [app run];
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save this as &lt;code&gt;coke-o.m&lt;/code&gt; and compile it (you&amp;#8217;ll need the Xcode tools installed):&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ gcc -framework Cocoa -o coke-o coke-o.m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run it: &lt;code&gt;./coke-o&lt;/code&gt; (although the window will appear behind your terminal for now.)&lt;/p&gt;
&lt;p&gt;While we&amp;#8217;re messing with gcc, let&amp;#8217;s compile it as a Universal binary.  Since Universal binaries aren&amp;#8217;t that much bigger for little progs.&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ export MACOSX_DEPLOYMENT_TARGET=10.4
$ gcc -O -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc \
    -framework Cocoa -o coke-o coke-o.m
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;Now, about the window coming up behind the Terminal.  To me, this just seems to be one of those things that Apple is so pedantic about that it ends up not really making sense when you&amp;#8217;re starting out with Cocoa.&lt;/p&gt;
&lt;p&gt;From what I can gather, this is all tied into OS X&amp;#8217;s demand that every app have a menu and an icon.  And, in order to get a menu and an icon, you&amp;#8217;ve got to get an Info.plist file and a .app directory structure and all of that.  So, that&amp;#8217;s one way of solving the problem.&lt;/p&gt;
&lt;p&gt;Since our commandline app is being launched from the terminal, though, it&amp;#8217;s just becoming another window in Terminal.app&amp;#8217;s environment.&lt;/p&gt;
&lt;p&gt;So, if you just want your app to work from the terminal, expand the &lt;code&gt;win center&lt;/code&gt; call to also move the window up to the floating level.&lt;/p&gt;

&lt;pre&gt;&lt;code class="objc"&gt;[win center];
[win setLevel: NSFloatingWindowLevel];
[win makeKeyAndOrderFront: nil];
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;In the case of the Shoes installer, I also needed to sniff out the &lt;span class="caps"&gt;CPU&lt;/span&gt; type.  I was okay having the little installer stub be a universal binary, but I really wanted to have it download other libraries specific to either PowerPC or Intel architectures.&lt;/p&gt;
&lt;p&gt;The simplest way of doing this is to use the &lt;code&gt;__ppc__&lt;/code&gt; pre-processor def.&lt;/p&gt;

&lt;pre&gt;&lt;code class="objc"&gt;NSString *url;
#ifdef __ppc__
url = @"http://hacketyhack.net/pkg/osx/shoes-ppc";
#else
url = @"http://hacketyhack.net/pkg/osx/shoes";
#endif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the universal binary will actually compile this code twice (once for &lt;code&gt;-arch i386&lt;/code&gt; and once for &lt;code&gt;-arch ppc&lt;/code&gt;,) each of the &lt;code&gt;url&lt;/code&gt; assignment lines will end up in the proper side of the binary for its architecture.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;d rather sniff out the &lt;span class="caps"&gt;CPU&lt;/span&gt; type at runtime (or if you need the &lt;span class="caps"&gt;CPU&lt;/span&gt; frequency as well,) I&amp;#8217;d look into calling &lt;code&gt;sysctl&lt;/code&gt; with the &lt;code&gt;CTL_HW&lt;/code&gt; and &lt;code&gt;HW_CPU_FREQ&lt;/code&gt; flags.  For some nice, complete examples, see the &lt;a href="http://plumber.gnu-darwin.org/home/pub/Documents/samples-c/arch_info.c"&gt;arch_info.c&lt;/a&gt; sample from the Darwin sources.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Lastly, I will mention briefly about linking to C.  While you may be able to get away with just writing plain C functions in &lt;code&gt;coke-o.m&lt;/code&gt; that can be exposed to C, you might eventually need to pass around Cocoa objects in your C structs.&lt;/p&gt;
&lt;p&gt;Since Objective-C is a superset of C, I found it easiest to just compile everything as Objective-C.  All of Cocoa&amp;#8217;s controls can be passed as C pointers.&lt;/p&gt;
&lt;p&gt;I kept all my C sources with the &lt;code&gt;.c&lt;/code&gt; extension and added &lt;code&gt;-x objective-c&lt;/code&gt; to the gcc flags when compiling vanilla C sources.  And then you can safely &lt;code&gt;#include &amp;lt;Cocoa/Cocoa.h&amp;gt;&lt;/code&gt; throughout your program.&lt;/p&gt;</content></entry><entry xml:base="/2008/06/19/stampingExesAndDmgs.html"><title xml:space="preserve">Stamping EXEs And DMGs</title><link href="/2008/06/19/stampingExesAndDmgs.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FstampingExesAndDmgs</id><published xml:space="preserve">2008-06-19T15:15:31Z</published><updated xml:space="preserve">2008-06-19T15:15:31Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-packager.png" class="c" alt="" /&gt;&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;Here&amp;#8217;s a brief Ruby script&amp;#0151;one that demos Shoes&amp;#8217; events and animation&amp;#0151;and it&amp;#8217;s been injected into an &lt;span class="caps"&gt;EXE&lt;/span&gt; and a &lt;span class="caps"&gt;DMG&lt;/span&gt;:&lt;/p&gt;
&lt;div style="text-align: center; width: 340px; margin: 0 auto"&gt;&lt;div style="width: 160px; float:left"&gt;&lt;a href="http://hackety.org/bin/accordion.exe"&gt;&lt;img src="http://hackety.org/images/accordion-exe.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://hackety.org/bin/accordion.exe"&gt;accordion.exe&lt;/a&gt;&lt;br /&gt;&lt;span style="color:#552"&gt;84.0 KB&lt;/span&gt;&lt;/div&gt;&lt;div style="width: 160px; float:left"&gt;&lt;a href="http://hackety.org/bin/accordion.dmg"&gt;&lt;img src="http://hackety.org/images/accordion-dmg.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://hackety.org/bin/accordion.dmg"&gt;accordion.dmg&lt;/a&gt;&lt;br /&gt;&lt;span style="color:#552"&gt;43.4 KB&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;/div&gt;&lt;br clear="all" /&gt;&lt;/p&gt;
&lt;p&gt;These are web installers. Using native calls on each platform, they&amp;#8217;ll install Shoes if no Shoes is found on the system.  (If you already have Shoes installed, though, it won&amp;#8217;t &lt;a href="http://code.whytheluckystiff.net/shoes/wiki/RecentBuilds"&gt;update&lt;/a&gt; for you.)  Not a big deal, not worth mentioning really.  Installers.&lt;/p&gt;
&lt;p&gt;But, hold up.  What if I told you that Shoes can build EXEs and DMGs on any of its platforms?  Without needing a compiler.  You can build EXEs and DMGs from Windows.  And from Linux.  And from OS X.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-packager.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;This is the built-in packager, which can be brought up with &lt;code&gt;shoes -p&lt;/code&gt; (or &lt;code&gt;shoes --package&lt;/code&gt; on Windows and Linux.  On OS X, it&amp;#8217;s in the Shoes main menu.  (Or ⌘-x.)&lt;/p&gt;
&lt;p&gt;Even as Ron Popeil is to the unflavored turkey, so is Shoes to these most willing binaries.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;The magic is a little Ruby extension I use for manipulating EXEs and DMGs.  It&amp;#8217;s based on two programs: &lt;a href="http://www.mikekohn.net/file_formats/anal_pe.php"&gt;anal_pe&lt;/a&gt; and &lt;a href="http://github.com/planetbeing/xpwn"&gt;xpwn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you poke around with a hex editor inside Windows&amp;#8217; PE binary format, you&amp;#8217;ll find an .rsrc section at the end of the file which contains the icons and dialog boxes.  I insert the Ruby script into this mess.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;binj = Binject::EXE.new("blank.exe")
binj.inject("SHOES_FILENAME", "simple-accordion.rb")
File.open("simple-accordion.rb") do |f|
  binj.inject("SHOES_PAYLOAD", f)
end
binj.save("accordion.exe")
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;blank.exe&lt;/strong&gt; is the empty web installer.  It&amp;#8217;s an executable that scans its own resources and then bases its moves on what it finds.  If it finds a Ruby script (or Shy file) in the &lt;code&gt;SHOES_PAYLOAD&lt;/code&gt; resource, it&amp;#8217;ll run it.  And if it finds an installer in the &lt;code&gt;SHOES_SETUP&lt;/code&gt; resource, it&amp;#8217;ll run the installer rather than going out to the web.&lt;/p&gt;
&lt;p&gt;So, yeah, &lt;strong&gt;blank.exe&lt;/strong&gt; comes with Shoes and we inject when you go to package.  Yeah?&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Fabricating DMGs is a different kind of binary hacking.  In this case, we&amp;#8217;ve got to build an HFS+ partition and then convert that to a &lt;span class="caps"&gt;DMG&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;One would start by making a small, raw HFS+ file and gzipping it.&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ dd if=/dev/zero of=blank.hfs bs=512K count=1
$ mkfs.hfsplus -v Shoes blank.hfs
$ gzip blank.hfs
$ mv blank.hfs.gz blank.hfz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This blank disk gets included with Shoes as well.  And at runtime, we use our extension to build the innards of the &lt;span class="caps"&gt;DMG&lt;/span&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;binj = Binject::DMG.new("blank.hfz", "Accordion")
binj.inject_dir("Accordion.app", "/tmp/accordion.app")
binj.chmod_file(0755, "Accordion.app/Contents/MacOS/accordion-launch")
binj.save("accordion.dmg")
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This amazing code is only possible due to tons of incredible work by the XPwn dev team.  First of all, their &lt;span class="caps"&gt;DMG&lt;/span&gt; and HFS+ code is totally portable and only depends on zlib.  But also, the &lt;span class="caps"&gt;API&lt;/span&gt; is just too easy.  Their project is going to catch on big, not only in jail breaking the iPhone from any platform, but in building DMGs from the commandline on OS X itself!&lt;/p&gt;
&lt;p&gt;Since both anal_pe and XPwn are &lt;span class="caps"&gt;GPL&lt;/span&gt;, I&amp;#8217;m afraid this extension must be &lt;span class="caps"&gt;GPL&lt;/span&gt; as well.  The rest of Shoes is &lt;span class="caps"&gt;MIT&lt;/span&gt;.  Which is okay I guess since the packager isn&amp;#8217;t really needed to run Shoes apps.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;This is all a bleeding fresh part of &lt;a href="http://code.whytheluckystiff.net/shoes/wiki/RecentBuilds"&gt;today&amp;#8217;s&lt;/a&gt; builds.&lt;/p&gt;
&lt;p&gt;As for Linux.  I&amp;#8217;m not decided as to what to do about generating Linux binaries.  Either using &lt;a href="http://megastep.org/makeself/"&gt;makeself&lt;/a&gt; or plain shell scripting will do.  But do I download binaries or automate package manager steps?  The unsurety.&lt;/p&gt;</content></entry><entry xml:base="/2008/06/12/martinDemellosGooeyChallenge.html"><title xml:space="preserve">Martin DeMello's Gooey Challenge</title><link href="/2008/06/12/martinDemellosGooeyChallenge.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FmartinDemellosGooeyChallenge</id><published xml:space="preserve">2008-06-12T17:57:48Z</published><updated xml:space="preserve">2008-06-12T17:57:48Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/304341"&gt;Martin DeMello&lt;/a&gt;: One of the most interesting facets of a desktop &lt;span class="caps"&gt;GUI&lt;/span&gt; system is how easy it makes it to go off the beaten track, particularly how well you can add &amp;#8220;first class&amp;#8221; components to the system. (Using &amp;#8216;first class&amp;#8217; here to mean &amp;#8216;on an equal footing with the widgets supplied by the toolkit&amp;#8217;). Also, as a ruby programmer, I&amp;#8217;d naturally rather not drop down into C (or Java) to do this.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So how does &lt;a href="http://code.whytheluckystiff.net/shoes/"&gt;Shoes&lt;/a&gt; hold up against this challenge?  And i mean: without hacking anything new into Shoes, using syntax introduced before the challenge was posed.&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/304341"&gt;Martin DeMello&lt;/a&gt;: One of the most interesting facets of a desktop &lt;span class="caps"&gt;GUI&lt;/span&gt; system is how easy it makes it to go off the beaten track, particularly how well you can add &amp;#8220;first class&amp;#8221; components to the system. (Using &amp;#8216;first class&amp;#8217; here to mean &amp;#8216;on an equal footing with the widgets supplied by the toolkit&amp;#8217;). Also, as a ruby programmer, I&amp;#8217;d naturally rather not drop down into C (or Java) to do this.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So how does &lt;a href="http://code.whytheluckystiff.net/shoes/"&gt;Shoes&lt;/a&gt; hold up against this challenge?  And i mean: without hacking anything new into Shoes, using syntax introduced before the challenge was posed.&lt;/p&gt;
&lt;p&gt;Well, let&amp;#8217;s go over the four parts of the contest:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;&lt;strong&gt;A component consisting of a series of existing components hooked together to act as a single widget.&lt;/strong&gt; (Such as an &lt;code&gt;icon&lt;/code&gt; widget that incorporates a picture and a textfield.  With options to turn off or size the image and make the text editable.)&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;A component built &amp;#8216;from scratch&amp;#8217; atop a canvas, that is, handling its own drawing and event management.&lt;/strong&gt; (Like a speedometer-type dial with a configurable range and tick interval.)&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;A component combining a canvas and existing widgets.&lt;/strong&gt; (For example, a box that holds a component and paints a customised border around it.)&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;A container that takes a collection of widgets and lays them out according to some userdefined algorithm.&lt;/strong&gt; (He suggests a pure-ruby implementation of a &lt;a href="http://zem.novylen.net/ruby/wrapboxdemo.png"&gt;wrapbox&lt;/a&gt;, but I thought this point might be better illustrated by a cascading container.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My initial &lt;a href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/304372"&gt;response&lt;/a&gt; had a few simple code examples, but I didn&amp;#8217;t take the chance to do all the examples just as Martin described.  That e-mail touches on how Shoes custom widgets work.&lt;/p&gt;
&lt;p&gt;Now I&amp;#8217;ve had a minute and I&amp;#8217;d like to present just my new, unadorned entries.  In each of these, a custom Shoes widget is setup by inheriting from the &lt;code&gt;Widget&lt;/code&gt; class.  And Shoes then creates a method using the lowercased name of the class which is used in the &lt;code&gt;app&lt;/code&gt;. (And you can try these out with today&amp;#8217;s super-fresh &lt;a href="http://code.whytheluckystiff.net/shoes/wiki/RecentBuilds"&gt;recent builds&lt;/a&gt;.)&lt;/p&gt;
&lt;hr /&gt;

&lt;h2&gt;Icon Widget (&lt;code&gt;challenge1.rb&lt;/code&gt;)&lt;/h2&gt;

&lt;pre&gt;&lt;code class="rb"&gt;class Icon &lt; Widget
  attr_accessor :image, :caption
  def initialize opts = {}
    @stack = stack
    @image = @stack.image(*opts[:image]) if opts[:image]
    @caption = @stack.para(*opts[:text])
  end
  def edit
    return if @edit
    @caption.hide
    @stack.append do
      @edit = edit_line :width =&gt; 200, :text =&gt; @caption
    end
  end
  def save
    return unless @edit
    @caption.replace @edit.text
    @edit.remove
    @caption.show
  end
end

Shoes.app do
  stack do
    @icon = icon :image =&gt; "static/shoes-icon.png",
                 :text =&gt; "Welcome!"

    button("image.hide") { @icon.image.hide }
    button("image.show") { @icon.image.show }
    button("image.size") { @icon.image.style :width =&gt; 64, :height =&gt; 64 }
    button("text.edit") { @icon.edit }
    button("text.save") { @icon.save }
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;h2&gt;Speedometer Widget (&lt;code&gt;challenge2.rb&lt;/code&gt;)&lt;/h2&gt;

&lt;pre&gt;&lt;code class="rb"&gt;class Speedometer &lt; Widget
  attr_accessor :range, :tick, :position
  def initialize opts = {}
    @range = opts[:range] || 200
    @tick = opts[:tick] || 10
    @position = opts[:position] || 0
    @cx, @cy = self.left + 110, self.top + 100

    nostroke
    rect :top =&gt; self.top, :left =&gt; self.left,
      :width =&gt; 220, :height =&gt; 200
    nofill
    stroke white
    oval :left =&gt; @cx - 50, :top =&gt; @cy - 50, :radius =&gt; 100
    (ticks + 1).times do |i|
      radial_line 225 + ((270.0 / ticks) * i), 70..80
      radial_line 225 + ((270.0 / ticks) * i), 45..49
    end
    strokewidth 2
    oval :left =&gt; @cx - 70, :top =&gt; @cy - 70, :radius =&gt; 140
    stroke lightgreen
    oval :left =&gt; @cx - 5, :top =&gt; @cy - 5, :radius =&gt; 10
    @needle = radial_line 225 + ((270.0 / @range) * @position), 0..90
  end
  def ticks; @range / @tick end
  def radial_line deg, r
    pos = ((deg / 360.0) * (2.0 * Math::PI)) - (Math::PI / 2.0)
    line (Math.cos(pos) * r.begin) + @cx, (Math.sin(pos) * r.begin) + @cy,
      (Math.cos(pos) * r.end) + @cx, (Math.sin(pos) * r.end) + @cy
  end
  def position= pos
    @position = pos
    @needle.remove
    append do
      @needle = radial_line 225 + ((270.0 / @range) * @position), 0..90
    end
  end
end

Shoes.app do
  stack do
    para "Enter a number between 0 and 100"
    flow do
      @p = edit_line
      button "OK" do
        @s.position = @p.text.to_i
      end
    end

    @s = speedometer :range =&gt; 100, :ticks =&gt; 10
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;h2&gt;Native Button with Custom Border (&lt;code&gt;challenge3.rb&lt;/code&gt;)&lt;/h2&gt;

&lt;pre&gt;&lt;code class="rb"&gt;class BorderButton &lt; Widget
  def initialize *args, &amp;blk
    opts = args.detect { |a| a.is_a? Hash }

    border opts[:border], :strokewidth =&gt; opts[:strokewidth]

    args[args.index(opts)] = opts.
      merge(:width =&gt; opts[:width] - (opts[:strokewidth] * 2))
    stack(:margin =&gt; opts[:strokewidth]).button *args, &amp;blk
  end
end

Shoes.app do
  borderbutton "OK", :width =&gt; 200,
    :strokewidth =&gt; 4, :border =&gt; "#000".."#FFF" do
      alert("PROOF!")
  end   
end
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;h2&gt;Cascading Container (&lt;code&gt;challenge4.rb&lt;/code&gt;)&lt;/h2&gt;

&lt;pre&gt;&lt;code class="rb"&gt;class Cascade &lt; Widget
  def initialize &amp;blk
    instance_eval &amp;blk
  end
  def draw(a,b)
    x, y = 0, 0
    contents.each do |e|
      if x != e.left &amp;&amp; y != e.top
        e.move x, y
      end
      x += e.height
      y += e.width
    end
    super(a,b)
  end
end

Shoes.app do
  cascade do
    button "1"
    button "2"
    button "3"
  end
end
&lt;/code&gt;&lt;/pre&gt;</content></entry><entry xml:base="/2008/06/09/dismantlingBrowserPlus.html"><title xml:space="preserve">Dismantling BrowserPlus</title><link href="/2008/06/09/dismantlingBrowserPlus.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FdismantlingBrowserPlus</id><published xml:space="preserve">2008-06-09T08:58:47Z</published><updated xml:space="preserve">2008-06-09T08:58:47Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;I had to peek a little.  And to pry it open like the upturned crab it was.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/bplus.png" class="cb" alt="" /&gt;&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;So, Yahoo!&amp;#8217;s &lt;a href="http://browserplus.yahoo.com/"&gt;BrowserPlus&lt;/a&gt; came out of nowhere.  And, well, it does other things, but I want to look at how it embeds Ruby in the browser.  I know, I know.  Enough with Ruby in the browser.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/bplus.png" class="cb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;But I had to peek a little.  And to pry it open like the upturned crab it was.  Don&amp;#8217;t ask me if they are doing the &lt;strong&gt;right&lt;/strong&gt; thing or if they are going to &lt;strong&gt;win&lt;/strong&gt; or anything.  (I look at Facebook and all I see is a guy in an over-starched shirt with a blue collar who leans on the wall with his legs crossed and has to analyze everything I say &lt;em&gt;to death&lt;/em&gt;.  Go peg your pants, guy.)&lt;/p&gt;
&lt;p&gt;I wanted to find out if the interpreter was sandboxed, first of all.  And, next, I wanted to set up an &lt;code&gt;eval&lt;/code&gt; method so you can run arbitrary Ruby code.&lt;/p&gt;
&lt;p&gt;And to what end?  Come on, everybody quit asking me that.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Actually, BrowserPlus doesn&amp;#8217;t come with a Ruby interpreter.  Ruby is an addition you can download and a couple of the samples use it for talking to Flickr and for storing marshalled data.  (Their equivalent to Google Gears&amp;#8217; SQLite is just a PStore wrapper?)  The Photo Uploader and &lt;span class="caps"&gt;IRC&lt;/span&gt; Client &lt;a href="http://browserplus.yahoo.com/demos/"&gt;demos&lt;/a&gt; both use the Ruby interp.&lt;/p&gt;
&lt;p&gt;It also doesn&amp;#8217;t really embed Ruby directly in the browser.  Plus runs as a separate process that you pass calls out to.  The process hosts these additions called &lt;em&gt;services&lt;/em&gt; or &lt;em&gt;corelets&lt;/em&gt; (but I like to call them roo-dads!)  And they are sort of like a ruby gem.  A versioned library that&amp;#8217;s downloaded only when you need it.&lt;/p&gt;
&lt;p&gt;Okay, so that&amp;#8217;s the nutshell.  And the hacks hereforward will be:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;A web app which serves up the roo-dads.&lt;/li&gt;
	&lt;li&gt;Some OpenSSL commandline scripting to sign them.&lt;/li&gt;
	&lt;li&gt;Some Ruby scripts in a zip file to act as our sample.&lt;/li&gt;
	&lt;li&gt;Injecting a library into their RubyInterpreter corelet.&lt;/li&gt;
	&lt;li&gt;And a few hacks on the user-side to get things opened up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After you&amp;#8217;ve got a handle on these hacks, you&amp;#8217;ll be able to screw around with Plus yourself, rather than being confined to the restricted set of demos on the Yahoo! dev site.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Let&amp;#8217;s free up Plus first.  Since this is just a beta release, the Plus team have it all locked up so you can only use it on the &lt;code&gt;browserplus.yahoo.com&lt;/code&gt; subdomain.&lt;/p&gt;
&lt;p&gt;Step one.  Edit &lt;strong&gt;BrowserPlus.config&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;On Windows, look for &lt;code&gt;C:\Program Files\Yahoo! BrowserPlus\2.0.4\BrowserPlus.config&lt;/code&gt;.&lt;/li&gt;
	&lt;li&gt;On OS X, it&amp;#8217;s at &lt;code&gt;$HOME/Library/Application Support/Yahoo!/BrowserPlus/2.0.4/BrowserPlus.config&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class="js"&gt;/* THIS FILE IS GENERATED BY THE BUILD SYSTEM.  EDIT THE CORRESPONDING .cmakeIn FILE.
 */

/*
 * This is the configuration file for BrowserPlus
 */

{
   // the base url for the "primary" distribution server.  This server will
   // be the single source of truth for Permissions, and will used to
   // attain services
   "DistServer": "http://browserplus.yahoo.com",

   // An array of "secondary" distribution servers, which will be checked
   // in order for services if the primary server has no components
   // available which match an issued require statement.
   "SecondaryDistServers": [ "" ],

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alter the &lt;code&gt;DistServer&lt;/code&gt; line.  Change &lt;code&gt;"http://browserplus.yahoo.com"&lt;/code&gt; to &lt;code&gt;"http://plus.hackety.org"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Second step, we&amp;#8217;ve got to add some S/&lt;span class="caps"&gt;MIME&lt;/span&gt; keys.  I can&amp;#8217;t sign stuff as Yahoo! considering that I lack their private keys.&lt;/p&gt;
&lt;p&gt;Download &lt;a href="http://hackety.org/crt/BrowserPlus.crt"&gt;BrowserPlus.crt&lt;/a&gt; and copy to the &lt;strong&gt;Permissions&lt;/strong&gt; folder.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;On Windows, this is at &lt;code&gt;C:\Program Files\Yahoo! BrowserPlus\Permissions\BrowserPlus.crt&lt;/code&gt;.&lt;/li&gt;
	&lt;li&gt;On OS X, go to &lt;code&gt;$HOME/Library/Application Support/Yahoo!/BrowserPlus/Permissions/BrowserPlus.crt&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&amp;#8217;t worry about overwriting the file.  Yahoo!&amp;#8217;s cert is still in there as well.&lt;/p&gt;
&lt;p&gt;Third and last step, alter the &lt;strong&gt;Permissions&lt;/strong&gt; file.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;On Windows, you&amp;#8217;ll want &lt;code&gt;%LOCALAPPDATA%/Yahoo!/BrowserPlus/Permissions/Permissions&lt;/code&gt;.&lt;/li&gt;
	&lt;li&gt;On OS X, edit &lt;code&gt;$HOME/Library/Application Support/Yahoo!/BrowserPlus/Permissions/Permissions&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class="js"&gt;{
    // What domains are approved?  Entries are PCRE regular expressions
    // which must match the scheme://host:port of the requesting URI.
    // Regular expressions must be anchored with $.
    // Note that the PCRE expression may need to be protected in order
    // to be valid JSON (e.g. a '\' needs to be '\\')
    //
    "whitelist" : [
      "^http(s?)://(.*)\\.yahoo\\.com$",
      "^http(s?)://(.*)\\.yahoo\\.com:[0-9]+$"
    ],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add to the &lt;code&gt;"whitelist"&lt;/code&gt; array an entry for hackety.org: &lt;code&gt;"^http(s?)://(.*)\\.hackety\\.org$"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to write your own web pages using BrowserPlus, you can follow that last step for your site as well.  However, to actually write Ruby extensions, you&amp;#8217;ll need a cert and a dist server of your own, until Yahoo! opens up their dist server.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;The certificate is an &lt;a href="http://kb.mozillazine.org/Getting_an_SMIME_certificate"&gt;S/&lt;span class="caps"&gt;MIME&lt;/span&gt; cert&lt;/a&gt;.  Which is used for signing e-mail.  In fact, all the meat cutlet core-dads get served as &lt;code&gt;smime.p7m&lt;/code&gt;.  They look like e-mail attachments.  (Here&amp;#8217;s &lt;a href="http://browserplus.yahoo.com/api/v1/corelet/strings/ImageAlter/1.2.6/win32"&gt;one&lt;/a&gt; for your inspection, if you like.)&lt;/p&gt;
&lt;p&gt;So, let&amp;#8217;s say you get a cert and end up with a &lt;span class="caps"&gt;PEM&lt;/span&gt; file that has a list of certs and your private key.  You&amp;#8217;ll want to split that file up into separate files.  From a &lt;a href="http://www.kfu.com/~nsayer/encryption/openssl.html"&gt;really great &lt;span class="caps"&gt;HOWTO&lt;/span&gt;&lt;/a&gt; that explains it all:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You should get out a bunch of certificates. You&amp;#8217;ll need to look at the text above each one to find the one that is your certificate. The rest are part of Thawte&amp;#8217;s Certifying Authority. It turns out that if you want your messages to verify correctly, you must also include Thawte&amp;#8217;s intermediate CA key.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;There should be 3 certificates. The one whose identity is your e-mail address is your certificate. The one whose subject and issue are identical is the Thawte CA root. You won&amp;#8217;t need that one, since we&amp;#8217;ll include it in the trusted root file later. The 3rd one will have the CA root as the issuer and something else as the subject (which will be the same as the issuer of your certificate). You need to save that vertificate as an additional certificate for signing. We&amp;#8217;ll refer to the file containing this cert as othercert.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To &lt;code&gt;BrowserPlus.crt&lt;/code&gt;, you add the 3 certificates.  If your CA is a common one, you can probably omit the CA root certificate.&lt;/p&gt;
&lt;p&gt;As for signing the payload, you take a zip with Ruby scripts inside and use &lt;code&gt;openssl&lt;/code&gt; from the commandline.&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ openssl smime -sign -inkey HacketyPlus.key -signer HacketyPlus.crt \
    -certfile HacketyPlus.other -in Payload-1.0.0.zip -out Payload-1.0.0.p7m \
    -nodetach -binary
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can test unpacking the payload with your master &lt;code&gt;BrowserPlus.crt&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ openssl smime -verify -CAfile BrowserPlus.crt \
    -in Payload-1.0.0.p7m -out Payload-1.0.0.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, yeah, cert created.  Signing and unpacking is good.&lt;/p&gt;
&lt;p&gt;The zip itself should have Ruby scripts and a &lt;code&gt;manifest.json&lt;/code&gt; in the root.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  Eval-1.0.0.zip:
    manifest.json
    eval.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;Now, what goes in the Ruby scripts?  Here&amp;#8217;s a trivial example:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;
class Eval
  def initialize(args)
  end

  def eval(bp, args)
    begin
      code = args['code']
      obj = Kernel.eval(code, TOPLEVEL_BINDING)
      bp.complete(obj.inspect)
    rescue Exception =&gt; err
      bp.error('FAIL', "Error: #{err}")
    end
  end
end

RubyCoreletDefinition = {
  'class' =&gt; "Eval",
  'name' =&gt; "Eval",
  'major_version' =&gt; 1,
  'minor_version' =&gt; 0,
  'micro_version' =&gt; 0,
  'documentation' =&gt; 'Run any Ruby whatsoever.',
  'functions' =&gt;
  [
    { 
      'name' =&gt; 'eval',
      'documentation' =&gt; "Executes a string of Ruby code.
         Returns a string of the inspected data.",
      'arguments' =&gt;
      [ 
        { 
          'name'          =&gt; 'code',
          'type'          =&gt; 'string',
          'required'      =&gt; true,
          'documentation' =&gt; 'The code to execute.'
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gah.  That&amp;#8217;s a pretty arduous Hello World.  The definition at the bottom declares the signature of every method you expose to JavaScript.&lt;/p&gt;
&lt;p&gt;Take particular note of the &lt;code&gt;eval&lt;/code&gt; method.  For every method you offer up, you get a pair of arguments: the first being a &lt;code&gt;bp&lt;/code&gt; object for communicating with BrowserPlus and an &lt;code&gt;args&lt;/code&gt; hash containing the incoming &lt;span class="caps"&gt;JSON&lt;/span&gt;-marshalled object.&lt;/p&gt;
&lt;p&gt;We need not go through that mess, though.  Since I was able to unpack the RubyInterpreter corelet, I&amp;#8217;ve injected a bit of code into my version (3.1.2) to make this more comfortable for you.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;
class Eval
  bp_version "1.0.0"
  bp_doc "Run any Ruby whatsoever."

  def eval(code)
    obj = Kernel.eval(code, TOPLEVEL_BINDING)
    obj.inspect
  end
  bp_doc :eval, 
    "Executes a string of Ruby code.
     (code: string) The code to execute."
end

RubyCoreletDefinition = Eval.to_corelet
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code that makes this possible is found in &lt;a href="http://github.com/why/fakeplus/tree/master/stdlib/bp_hacks.rb"&gt;stdlib/bp_hacks.rb&lt;/a&gt; in the RubyInterpreter 3.1.2 download.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;As for serving up all these libs, I give you &lt;a href="http://github.com/why/fakeplus"&gt;FakePlus&lt;/a&gt;.  This is a very small Camping app, accompanied by a &lt;span class="caps"&gt;YAML&lt;/span&gt; index.  The index lists all the available additions.  The Camping app mimicks Yahoo!&amp;#8217;s web service at &lt;code&gt;browserplus.yahoo.com/api&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, you can mess with the Eval addition by visiting &lt;a href="http://plus.hackety.org/eval.html"&gt;here&lt;/a&gt; once you&amp;#8217;ve got the hacks at the beginning all nicely in place.  And I think if you horse around on that page for a little while, it will become very evident whether things are sandboxed or not.&lt;/p&gt;</content></entry><entry xml:base="/2008/05/31/desmalling.html"><title xml:space="preserve">De-smalling the Built-in Shoes Manual</title><link href="/2008/05/31/desmalling.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2Fdesmalling</id><published xml:space="preserve">2008-05-31T21:09:07Z</published><updated xml:space="preserve">2008-05-31T21:09:07Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;Shoes must cease growing to keep claiming its tinyness.  Except for the manual.  Let the manual bloat.  I want a fantastic manual.  The manual is pretty much the best Shoes app out there!&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-manual-1.png" class="c" alt="" /&gt;&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;Shoes must cease growing to keep claiming its tinyness.  Except for the manual.  Let the manual bloat.  I want a fantastic manual.  The manual is pretty much the best Shoes app out there!&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-manual-1.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;This is the built-in manual.  It&amp;#8217;s not terribly gorgeous yet.&lt;/p&gt;
&lt;p&gt;Use: &lt;code&gt;shoes -m&lt;/code&gt;.  Or Alt-Question-Mark on any Shoes app.  And there it is.  (In &lt;a href="http://code.whytheluckystiff.net/shoes/wiki/RecentBuilds"&gt;recent builds&lt;/a&gt;.)&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;If you&amp;#8217;ve not heard of &lt;a href="http://code.whytheluckystiff.net/shoes/"&gt;Shoes&lt;/a&gt;, it&amp;#8217;s a kit for writing graphical apps in Ruby.  I hope that it strikes balance between easiness and usefulness.  Some folks write &lt;a href="http://the-shoebox.org"&gt;games&lt;/a&gt; for Shoes.  I&amp;#8217;m writing a &lt;a href="http://hacketyhack.net/"&gt;teaching tool&lt;/a&gt; in Shoes.  The kit is pretty flexible, but concentrates on doing a very small number of things well.&lt;/p&gt;
&lt;p&gt;Like, for instance, there are &lt;a href="http://hackety.org/press/nks-9.html"&gt;ten essential&lt;/a&gt; commands in Shoes that are used in absolutely everything.&lt;/p&gt;
&lt;p&gt;Beyond those ten things, though, are a hundred more things.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-manual-2.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Shoes has about a hundred methods total for creating new elements, applying effects, manipulating the app, events, and all of that.  Most of these things just give beginners a place to go once they get past the basics.&lt;/p&gt;
&lt;p&gt;Add another hundred methods on top of that among all the various objects you can create.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Rather than memorizing those methods, I recommend to beginners that they commit &lt;strong&gt;only the ten essentials&lt;/strong&gt; to memory.  And, for everything else, use the manual&amp;#8217;s built-in search.&lt;/p&gt;
&lt;p&gt;Looking to draw a rectangle?  &lt;strong&gt;Use the search.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-manual-3.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;No submit button, it searches as you type.&lt;/p&gt;
&lt;p&gt;Search results display as a list of links throughout the manual.  Method links take you straight to that method.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-manual-4.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m only halfway completed with documenting Shoes.  But, it will be done for the next release.&lt;/p&gt;
&lt;p&gt;For me, having a good manual is more important than even Shoes&amp;#8217; stability, perhaps.  As one of the only app kits out there focused on beginners, it&amp;#8217;s crucial that Shoes become an example of how to innovate the way people learn.  I think having a really capable search in the built-in manual is a huge part of that.&lt;/p&gt;
&lt;p&gt;Another part is having a lot of examples that can be executed right there in the manual text (like you see with docs for jQuery or Scriptaculous.)  Processing&amp;#8217;s docs definitely benefit from having working applets next to their examples.&lt;/p&gt;
&lt;p&gt;To have the freedom to alter the code in the docs to get some understanding might really have an impact.&lt;/p&gt;</content></entry><entry xml:base="/2008/05/23/yamlOneTwo.html"><title xml:space="preserve">YAML 1.2 Ensconces and Envelopes JSON</title><link href="/2008/05/23/yamlOneTwo.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FyamlOneTwo</id><published xml:space="preserve">2008-05-23T18:13:29Z</published><updated xml:space="preserve">2008-05-23T18:13:29Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://yaml.org/spec/1.2/#id2560236"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt; 1.2&lt;/a&gt;: &lt;span class="caps"&gt;YAML&lt;/span&gt; can therefore be viewed as a natural superset of &lt;span class="caps"&gt;JSON&lt;/span&gt;, offering improved human readability and a more complete information model. This is also the case in practice; every &lt;span class="caps"&gt;JSON&lt;/span&gt; file is also a valid &lt;span class="caps"&gt;YAML&lt;/span&gt; file. This makes it easy to migrate from &lt;span class="caps"&gt;JSON&lt;/span&gt; to &lt;span class="caps"&gt;YAML&lt;/span&gt; if/when the additional features are required.&lt;/p&gt;
&lt;/blockquote&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;Hey, some progress.  Three-and-a-half years after the working draft of &lt;span class="caps"&gt;YAML&lt;/span&gt; 1.1, Oren Ben-Kiki has put up &lt;a href="http://yaml.org/spec/1.2/"&gt;a draft&lt;/a&gt; of &lt;span class="caps"&gt;YAML&lt;/span&gt; 1.2.  The big deal here is (at last) complete compatibility with &lt;span class="caps"&gt;JSON&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;From the new section (1.4) &lt;a href="http://yaml.org/spec/1.2/#id2560236"&gt;Relation to &lt;span class="caps"&gt;JSON&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt; can therefore be viewed as a natural superset of &lt;span class="caps"&gt;JSON&lt;/span&gt;, offering improved human readability and a more complete information model. This is also the case in practice; every &lt;span class="caps"&gt;JSON&lt;/span&gt; file is also a valid &lt;span class="caps"&gt;YAML&lt;/span&gt; file. This makes it easy to migrate from &lt;span class="caps"&gt;JSON&lt;/span&gt; to &lt;span class="caps"&gt;YAML&lt;/span&gt; if/when the additional features are required.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This took some movement from the triumverate (Oren, Ingy, Clark) who govern &lt;span class="caps"&gt;YAML&lt;/span&gt;.  JSON&amp;#8217;s adoption dwarfs YAML&amp;#8217;s.  They have always been very, very close.  But always cigarless.  The issues have been &lt;a href="http://groups.google.com/group/yaml-core/browse_thread/thread/ea6d1a3d9724e628"&gt;encoding&lt;/a&gt;, whitespace, escape characters and the &lt;a href="http://groups.google.com/group/yaml-core/browse_thread/thread/5762db91764efc34"&gt;colon issue&lt;/a&gt; (fourth paragraph.)&lt;/p&gt;
&lt;p&gt;Still, &lt;span class="caps"&gt;YAML&lt;/span&gt; has seen some in-roads with nods from Rails and RubyGems and App Engine.  Which is surprising since &lt;span class="caps"&gt;YAML&lt;/span&gt; development has always been slow.  Writing a &lt;span class="caps"&gt;YAML&lt;/span&gt; parser is pretty difficult.&lt;/p&gt;
&lt;p&gt;Finally, these discrepancies are all ironed out.  And, so, &lt;span class="caps"&gt;YAML&lt;/span&gt; 1.2 describes itself as &amp;#8220;mostly a superset of version 1.1, defined for the purpose of ensuring &lt;span class="caps"&gt;JSON&lt;/span&gt; compatibility.&amp;#8221;  Uhh, let&amp;#8217;s see, what else can I tell you&amp;#8230; The name &lt;em&gt;&lt;span class="caps"&gt;JSON&lt;/span&gt;&lt;/em&gt; appears 72 times?&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Oh, and, the new &lt;a href="http://yaml.org"&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;.org&lt;/a&gt; is sensational.  A snapshot, in case it changes one day:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/yaml-org.png" class="cb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Reminiscent of the &lt;a href="http://clarkevans.com"&gt;home page&lt;/a&gt; of Clark Evans, one of YAML&amp;#8217;s creators.  Eat whitespace, microformats!  I&amp;#8217;m really glad to see &lt;span class="caps"&gt;YAML&lt;/span&gt;.org embrace the two-space indent, though.&lt;/p&gt;
&lt;p&gt;The odd part of the &lt;span class="caps"&gt;YAML&lt;/span&gt;.org serialization is the news section:&lt;/p&gt;

&lt;pre&gt;&lt;code class="yaml"&gt;News:
  - 11-MAY-2008 -- Oren Ben-Kiki has released a new YAML 1.2 spec draft.
  - 29-NOV-2007 -- Alexey Zakhlestin has updated his Syck (YAML 1.0) binding for PHP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of these one-liner announcements is an entry in a &lt;span class="caps"&gt;YAML&lt;/span&gt; array.  However, the line itself is just a string.  Despite having a timestamp format and inline lists, here we&amp;#8217;ve got an array of strings.&lt;/p&gt;
&lt;p&gt;A nested array means no extra parser to read the dates.&lt;/p&gt;

&lt;pre&gt;&lt;code class="yaml"&gt;News:
  - [2008-05-11, Oren Ben-Kiki has released a new YAML 1.2 spec draft.]
  - [2007-12-29, Alexey Zakhlestin has updated his Syck (YAML 1.0) binding for PHP]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now the page is its own news feed.  If you remove all the &lt;span class="caps"&gt;HTML&lt;/span&gt; tags and serve it as text/x-yaml, that is.&lt;/p&gt;</content></entry><entry xml:base="/2008/05/22/theImageBlockAtTheBottomOfShoes.html"><title xml:space="preserve">The Image Block At The Bottom Of Shoes</title><link href="/2008/05/22/theImageBlockAtTheBottomOfShoes.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FtheImageBlockAtTheBottomOfShoes</id><published xml:space="preserve">2008-05-22T23:57:35Z</published><updated xml:space="preserve">2008-05-22T23:57:35Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;This image syntax creates a bitmap surface in memory.  Upon which shapes are drawn on to.  And it&amp;#8217;s then treated like an image loaded from a file.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-image-4.jpg" class="c" alt="" /&gt;&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-image-1.png" class="r" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;In Shoes, images have been much as they are in &lt;span class="caps"&gt;HTML&lt;/span&gt;.  Generally, a path to the image.  And maybe its width and height.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;Shoes.app do
  image "static/shoes-icon.png"
end
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;You have a picture window.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;In recent times, some Shoesers have been eschewing images and preferring to fabricate the shapes on their own.  An example would be the minesweeper &lt;a href="http://the-shoebox.org/apps/36"&gt;clone&lt;/a&gt;, which paints the circles and lines for all the bombs and flags.  Probably to keep things as a single Ruby script.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-image-2.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;The little bombs get drawn with something akin to this &lt;code&gt;bomb&lt;/code&gt; method.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;module Arsenal
  def bomb x, y
    nostroke
    fill "#000"
    oval x+3, y+3, 13
    fill "#333"
    oval x+5, y+5, 7
    fill "#AAA"
    oval x+6, y+6, 3

    fill "#000"
    stroke "#222"
    strokewidth 2
    oval x+12, y+3, 2
    oval x+14, y+3, 1
  end
end

Shoes.app do
  extend Arsenal
  background white
  bomb 0, 0
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, the bomb gets drawn at &lt;code&gt;0, 0&lt;/code&gt;.  Holy goalie, though.  Look at all that math.  And the worst part of it is that every time you move the bomb, you&amp;#8217;ve got to recompute and redraw everything.  Either that or you&amp;#8217;ve got to move each little oval individually.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-image-3.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Rather, let&amp;#8217;s draw a bomb inside an image block.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;module Arsenal
  def bomb x, y
    image 20, 20, :top =&gt; x, :left =&gt; y do
      nostroke
      fill "#000"
      oval 3, 3, 13
      fill "#333"
      oval 5, 5, 7    
      fill "#AAA"
      oval 6, 6, 3    

      fill "#000"
      stroke "#222"
      strokewidth 2
      oval 12, 3, 2
      oval 14, 3, 1
    end
  end
end
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;This image syntax creates a bitmap surface in memory.  In this case: 20 pixels by 20 pixels.  Upon which shapes are drawn on to.  And it&amp;#8217;s then treated like an image loaded from a file.&lt;/p&gt;
&lt;p&gt;The advantages in this case are:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The image block is only drawn &lt;strong&gt;once&lt;/strong&gt;.  So faster paint times.  Shapes aren&amp;#8217;t constantly recomputed.&lt;/li&gt;
	&lt;li&gt;Everything is positioned according to the image&amp;#8217;s origin.  Less math.  (Notice no &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; used in the image block.)&lt;/li&gt;
	&lt;li&gt;Image effects (such as &lt;code&gt;blur&lt;/code&gt; and &lt;code&gt;shadow&lt;/code&gt; effects) can be applied to this image.&lt;/li&gt;
	&lt;li&gt;Can be moved and resized as a single image.  Conceptually easier, I guess.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;Shoes is pretty meager on image effects right now, but a good example of using the &lt;code&gt;blur&lt;/code&gt; effect is in &lt;a href="http://github.com/why/shoes/tree/master/samples/simple-sphere.rb"&gt;samples/simple-sphere.rb&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-image-4.jpg" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Alls I did was follow &lt;a href="http://www.axialis.com/tutorials/tutorial-misc003.html"&gt;this&lt;/a&gt; Photoshop tutorial, each of its steps for piling on the inner and outer glows and gradients.  This was a lot of fun to do and I think could be the basis of a pretty inspiring class for kids.  Give them a Photoshop tutorial and work through expressing each of its steps in code.&lt;/p&gt;
&lt;p&gt;You will need a &lt;a href="http://code.whytheluckystiff.net/shoes/wiki/RecentBuilds"&gt;recent build&lt;/a&gt; to make any of this fly, though.&lt;/p&gt;</content></entry><entry xml:base="/2008/05/16/blimlimb.html"><title xml:space="preserve">BlimLimb, the Travelling Bot Troupe</title><link href="/2008/05/16/blimlimb.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2Fblimlimb</id><published xml:space="preserve">2008-05-16T16:39:25Z</published><updated xml:space="preserve">2008-05-16T16:39:25Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">
&lt;pre&gt;&lt;code class="blimlimb"&gt;s: oh, take heart, young lad.
s: after all, we are most definitely the /ONE', the /ONLY', the .(OFFICIAL).
s: entertainers of #camping, a gift from your KING&lt; a true offering
s: of haste and scenic tales &gt;PERFORMING TODAY!&lt; for your gifted
(a) gasps.
s: minds, to lift your spirits and to spread luxurious laughter, as
b: ** the show starts in roughly -- checks watch -- four minutes -- get comfortable. **
s: it were a catapult travelling o'er the oval moons and roof boats!!
a: :) alright
a: i am aidelaide.
s: and:            i am starlight huxley!
b: and: :)                               i am b.e.o. stepfisdein.
s:    &gt;&gt;     we are blimLimb, the bot troupe!        &lt;&lt;&lt;
b: previously known as blimLimb limLirrm, the bot troupe.
a: we go by either :)
F: I PLAY MYSELF.
&lt;/code&gt;&lt;/pre&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;I thought I had deleted this hack!  I probably should still delete this hack, it was so poorly received!  To quote those who were there: &amp;#8220;the implementation lacketh much.&amp;#8221;  Oh, and: &amp;#8220;what&amp;#8217;s going on?&amp;#8221;&lt;/p&gt;
&lt;p&gt;Well, the idea was: what if you had a dozen bots that you could feed dramatic scripts to and they would perform the show in synchronization on &lt;span class="caps"&gt;IRC&lt;/span&gt;?  And, furthermore, what if you could stop the bots and issue ad-hoc improvisational scripting to really spook the guests?  To give the idea that the bots are either pretty smart or pretty broken. Low-bandwidth txt vaudville, here we come!&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;The troupe was called &lt;a href="http://github.com/why/blimlimb"&gt;BlimLimb&lt;/a&gt; (previously known as the Tragicepticons).&lt;/p&gt;

&lt;pre&gt;&lt;code class="blimlimb"&gt;s: oh, take heart, young lad.
s: after all, we are most definitely the /ONE', the /ONLY', the .(OFFICIAL).
s: entertainers of #camping, a gift from your KING&lt; a true offering
s: of haste and scenic tales &gt;PERFORMING TODAY!&lt; for your gifted
(a) gasps.
s: minds, to lift your spirits and to spread luxurious laughter, as
b: ** the show starts in roughly -- checks watch -- four minutes -- get comfortable. **
s: it were a catapult travelling o'er the oval moons and roof boats!!
a: :) alright
a: i am aidelaide.
s: and:            i am starlight huxley!
b: and: :)                               i am b.e.o. stepfisdein.
s:    &gt;&gt;     we are blimLimb, the bot troupe!        &lt;&lt;&lt;
b: previously known as blimLimb limLirrm, the bot troupe.
a: we go by either :)
F: I PLAY MYSELF.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scripts follow an extremely simple syntax.  Every bot has a short alias.  So, in the above script, Starlight Huxley is abbreviated as &lt;strong&gt;s&lt;/strong&gt;.  You set up the aliases at the beginning of the script.&lt;/p&gt;

&lt;pre&gt;&lt;code class="blimlimb"&gt;[a] aidelaide
[F] FNONt
[t] teslyANDERSowens
[s] ^sHuxley^
a: hello
s: (( //......\\'''''''//.....\\ ))
s:  )) &lt;good&gt;&lt;day&gt;&lt;#camping&gt;&lt;!&gt; ((
[b] beo_stepfIsDein
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the square brackets contain the bot&amp;#8217;s short alias.  Followed by the nick the bot will use in the channel.  Those lines also cause the bot to join the channel.&lt;/p&gt;
&lt;p&gt;Other commands are:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;alias:&lt;/code&gt; speaks a line of dialogue.&lt;/li&gt;
	&lt;li&gt;&lt;code class="alias"&gt;alias)&lt;/code&gt; performs an action or aside.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;alias=&lt;/code&gt; re-nicks the bot.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;-alias&lt;/code&gt; causes a bot to part the channel.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;+n&lt;/strong&gt; delays for &lt;strong&gt;n&lt;/strong&gt; seconds.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;*&lt;/code&gt; enters puppet mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A short sample &lt;a href="http://github.com/why/blimlimb/tree/master/scripts/the.unchanging.monkey.that.smiled"&gt;script&lt;/a&gt; illustrates each of these.&lt;/p&gt;
&lt;p&gt;All the while, you, Gepetto, are watching the console.  Dialogue is spewed by the bots with pseudorandom timing, to avoid flooding and make things a bit more stilted.  Once you hit puppet mode, you gain control of the console and can use all the commands above to control the bots.  And you can use the asterisk command to deliver the bots back to their script.&lt;/p&gt;
&lt;p&gt;Annoying, yes.  Certainly that is the whole idea.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;The cast had six players in all:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;starlight huxley&lt;/strong&gt;, he&amp;#8217;s a part-time electrician and deeply religious bot, wears glasses, central figure in the troupe, calls on volunteers, hands out cash prizes, handsome but elderly, easily pushed over.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;aidelaide&lt;/strong&gt;, she&amp;#8217;s a hillbilly acrobat, largely sticks to doing the splits and winking, some light &lt;span class="caps"&gt;ASCII&lt;/span&gt; art, emotionally needy, always trying to smooth over conflict, married to &lt;strong&gt;tesly anders owens&lt;/strong&gt;, has a ponytail.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;b.e.o. stepfinsdein&lt;/strong&gt;, retired biplane pilot, nationally-ranked lightbulb collector, counterpart to starlight huxley, heavy on the &lt;span class="caps"&gt;ASCII&lt;/span&gt; art, pompous and neoconservative.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;FNONt&lt;/strong&gt;, an ancient and boxy bot, yelly and numeric.  May not actually be in the troupe, possibly just heat-seeking.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;poor kit kalloway&lt;/strong&gt;, the naive understudy, shoeless, sometimes sleeveless, constantly leeching movie soundtrack mp3s from bittorrent, adores Peter Gallagher, stubborn and plays viola.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;tesly anders owens&lt;/strong&gt;, a smoker, man about town, given to fine apparels, handles the spotlight, estranged from the others, enjoys fisticuffs with audience members.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;

&lt;p&gt;I only have logs from two shows.  Both were total disasters.&lt;/p&gt;
&lt;p&gt;The first was a showing of &lt;a href="http://github.com/why/blimlimb/tree/master/logs/edward.and.his.book.knowledge.txt"&gt;Edward and His Book Knowledge&lt;/a&gt; from November 9th, 2006.  This was the first script and it was clearly pretty empty, mostly just a test.  The trouble is that the scripts are heavy on participating with the crowd and the crowd is, well, idling, of course.&lt;/p&gt;
&lt;p&gt;And the second was a bit better.  This one was titled &lt;a href="http://github.com/why/blimlimb/tree/master/logs/prin.and.whitely.txt"&gt;Prin and Whitely&lt;/a&gt; and the show lasted about an hour and a half on November 10th, 2006.  A bit more reaction here, but mostly ambivalence.  And by the end, the channel is totally dead.&lt;/p&gt;
&lt;p&gt;And that&amp;#8217;s how Freenode&amp;#8217;s only travelling &lt;span class="caps"&gt;IRC&lt;/span&gt; bot troupe bombed on opening night.&lt;/p&gt;</content></entry><entry xml:base="/2008/05/09/someChromeForPjs.html"><title xml:space="preserve">Some Chrome For Pjs</title><link href="/2008/05/09/someChromeForPjs.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FsomeChromeForPjs</id><published xml:space="preserve">2008-05-09T19:24:15Z</published><updated xml:space="preserve">2008-05-09T19:24:15Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;So, yeah, &lt;a href="http://ejohn.org/blog/processingjs/"&gt;Processing.js&lt;/a&gt;.  Big fans of Pjs in this vicinity.  Doesn&amp;#8217;t do all that Processing does (in the way of: video exports, 3-D, and plugin support) but it&amp;#8217;s totally amazing what it does with a few K of JavaScript and a browser.  This will wipe out the applet completely.&lt;/p&gt;
&lt;p&gt;Rather than just link to it and yelp &lt;em&gt;&lt;span class="caps"&gt;HOORAY&lt;/span&gt;!&lt;/em&gt;, I thought I&amp;#8217;d keep Resig&amp;#8217;s birthday jaunting along with some &lt;span class="caps"&gt;XUL&lt;/span&gt; chrome, designed to mimick the real Processing.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://github.com/why/processor"&gt;&lt;img src="http://hackety.org/images/processor.png" class="c" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One great thing about this is that you can run this app if you have Firefox 3.&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;firefox -app /path/to/processor/application.ini
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Processing.js is included in the app&amp;#8217;s &lt;a href="http://github.com/why/processor/tarball/master"&gt;tarball&lt;/a&gt;.  Just add Firefox or XULRunner.&lt;/p&gt;
&lt;p&gt;I don&amp;#8217;t know if anyone else has tried this yet.  Don&amp;#8217;t want to step on any toes.  The git repo is &lt;a href="http://github.com/why/processor"&gt;open&lt;/a&gt; for contribs.&lt;/p&gt;</content></entry><entry xml:base="/2008/05/08/clearingUpTheWholeShoesAndRubyGemsDeal.html"><title xml:space="preserve">Clearing Up The Whole Shoes And RubyGems Deal</title><link href="/2008/05/08/clearingUpTheWholeShoesAndRubyGemsDeal.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FclearingUpTheWholeShoesAndRubyGemsDeal</id><published xml:space="preserve">2008-05-08T05:18:32Z</published><updated xml:space="preserve">2008-05-08T05:18:32Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;Here&amp;#8217;s my reason for stalling on the question of Shoes and how to get gems to work with it.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-setup-1.png" class="c" alt="" /&gt;&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;So, I have a matter of Shoes business.  Everybody always asks about how to get SQLite working with Shoes.  Or, uh, what&amp;#8217;s the other one: ImageMagick?  No, that can&amp;#8217;t be it.  Well, krikey, I can&amp;#8217;t remember.&lt;/p&gt;
&lt;p&gt;At any rate, here&amp;#8217;s my reason for stalling.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/shoes-setup-1.png" class="c" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;This is Shoes.  Which isn&amp;#8217;t a Ruby library.  It&amp;#8217;s not a gem.  It&amp;#8217;s a kit.  Shoes has an installer.  You click on a .shy file (such as &lt;a href="http://the-shoebox.org/apps/25"&gt;Tetris&lt;/a&gt;) and it launches Shoes.&lt;/p&gt;
&lt;p&gt;And Shoes does the pulling down of gems and libraries.  Take this small and very contrived Shoes script:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;Shoes.setup do
  gem 'json &gt;= 1.1.1'
  gem 'activerecord'
end

require 'json'
require 'activerecord'

Shoes.app do
  @msg = para "ALL SYSTEMS GO"
  animate(20) { @msg.toggle }
end
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;By putting your gem list in the &lt;code&gt;Shoes.setup&lt;/code&gt; block, you&amp;#8217;ll end up encountering the Shoes popup seen above if any of the gems happens to be absent.  Gems are installed in &lt;code&gt;~/.shoes&lt;/code&gt;, to avoid needing superuser rights.  (And just to keep Shoes away from messing with your normal Ruby stuff.)  Leopard has some compiling problems, but other platforms are doing good.  That setup window needs a cancel button.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;And that&amp;#8217;s all.  I hope you have learned something.  I know I did.  Because I realized the thing about the cancel button.&lt;/p&gt;
&lt;p&gt;Yes, well, and for Ruby scripts that aren&amp;#8217;t in a gem: no sweat.  Just drop those in the same directory as your Shoes script and require them.  That was probably obvious, but it still feels good to get that off my chest.&lt;/p&gt;
&lt;p&gt;Oh, also: the SQLite lib comes with Shoes since about a week ago.  See, sometimes I need these gems, too.&lt;/p&gt;</content></entry><entry xml:base="/2008/05/05/sneakingRubyThroughGoogleAppEngine.html"><title xml:space="preserve">Sneaking Ruby Through Google App Engine (and Other Strictly Python Places)</title><link href="/2008/05/05/sneakingRubyThroughGoogleAppEngine.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FsneakingRubyThroughGoogleAppEngine</id><published xml:space="preserve">2008-05-05T16:11:42Z</published><updated xml:space="preserve">2008-05-05T16:11:42Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">
&lt;pre&gt;&lt;code class="sh"&gt;$ git clone git://github.com/why/unholy.git
$ cd unholy
$ vim hello.rb
$ bin/unholy hello.rb
$ PYTHONPATH=python python hello.rb.pyc
&lt;/code&gt;&lt;/pre&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;Despite all the clicking that has been done on a certain star.gif, I really don&amp;#8217;t think Ruby is coming to Google&amp;#8217;s App Engine.  Google doesn&amp;#8217;t have much of a toe in the Ruby scene and App Engine is clearly entrenched in Python technologies (such as Pypy and WSGI).  Well, cripes.&lt;/p&gt;
&lt;p&gt;But, for my purposes, I am really enticed by App Engine, because it gives beginners a supremely cinchy way to make a web app.&lt;/p&gt;
&lt;p&gt;So, maybe let&amp;#8217;s just convert Ruby 1.9 bytecode to Python 2.5 bytecode and decompile.  In other words: put in a .rb and get out a .pyc.  Yes, could work?&lt;/p&gt;
&lt;hr /&gt;

&lt;h2&gt;A Drop of Puts&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s start with converting the most trivial Ruby app to an App Engine script.  A translation of the &lt;a href="http://code.google.com/appengine/docs/gettingstarted/helloworld.html"&gt;intro code&lt;/a&gt; in the App Engine docs.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;puts "Content-Type: text/plain"
puts
puts "Hello, world!"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&amp;#8217;ve got Ruby 1.9 installed, you can output the bytecode for this script by looping through the arrays that come out of &lt;code&gt;VM::InstructionSequence&lt;/code&gt;.  Here&amp;#8217;s the code from my &lt;code&gt;rb-compile&lt;/code&gt; script:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;#!/usr/bin/env ruby
iseq = VM::InstructionSequence.compile(IO.read(ARGV[0]))
iseq.to_a.last.each do |inst|
  p inst
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I run &lt;code&gt;./rb-compile hello.rb&lt;/code&gt;, the bytecode dump is:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;1
[:putnil]
[:putstring, "Content-Type: text/plain"]
[:send, :puts, 1, nil, 8, nil]
[:pop]
2
[:putnil]
[:putstring, ""]
[:send, :puts, 1, nil, 8, nil]
[:pop]
3
[:putnil]
[:putstring, "Hello, world!"]
[:send, :puts, 1, nil, 8, nil]
[:leave]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The numbers are line numbers.  I&amp;#8217;m going to skip those for now, but Python bytecode has a place for those as well.  (And, in fact, you can read about all of that in Ned Batchelder&amp;#8217;s &lt;a href="http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html"&gt;The Structure of .pyc Files&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re just going to morph these messages into Python method calls.  But one problem with this bytecode: the receiver of &lt;code&gt;puts&lt;/code&gt; is &lt;code&gt;nil&lt;/code&gt;.  You see all those &lt;code&gt;:putnil&lt;/code&gt; lines?  Since there&amp;#8217;s no receiver, Ruby sees these as Kernel methods.&lt;/p&gt;
&lt;p&gt;Instead, when pythongineering this code, let&amp;#8217;s send all these methods to a &lt;code&gt;Kernel&lt;/code&gt; class, where we can store the Ruby built-ins.  The resulting py-bytecode for a single method call will look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;36 LOAD_NAME                0 (Kernel)
39 LOAD_ATTR                1 (puts)
42 LOAD_CONST               4 ('Hello, world!')
45 CALL_FUNCTION            1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is really the largest challenge when converting rbc to pyc.  The ordering of things.  In Ruby, the message name is part of the &lt;code&gt;:send&lt;/code&gt; instruction.  However, in Python, the method name is popped on to the stack, right after the receiver.&lt;/p&gt;
&lt;p&gt;But, hey, that&amp;#8217;s all the fun of it, right?&lt;/p&gt;
&lt;p&gt;And, so, we store in Kernel.py:&lt;/p&gt;

&lt;pre&gt;&lt;code class="py"&gt;def puts(*args):
  for x in args: print x
  if not args: print
&lt;/pre&gt;&lt;/code&gt;

&lt;hr /&gt;

&lt;h2&gt;Getting Unholy&lt;/h2&gt;
&lt;p&gt;Okay, to watch the above take place, you&amp;#8217;ll need to download Unholy, the Ruby-to-&lt;span class="caps"&gt;PYC&lt;/span&gt; converter I&amp;#8217;m working on.  The project is &lt;a href="http://github.com/why/unholy"&gt;here&lt;/a&gt;.  &lt;strong&gt;&lt;span class="caps"&gt;HEED&lt;/span&gt; &lt;span class="caps"&gt;THIS&lt;/span&gt;: It needs Ruby 1.9 and Python 2.5.&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ git clone git://github.com/why/unholy.git
$ cd unholy
$ vim hello.rb
$ bin/unholy hello.rb
$ PYTHONPATH=python python hello.rb.pyc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, great, we&amp;#8217;ve got some Ruby compiling to Python bytecode!  (The &lt;span class="caps"&gt;PYTHONPATH&lt;/span&gt; var is there to get our Kernel.py loading.)&lt;/p&gt;
&lt;p&gt;Unfortunately, you can&amp;#8217;t just upload the .pyc files to Google&amp;#8217;s machines.  How about we generate some Python source code for this?&lt;/p&gt;
&lt;p&gt;So, I&amp;#8217;m using a patched version of decompyle, which still has some issues, but let&amp;#8217;s get it installed.&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ cd unholy/decompyle
$ python setup.py build
$ sudo python setup.py install
$ decompyle hello.rb.pyc &gt; hello.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And, there you are, the &lt;code&gt;hello.py&lt;/code&gt; should amount to:&lt;/p&gt;

&lt;pre&gt;&lt;code class="py"&gt;# emacs-mode: -*- python-*-
import Kernel
Kernel.puts('Content-Type: text/plain')
Kernel.puts()
return Kernel.puts('Hello, world!')

# local variables:
# tab-width: 4
&lt;/pre&gt;&lt;/code&gt;

&lt;hr /&gt;

&lt;h2&gt;Yeah So Puts is Useless&lt;/h2&gt;
&lt;p&gt;Now, I admit, I&amp;#8217;m not that far into this idea.  And, you know, I can&amp;#8217;t see this thing holding my attention for another four hours.  But I am close to running &lt;a href="http://code.google.com/appengine/docs/gettingstarted/usingwebapp.html"&gt;this script&lt;/a&gt;.  The main problem is inheriting from &lt;code&gt;webapp.RequestHandler&lt;/code&gt;, which Ruby sees as a chained Kernel method call.&lt;/p&gt;
&lt;p&gt;The code I&amp;#8217;m working off of looks like this so far:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;import "wsgiref.handlers"
from "google.appengine.ext", "webapp" =&gt; :WebApp

class MainPage &lt; WebApp::RequestHandler
  def get
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, webapp World!')
  end
end


def main
  application = WebApp::WSGIApplication.new([tuple('/', MainPage)])
  wsgiref.handlers.CGIHandler().run(application)
end

if __FILE__ == $0
  main
end
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;So, here&amp;#8217;s what I&amp;#8217;d do if I were you and I (playing you) wanted this to be perfectly class act.  I would call up the JRuby guys and get them to write a compiler backend for targetting Python bytecode.  I&amp;#8217;ll bet they could crank this out in two licks of an anteater&amp;#8217;s tongue.&lt;/p&gt;
&lt;p&gt;What amazes me is how close Ruby 1.9 bytecode and Python 2.5 bytecode are.  Some things translate almost directly.  It is completely obvious that Koichi took his cues from Python.  Storing argcount, nlocals, stacksize first.  Marshalling bytecodes.  Storing classes and methods as nested bytecode fragments.&lt;/p&gt;
&lt;p&gt;And, really, if that&amp;#8217;s true (and I vouch that it is truly, truly true,) then how are Python and Ruby still on separate runtimes?  All of these bogus scaling wars and indented code battles are a huge waste of time.  Do we &lt;em&gt;still&lt;/em&gt; have to be better than each other in 2008?  No way, Rufus said, &amp;#8220;Be excellent to each other.&amp;#8221;  Very slight (albeit legit) syntax choices, people.&lt;/p&gt;
&lt;p&gt;Now, imagine a hypothetical &lt;code&gt;potion&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;$ potion hello.py
HELLO FROM PYTHON
$ potion hello.rb
KONNICHIWA FROM RUBY
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Neither of us stands a chance against Javascript.  Why persist with this pitiful feud?&lt;/p&gt;</content></entry><entry xml:base="/2008/04/23/yourEightSecondCallingCard.html"><title xml:space="preserve">Your Eight Second Calling Card</title><link href="/2008/04/23/yourEightSecondCallingCard.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FyourEightSecondCallingCard</id><published xml:space="preserve">2008-04-23T15:04:44Z</published><updated xml:space="preserve">2008-04-23T15:04:44Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;See, it&amp;#8217;s just a lick of Javascript.  And you end up with this:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/chirrp-rec.png" class="cb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Which makes &lt;a href="http://tweetscan.com/?s=chirrp.net"&gt;these&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://twitter.com/manveru/statuses/791634854"&gt;manveru&lt;/a&gt;: now we can have asynchronous phone calls!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Get your Firefox extension, Greasemonkey script or bookmarklet at &lt;a href="http://chirrp.net"&gt;this&lt;/a&gt; place.&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;See, it&amp;#8217;s just a lick of Javascript.  And you end up with this:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/chirrp-rec.png" class="cb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Which makes &lt;a href="http://tweetscan.com/?s=chirrp.net"&gt;these&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://twitter.com/manveru/statuses/791634854"&gt;manveru&lt;/a&gt;: now we can have asynchronous phone calls!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Get your Firefox extension, Greasemonkey script or bookmarklet at &lt;a href="http://chirrp.net"&gt;this&lt;/a&gt; place.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;But, more pertinently, &lt;em&gt;for the hacketeers&lt;/em&gt;: I would like to add that Chirrp was largely written in &lt;a href="http://haxe.org/intro"&gt;haXe&lt;/a&gt;.  So, the code for the Flash recorder and player is &lt;a href="http://github.com/why/chirrup"&gt;here&lt;/a&gt;.  And, if you&amp;#8217;ve got haXe and swfmill installed, you run &lt;code&gt;./build.sh&lt;/code&gt;.  And you wind up with two Flash movies.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m sure quite a few of you have used haXe (or its relative: Neko VM.)  This language&amp;#8217;s even closer to Javascript than the ScummC language mentioned yesterday.  You&amp;#8217;ve even got anonymous functions in haXe.&lt;/p&gt;

&lt;pre&gt;&lt;code class="haxe"&gt;static function startRecording(e) {
  stopButton.visible = true;
  recButton.visible = false;
  Base.record(uuid + ".flv", function(ns) {
    start = 0;
    clock = new Timer(200);
    clock.run = timeRecording;
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if you decide to hack the recorder to capture longer messages, just keep in mind that there&amp;#8217;s a 200k file limit on the server, so you might be able to squeeze twenty seconds out, but it&amp;#8217;ll be a bit tight.  Maybe take the microphone rate down to 11 or something.&lt;/p&gt;</content></entry><entry xml:base="/2008/04/22/makingScummGames.html"><title xml:space="preserve">Making SCUMM Games</title><link href="/2008/04/22/makingScummGames.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FmakingScummGames</id><published xml:space="preserve">2008-04-22T15:44:45Z</published><updated xml:space="preserve">2008-04-22T15:44:45Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;&lt;img src="http://hackety.org/images/openquest.png" class="rb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;After three years since their last release, &lt;a href="http://alban.dotsec.net/Projects/ScummC"&gt;ScummC&lt;/a&gt; has hit 0.2.0.  And it&amp;#8217;s looking really good.  They have a short but rather complete example (a port of Michael Sheail&amp;#8217;s OpenQuest,) the &lt;a href="https://dotsec.net/repos/scummc/trunk/examples/openquest/"&gt;source code&lt;/a&gt; for which is included in the ScummC distro.  We&amp;#8217;re talking: roughly 3000 lines of a JavaScript-ish language.  So, hey, this seems to be a great avenue for creating adventure games, folks.&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;&lt;img src="http://hackety.org/images/openquest.png" class="rb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;After three years since their last release, &lt;a href="http://alban.dotsec.net/Projects/ScummC"&gt;ScummC&lt;/a&gt; has hit 0.2.0.  And it&amp;#8217;s looking really good.  They have a short but rather complete example (a port of Michael Sheail&amp;#8217;s OpenQuest,) the &lt;a href="https://dotsec.net/repos/scummc/trunk/examples/openquest/"&gt;source code&lt;/a&gt; for which is included in the ScummC distro.  We&amp;#8217;re talking: roughly 3000 lines of a JavaScript-ish language.  So, hey, this seems to be a great avenue for creating adventure games, folks.&lt;/p&gt;
&lt;p&gt;The language consists mostly of setting up objects in various rooms and laying out what sorts of ways you can manipulate those objects.  So, an object could be a gun.  Or it could be an &lt;span class="caps"&gt;NPC&lt;/span&gt;, such as Carol:&lt;/p&gt;

&lt;pre&gt;&lt;code class="scummc"&gt;object carolObj {
  name = "Indigenous lifeform";
  verb(int vrb,int objA,int objB) {
    case TalkTo:
      zobTalkToCarol();
      return;
    case LookAt:
      if( ! knowsCarol ) {
          egoSay("A large carbon based mammal, it seems.");
          waitForMessage();
      }   
      else {
          egoSay("It is Carol, one of the local lifeforms.");
          waitForMessage();
      }   
      return;
    case Smell:
      egoSay("This creature has an overwhelming pungent smell.");
      return;
    case Move:
      egoSay("The lifeform is surprisingly sturdy, I don't believe I can use force.");
      return;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The bulk of the code is spent setting up the various rooms and objects and bits of dialog.  But you are also free to set up all of the action buttons and the inventory drawer.  In fact, a fairly large chunk of code in the sample game is used to setup a subscreen for picking your dialog from a script (just as in &lt;em&gt;The Secret of Monkey Island&lt;/em&gt; or &lt;em&gt;Day of the Tentacle&lt;/em&gt;.)&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;And yet, this project has been virtually ignored by everyone, including the ScummVM community, with no mention of it on &lt;a href="http://wiki.scummvm.org/index.php/Main_Page"&gt;their wiki&lt;/a&gt; and only &lt;a href="http://www.google.com/search?q=scummc%20site%3Ascummvm.org"&gt;smatterings&lt;/a&gt; of discussion on the forums last year.&lt;/p&gt;
&lt;p&gt;Now, of course, &lt;span class="caps"&gt;SCUMM&lt;/span&gt; games are lo-res and old-fashioned.  Why would anyone target the ScummVM?  Well, perhaps for nostalgia.  Maybe because it&amp;#8217;s pretty easy.  And there is a wealth of fans that&amp;#8217;s already obsessed with the VM.&lt;/p&gt;
&lt;p&gt;But, come on.  ScummVM is hugely &lt;a href="http://scummvm.org/downloads.php"&gt;cross-platform&lt;/a&gt;.  We&amp;#8217;re not just talking Linux, OS X and Windows.  It&amp;#8217;s perfectly stable on the iPhone, Palm OS 5, Windows Mobile and the Nintendo DS.  Seriously: Dreamcast?  &lt;span class="caps"&gt;SCUMM&lt;/span&gt; always hits a platform first.&lt;/p&gt;
&lt;p&gt;For now, it&amp;#8217;s something of a sweet spot between VMs and emulators.  Reaching platforms that Java and Flash neglect.  While not being as primitive as &lt;span class="caps"&gt;NES&lt;/span&gt; or Genesis emulators are to develop for.&lt;/p&gt;</content></entry><entry xml:base="/2008/03/20/jasonGarberFinishesSuperRedClothForMe.html"><title xml:space="preserve">Jason Garber Finishes SuperRedCloth For Me</title><link href="/2008/03/20/jasonGarberFinishesSuperRedClothForMe.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FjasonGarberFinishesSuperRedClothForMe</id><published xml:space="preserve">2008-03-21T00:02:52Z</published><updated xml:space="preserve">2008-03-21T00:02:52Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p style="text-align:center;"&gt;&lt;img src="http://hackety.org/images/superred.png" alt="" /&gt;&lt;/p&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;At last, open source works as it should.  Certainly, patching is cool.  Branching is cool.  But nothing beats intruding in my repo and just finishing the whole thing.  Which is what Jason Garber has done for SuperRedCloth.&lt;/p&gt;
&lt;p style="text-align:center;"&gt;&lt;a href="http://code.whytheluckystiff.net/redcloth/timeline?from=03%2F15%2F08&amp;amp;daysback=418&amp;amp;changeset=on&amp;amp;update=Update"&gt;&lt;img src="http://hackety.org/images/superred.png" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I started rewriting &lt;a href="http://code.whytheluckystiff.net/redcloth/"&gt;RedCloth&lt;/a&gt; on the 22nd of January, 2007.  Revision &lt;a href="http://code.whytheluckystiff.net/redcloth/changeset/134"&gt;134&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And Jason finished on the 15th of March, 2008.  Revision &lt;a href="http://code.whytheluckystiff.net/redcloth/changeset/271"&gt;271&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Of those 137 commits, 102 are Jason.&lt;/strong&gt;  SuperRedCloth is his.  Seriously, what a champ!&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;I don&amp;#8217;t know if this has happened to any of you out there.  With your projects.  But if you think open source is just about copyright or simply about pleasing users, no way.  In this case, the original hacker benefited just as much as anyone.  Maybe more.  SuperRedCloth was finished due to no effort of mine and is obviously superior to how I would have finished it.  You might say I&amp;#8217;m an avid fan of SuperRedCloth.&lt;/p&gt;
&lt;p&gt;I have altered the copyrights and credits to reflect Jason as the owner.  Please give Jason your thanks and give the newly merged RedCloth 4 a try!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gem install RedCloth --source http://code.whytheluckystiff.net&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Discussion at &lt;a href="http://rubyforge.org/mailman/listinfo/redcloth-upwards"&gt;RedCloth-Upwards&lt;/a&gt;.&lt;/p&gt;</content></entry><entry xml:base="/2008/03/04/theTinyAndAccomplishedLew.html"><title xml:space="preserve">The Tiny And Accomplished Lew</title><link href="/2008/03/04/theTinyAndAccomplishedLew.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FtheTinyAndAccomplishedLew</id><published xml:space="preserve">2008-03-05T05:30:54Z</published><updated xml:space="preserve">2008-03-05T05:30:54Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;François Lamotte took some time to e-mail in about his friend 4P8.  He thought we&amp;#8217;d like this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://www.4p8.com/lew/manifesto.html"&gt;4P8&lt;/a&gt;: In the same time, the personal computing paradigm matured. The original rogue program that assumed a total control over what was perceived as a hobbyist toy have now to behave nicely. It has to comply with access rights, to accept and process requests from the graphical environment, to follow a rigorous syntactic and conceptual model and so on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, here we&amp;#8217;ve got another tricky, little graphical gear to spin!  &lt;a href="http://www.4p8.com/lew/"&gt;&lt;span class="caps"&gt;LEW&lt;/span&gt;&lt;/a&gt; is an art tool which embeds the Lua language and it&amp;#8217;s inspired by (you guessed it) NodeBox and Processing.  And &lt;span class="caps"&gt;BASIC&lt;/span&gt;.  The gallery is incredibly impressive.  And equally impressive is the brevity and usefulness of its &lt;a href="http://www.4p8.com/lew/reference.html"&gt;manual&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/lew-flight.png" class="rb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;You know, things continue to bode well for a real return to the days of bloat-free, starter programming environments.  Small and for the layfolk.  In fact, &lt;span class="caps"&gt;LEW&lt;/span&gt; makes me feel like Shoes is bloated:  &lt;span class="caps"&gt;LEW&lt;/span&gt; is a single binary, around 400k.  (Windows only, for now.)&lt;/p&gt;
&lt;p&gt;The image at right is a clone of a &lt;a href="http://flight404.com/"&gt;flight404&lt;/a&gt; demo called &amp;#8220;Solar&amp;#8221;.  Roughly 250 lines of Lua code.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Continuing the quote from earlier:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is this an advocacy to return to &lt;span class="caps"&gt;BASIC&lt;/span&gt; ? No: the old &lt;span class="caps"&gt;BASIC&lt;/span&gt; is clearly too primitive to handle problems we&amp;#8217;re now used to deal with, and new &lt;span class="caps"&gt;BASIC&lt;/span&gt; implementations have just grow in complexity the same way other languages did.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The conclusion is a need to an equivalent of what &lt;span class="caps"&gt;BASIC&lt;/span&gt; was in old personal computer industry: a simple, exploratory language.&lt;/p&gt;
&lt;/blockquote&gt;</content></entry><entry xml:base="/2008/03/04/shoesIsSortOfMakingItsWayThroughTheLinuxPackagingMachines.html"><title xml:space="preserve">Shoes Is Sort Of Making Its Way Through The Linux Packaging Machines</title><link href="/2008/03/04/shoesIsSortOfMakingItsWayThroughTheLinuxPackagingMachines.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FshoesIsSortOfMakingItsWayThroughTheLinuxPackagingMachines</id><published xml:space="preserve">2008-03-04T16:44:57Z</published><updated xml:space="preserve">2008-03-04T16:44:57Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;ul&gt;
	&lt;li&gt;Bram Senders has put together &lt;a href="http://packages.debian.org/testing/interpreters/shoes"&gt;a Debian package&lt;/a&gt; for Shoes.  Available from apt in unstable and testing.&lt;/li&gt;
	&lt;li&gt;And an &lt;a href="http://aur.archlinux.org/packages.php?ID=15462"&gt;Archlinux package&lt;/a&gt; is out from Michael Fellinger.&lt;/li&gt;
	&lt;li&gt;A Gentoo ebuild was also &lt;a href="http://code.whytheluckystiff.net/list/shoes/2008/02/06/2161-re-gentoo-shoes.html"&gt;posted&lt;/a&gt; to the Shoes list by Shanoah.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And, beyond that, I develop and build from source daily on Ubuntu and DragonFly.&lt;/p&gt;</content></entry><entry xml:base="/2008/03/02/nksIsOpp.html"><title xml:space="preserve">NKS is OPP</title><link href="/2008/03/02/nksIsOpp.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FnksIsOpp</id><published xml:space="preserve">2008-03-02T07:30:40Z</published><updated xml:space="preserve">2008-03-02T07:30:40Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;As of today, &lt;em&gt;Nobody Knows Shoes&lt;/em&gt; doesn&amp;#8217;t belong to me any more.  I have disowned it.  Disowned it in both &lt;a href="http://hackety.org/press/"&gt;&lt;span class="caps"&gt;PDF&lt;/span&gt; and &lt;span class="caps"&gt;HTML&lt;/span&gt;&lt;/a&gt;.  I have given it to the publick.  It was a bad idea.  I realize that now.  Clearly!  So much egg on my face.  And sooooo many pancakes on my knee.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://flickr.com/photos/rocketraccoon/2209545886/"&gt;&lt;img src="http://hackety.org/images/nks-rr.jpg" class="rb" alt="" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Many of you tried to tell me it was a bad idea.  In fact, you told me that all of my ideas were bad.  All present and future ideas.  So I could have concluded that this was, as well, &lt;span class="caps"&gt;YES&lt;/span&gt;, a bad idea.&lt;/p&gt;
&lt;p&gt;But it simply did not occur to me, at the time, that I was probably crazy and probably even insane.  I could not see this clearly, from inside my head.  However, now that I have had a chance to read a bit more about myself on the Internet, I now see realize I am very berserk nuts and probably doing drugs.  I mean, duh, guys, everything that I say is totally unintelligible!  You can hardly make out an actual English word when I write.  It&amp;#8217;s bad.  I am spamming the good hackers of our planet.&lt;/p&gt;
&lt;p&gt;My first step to recovery is to completely denounce &lt;em&gt;Nobody Knows Shoes&lt;/em&gt;.  I am distancing myself from it.  It&amp;#8217;s now completely on its own.  I hate it.  It reminds me of a very dark time in my life.  Don&amp;#8217;t make me go there.  It gives me the chilly willies to even think about.&lt;/p&gt;</content></entry><entry xml:base="/2008/02/29/conkerorComesUnstuck.html"><title xml:space="preserve">Conkeror Comes Unstuck</title><link href="/2008/02/29/conkerorComesUnstuck.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FconkerorComesUnstuck</id><published xml:space="preserve">2008-02-29T23:36:25Z</published><updated xml:space="preserve">2008-02-29T23:36:25Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;&lt;img src="http://hackety.org/images/conkeror-links.png" class="rb" alt="" /&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Like: the command &lt;code&gt;g hackety.org&lt;/code&gt; takes you here.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;Ctrl-u g hackety.org&lt;/code&gt; opens this site in a new buffer (like a tab.)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;f 12&lt;/code&gt; opens link #12.&lt;/li&gt;
	&lt;li&gt;Thus, &lt;code&gt;Ctrl-u f 12&lt;/code&gt; opens link #12 in a new buffer.&lt;/li&gt;
&lt;/ul&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p&gt;The surgery was a success.  Conkeror has landed its separation manuever.  Really, honestly, I wasn&amp;#8217;t sure for a second.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/conkeror-links.png" class="rb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://conkeror.mozdev.org"&gt;Conkeror&lt;/a&gt; is a keyboard-driven browser.  You navigate links with the keyboard.  Select text with the keyboard.  And you can add commands using JavaScript.  Since its commands are loosely based on Emacs commands, you can combine key cuts in really fantastic ways.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Like: the command &lt;code&gt;g hackety.org&lt;/code&gt; takes you here.&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;Ctrl-u g hackety.org&lt;/code&gt; opens this site in a new buffer (like a tab.)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;f 12&lt;/code&gt; opens link #12.&lt;/li&gt;
	&lt;li&gt;Thus, &lt;code&gt;Ctrl-u f 12&lt;/code&gt; opens link #12 in a new buffer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use &lt;code&gt;Ctrl-h t&lt;/code&gt; to get briefed.  And &lt;code&gt;Ctrl-h b&lt;/code&gt; for the cheat codes.&lt;/p&gt;
&lt;p&gt;Previously, Conkeror was a Firefox extension.  Late last year the team detached from Firefox and started rebuilding the browser on top of Xulrunner.  This seemed a perilous move at first, but it&amp;#8217;s really &lt;a href="http://repo.or.cz/w/conkeror.git?a=shortlog"&gt;energized&lt;/a&gt; the developers and has resulted in some great features.  Such as the minibuffer, simpler bookmarks work with &lt;code&gt;g&lt;/code&gt;, much improved link highlighting.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Installation is a bit of a trial.  You just need to be sure you start with the specific Xulrunner distro &lt;a href="http://conkeror.mozdev.org/drupal/content/common-problems"&gt;mentioned&lt;/a&gt; on the wiki.&lt;/p&gt;
&lt;p&gt;On Linux, after you get Xulrunner, it&amp;#8217;s along the lines of:&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;git clone git://repo.or.cz/conkeror.git
cd conkeror
./build.sh xulapp
sudo ./install.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you&amp;#8217;ll have &lt;code&gt;/usr/local/bin/conkeror&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;The new link following is just genius.  Previously, links on the page were all numbered right when you hit a page.  Some times this would blow the layout.  (Such as the del.icio.us sidebar.)  And a lot of times it would slow page loading.  Really, it wasn&amp;#8217;t until I was able to kill half of my custom link styling javascripts that I realized how nicely Conkeror was coming along.&lt;/p&gt;
&lt;p&gt;Now, pages are loaded just as in Firefox.  You hit &lt;code&gt;n&lt;/code&gt; and it hovers a highlight over all links and form elements &lt;em&gt;in view&lt;/em&gt;.  The highlight doesn&amp;#8217;t break any layouts.  And, yeah, it&amp;#8217;s doesn&amp;#8217;t highlight the whole page.  Just the stuff you can actually see.&lt;/p&gt;
&lt;p&gt;So even if you&amp;#8217;re at the bottom of the page, the link numbers aren&amp;#8217;t in the hundreds.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://hackety.org/images/conkeror-letter.png" class="cb" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;d rather not use numbers, you can also type the text of the link to follow.  I type &lt;code&gt;s&lt;/code&gt; and it limits the links to those with text containing that letter.&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;Okay, check out this script.  This goes in the &lt;code&gt;.conkerorrc&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class="js"&gt;interactive("proxy-port",
  "Change the HTTP proxy port.",
  function (I) {
    var portnum;
    user_pref("network.proxy.http_port", 
      portnum = (yield I.minibuffer.read($prompt = "Proxy port:")));
    I.minibuffer.message("port set to " + portnum);
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See, you can use the minibuffer (the command line) as a prompt.  In this script, I&amp;#8217;m prompting for a port number.  Using the &lt;code&gt;minibuffer.read("Proxy port:")&lt;/code&gt; line.  And print a success with &lt;code&gt;minibuffer.message&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a similar method &lt;code&gt;read_hinted_element_with_prompt&lt;/code&gt; that prompts for an element and you can type in the element&amp;#8217;s text or pick its number off the page.  Really, this browser needs a nice scripter&amp;#8217;s guide once the git pull dries up.&lt;/p&gt;</content></entry><entry xml:base="/2008/02/27/dragonflysFreezer.html"><title xml:space="preserve">DragonFly's Freezer</title><link href="/2008/02/27/dragonflysFreezer.html" rel="alternate" type="text/html" xml:space="preserve" /><id xml:space="preserve">tag:hackety.org,2008:blogentry%2Fnews%2FdragonflysFreezer</id><published xml:space="preserve">2008-02-27T21:19:18Z</published><updated xml:space="preserve">2008-02-27T21:19:18Z</updated><dc:subject xml:space="preserve">news</dc:subject><category term="news" scheme="http://hobix.com/tags" xml:space="preserve" /><summary mode="escaped" type="html" xml:space="preserve">&lt;p&gt;The dfly 1.12 release was yesterday.&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;require 'dfly'
puts "THIS APP IS GOING TO FREEZE"
case Dfly.checkpoint("test.ckpt.gz")
when Dfly::FROZEN
  exit
when Dfly::RESUMED
  puts "THIS APP IS THAWED OUT"
end
&lt;/code&gt;&lt;/pre&gt;</summary><author><name xml:space="preserve">why the lucky stiff</name><uri xml:space="preserve">http://whytheluckystiff.net/</uri><email xml:space="preserve">why@whytheluckystiff.net</email></author><content type="html" xml:space="preserve">&lt;p style="float:right;"&gt;&lt;img src="http://hackety.org/images/dfly-bug.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;People tend to dismiss &lt;a href="http://dragonflybsd.org"&gt;&lt;strong&gt;DragonFlyBSD&lt;/strong&gt;&lt;/a&gt; as the pipe dream of Matt Dillon, discounting all the work Matt and his little team have done.  DragonFly is my favorite OS and it works great for me.  Virtual kernels are nicer than jails (in my opinion) and are becoming much easier thanks to stuff like the &lt;a href="http://leaf.dragonflybsd.org/mailarchive/kernel/2008-02/msg00051.html"&gt;vkernel manager&lt;/a&gt;.  Pkgsrc integration is getting much better (try: &lt;code&gt;man pkg_radd&lt;/code&gt;) and checkpointing is such a juicy feature.  And, of course, we&amp;#8217;re all excited about &lt;a href="http://leaf.dragonflybsd.org/mailarchive/kernel/2008-02/msg00072.html"&gt;&lt;span class="caps"&gt;HAMMER&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The dfly 1.12 release was yesterday.  And I guess it&amp;#8217;s not much of a big deal compared to what 2.0 will be.  But if you&amp;#8217;re new to dfly, then you need some time to catch up anyway.  (Look, just play with it.  Run the &lt;a href="http://www.theshell.com/pub/DragonFly/iso-images/dfly-1.12.0_REL.iso.gz"&gt;1.12 iso&lt;/a&gt; under Qemu 0.9.1.  &lt;a href="http://forums.bsdnexus.com/viewtopic.php?id=142"&gt;These instructions&lt;/a&gt; still apply.)&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;But let&amp;#8217;s get back to &lt;strong&gt;checkpointing&lt;/strong&gt;.  Awhile ago, I started working on improvements to Try Ruby.  To allow users to save and resume their sessions.  A perfect job for checkpointing.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the deal.  On DragonFly, you can send &lt;a href="http://leaf.dragonflybsd.org/cgi/web-man?command=signal&amp;#38;section=3"&gt;&lt;span class="caps"&gt;SIGCKPT&lt;/span&gt;&lt;/a&gt; to a process.  And that process will be frozen to disk.  It saves as an executable binary.  You run the binary and the app is back.  Similar to how the Terminator was back, if that helps at all.  (On Linux, you can use &lt;a href="http://cryopid.berlios.de/"&gt;CryoPID&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;After some searching, I found some broken links to a lib that Michael Neumann had scrabbled together.  A checkpointing &lt;span class="caps"&gt;API&lt;/span&gt; for Ruby.  Believe it!  He sent me his work by e-mail and I added some auto-gzip support to it.&lt;/p&gt;

&lt;pre&gt;&lt;code class="sh"&gt;svn co http://code.whytheluckystiff.net/svn/dfly/trunk dfly&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Checkpointing as an &lt;span class="caps"&gt;API&lt;/span&gt; call is just marvellous.  In a way, it&amp;#8217;s sort of treated like forking.  Because when you fork, you return from the same method twice: once in the child process and once in the parent.  And each scenario gets a different return value.&lt;/p&gt;
&lt;p&gt;You call &lt;code&gt;Dfly.checkpoint("test.ckpt.gz")&lt;/code&gt; to save a snapshot.  And the &lt;code&gt;checkpoint&lt;/code&gt; either returns &lt;code&gt;Dfly::FROZEN&lt;/code&gt; or &lt;code&gt;Dfly::RESUMED&lt;/code&gt;.  So, &lt;code&gt;Dfly::FROZEN&lt;/code&gt; is returned after the snapshot is saved.  And &lt;code&gt;Dfly::RESUMED&lt;/code&gt; is returned if the app is just now coming back into animation.&lt;/p&gt;
&lt;p&gt;So like, imagine:&lt;/p&gt;

&lt;pre&gt;&lt;code class="rb"&gt;require 'dfly'
puts "THIS APP IS GOING TO FREEZE"
case Dfly.checkpoint("test.ckpt.gz")
when Dfly::FROZEN
  exit
when Dfly::RESUMED
  puts "THIS APP IS THAWED OUT"
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you can give &lt;code&gt;Dfly.checkpoint&lt;/code&gt; a block that runs only when the app is resumed.  Great for re-opening sockets and the like.  This block was Michael Neumann&amp;#8217;s idea in the first place, so &lt;a href="http://www.ntecs.de/blog"&gt;give him&lt;/a&gt; the extra bonus lives and carnation wreaths.&lt;/p&gt;
&lt;p&gt;To resume: &lt;code&gt;Dfly.resume("test.ckpt.gz")&lt;/code&gt;.  Which can be done from another script entirely and the process will jump back to when it was first clubbed on the head.&lt;/p&gt;</content></entry></feed>
