Nothing made this more clear than working with Rake, Make, and Ant—all in the same day. Make is ridiculous, Ant is reasonable, and Rake rocks. Let me show you what I mean. I’ll describe each of my challenges, explain how I dealt with it, and you can tell me what you think.
For interactive animation on the Web, there’s no better choice than Flash. Though Flash Studio isn’t particularly hacker friendly.
(Oh, and your site just hung my browser. Isn’t that lovely? Excuse my while I record my open tabs, so that I can reopen them after I kill and reincarnate this pig. Have I ever publicly stated that the browser is a poor operating system? Let the record show that the web browser makes for a poor operating system. What’s this? We have a response from the browser? It’s unhung itself. Secrets of the universe, mystery, and awe.)
As I was saying, Flash isn’t hacker friendly. Who’s going to buy Flash Studio when all he wants to do is compile ActionScript? I mean you could, but Motion-Twin has left us with an alternative: fast, effective, and free. With MTASC (the Motion-Twin ActionScript Compiler), you add ActionScript to an existing .swf or it can generate a basic .swf in its own (.swf being Flash’s compiled file extension).
What if you want more than a .swf shell but less than a whole Flash Studio .fla? Suppose you just want to bundle up some icons and sound files for your animation? Is there an answer oh Google? “Yes, child of the Web.” We use swfmill. Swfmill takes an XML description of your movie clip resources and gives you back a .swf. Thanks swfmill.
Finally, to play in the browser, we embed our .swf in some HTML.
Let’s review the steps. Start with a bunch of image resources in, say, resources/images. Based off the images in the directory, generate a resources/images.xml. Use swfmill to make resources/images.swf. Use MTASC to mix ActionScript into resources/images.swf resulting in ”#{PROJECT_NAME}.swf”. Finally, generate #{PROJECT_NAME}.html to embed ”#{PROJECT_NAME}.swf”. This looks like a job for a build system, and I decided to use Rake.
Rake is make done the Ruby way. Instead of building a special purpose file format and processing engine, a Rakefile is plain Ruby source code—with two twists. The first twist is that you don’t call your Rakefile directly. Instead you use the rake command. Why do that? To support the second twist. The second twist is that rake defines some methods before invoking your Rakefile. Those methods effectively provide a domain specific extension of Ruby for defining file lists and build targets. Once your Rakefile’s been read, Rake uses that determines task dependencies and then runs them in order.
As a result, using Rake has some great properties. First, if you already know Ruby you just need to learn some new methods. Secondly, for the body of a task, you can write arbitrary Ruby code. Furthermore, you can write arbitrary Ruby code around your task definitions. This enables you to conditionally or iteratively define tasks. Nice. Let’s go tackle Flash.
Our first task is a doozy. Generate resources/images.xml from the files in resources/images. First, we get the image files:
IMAGES = FileList['resources/images/*']
Now FileList defined by Rake, and [] is a normal method in Ruby with the special invocation syntax of putting your arguments between the brackets. It’s normal Ruby code used in a build centric way.
Next we say that resources/images.xml is generated from the IMAGES:
file 'resources/images.xml' => IMAGES do # put Ruby code to generate "images.xml" here. end
Again no special syntax, just an idiosyncratic use of a method call. What’s with the arrow =>? It’s syntax for Ruby’s hash table literal. A more explicit rewriting may help demystify the idiomatic usage:
file({'resources/images.xml' => IMAGES}) do
# put Ruby code to generate "images.xml" here.
end
So file is a method which takes one argument and a block. The argument is a Hash object with one pair whose key is ‘resources/images.xml’ and whose value is IMAGES. Rake interprets the table as containing a target/dependency pair.
Since we can use any ruby code we want to generate “images.xml”, we’ll use a XML generation library Builder. Here we go:
SWF_VERSION = 7 SWF_WIDTH = 450 SWF_HEIGHT = 550 SWF_BACKGROUND = "#ffffff" SWF_FRAMERATE = 24
file 'resources/images.xml' => IMAGES do
xml = Builder::XmlMarkup.new :indent => 2
xml.instruct! :xml, :version=>"1.0", :encoding => "iso-8859-1"
xml.movie :version => SWF_VERSION, :width => SWF_WIDTH, :height => SWF_HEIGHT, :framerate => SWF_FRAMERATE do
xml.background :color => SWF_BACKGROUND
xml.frame do
xml.library do
IMAGES.each do |imagePath|
imageName = imagePath.sub /\.(png|jpg)$/, ""
imageName = imageName.sub /.*\//, ""
xml.clip :id => imageName, :import => imagePath
end
end
end
end
open 'resources/images.xml', 'w' do |f|
f.write xml.target!
end
end
I won’t go through all that except to note that Builder uses Ruby methods in clever ways too. The methods of xml are used to generate nodes. Since we have all sorts of tag names (movie, background, frame, library, clip) the implementation of xml uses method_missing to parametrically allow almost anything tag name to be used as a method. Exceptions such as instruct! and target! use an exclamation point. Let’s see an example. Suppose resources/images contains first.png and second.jpg, then the resulting file is:
<?xml version="1.0" encoding="iso-8859-1"?>
<movie version="7" width="450" height="550" framerate="12">
<background color="#ffffff"/>
<frame>
<library>
<clip id="first" import="resources/images/first.png"/>
<clip id="second" import="resources/images/second.jpg"/>
</library>
</frame>
</movie>
That’s enough for one day. Next time, we continue with our Flash Rakefile.
Commentary