nested hashes with default value 17. Jun 2008

To instantiate arbitrarily nested hashes in Ruby you can write

h = Hash.new{|hash,key| hash[key] = Hash.new(&h.default_proc)}

This let’s you do

h[1] = 2 # => 2
h[2][1] = 3 # => 3
h[3][1][1] = 4 # => 4
h.inspect # => {1=>2, 2=>{1=>3}, 3=>{1=>{1=>4}}}

That’s nice, but maybe you want your innermost hash to have a default value to access the keys via +=-operator without having to check if the key has been initialized before? You might be tempted to write the following.

h = Hash.new(Hash.new(0))

Looks nice. Now let’s assign a value and check if it’s stored correctly.

h[1][1] = 2 # => 2
h[2][2] = 3 # => 3

Looks fine too. Now let’s inspect the hash.

h.inspect # => {}

Whoops! Where have my keys gone? Let’s examine the inner hashes.

h[1].object_id # => 1751410
h[2].object_id # => 1751410

Okay, so obviously instead of instantiating new hashes for each key the same hash was used. Let’s examine the default value.

h.default.object_id # => 1751410

Ah, so the keys were stored in the default value hash. Let’s check and see.

h.default # => {1=>2, 2=>3}

So instead of generating a nested structure for each key all keys are stored flatly in the innermost hash. That is not what we wanted. We can however talk the hash into doing the intented by using a default procedure instead of a default value, which is of course documentated in the lovely core API documentation.

h = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = 0}}

While working fine in Ruby 1.9, this causes problems in in Ruby 1.8 because block arguments are not local there.

h = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = 0}} # => {}
h[1][1] = 1 # => 1
h.inspect # {1=>{1=>1}}
h[2][1] # => 0
h.inspect # => {1=>0}

To get it working in Ruby 1.8 the variables in each block have to be uniquely named.

h = Hash.new {|h,k| h[k] = Hash.new {|h1,k1| h1[k1] = 0}} # => {}
h[1][1] = 1 # => 1
h.inspect # {1=>{1=>1}}
h[2][1] # => 0
h.inspect # {1=>{1=>1}, 2=>{1=>0}}

Now we can fill the twodimensional hash with data and see the result.

h = Hash.new {|h,k| h[k] = Hash.new {|h1,k1| h1[k1] = 0}} # => {}
h[1][1] += 1 # => 1
h[1][2] += 2 # => 2
h[2][3] += 3 # => 3
h.inspect # => "{1=>{1=>1, 2=>2}, 2=>{3=>3}}"
h[1][3] # => 0
h.inspect # => {1=>{1=>3, 2=>4, 3=>0}, 2=>{1=>0, 3=>6}}

Et voilà!

 

installing ruby 1.9 on leopard 17. Jun 2008

Thanks to Caspar Florian Ebeling installing Ruby 1.9 on Mac OS 10.5 is now a breeze. Just install MacPorts if you haven’t already and then issue a

sudo port install ruby19

Now you can launch Ruby 1.9 with ruby1.9 and irb with irb1.9.

For the more adventurous souls among you here’s how to compile it by hand. To compile Ruby 1.9 you need Readline 5.2, therefore it will be installed if it isn’t already there. Commands with a trailing backslash are multiline statements. If you prefer to compile Readline 5.2 on Leopard by hand, you should have a look at this patch.

sudo port install readline
curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.0-0.tar.gz
tar xvfz ruby-1.9.0-0.tar.gz
cd ruby-1.9.0-0
./configure --prefix=`echo ~`/ruby19 --program-suffix=1.9 \
--with-readline-dir=/opt/local
make
make install

Now add ~/ruby19/bin to your PATH-variable (for example in ~/.bash_profile or ~/.bashrc)

export PATH=~/ruby19/bin:$PATH

To activate the changes just launch a new terminal window. Now you can launch Ruby 1.9 with ruby1.9 and irb with irb1.9.

 

smart sleep for macbooks 03. Jun 2008

Since MacBook’s safe sleep is not much fun, when you’re on the go most of you have probably addressed this issue manually on the command line or utilized a script. Thanks to Patrick Stein there is now a much nicer way of handling things. With his SmartSleep prefPane you no longer have to decide between sleep and hibernation, but you can rather get best of both worlds. Usually you get the quick sleep but when your battery is lower than a predefined threshold, the prefPane switches the notebook to safe sleep so you don’t lose your data in case of a longer time without power supply.

 

uninstall all gems 31. May 2008

To perform batch operations in MacPorts you can use the predefined pseudo-portnames. RubyGems has no such thing, but instead it can handle regular expressions. So to uninstall all gems of your default RubyGems installation you can simply issue the following command.

sudo gem uninstall --a --ignore-dependencies .+

For some weird reason in my case I had to call it twice, but then all gems where gone.

Bharat Ruparel asked in the comments if there is a way to automatize the reinstallation of the gems. Yes, there is. This solution generates a file containing a list of all the gems and after removing all gems reads the gem names from this file to reinstall them. The solution does not however honor the installed version of the gems, so if you care about the versions you’re going to have to find a different way. I have tested this on Mac OS X, if you’re using Linux you’d want to replace the -E with an -r.

gem list --no-versions | sed -E '/^(*|$)/d' > installed_gems
sudo gem uninstall --a --ignore-dependencies .+
cat installed_gems | xargs sudo gem install
rm installed_gems
 

finding large directories 30. Oct 2007

Every once in a while there is the need to clear out the system. The question that inevitably pops up is: Where has all the disk space gone ? The solution of course lies in locating the largest directories. With WhatSize and OmniDiskSweeper there are two great tools on Mac OS X. There are also feasible Tools on Linux like Filelight, but if you’re administering a remote server and you don’t want to use remote x11 access you need a solution that can be controlled either on the command line or via browser. Of course you could use sturdy old du.


# Find all directories with the largest directories on top
$ sudo du -Sx / | sort -nr | less
# Find directories over 100MB with the largest directories on top
$ sudo du -Sh / | grep ^[1-9][0-9][0-9][0-9\.]*M | sort -nr

Or you could hack together some fancy perl script, but wouldn’t it be nice to have an interactive graphical visualisation through which you could comfortably navigate ? This is where Philesight pops in, which offers both command line and browserbased control. Putting it up on Debian is a breeze. Just install cairo and berkley db and their ruby bindings and then get rolling.


$ wget http://zevv.nl/code/philesight/philesight-2035.tgz
$ tar xvfz philesight-2035.tgz
$ sudo apt-get install libcairo2 libcairo-ruby
$ sudo apt-get install libdb4.2 libdb4.2-ruby1.8
# Generate index
$ sudo ./philesight --db base.db --index /
# Check if image is correctly generated
$ ./philesight --db base.db --path / --draw base.png

Firing up the web interface is just three small steps further. First configure the CGI-File.


# philelight.cgi
db = "./base.db"
default_path = "/"

Then create a reasonably named subdomain and after that just add a few lines to your lighttpd configuration file (or the corresponding lines to the webserver of your choice).


$HTTP["host"] =~ "philesight.yourdomain.net" {
  cgi.assign = ( ".cgi" => "/usr/bin/ruby" )
  alias.url = ( "/" => "/path/to/philesight/philesight.cgi")
  server.document-root = "/path/to/philesight"
}

That’s all there’s to it. Have fun.

 

1 2 3 4