89 Posts Tagged 'Ruby' RSS

Ruby singleton classes

I was writing some Ruby and I must've had my head stuck in Java, because I wrote something like this:

class Test1
    @x = 1
end

That is very different from this:

class Test2
    def initialize
        @x = 1
    end
end

In the latter case I made a normal instance variable which any object that is_a? Test2 can access. In the former case I made an instance variable of Test1 itself. Classes in Ruby are Objects like any other; I gave the Test1 object itself an instance variable. So:

Test1.new.instance_variables # => []
Test2.new.instance_variables # => ["@x"]

Chapter 24 of the Pickaxe book talks about singleton classes. You can edit objects (add methods and instance variables etc.) on a per-object basis rather than a per-class basis. You can do this because Ruby makes a "virtual" singleton class, makes that singleton class the direct class of your object, and makes your object's original class the superclass of this new singleton class. That way the object's original class is untouched and your meddling doesn't affect any other objects of the same class.

In Test1 above this is what I did. Ruby made a new singleton class, which the Pickaxe calls Test1, and made an instance variable @x in it. That instance variable is then accessible as a part of the Test1 object itself. If it were theoretically possible to instantiate another object of type Test1', it would presumably have a @x too, but this isn't possible.

So say I want to access this @x that belongs to Test1. The Pickaxe tells you how. In general, if you have an object and you want to access an instance variable of it, you define a "getter" method in your object's class that returns the variable's value. You can use the shortcut attr_reader to do this.

So if you want to access an instance variable of Test1 itself, you need to define a method in Test1's class (i.e. the singleton class, Test1'). The Pickaxe says to do this:

class Test1
    class << self
        attr_reader :x
    end
end

Test1.x  # =>; 1

It makes perfect sense that you can also do it this way:

class << Test1
    attr_reader :x
end

Test1.x  # => 1

The Pickaxe gives a couple reasons why you might want to do this kind of thing. But I'm going to go ahead and label it "black magic" and stay away from it, I think.

March 22, 2007 @ 3:26 AM PDT
Cateogory: Programming
Tags: Ruby

Dilemmas continued

I previously prattled on about what the right abstraction should be for an icon theme. I said a hierarchy of subclasses would work.

I was very wrong. It didn't work at all. For example I tried to write a == method for my Icon class. With the class structure I was using before, the only thing I could compare between two Icons is the icon name. In which case, two icons of different sizes in different contexts would have to be considered equal. That's not right. The only other option was to make some kind of is_equal? method all the way at the top of the class hierarchy, and have it construct a complete picture of an icon on the fly, and use that in the comparison. That kind of defeats the whole purpose of setting up the class hierarchy that way to begin with.

A better abstraction is a single Icon class with size and context as properties. An icon needs to know these things about itself. I recently was reading Head First Design Patterns and one of the first "rules" it gives is:

Favor composition over inheritance.

It's right in this case. I was tripped up by the fact that on disk a size does contain a context, and a context does contain an icon. But there's no reason my abstraction needs to match that structure.

On a side note, it's a good book, even though it mostly deals with Java. Many of the "design patterns" it gives are workarounds for the inflexibility of Java itself, but it does give some interesting ways of looking at certain problems.

March 18, 2007 @ 6:13 AM PDT
Cateogory: Programming

Programming dilemmas

I scrapped the throw-away version of the KDE <=> Gnome theme converter script which I started many months ago, and started over. I'm trying to do it "right" this time.

It looks like the same index.theme files (if properly written) can be read by Gnome and KDE both, which is nice. I found this handy index.theme specification which should help. Assuming Gnome and KDE implement the spec correctly.

One thing I tend to struggle with is the "big picture". I'm good at getting the details right (or if not right, at least working), but organizing all the bits into a comprehensive, clean whole is something I have trouble getting my head wrapped around at times. Ruby is a nice Object Oriented (tm) language, and that helps a bit with organizing code, assuming a problem maps nicely onto OO-space. But it's oh so very easy to write bad OO code too. Dumping sucky code into a bunch of classes doesn't make the sucky code suck any less.

I think this program is a good exercise in properly abstracting a problem. At the top level is the Theme, which contains Contexts ("applications", "filesystem", etc.), which contain Icons. Probably I need a Size abstraction in there too, each of which contains many Contexts, because each Context can be listed under multiple sizes.

