Thursday, August 31, 2006

» Hyper-fatugly! +

Good Lord, how can anyone want to use perl6/pugs, let alone develop it! The syntax is so awful they had to start adding superlatives to describe its level of convolusion! I give you the hyper-fatarrow (yes, that's really the name, at least colloquially); actually...you may want to sit down first, it could give you a brain hemorrhage just looking at it. Here it comes: »=>«. What does it do? Well...um...according to Mr. Wall, "reduce operators only turn infix into list operators. What you really want here is a hyper-fatarrow: my %h = [at]k »=>« @v;" See, clear as...mud...hardened mud with a lead casing, and you're blind-folded, or better yet, your eyes were ripped out by scavenging baboons then taped back into their sockets by a muppet, and...well, you get the idea. Yes, indeed, perl5 always makes me glad that I'm a ruby user; and it looks like that will hold true for the next incarnation as well. Not to knock perl users -- they can have all the hyper-fatugliness they want, but give me the fresh mountain breeze which is ruby any day of the week, and twice on sundays.


"perl is the devil!"

Tuesday, August 29, 2006

» Of Rocks and Reptiles... +

A blog article was posted by jesusphreak the other day about why he chose python over ruby. Several people have commented on this in the blogosphere and mailing-lists (e.g., here, here and here), and in his comments section. So I might as well add my two bits.

I use both ruby and python, though I've only been using python for about 6 months (been using ruby for like...hmmm...3 years or so). Knowing ruby and chrome-level javascript (i.e., from writing firefox extensions) greatly decreased the python learning curve for me because of the similarities with those languages, and I can pretty much do everything I want to in python with minimal effort after just 6 months (writing fairly complex gui apps in any language in under 6 months bespeaks the power and intuitivness of the language -- 'course previous knowledge of ruby and javascript helped!). In fact, I've just been porting a desktop app I wrote in ruby+gtk over to python, and everything is going smoothly. The reason for porting it is mainly for ease of distribution -- I'm getting near to public release, and all the python extensions I'm using have graphical installers for win32, plus python is installed by default on many flavors of *nix, and the speed boost from psyco is nice too.

But don't worry, fellow rubyists, for admin scripts and personal projects I use ruby, mainly because of its expressiveness. Adding methods to built-in objects, transparent regexp support, code blocks, and so forth; I know that I can get all the same results in python, using different techniques, but some of them just look...ugly, by comparison. And I really dislike top-level methods like len() and str()...yuck! I know I can use a class-wrapper to get around this in python...like adding a str() method to a UserInteger wrapper class for class int, and then initializing all my ints as UserInteger instances:

class UserInteger(int):
def str(self):
return str(self)
## YES! You need these...
## the arithmetic operators cast their
## result as a normal int
def __add__(self, i):
return UserInteger(int(self).__add__(int(i)))
def __sub__(self, i):
return UserInteger(int(self).__sub__(int(i)))
def __mul__(self, i):
return UserInteger(int(self).__mul__(int(i)))
def __div__(self, i):
return UserInteger(int(self).__div__(int(i)))
def __radd__(self, i):
return UserInteger(int(i).__radd__(int(self)))
def __rsub__(self, i):
return UserInteger(int(i).__rsub__(int(self)))
def __rmul__(self, i):
return UserInteger(int(i).__rmul__(int(self)))
def __rdiv__(self, i):
return UserInteger(int(i).__rdiv__(int(self)))

n = 5
i = UserInteger(n)
i = ((i * i) / 3) + 2
i.str() # => '10'

...but that's a lot of trouble just to add a str() method to class int -- especially the part about initializing all int objects as instances of the class wrapper rather than just using a literal, not to mention adding methods to the wrapper to ensure type safety. Compare that to ruby's equivalent:

class Fixnum
def str()
self.to_s
end
end

i = 5
i = ((i * i) / 3) + 2
i.str # => "10"

No need to initialize instances of a subclass before my custom methods are accessible -- I've actually extended the base-class itself, not created a custom subclass.

