A few years ago, I wrote a Rails (1.0.0) application for Nicola (my wife), to help her with her PhD research. It ran on her Linux laptop, happily, for those few years.
However, once the new laptop has arrived, I knew I'd have to migrate the application from Linux to Windows; I also wanted to avoid having to update the application for a newer version of Rails. How painful could it be? Fairly.
First I needed an old MySQL server (in case the API has changed), 5.0.15 to be precise. It is practically impossible to find archived downloads on the MySQL website, but I got there eventually.
Next I needed to get an old Ruby (1.8.4) for Windows. Again, virtually impossible to find old versions of Ruby with an installer. When I first did this, there was a Ruby 1.8.4 One-Click Installer for Windows, which seems to have disappeared. I finally tracked it down to some website run off some bloke's back somewhere out East.
Then, I needed Rails 1.0.0. For whatever reason, the Ruby I installed couldn't get Rails off the official gems repository (probably because the gem repo format changed). So I installed rails 1.0.0 on a different machine, created a new Rails project, then froze the 1.0.0 gems into it; then copied the frozen gems over to my app on the new machine. Phew.
Finally, I'd used RedCloth in the app. However, after a couple of attempts, I decided it was easier to rip it out than try to install it on Windows. So I did some surgery.
Add to that the fact that Nicola had forgotten her password (Firefox had been saving it), so I had to manually edit the db to add one; plus no decent text editor on Windows 7; plus MySQL not removing its service properly when I installed the wrong version then uninstalled it (sc delete MySQL removes errant services, by the way); plus Windows 7 making it difficult to get an administrator command prompt; etc. etc..
So overall a frustrating experience, but I did finally get there.
(On top of that, I also migrated several thousand POP-ped emails from Thunderbird 2 on Linux to Thunderbird 3 on Windows: I thought there would be an import wizard which would know what to do, but I saw no sign of it. And moved all her data over and installed OpenOffice. Entertainment all round.)
Once I've got over my trauma, I will provide links to where to find ancient versions of apps and libraries. Perhaps I should be an archaeologist.
Attached is my Ruby script for parsing log files I keep at work. I have to complete a weekly report, and this forms the basis of that; as I don't keep regular office hours (I flexi-work around child care), it also helps me keep track of the hours I've worked.
The basic principle of operation is as follows:
parse_work_log <file name> > summary.out on the file to produce a log of what I've donesummary.out file into an email I send roundThe format of the file is deliberately simple to make it easy to maintain; there are a handful of formatting rules. An example is shown below.
********** 2009-12-21 09:30-12:00 Researching this and that #research 12:00-13:00 -Lunch 13:00-14:00 More research 14:00-18:00 Writing some application #coding ********** 2009-12-22 09:30-10:00 Admin #Admin 10:00-10:15 -Break 10:00-12:00 Found out something rather marvellous #Very important research 12:00-12:45 -Lunch 12:45-18:00 Writing another application #coding ********** +NEXT Have lots of fun banging my head on a brick wall +NEXT Reinstall my operating system
Which, when parsed, produces this on stderr:
************************************* Worked 15.25 hours
And this on stdout:
************************************* This week: Admin: * Admin Very important research: * Found out something rather marvellous coding: * Writing some application * Writing another application research: * Researching this and that ************************************* Next: * Have lots of fun banging my head on a brick wall * Reinstall my operating system
Notes on formatting:
HH:MM-HH:MM <...whitespace...> <text> <#optional tag>\n-Lunch or -Break# creates a tagged entry which gets included in the report; it appears as a bullet point with the tag as its heading; any entries with the same tag get aggregated under a single heading; tags and entries appear in the order they occur in the file+NEXT gets listed in the Next section at the end; tags don't work on this (could, but don't at the moment)It is pretty primitive, but it does the job for me.
Installation: there isn't any, really. It works from the command line and needs Ruby. The licence? BSD, I guess.
This turned into a pretty long blog entry. To help you decide whether you want to stick it out, it's about:
I started work at Intel's Open Source Technology Centre 6 weeks ago (just realised I haven't written about that here yet), working on the Moblin SDK. Moblin is a Linux-based platform for mobile devices (netbooks, UMPCs, that sort of thing); my role in the project is working as part of the SDK team - basically trying to help third-party developers outside Intel develop software which runs on Moblin. So far, this has involved learning as much about the Moblin stack and ecosystem as possible, as well as helping organise and add to the content on Moblin-related websites. (It's challenging, exciting work and I'm thoroughly enjoying it so far.)
Currently, software development on Moblin is comparable to generic Linux development: languages like Python, Ruby, C++, Perl, Java, C# (via Mono) etc. are all available. However, if you want to make use of the full range of Moblin libraries (like our stuff for managing media metadata or interacting with social networking sites), and combine them with Clutter (our GUI toolkit), the best approach (at the moment) is to program in C. While many of the Moblin libraries have bindings to other languages (like Python and Perl), C gives you the best, most consistent access to all of them (as they're all written in C). You'll also find Moblin development easier if you're familiar with GNOME and glib, both of which are fairly C-centric.
My background isn't in low-level system programming. I have been learning C since starting at Intel, and have enjoyed it thoroughly, but there are several aspects of it I could live without:
automake, autoconf) actually wrap other tools (make) which are even more complicated. This is partly a consequence of the job they do (making it possible to compile software across different platforms), but means they are not approachable. See the diagram on the autools Wikipedia page to get an idea of how complicated they are.So, with my background, and with the goal of making Moblin development easy and attractive, I am keen to find alternatives to having to program in C all the time. Dynamic, interpreted languages (like Ruby or JavaScript) seem to me to be better suited to exploratory, iterative programming: the kind of programming most people do most of the time. This is a goal shared by the GObject Introspection project:
C is good for graphics, multimedia, and lower level systems work. However, writing complex software is difficult and error-prone without garbage collection. A managed runtime such as JavaScript, Python, Java, Lua, .NET, Scheme etc. makes a lot of sense for non-fast-path application logic such as configuration, layout, dialogs, etc. ... Thus, one of the major goals of the GObject introspection project is to be a convenient bridge between these two worlds, and allow you to choose the right tool for the job, rather than being limited inside one or the other. (from http://live.gnome.org/GObjectIntrospection)
GObject Introspection is already being used in GNOME development, to generate JavaScript bindings for the GNOME libraries: the next version of the GNOME desktop (in GNOME 3), GnomeShell, uses JavaScript extensively (calling Clutter, the Moblin graphics library, incidentally). Gjs is the name of the project producing the bindings, as well as being the home of the JavaScript engine itself (also called gjs, and based on Mozilla's implementation of JavaScript in C, SpiderMonkey).
JavaScript is an interesting language. I've used it in web applications for quite a long time (since about 2000), but never entertained the idea of using it for anything other than client-side web programming. Occasionally I'd come across server-side JavaScript (persevere), JavaScript implementations like Rhino, and even wrote some ASP code in JScript (as Microsoft called it) a few years ago. Frameworks like jQuery and the thorough integration of JavaScript with Rails meant that I've done more of it in recent months, but I never thought of using it as a general purpose language, or one for desktop development.
Looking for an alternative to C, and hearing from my colleagues about JavaScript, made me decide to get a feel for Gjs et al, as well as write something to run on Moblin. I decided to write a JavaScript application which made use of the Moblin graphics library, Clutter. Eventually, I ended up successfully writing a JavaScript implementation of tic-tac-toe, using Clutter for the user interface, and even including some simple animation. As an application, it is a bit scrappy (the code is attached), but as a learning aid, doing the project was very useful. It wasn't an easy journey, but it was an eventually satisfying one.
I did my development work on a Fedora Core 11 laptop, with an Intel chipset.
I tried a variety of ways to install all the pieces I needed for JavaScript development with Gjs, GObject Introspection, GNOME libraries, and Moblin libraries. Even getting an idea of what all the pieces do is painful, and the documentation is pretty woeful. I ended up doing lots of source code grep-ing and googling.
In the end, it turns out that the following packages are available for Fedora Core:
gobject-introspection: This provides tools and APIs for working with GIR XML files and typelib (binary versions of the GIR files): these are two formats for storing introspection data about GObject classes (basically, mapping the API of a C library into language-neutral descriptions of the APIs). The metadata for several common libraries (like GLib) is part of this package.gir-repository: This provides more introspection metadata about other common libraries (like GTK+ and DBus-GLib)gjs: This enables you to use the gobject-introspection data to access C libraries from JavaScript (i.e. it's the JavaScript bindings for GNOME libraries).Now, unfortunately, the versions in the repositories for Fedora 11 are out of date: generating the Clutter bindings requires gobject-introspection-1.0, which isn't available. Plus, this code is so new that it's changing very rapidly, and you probably need the latest version from the source repository to get stuff to work.
To compile all these bits, you're going to need the basic build tools. On Fedora, that means this lot:
$ sudo yum install git flex bison gnome-common libgnome-devel libffi-devel \
python-devel libtool automake autoconf make gtk-doc xulrunner-devel
(And maybe others: I lost track. But if you're trying to do this yourself, you'll probably know what to do if something's missing. Anyway, Josh is going to sort some packages out before long, hopefully. If you know a better way, let me know.)
Once you've got those installed, though, you should be able to build everything from source fairly easily.
As we're installing into /usr/local, first thing to do is make sure the compiler/linker can find your custom-compiled packages. In your ~/.bashrc file, add this line:
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
This tells the pkg-config tool (used by autotools to locate libraries and headers) where your custom installations are. Then reload your bash settings with:
$ source ~/.bashrc
to make sure they're picked up. Any future bash sessions will use this setting. Now you're ready to build.
gobject-introspection:
$ git clone git://git.gnome.org/gobject-introspection $ cd gobject-introspection $ ./autogen.sh --prefix=/usr/local $ make $ sudo make install
gir-repository:
$ git clone git://git.gnome.org/gir-repository $ cd gir-repository $ ./autogen.sh --prefix=/usr/local $ make $ sudo make install
gjs:
$ git clone git://git.gnome.org/gjs $ cd gjs $ ./autogen.sh --prefix=/usr/local $ make $ sudo make install
You can check gjs is working by doing:
$ /usr/local/bin/gjs gjs> 1+1 2 gjs>
It's a good idea to add /usr/local/bin to your path, again in ~/.bashrc:
export PATH=$PATH:/usr/local/bin
(and source ~/.bashrc to pick it up if necessary)
To compile Clutter itself, you'll also need these library headers:
$ sudo yum install glib2-devel mesa-libGL-devel libXext-devel \
libXfixes-devel libXdamage-devel pango-devel cairo-devel
(Again, maybe others too.)
Once you've got those too, you can checkout Clutter using git, and build it with introspection turned on:
$ git clone git://git.clutter-project.org/clutter $ cd clutter $ ./autogen.sh --prefix=/usr/local --enable-introspection $ make $ sudo make install
The final step is one which doesn't appear to be documented anywhere, but which is utterly vital: without it, gjs can't find any of the introspection metadata, and won't be able to call any of the C libraries through their JavaScript bindings. Add this line to your ~/.bashrc file:
export GI_TYPELIB_PATH=$GI_TYPELIB_PATH:/usr/local/share/gir-1.0/
(Again, use source if necessary.)
To test the install, and make sure the Clutter JavaScript bindings are working, put this code into a file test.js:
const Clutter = imports.gi.Clutter;
Clutter.init (0, null);
let stage = Clutter.Stage.get_default ();
stage.title = "Test";
stage.set_color(new Clutter.Color( {red:150, blue:0, green:0, alpha:255} ));
stage.show ();
Clutter.main ();
stage.destroy ();
Run it from a command line with:
$ gjs test.js
You should see something like this:

If this works, you should be able to run my tic-tac-toe (semi-)implementation, linked at the end of this blog entry. To run it, copy the two files into a directory, remove the .txt suffixes, then get a command line in that directory and run:
$ gjs ox.js
There's no AI (so you'll have to play both sides), but notice the use of a scale animation as each move is written to the board. Also interesting is how you can use anonymous JavaScript functions to define callbacks for signals.
I've not experimented extensively, beyond calling a few bits of the Clutter and Netbook Toolkit APIs. If you want more examples, have a look at the GnomeShell tests and UI code. The mx tests are another source of sample code I used.
Another tip: if you're trying to work out what's available in the JavaScript bindings, there's unfortunately no nice way to do it at the moment. Your only recourse is to grep the XML GIR files, which are located in:
/usr/local/share/gir-1.0/
Reading these is a bit of a chore, and I also found it difficult to figure out what was in the API. For example, if you're trying to use a Clutter constant like CLUTTER_X_AXIS, the GIR file has this entry:
<namespace name="Clutter"
version="1.0"
shared-library="libclutter-glx-1.0.so.0"
c:prefix="Clutter">
... loads of cruft ...
<enumeration name="RotateAxis"
doc="Axis of a rotation."
version="0.4"
glib:type-name="ClutterRotateAxis"
glib:get-type="clutter_rotate_axis_get_type"
c:type="ClutterRotateAxis">
<member name="x_axis"
value="0"
c:identifier="CLUTTER_X_AXIS"
glib:nick="x-axis"/>
</enumeration>
... loads more cruft ...
</namespace>
So, to reference the CLUTTER_X_AXIS constant, my first thought was look up the member element with a c:identifier corresponding to the constant I want; then use the path from the namespace, through the enumeration name, to the member name, i.e.:
Clutter.RotateAxis.x_axis
But in fact it's:
Clutter.RotateAxis.X_AXIS
(the name attribute, capitalised).
I also found that some methods and classes in the GIR for GLib didn't work (like GLib.printf). I'm sure there's a good reason why, it's just I couldn't work it out.
I spent quite a few hours figuring this stuff out, and stumbled a lot along the way. Partly because I'm not a C programmer, partly because I'm new to GNOME, I'm sure. But I think there are a few things which would have improved my experience:
print function - there is a patch to gjs which will give you that - see here: http://bugzilla-attachments.gnome.org/attachment.cgi?id=135898 - but you'll have to patch manually at the moment to get it)And finally, it might be worth taking a look at Seed, which is an alternative JavaScript implementation, but one which still understands GObject introspection. Seed is interesting in that it seems to provide more niceties, like a better REPL environment, more documentation, and a fuller standard library (filesystem and environment interfaces in particular).
It looks as if the JavaScript world is hotting up at the moment. This is a recent news report on the state of JavaScript:
http://arstechnica.com/web/news/2009/12/commonjs-effort-sets-javascript-...
It mentions the CommonJS standardisation effort, which is attempting to resolve some of the shortcomings of JavaScript as a general purpose language.
Reading through the CommonJS wiki, I was particularly interested in Narwhal, which resolves some of the issues I mentioned in the last section: it includes a package manager, module system, and standard library. It also provides support for different JavaScript engine back-ends: currently this includes a SpiderMonkey adapter. Given that Gjs is broadly based on SpiderMonkey, it seems likely that creating a Gjs back-end for Narwhal wouldn't be too onerous.
One other thing which could potentially be useful in Narwhal is the availability of separate Narwhal environments (called "seas") with their own set of packages. This could help isolate applications from each other in an environment like Moblin, where you want third parties to be able to develop applications without trampling on each other's libraries.
Another tool of interest is jake, a port of Ruby rake to JavaScript, which seems to be optimised to work with Narwhal. This could provide a reasonable alternative to autotools for the simpler build requirements of a JavaScript application (where there's no need for a compile step or library linking).
A combination of Narwhal (packaging/modules), with the power of Gjs binding to pretty much any C library, would make an attractive environment to develop in.
I should also mention node.js, server-side JavaScript primarily designed for building network programs, but which incorporates a CommonJS module system. It's built on Google's V8 JavaScript engine. It's also interesting because it is designed to be highly performant and memory-efficient, and has an interesting event-driven I/O model, like EventMachine or Twisted.
I'm definitely looking forward to seeing what happens next.
A few years ago, I used to work on RedHat Linux, then Fedora: my first Linux machine ran RedHat Linux. Then I got sick of things breaking every time I upgraded (on one traumatic occasion, my audio stopped working after a Fedora upgrade) and switched to Ubuntu.
But in my new job, I need Linux, but also need to encrypt my laptop's hard drive. I had a go at doing this with Ubuntu, with disastrous results. Didn't really know what I was doing (I did manage to encrypt the hard drive, but couldn't figure out how to mount it during boot). Fedora makes this simple, though. So I moved across, and have to say I'm pretty glad I did. It's very similar to Ubuntu in many ways, and I don't feel like I'm missing anything. My main annoyance was SELinux, which just wanted to stop me having any kind of fun with my computer; so I switched it off.
So, if you need to PGP encrypt your hard drive, but currently use Ubuntu, have a look at Fedora instead.
I have an EeePC with Moblin 2.1 (release candidate) installed on it (via the packages in http://repo.moblin.org/moblin/releases/2.1/ia32/os/). By default, you don't get an mp3 decoder with this distribution. It wasn't obvious (when I googled for it) how to install the right GStreamer plugins: several blog entries suggested a variety of pretty hacky methods and work-arounds.
Then I chanced on a blog entry which mentioned that Fluendo have a free legally-downloadable RPM of their own mp3 encoder; I remember someone at work mentioning it too. I downloaded and installed this onto my netbook with no difficulties. You have to register for an account and go through a faux shopping basket to get it, but it is fairly painless. Here's the product page URL:
Above is the first movie I've made with xtranormal, called Ex-super villain first date. Not sure how I've never come across this site before, but it is a fantastic piece of web engineering: an in-browser (Flash), animated movie creation application. Basic accounts are free, and give you a pretty good range of stuff you can do. The interface is very simple and intuitive. It seems best suited to the sort of stilted absurdist style I attempted above, which took me maybe an hour to put together. Much fun.
(Just a quick note about JQuery and rdfquery and memory leaks and htmlunit and such like for lost souls googling desperately for clues.)
I was having issues with some Webdriver tests, running under the htmlunit driver: they worked locally, but ran into Java "out of memory: heap space exhaustion" issues when running as part of a Hudson build on our Linux build server. The build also worked on another virtual machine with the Internet Explorer driver, so it seemed to be something specific to the htmlunit driver (maybe). After upgrading Java, upgrading Hudson (the continuous integration server), and tweaking the Java heap size, and still no joy, I found a few mentions of memory leaks in relation to htmlunit and JQuery (the Javascript library we're using), which I decided were worth investigating.
One comment in particular seemed plausible: that the Javascript on the pages under test was causing memory leaks in the Javascript engine in htmlunit (Rhino, I think?). It seemed possible that how we were using JQuery was causing Javascript to leak memory, but only in the context of the htmlunit driver. I trawled around the various bug trackers for htmlunit and webdriver, and got a few ideas (though couldn't pin the issue down enough to raise a bug myself).
One suggested fix (in the general context of JQuery, rather than specifically with regard to htmlunit) was to ensure that any event handlers bound to DOM elements via JQuery should explicitly be removed when the page unloads. This was easy to implement: I just added a final event binding to each page which looked like this (inside a <script> tag, obviously):
$(document).unload(function() {$('*').unbind(); });
I was also using the short-hand JQuery event binding syntax in my code, i.e. things like this:
$('#save').click(function() {
// ...
});
I changed these to the long-hand form (as this issue only started appearing when I started using the short-hand version):
$('#save').bind('click', function() {
// ...
});
One last thing I did was ensure that I called the quit() method on any Webdriver driver instances after I'd finished with them; and closeAllWindows() on any htmlunit WebClient instances when finished with them.
Unfortunately, I didn't do this very scientifically, and made all these changes at once. But the end result was that the build started running again. So if you're having out of memory errors with Webdriver/htmlunit/Hudson/JQuery/rdfquery, you at least have somewhere to start from :)

make "sky 1 make "into 2 to understand load "me clean home see home see obscure repeat :sky + :into [ see ] end to see television setscrunch :into golden :sky repeat often [ right pan forward sky ] end to golden :offering output :into * :offering end to television make "me happy end to happy op "erate "the "switches end to often make "trouble :sky + :into + :sky output power (:into + :sky) :trouble end to pan output :sky end to obscure hideturtle end
This is a Logo program which is also (kind of) a piece of poetry and image generator (it creates the image shown above). I did it a while back and found it an interesting exercise. I thought you might also find it interesting. Have you done anything similar? Anyone?
I'm not going to try to explain RDF and/or RDFa here, but thought any poor suckers looking for RDFa examples might benefit from me posting what I finally worked out, with help from my colleague Rob. Namely, how to annotate an HTML ordered list (<ol>) with RDFa attributes; and how to put RDFa attributes onto form elements.
Here's the HTML page with RDFa embedded in it. What I'm representing here is a sequence of collections, and the individual collections within it:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Collections</title>
</head>
<body>
<h1>Collections</h1>
<form method="post" action="http://receptacular.org/collections">
<ol xmlns="http://www.w3.org/1999/xhtml" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:rec="http://receptacular.org/schema#" typeof="rdf:Seq" about="http://receptacular.org/collections">
<li rel="rdf:_1" resource="http://receptacular.org/collections/1">
<span style="display:none;" rel="rdf:type" resource="http://receptacular.org/schema#Collection"></span>
<div class="collection-label" property="rdfs:label">Vague Collection</div>
<input type="checkbox" id="collections-1-hidden" property="rec:hidden" datatype="xsd:boolean" content="false"/>
<label for="collections-1-hidden">hidden</label>
<input type="checkbox" id="collections-1-defaultSearch" property="rec:defaultSearch" datatype="xsd:boolean" content="false"/>
<label for="collections-1-defaultSearch">use for searches</label>
</li>
<li rel="rdf:_2" resource="http://receptacular.org/collections/2">
<span style="display:none;" rel="rdf:type" resource="http://receptacular.org/schema#Collection"></span>
<div class="collection-label" property="rdfs:label">Archive Collection</div>
<input type="checkbox" id="collections-2-hidden" property="rec:hidden" datatype="xsd:boolean" content="false"/>
<label for="collections-2-hidden">hidden</label>
<input type="checkbox" id="collections-2-defaultSearch" property="rec:defaultSearch" datatype="xsd:boolean" content="false"/>
<label for="collections-2-defaultSearch">use for searches</label>
</li>
<li rel="rdf:_3" resource="http://receptacular.org/collections/3">
<span style="display:none;" rel="rdf:type" resource="http://receptacular.org/schema#Collection"></span>
<div class="collection-label" property="rdfs:label">Main Collection</div>
<input type="checkbox" id="collections-3-hidden" property="rec:hidden" datatype="xsd:boolean" content="true" checked="checked"/>
<label for="collections-3-hidden">hidden</label>
<input type="checkbox" id="collections-3-defaultSearch" property="rec:defaultSearch" datatype="xsd:boolean" content="true" checked="checked"/>
<label for="collections-3-defaultSearch">use for searches</label>
</li>
</ol>
<p>
<input type="button" value="Save" id="save-collections"/>
</p>
</form>
</body>
</html>
Available online here: http://receptacular.org/collections
Things of note:
To see the RDF which can be extracted from this page, you can use the W3C's RDFa Distiller. Here's the resulting RDF:
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
xmlns:dist="http://www.w3.org/2007/08/pyRdfa/distiller#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:rec="http://receptacular.org/schema#"
xmlns:xhv="http://www.w3.org/1999/xhtml/vocab#"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
>
<rdf:Seq rdf:about="http://receptacular.org/collections">
<rdf:_1>
<rec:Collection rdf:about="http://receptacular.org/collections/1">
<rec:hidden rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">false</rec:hidden>
<rec:defaultSearch rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">false</rec:defaultSearch>
<rdfs:label>Vague Collection</rdfs:label>
</rec:Collection>
</rdf:_1>
<rdf:_2>
<rec:Collection rdf:about="http://receptacular.org/collections/2">
<rec:hidden rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">false</rec:hidden>
<rec:defaultSearch rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">false</rec:defaultSearch>
<rdfs:label>Archive Collection</rdfs:label>
</rec:Collection>
</rdf:_2>
<rdf:_3>
<rec:Collection rdf:about="http://receptacular.org/collections/3">
<rec:hidden rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">true</rec:hidden>
<rec:defaultSearch rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">true</rec:defaultSearch>
<rdfs:label>Main Collection</rdfs:label>
</rec:Collection>
</rdf:_3>
</rdf:Seq>
</rdf:RDF>
Saving changes to an RDFa-enabled form like this is another challenge, for which we used rdfquery, and RDF library for JQuery. (I recommend you use the latest svn HEAD version of this library, as older versions have a bug where they ignore RDFa elements nested inside elements without RDFa attributes.) Maybe I'll get round to that another time.
This is a Ruby script which randomly copies mp3 files from one directory to an mp3 player. I wrote it so I could easily fill up my mp3 player from the 9000 odd mp3s I have on a different external drive.
To run it, you'll need the sys-filesystem gem (see http://rubyforge.org/projects/sysutils):
$ gem install sys-filesystem
Next, edit these variables in the script (near the top):
* source_dir to the directory containing the mp3s you want to select from
* dest_dir to the path for the directory on your mp3 player you want to copy to
Be a bit careful, as this will attempt to fill the dest_dir you specify with mp3 files from source_dir. You might end up filling the wrong disk up.
Then just run it with ruby from the command line:
$ ruby mp3s_random.rb
Note that it won't delete anything from the destination drive, and will attempt to fill all the space available. Also note that it doesn't keep trying mp3s until it finds one which will fit the last remaining space: once it tries to copy a file which won't fit, it stops. You can always run it again to see whether the next run finds a file small enough to fit.
I've only tested it on Linux, but, who knows, it might work on Windows too. (No operating-system specific commands are used in the script, as it uses Ruby for all file operations.)
The code is below, but I've attached it as well.
require 'rubygems'
require 'sys/filesystem'
require 'ftools'
source_dir = '/media/disk/music'
dest_dir = '/media/disk-1/music'
files = Dir[File.join(source_dir, '/**/*.mp3')]
num_files = files.size
stat = Sys::Filesystem.stat(dest_dir)
disk_free_space_kb = (stat.blocks_free * stat.fragment_size).to_kb
files_selected = []
while disk_free_space_kb > 0 and num_files > 0 do
# choose an mp3
file_num_to_copy = rand(num_files - 1) + 1
file_path = files.delete_at(file_num_to_copy)
num_files = num_files - 1
# work out how big file is
file_size_kb = File.stat(file_path).size.to_kb
# subtract from free space
if (disk_free_space_kb - file_size_kb) > 0
files_selected << file_path
disk_free_space_kb = disk_free_space_kb - file_size_kb
else
break
end
end
files_selected.each do |f|
copy_to_path = File.join(dest_dir, File.basename(f))
puts "Copying #{f} to device"
File.copy(f, copy_to_path)
end