So the end target in all this is really just a bunch of filenames. Now, where should those be put in this abstraction? Do I let the Icons manage and deal with their own absolute pathnames? Or the Contexts? Or the Theme? Or a higher level? Taking a step back, it seems that a relative base directory, "ThemeName", is intrinsic to a Theme, but an absolute pathname, "/home/user/some/dir/ThemeName" isn't; rather the absolute name is part of an operation you do on a theme: "export yourself into this directory". Likewise a Context has (is) a directory name ("applications") and possibly shouldn't care which Theme it's a part of, and an Icon has a filename ("icon.png") and seemingly shouldn't care which Context it lives in.

But the more I think about it, should an Icon know more? Should an Icon know exactly what absolute path on the filesystem it maps to? If not, there needs to be some mechanism to convert Icons into absolute pathnames, somewhere along the line. Where should that go? Should I just crawl down the tree top to bottom, Theme -> Sizes -> Contexts -> Icons, and have it return the pathname that way by building it out of parts? Seems cleaner conceptually, but it's so darned expedient to give the absolute pathname to every object. I think I tend to err on the side of expediency, usually to ill effect, so I'm going to try the other, "proper" way this time and we'll see how it goes.

I also need the ability to sort and search Icons by size; that seems trivial if I have a Size abstraction. And I need the ability to search for Icons by name; seems like a good place to use hash lookups. But I see other possible choices that could work too. Then I just write a tool that takes two Theme abstractions, matches the abstractions in some way, and maps the conversion to real file operations. And some tools to read index.theme files and build Themes from them, and some tools to take a Theme and write out an index.theme file. Seems easily doable. All of these little design questions are the kind of thing that I tend to get hung up (i.e. tripped up) on. It's good exercise at least. I learn a little every time I go through this kind of thing.

March 12, 2007 @ 4:09 PM PDT
Cateogory: Programming
Tags: KDE, Ruby, Gnome

Vim regexes

One thing I very much miss in Gentoo is controlling what is compiled into my Vim. You need to enable perldo and rubydo support at compiletime. Gentoo had USE flags to do it. In Ubuntu I get perldo but no rubydo by default, which is annoying.

The reason I need perldo/rubydo is because Vim's regexes are so inconsistent. * is special when not escaped, but + is special when escaped. You can use \{x,y} (escape only opening bracket), but you have to use \( \) (escape both parens), and with [] it's special when you don't escape either. I simply can't remember these, especially when coding at full speed, and speed is one of the reasons to use Vim in the first place.

Then Vim has magic and nomagic. And you can use \v to set "very magic". Very magic is almost what I want, but you can't set it in your .vimrc and even if you could, the Vim manual tells you to leave the setting of magic alone if you know what's good for you.

PCREs are so much more consistent and easier to remember (excepting Perlisms like "dot matches newline" inconsistencies). The special characters are always special, and you escape them all to make them non-special. But perldo and rubydo in Vim can't do everything Vim regexes can do; they can't properly span lines, is the major thing. They don't highlight text like Vim does with its builtin regexes if you have hls set.

I read somewhere that Vim regexes are set up to let you match C code easily, and that's why for example {} are non-special by default. I don't remember where I read it or if it's true. Doesn't help a lot when writing non-C code though.

March 05, 2007 @ 11:43 AM PST
Cateogory: Programming
Tags: Perl, Ruby, Vim, Gentoo

Taking screenshots of a single window

I am running a game and I need to take many screenshots of the game window. There are lots of Linux tools that take screenshots: scrot, import (part of ImageMagick), gnome-screenshot, ksnapshot, and the GIMP does it too.

It turns out none of them does EXACTLY what I want, on its own. My requirements are:

  1. Take a snapshot via a single configurable keystroke.
  2. Save it in a directory of my choosing.
  3. Take a snapshot of a SINGLE window. And crop off the window borders. I need to take way too many snapshots to have time to go around editing them afterwards.
  4. Look at what files already exist in the directory I pick, and give the new file a filename that is next in ascending order after the files that already exist. It should pad the filename out to 5 digits.
  5. Do all of this non-interactively. It shouldn't ask me to confirm.

The way I finally ended up doing this is using import; I wrote a script to use it, and I assigned that script to a keystroke in my window manager. And once again, it's Ruby to the rescue:

#!/usr/bin/ruby