But minor issues aside, the two are very similar (kissing cousins, I'd say). Consider the following sed-like program (which I aptly named gsub ;) ), first in ruby, then in python:

unless ARGV.length > 2
puts 'usage: gsub "PATTERN" "REPLACEMENT" file [file ...]'
exit
end

pattern = ARGV.shift
replace = ARGV.shift
files = ARGV.dup

files.each { |filename|
File.open(filename, 'rb') { |fh|
data = fh.read
if data =~ /#{pattern}/
fh.reopen(filename, 'wb')
fh.write(data.gsub(/#{pattern}/, replace))
end
}
}

And in python:

import sys, re

if len(sys.argv) < 4:
print 'usage: gsub "PATTERN" "REPLACEMENT" file [file ...]'
sys.exit()

pattern = sys.argv.pop(1)
replace = sys.argv.pop(1)
files = sys.argv[1:]

for filename in files:
fh = open(filename, 'rb')
data = fh.read()
fh.close()
if re.search(pattern, data):
fh = open(filename, 'wb')
fh.write(re.sub(pattern, replace, data))
fh.close()

Anyhow, in my own humble opinion, both ruby and python are great languages (in the words of Rodgers and Hammerstein: "the farmer and the cowman should be friends"), and a person should not limit themselves to using only one or the other of them; or at least they should try using both for some time to see which is better suited to their own needs before they make a final decision. It seems that jp found python more adequate to his needs at the time, so more power to him for using it! No haterism here.

Labels: ,

Saturday, August 26, 2006

» Favorite editor +

I'm a pariah when it comes to my favorite (grafical) text-editor. I've tried gvim, xemacs, scite, and I have gripes with all of them. I like Gedit. That's right. Gedit. Now that you're done gawking at how non-geeky, non-hacker-ish, lame I am, let me explain why.

Gvim
  • Moded editing...need I say more? Yes? OK, what about...moded editing!

Xemacs
  • Funky keys
  • Bloat...I personally don't want an editor that checks my email, triangulates satellite positions, and brews me coffee. Some people like that. I'd rather have software that does one job, and does it well.
  • Elisp? Eh, no thanks. I don't want to have to write a program just to change tab spacing or background color or syntax hilighting (OK... extensibility through a scripting language is nice... but Lisp... hmmm, not may favorite language).

SciTE
  • I really like this editor, my only gripe is that switching to utf-8 encoding inserts a BOM with no option to do otherwise! A utf-8 BOM is retarded anyhow. The encoding of the document should be determined by the locale, or by explicitly setting it, not by some magic byte-sequence.

So what about Gedit? Well Gedit supports all the standard keys that I use on the rest of the system (Ctrl-C, Ctrl-X, Ctrl-Z, &c). It also supports the various GTK input methods. It is very un-bloated (multi-line indentation, case conversion and so forth are handled by plugins which can be enabled or disabled by the user through the UI). And it supports customizable syntax hilighting of various languages (such as ruby!). It is utf-8 compliant and is free. It has user configurable tools which can be bound to custom accelerators, and a local snippet library plugin (many ruby snippets for doing common things like if...then) with support for binding snippets to accelerators (or just use tab-completion on any snippet). So why not use it?

Here is my current ruby hilighting scheme for Gedit (based roughly on the ruby-lang scheme for displaying code fragments. This is a gconf dump, use gconftool-2 to load it):

<gconfentryfile>
<entrylist base="/apps/gedit-2/preferences/syntax_highlighting/Ruby">
<entry>
<key>Attribute@32@Definitions</key>
<value>
<string>2/#2f2f8c8c8787/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>Builtins</key>
<value>
<string>2/#7e7e7e7e7e7e/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>Class@32@Variables</key>
<value>
<string>2/#2f2f8c8c8787/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>Constants</key>
<value>
<string>2/#d3d363632929/#000000000000/0/0/0/0</string>
</value>
</entry>
<entry>
<key>Definitions</key>
<value>
<string>2/#f9f9bbbb0000/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>Double@32@Quoted@32@String</key>
<value>
<string>2/#0000cccc0000/#000000000000/0/0/0/0</string>
</value>
</entry>
<entry>
<key>Global@32@Variables</key>
<value>
<string>2/#2f2f8c8c8787/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>Instance@32@Variables</key>
<value>
<string>2/#2f2f8c8c8787/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>Keywords</key>
<value>
<string>2/#f9f9bbbb0000/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>Line@32@Comment</key>
<value>
<string>2/#42428b8bdddd/#000000000000/0/0/0/0</string>
</value>
</entry>
<entry>
<key>Module@32@Handlers</key>
<value>
<string>2/#ad92442737b1/#000000000000/0/0/0/0</string>
</value>
</entry>
<entry>
<key>Multiline@32@Comment</key>
<value>
<string>2/#42428b8bdddd/#000000000000/0/0/0/0</string>
</value>
</entry>
<entry>
<key>Pseudo@32@Variables</key>
<value>
<string>2/#7e7e7e7e7e7e/#000000000000/0/1/0/0</string>
</value>
</entry>
<entry>
<key>RegExp@32@Variables</key>
<value>
<string>2/#d3d363632929/#000000000000/0/0/0/0</string>
</value>
</entry>
<entry>
<key>Single@32@Quoted@32@String</key>
<value>
<string>2/#0000cccc0000/#000000000000/0/0/0/0</string>
</value>
</entry>
<entry>
<key>Symbols</key>
<value>
<string>2/#0000cccc0000/#000000000000/0/1/0/0</string>
</value>
</entry>
</entrylist>
</gconfentryfile>

Thursday, August 24, 2006

» Doing it the Ruby Way™ +

I posted a comment on Redhanded that got me thinking about the Ruby Way™ of doing string formatting. Since ruby is a true OO language, where everything is a first-order object, and all methods are object attributes, it doesn't seem to follow suite to leave it at Kernel#format which takes as its first argument the string object on which to operate. It seems like it would be much more rubyish to have format (and sprintf) as methods of the String class which operate on the caller, the same way that the % method does.

So that is simple enough to implement:

class String
define_method(:format) { |*args| Kernel.format(self, *args) }
alias_method(:sprintf, :format)
end

a, b = 'string', 2
s1, s2 = 'This is a %s', 'A %s and a %d'
#these all do the same thing
#literal
p(s1 % a)
p(s2 % [a, b])
#format
p(s1.format(a))
p(s2.format(a, b))
#sprintf
p(s1.sprintf(a)
p(s2.sprintf(a, b))

But wait...what about Kernel#p and so forth? Why are we using them from the top-level rather than as a method on the object? Well, for one thing, the print-like methods accept different types of objects as input. But that means they must be polymorphic -- that's just a fancy way of saying that they know what types of data they are operating on and treat each in a specific way. Strings are printed with double-quotes, arrays are printed as ["member", "member", ...], while hashes are printed as {"key"=>value, ...}. (Note, this only describes the p function, others behave slightly differently: try p, puts, and print with different data-types to get an idea of how they each work). So we can easily add these methods to Object without any extra work needed to determine data-types and so forth. We can even take arguments to them so that multiple objects can be printed at once, just as the Kernel methods allow.

class Object
define_method(:p) { |*args| Kernel.p(self, *args) }
define_method(:puts) { |*args| Kernel.puts(self, *args) }
define_method(:print) { |*args| Kernel.print(self, *args) }
alias_method(:say, :puts)
end
class String
define_method(:format) { |*args| Kernel.format(self, *args) }
define_method(:printf) { |*args| Kernel.printf(self, *args) }
alias_method(:sprintf, :format)
end

Now we can do stuff like this:

a = 'I like %s'
b = 'ruby'
a.format(b).print(" alot!\n")
['I', '[heart]', 'ruby', '!'].puts({"me"=>" too!"})

Fun. :)

Addendum: I was just informed of Object#display, which does basically the same thing as my Object#puts does. Nifty! ruby is Chock Full O'Goodness. :)

Tuesday, August 22, 2006

» Write it in C or face 10,000 slow deaths! +

Ruby is slow. Matz owns up to the fact. Benchmarks show that it's true. Ruby is not too slow to be useful or anything, just slower (at some things) that many other languages, including perl and python. It is getting faster (1.9 is faster than 1.8.5 in many areas), but even so, when you need speed you often need to drop to a lower-level language like C to get it. That's true for perl and python too, BTW.

But the question is, when? When will you actually gain speed by going to C? Well, always, duh! But more specifically, when will the increase in speed be of enough benefit to justify the evilness? When is the evil of C a necessary evil?

In a comp.lang.ruby post from July, Peter Hickman suggested porting an algorithm for finding Latin Squares from perl to C for the speed increase. He limited his example to finding 5th order (5x5) squares. Droping into C took him from 12 minutes for his optimized perl version, down to 5 seconds for the C version. Now that seems like a major performance boot to be sure. But something is hinky here (not to disparage Mr. Hickman, who has been coding longer than I've been breathing!).

Firstly, I noticed some time ago that when I wrote programs that print to standard output/error frequently, or with large amounts of data, they were quite slow. I always thought that it was just slowness due to ruby, but I found that the same was true for python. So I got to thinking about it. I tried writing to a file instead, and sure enough, the programs ran faster by orders of magnitude. Well duh! It makes sense that it is slower to go through all the various layers it takes to finally reach the screen than to go through the (relatively) transparent filesystem layer. So I figured out that I shouldn't print to the console unless it was infequently, with small amounts of data.

Secondly, I noticed that Mr. Hickman's C code was using printf to show each row of the squares. That is strange. The Latin Square algorithm should be producing 161,280 unique squares. Hmmmm. Let's do the math here. Each square is 5 * 5, so 25 numbers. We have 161,280 unique squares. That means we have 4,032,000 numbers (all being 8-bit numbers in the range 1-5). Now there are 1,048,576 bytes in a meg. So we should have about 3.84 megs of total data.

ruby -e "puts 161_280 * 25 / 1_048_576.0"
> 3.84521484375

OK, now let's just asume that C is so fast that it only takes 1 second to do all the maths, and 4 seconds to print the results. That almost a meg a second throughput rate! That's fa-a-a-st! But that can't be right! How could ruby and python be so slow at console output and C be so fast? Well, it turns out that it isn't. I wrote a simple test case in C that prints a 10-byte string to the console 15,000 times (150k of data) using a simple for loop with puts, and low and behold, it took about 25 seconds. The same program in ruby (I did it in python also) took 26 seconds. So the ruby-to-console throughput is not much slower than C after all.

But this raises the question: How fast would the ruby version of the algorithm be if it printed to a file rather than to standard output? Well, using the faster of the two algorithms mentioned on the list, which uses the pure-ruby Permutation class and the built-in Set class, and which takes about 26 minutes to run on my machine, takes a whoping 17 seconds when using file IO rather than standard output! (Actually, you can whittle it down to 15 seconds if you collect all the results into an array and only use one IO write with the join'd array -- a memory for speed tradeoff).

Now I don't know about you, but I can live with 17 seconds, rather than trying to muck around with writing a C extension or (dread!) writing the whole program in C. Shoot, 17 seconds is longer than it takes me (I'm no C guru) to figure out what headers I would need to require!

So don't just assume that your bottle-neck is the ruby interpreter! It may be the case that C would give you no great advantage at all, and thus would not be a necessary evil, just an evil! ;)

For source codes and such see my mailing list post.

Postscript: This also makes me wonder how many benchmarks in the above-mentioned shootout may be printing to standard output? Of course, if all versions of the program in the benchmark did so, then I suppose it would still be fair, but it would still add unecessary lag to all of them (which I think are meant to test only specific aspects of the languages: number-crunching, object creation/manipulation, and so forth).

Addendum: Jürgen Strobel made a good point in this regard. My numbers are biased by the terminal emulator. This is exactly what I was trying to demonstrate--the same would apply to the C version. So the problem was not in ruby being so slow (i.e., "ruby takes 26 minutes because it is slow"). On the same note, using a regular tty rather than an xterm, and writing to stdout, the ruby program takes 23 seconds. That's only 6 seconds longer than writing to a file. That's not too bad. So I ammend my suggestion not to write large amounts of data to stdout to apply to a terminal emulator rather than a real tty.

Friday, August 18, 2006

» Kazehakase...ruby in your browser?!! +

I just came across a project called Kazehakase. It is a web-browser based on Gecko (the Mozilla/Firefox HTML rendering engine). Big deal, you say. There are already Galeon and Epiphany and so forth. But I ask you, do any of those browsers have a ruby prompt sandbox (with autocompletion!) built in? No? Didn't think so. Well, kazehakase does!

The docs are mainly in Japanese and the wiki and mailing list are subject to spammage, but you can always check out the source of ext/ruby/kz/ruby-dialog.rb to see what's going on behind the scenes.

And on a related note -- there has been some talk in the Firefox community of late about allowing third-party scripting languages (notably python) to interface with XUL and the gecko innards. See here for example. In fact, in the Bon Echo 3 nightlies, one can use the python: protocol to launch the interpreter to evalauate a statement (the same way one can currently evaluate a javascript statement with javascript:). Does this mean that sometime in the not too distant future we'll be able to write extensions for Firefox in pure ruby? Let us hope and pray!

Wednesday, August 09, 2006

» ELER...Ruby-style +

A person using the handle RailsBlob posted a comment linking to their blog on Redhanded the other day and I found a particular line in this post to be rather humorous (the effect was intended). Now _why has blogged about RailsBlob, and I decided to throw together a little comic incorporating the line I liked (I also used one of the "Foxes" from Why's (Poignant) Guide To Ruby in one frame, assuming that all associated arts are included in the CC license -- so credit to whytheluckystiff!).

So for your viewing enjoyment, I present you with a Rubyism:

Saturday, August 05, 2006

» A Matter of Semantics +

It's all about semantics. Linguists (of both natural languages and engineered languages) have long realized that semantics are foundational to all communication. Computer Scientists have not been blind to this important fact. Recent developments such as the Semantic Web and Domain Specific Languages (DSLs) have only served to emphasize this fact. Actually these developments aren't recent, but they are gaining more notice of late.

Well speaking of semantics, my brother has been working on an interpreted language called SEML (said SEEM-L; short for SEMantic Language). This is a "scripting language" which will allow for some pretty cool implementation strategies. Firstly, it should be stressed that the name SEML is not set in stone. The language is still in the design phase, and the name may change. Beyond that, SEML is a fully Object-Oriented language similar to LISP or Scheme in functionality (recursive replacement), but without all the strangeness (i.e., prefix notation, lists and atoms, monads). It is basically an imperative language with the benefits of a functional language (hey, sounds kind of like Ruby!). I won't say too much, as the language is still emerging, and is prone to change on a whim; but I will say that the prospects look very promising.

With its scheduled pattern-based replacement syntax, it is not simply a platform for hosting DSLs--it is, in its very essence, a DSL. It will include a standard library of patterns for common functionality such as conditional code execution (e.g., if blocks), operators (e.g., +, -), and so on. But these can all be overrided when needed. Another neat feature of the language is (optional) delayed evaluation. Say that you have the statement (using an informal notation) y=x+3. Normally this would be evaluated at the time of assignment--whatever the value of x at the time of assignment, add three to it. But with a special syntax SEML knows that this statement should be evaluated every time y is accessed. If x changes, then when y is accessed it will reflect this change.

At present SEML is nowhere near complete; but it is getting there. My brother is either a genius or a madman; my money is on genius. Time shall tell.

Thursday, August 03, 2006

» Non-Recursive Factorial +

A common way to find a factorial in Ruby is to use a recursive function similar to this:

def fac(n)
if (n > 1)
n * fac(n - 1)
else
1
end
end

This is just the ruby implementation of the recursive Y-combinator for finding a factorial. Nothing wrong with that--if you live in a theoretical world of limitless stack space! But what if you want to calculate (why would you, I wonder?) the factorial of 926 or 1024 on your finite computer? Well, that's going to be a problem. On my machine, at about 860 levels of recursion into the #fac method ruby explodes with the error message: stack level too deep (SystemStackError). Doh.

But we can fix that quite easily. Instead of recursion (which involves allocating a new stack frame for every re-entry), we want iteration (which uses a single stack frame for every pass then discards it).

def fac(n)
sum = 1
sum.upto(n) { |i| sum *= i }
sum
end

Using the Fixnum#downto iterator method (Range#each would also work here, i.e., (1..n).each) we are able to calculate the factorial using a single stack that is discarded with each iteration--no more problem with running out of stack. And notice how elegantly this can be done in ruby; no gangly for or while loops, just a simple iterator and a block.

So what about speed? Both versions are pretty much equal. By cutting out the explicit comparison with 1, the explicit subtraction of 1 from n and the symbol table lookup for re-entry, we save a few clock-cycles; but not enough to make a big difference (around 30 milliseconds difference to find the factorial of 900).

Please note that I didn't invent this method of calculating factorials by iteration rather than recursion--it has been known for many years; I just thought it might prove useful to point out the difference between the two approaches in case anyone is not familiar, since the difference applies to other similar problems as well (e.g., file-system recursion).

Wednesday, August 02, 2006

» To "do" or to "squiggle" +

Well, I've been waivering lately between using do...end and {...} for blocks. I like the way do...end looks, and the end flows nicely with all the other ends. But on the other hand, a block is a bundle of code that is contextually and syntactically distinct from the code around it on the interpreted level. So should it also be distinct on the implementation level also? Obviously do...end is a different implementation from, say, if...end, but that is not easy to see at a glance (you have to follow the block back to the initial keyword to see if it is a "do" or an "if" or whatever).

I know the (general) convention in the Ruby community is to use {...} for one-liners and do...end for multi-liners; but I think I'm going to start using {...} for all blocks.

And speaking of syntaxis and such, you'll notice that I use parentheses when calling methods which take arguments, when defining methods and for conditional clauses. I realize that this is not necessary, and is more akin to the evil C-family of languages that to the Smalltalk/Lisp side of the gene-pool. But it helps me to code better, and I can read more easily. Mabye that's because I come from a PHP/JavaScript background. Whatever the case, if you don't like it, get over it--Ruby is all about having the freedom to code as efficiently as you are able, and for me that means parentheses.

module I
def so(bool, &block); end
def you_dont_like_it?(); true end
def I.could(blah); end
def care(); false end
end
class FalseClass
def less!(); end
end
include(I)

Hehe...

so(you_dont_like_it?) { I.could(care.less!) }

;P

» Simple batch processing +

Ok, so I wanted a way to backup my Anime in mpeg4. Simple, just use transcode or mencoder. But what about chapter splitting and such? Well, I could always use my trusty bash history -- up-arrow, edit, return. But that is rather tedious, and oddly enough don't work when I'm sleeping. It is especially irksome if your encoding something like 12 Kingdoms, with 5 episodes per disk. So I made a simple batch process dispatcher in Ruby. All it does is take file with a list of programs and run them in sequence (with a little bit of fluff, like starting from a process number, or running only a single process). Here it is, if anyone might find it useful.

#!/usr/bin/ruby

# = batch - Simple batch file processing module
#
# == Format of batch file
#
# - A # is a comment delimiter, ignore everything on the line.
# - Anything else is command to run - no line continuations!
#
# You can basically just use a list of commands understood
# by the shell.
#
# === Example batch file:
#
# echo "Cmd 1" # this is a comment
# df -h
# # this is a comment too
# ls -lh
#
# echo "Cmd 4"
#
# Simple, eh?
#
# == Version information
#
# * ver 1.1 - slight cleanup
# * ver 1.2 - remove interactive jobs (causing some bugs),
# rename variables to be more intuitive, add comments
#
# If you really want to have interactive jobs, you could use
# something like this bit of ugliness (all on one line!):
#
# read -t 30 -p "Run command? [Y] "; if [ -z ${REPLY} ] ||
# ([ ${REPLY:0:1} != "N" ] && [ ${REPLY:0:1} != "n" ]); then
# echo "Cmd 3"; fi
#
# * ver 1.3 - minor cleanup

$KCODE='U'

class SimpleBatchFile

# Read jobs from batch file, fill jobs and jobcount instance vars
def initialize(batchfile)
unless (File.file?(batchfile))
puts("Cannot find batch file: #{batchfile}")
exit(1)
end
@file = batchfile
data = File.open(batchfile, 'rb') { |f| f.readlines() }
@jobs = []
data.length.times { |i|
item = data[i].strip()
## skip comments and empty lines
next if (item[0,1] == '#' or item == '')
## format for jobs is (command, jobnum, linenum)
@jobs.push([item, @jobs.length + 1, i + 1])
}
@jobcount = @jobs.length
end

# Execute a command in a subshell, die on errors
def exec(command, jobnum, linenum, batchfile)
puts(" Starting job: #{jobnum}\n" +
"Executing command: #{command}\n")
unless (system(command))
puts("Problem running job...\n" +
"File: #{batchfile}\n" +
" Job: #{jobnum}\n" +
"Line: #{linenum}\n" +
'...dieing!')
unless ($?.exitstatus.nil?)
exit($?.exitstatus)
else
exit(1)
end
end
end

private :exec

# List jobs to stdout
def list()
@jobcount.times { |i|
if (i == 0)
pad = ''
else
pad = "========\n"
end
puts("#{pad} Job: #{@jobs[i][1]}\n" +
" Line: #{@jobs[i][2]}\n" +
"Command: #{@jobs[i][0]}")
}
end

# Run a job or jobs
#
# * to start from a given job use n, e.g., start=3
# * or to run a range of jobs use n..n, e.g., start=3, stop=5
# * or for a single job only use n..n, e.g., start=2, stop=2
# * no args means all jobs in batch are run
def run(start=1, stop=nil)
## some bounds checking and logics
stop = @jobcount unless (stop)
start = 1 if (start < 1)
start = @jobcount if (start > @jobcount)
stop = @jobcount if (stop > @jobcount)
stop = start if (stop < start)
(start - 1).upto(stop - 1) { |i|
exec(@jobs[i][0], @jobs[i][1], @jobs[i][2], @file)
}
end

end ## class SimpleBatchFile

if (__FILE__ == $0)
## standalone script

## get batch file name from argv
bfnm = ARGV[0]
## no file given, print usage and exit
if (bfnm.nil?)
me = File.basename($0)
puts("Usage: #{me} batch_file [start_job_number]")
exit(1)
end
## instantiate class
sbf = SimpleBatchFile.new(bfnm)
## get range from argv
scmd = ARGV[1]
if (scmd.nil?)
## run all jobs
sbf.run()
elsif (['-l', '--'].include?(scmd[0,2]))
## show job list
sbf.list()
elsif (scmd.include?('..'))
## run range of jobs
scmd, ecmd = scmd.split('..')
unless (ecmd)
ecmd = 0
end
sbf.run(scmd.to_i, ecmd.to_i)
else
## run all jobs including and after
sbf.run(scmd.to_i)
end

end

This way I can do things like this (rubyconf05.batch):

wget -c http://yhrhosting.com/rubyconf/AslakHellesoy320.mov
wget -c http://yhrhosting.com/rubyconf/AustinZiegler320.mov
wget -c http://yhrhosting.com/rubyconf/BrentRoman240S.mov
wget -c http://yhrhosting.com/rubyconf/DHH320.mov
wget -c http://yhrhosting.com/rubyconf/FowlerAndWeirich320.mov
wget -c http://yhrhosting.com/rubyconf/JimWeirich320.mov
wget -c http://yhrhosting.com/rubyconf/KarlinFox320.mov
wget -c http://yhrhosting.com/rubyconf/MatzKeynote320.mov
wget -c http://yhrhosting.com/rubyconf/NathanielTalbott320.mov
wget -c http://yhrhosting.com/rubyconf/OpenSpace320.mov
wget -c http://yhrhosting.com/rubyconf/RyanDavis320.mov
wget -c http://yhrhosting.com/rubyconf/rc01-fri-morning-david_black2.mp3
wget -c http://yhrhosting.com/rubyconf/rc02-fri-morning-francis_hwang.mp3
wget -c http://yhrhosting.com/rubyconf/rc03-fri-morning-akira_tanaka.mp3
wget -c http://yhrhosting.com/rubyconf/rc045-fri-aftnoon-koichi_sasada.mp3
wget -c http://yhrhosting.com/rubyconf/rc04-fri-aftnoon-charles_nutter.mp3
wget -c http://yhrhosting.com/rubyconf/rc05-fri-aftnoon-eric_hodel.mp3
wget -c http://yhrhosting.com/rubyconf/rc06-fri-eve-matz.mp3
wget -c http://yhrhosting.com/rubyconf/rc07-sat-morning-david_black.mp3
wget -c http://yhrhosting.com/rubyconf/rc08-sat-morn-kevin_baird.mp3
wget -c http://yhrhosting.com/rubyconf/rc09-sat-morn-brent_roman.mp3
wget -c http://yhrhosting.com/rubyconf/rc10-sat-morn-austin_ziegler.mp3
wget -c http://yhrhosting.com/rubyconf/rc11-sat-aftnoon-ryan_davis.mp3
wget -c http://yhrhosting.com/rubyconf/rc12-sat-aftnoon-jim_weirich.mp3
wget -c http://yhrhosting.com/rubyconf/rc13-sat-aftnoon-karlin_fox.mp3
wget -c http://yhrhosting.com/rubyconf/rc15-sat-eve-matz.mp3
wget -c http://yhrhosting.com/rubyconf/rc16-sun-morn-david-hh.mp3
wget -c http://yhrhosting.com/rubyconf/rc17-sat-morn-nathaniel_talbott.mp3
wget -c http://yhrhosting.com/rubyconf/rc18-sun-morn-aslak_hellessoy.mp3

# batch rubyconf05.batch

Crossing the Ruby-con. :)

Of course, I could also just sh rubyconf05.batch, but then that wouldn't been very Ruby-ish, now would it? Plus I couldn't very well start where I left off if my ethernet barfed when downloading Matz' keynote presentation by doing it that way now could I? But this way I'd just batch rubyconf05.batch 8 and go on about my buisness. Or if the other files got downloaded but only Matz' speach didn't, cause the server was having trouble, then it's batch rubyconf05.batch 8..8 (run only job #8 and quit). No editing files, no looking through shell history and so on.

» Implementing a state pattern in Ruby +

A few weeks ago, Maurice Codik blogged about a better state pattern than the one on RubyGarden. I posted a comment suggesting an alternative implementation using a hash of state blocks. Here is an updated version with a little bit of fleshing out:

module Stateful
class StateInitError < Exception; end
class StateSetError < Exception; end
def with_key(key, ex, &b)
if (@s_states.has_key?(key))
yield b
else
raise(ex, "No such state `#{key}'", caller)
end
end
def accept_states(*states)
@s_states = {}
@s_initial = @s_current = states[0]
states.each { |s| @s_states[s] = nil }
end
def reject_states(*states)
states.each { |s|
@s_states.delete(s)
if (@s_current == s)
if (@s_initial == s)
@s_current = nil
else
@s_current = @s_initial
end
end
}
end
def transition_to(s)
with_key(s, StateSetError) {
if (@s_states[s])
@s_states[s].call
end
@s_current = s
}
end
def state(s=nil, &b)
return @s_current if (s.nil?)
with_key(s, StateInitError) {
if (block_given?)
@s_states[s] = b
if (@s_initial == s)
transition_to(s)
end
else
transition_to(s)
end
}
end
end

########

class Connection
include(Stateful)
def initialize()
accept_states(:initial, :connected)
state(:initial) {
def connect
puts("connected")
transition_to(:connected)
end
def disconnect
puts("not connected yet")
end
}
state(:connected) {
def connect
puts("already connected")
end
def disconnect
puts("disconnecting")
transition_to(:initial)
end
}
end
def reset()
puts("reseting outside a state")
transition_to(:initial)
end
end

c = Connection.new
c.disconnect # not connected yet
c.connect # connected
c.connect # already connected
c.disconnect # disconnecting
c.connect # connected
c.reset # reseting outside a state
c.disconnect # not connected yet
c.transition_to(:connected)
c.disconnect # disconnecting
p c.state # :initial
c.reject_states(:initial)
p c.state # nil
c.disconnect # not connected yet
c.transition_to(:initial) # No such state...

Updated: Added #reject_states and made it auto-initialize the default state (i.e., first arg to #accept_states) without needing to call #transition_to explicitly in the class initialization, refactored #transition_to and #state.