Dir.chdir('/SOME/DIRECTORY') do
    begin
        num = sprintf '%05d', Dir.glob('*').select{
            |x| x.match(/^\d+\.png/)
        }.sort[-1].match(/^(\d+)/)[1].to_i + 1
    rescue Exception => e
        puts e
        num = '00001'
    end
    raise "How'd that happen?" if File.exist? "#{num}.png"
    `import -window 'NameOfWindow' #{num}.png`
end

That this kind of thing is possible is why I love Linux. I can do so much more. I can have it save in multiple file formats. I can have it generate thumbnails as it saves new snapshots. (It's so easy to generate all the thumbnails later using convert that I'm not going to bother.) I could timestamp the filenames rather than using incrementing integers. (There is a race condition in this script that would be fixed if I did this, but I don't care enough to do it that way.)

March 03, 2007 @ 3:23 PM PST
Cateogory: Programming

Mmmm rubygems

I put together my first ruby gem today (for internal use at work). Here is a good tutorial on how to do it. Or else look at chapter 17 in the Pickaxe book. Simply make a gemspec file, define a few attributes listing and describing your files and then compile it. It was astoundingly easy and such a clean way to handle versioning and distributing Ruby code. I always had some weird impression that rubygems was clunkier than Perl's CPAN, but I think I had it wrong. Not at all hard to use.

Setting the RUBYOPT environment variable to 'rubygems' lets you get away with doing a require on gem-provided modules without doing require 'rubygems' in every program you write. I'm sure I must've set RUBYOPT myself a year ago on this machine and forgotten. I was sort of wondering one day, hey, how is Ruby finding those modules? This kind of thing is good to know when you need to set up a Ruby environment on someone else's computer in the near future, I imagine. This is one bad thing about having a computer that doesn't need to be reformatted every six months. Who knows how many other tweaks I've done on this machine and forgot about and now silently depend on.

February 21, 2007 @ 7:51 AM PST
Cateogory: Programming
Tags: Perl, Ruby

ncftp, wget

ncftp simply doesn't like to do recursive GETs. It'll churn along happily until it hits some random file and then timeout. If I restart the same command it'll sometimes get further and timeout, sometimes timeout sooner. Oh how I wish I could find a linux FTP client that doesn't suck half the time. I still entertain notions of writing my own someday. I know pretty well how I want the UI to work, it's the backend that I always get hung up on. I'm not going to re-implement the FTP protocol. Ruby has a nice FTP library but it seems somewhat limited in certain ways. Or else I haven't delved into it enough.

Either way. I'm going to use wget for my recursive FTP downloads from now on. Probably should've done that to begin with.

wget -r -x --ftp-user USERNAME  --ftp-password PASSWORD 'ftp://SERVER/DIRECTORY/*'

I at first was upset about having my FTP password lying around on the command line / process list but then I realized that I'm the only person who uses now and who will ever use this computer. I'll delete it from ~/.bash_history after I'm done. If someone is psing my process list right now, I have much bigger problems than having them gank my FTP password.

February 11, 2007 @ 4:36 AM PST
Cateogory: Linux

Ow, my brains. (DRY)

At work I was faced with dumping out some reports of some stuff. No big deal, Ruby to the rescue. I have a huge array of lines of data and I need to select out a few bits and output them, doing some simple manipulation to it in the process. So I ended up with something like:

File.open('some_filename', 'w') do |f|
    f.write lines.sort{|a,b| a[0] <=> b[0]}.select{|a| a[0] < 50000}.collect{|a| a[1]}
end

But it turns out I need to do this multiple times. Each time I do it, it will have a different filename AND a different select criteria. Changing the filename is easy; just make an array of strings and iterate over it.

But how to change the select criteria? If it was something easy, say I always wanted all the data < X for various values of X, then it would be simple to code; just pass in X as a parameter. But say I want the data greater than X in one file and the data less than Y in another file? Or maybe a third report with values == Z. Or who knows what else. Now it's not just data varying; the logic varies also.

A few years ago I'd probably have copy/pasted all the code and just changed the little bit in the select block, like a goon, and rightly suffered for it. But Ruby lets you assign blocks to variables. Then you can send the block to an iterator using &block:

[ ['one_filename', lambda{|a| a[0] < 50000}], ['another_filename', lambda{|a| a[0] > 70000}]].each do |fn, test|
    File.open(fn, 'w') do |f|
        f.write lines.sort{|a,b| a[0] <=> b[0]}.select(&test).collect{|a| a[1]}
    end
end

It's funny how the code is simultaneously shorter AND better AND more powerful than it would be copy/pasting or using some other method. I remember reading in a Lisp book that this is often a sign that the code is good. It's essentially the DRY principle at work.

This kind of programming is very "emotionally satisfying", but at the same time it's often very hard to wrap my head around. It took me a while to figure out that the above is what I wanted, and to realize that Ruby was capable of it.

I remember very well when I was just learning QBASIC 10+ years ago in high school, this is exactly the kind of thing I always WANTED to do. I have variables; let me use variables to vary EVERYTHING, data and logic and code, everything. I couldn't do it then, obviously. If only I'd have picked Lisp instead of BASIC. Now I feel like I'm kind of re-learning or un-learning all the limitations of the iterative, clunky ways of thinking of programming that were so ingrained by college and years of C/C++. (I remember a professor saying that function pointers in C++ are "scary things, and probably too advanced for this class to cover".)

February 01, 2007 @ 7:11 AM PST
Cateogory: Programming
Tags: Lisp, Ruby

Unique-ify lines in Vim

If I want to get rid of duplicate lines in a text file, in Linux I can simply pipe it through uniq. Windows doesn't have an equivalent (or else I don't know about it). There may be a Vim equivalent also, but I don't know it if there is. Instead you can use perldo to do it like this:

:perldo BEGIN{%x={}} $_ = ' ' if $x{$_}++

The neat thing about perldo (and rubydo) is that whatever you do is persistent across runs of perldo. That means if you run this:

:perldo $_ = ' ' if $x{$_}++

it'll work the first time you run it, but the second time, $x will have retained its values from the first run, and it will instead delete every line. There are few limits to what you can do with a one-liner perldo command in Vim. It's great, if you know Perl better than you know Vim.

EDIT TWO YEARS LATER: How about :sort u.

January 11, 2007 @ 5:15 AM PST
Cateogory: Programming
Tags: Perl, Ruby, Vim, Windows

Perl vs. Ruby breaking from nested loops

Breaking out of nested blocks is apparently done much differently in Perl and Ruby. In Perl if you run this:

my $counter = 0;
foreach my $outer (1..5) {
    foreach my $inner ('A'..'E') {
        print "$outer: $inner\n";
        last if ++$counter > 4
    }
}

The last is going to break out of the inner-most foreach loop by default. If you want to break out of the OUTER foreach loop you can use line labels:

my $counter = 0;
OUTER: foreach my $outer (1..5) {
    foreach my $inner ('A'..'E') {
        print "$outer: $inner\n";
        last OUTER if ++$counter > 4
    }
}

In Ruby to do the same thing, you have to use a throw/catch block. This is a completely separate mechanism from Ruby's begin/rescue Exception-handling mechanism. throw/catch rather appears to be simply a flow-control mechanism. You raise Exceptions, but you throw Strings or Symbols. The equivalent of the above Perl in Ruby:

counter = 0
catch :OUTER do
    1.upto(5) do |outer|
        'A'.upto'E' do |inner|
            puts "#{outer}: #{inner}"
            throw :OUTER if (counter += 1) > 4
        end
    end
end

EDIT: Originally I thought that Perl's last LABEL; would not find labels except in the same static scope. It must've been a bug in my test code however, because this does work. My mistake! So this does work in Perl:

sub test {
    HERE: foreach my $really_outer (1..5) {
        test2();
    }
}

sub test2 {
    my $counter = 0;
    foreach my $outer (1..5) {
        foreach my $inner ('A'..'E') {
            print "$outer: $inner\n";
            last HERE;
        }
    }
}

test

You can do this in Ruby also. The other nice thing about Ruby is you can return a value from the throw:

def test
    result = catch :HERE do 
        1.upto(5) do |really_outer|
            test2
        end
    end

    puts "RESULT: #{result}"
end

def test2
    counter = 0
    1.upto(5) do |outer|
        'A'.upto'E' do |inner|
            puts "#{outer}: #{inner}"
            throw :HERE, "YOINK" if (counter += 1) > 4
        end
    end
end

test

In Perl you also have the option of using Error.pm, but it looks horrendously hackish to me, as Perl code often does.

The only thing I can think of that's bad about Ruby is that it has two things which are really similar, one for Exceptions and one not. People coming from a different language may think that throw/catch is the Exception-handling mechanism when it's not. But Ruby still wins here.

December 03, 2006 @ 4:13 AM PST
Cateogory: Programming
Tags: Perl, Ruby