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.

 

the beauty of each_with_index 13. Jun 2008

Zum iterieren auf Arrays bietet Ruby den each-Iterator an. Das ist praktisch und erhöht die Lesbarkeit des Quelltextes.

letters = ['a','b','c']

letters.each do |letter|
   print "#{letter}\n"
end

Der Nachteil des each-Iterators im Gegensatz zur klassischen For-Schleife ist aber, dass einem nun nicht mehr die Laufvariable zur Verfügung steht. Wenn man dieses doch mal braucht, dann wird es sehr unschön.

i = 0
letters.each do |letter|
  print "#{letter}#{i}\n"
  i += 1
end

Als Ersatz bietet sich der times-Iterator an, wirklich schön ist das aber auch nicht, da nun das Array wieder explizit referenziert werden muss.

letters.size.times do |i|
  print "#{letters[i]}#{i}\n"
end

Das ist die Stelle wo der each_with_index-Iterator glänzen kann. Der liefert nämlich genau das, was er verspricht. Hier hat man gewissermaßen best of both worlds.

letters.each_with_index do |letter, i|
  print "#{letter}#{i}\n"
end
 

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 .+

You may have to execute it twice before all gems are gone. If the command doesn’t work then try this shell script (thanks to melzz):

#!/bin/bash

GEMS=`gem list --no-versions`
for x in $GEMS ; do gem uninstall $x; done

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 and escape the asterik with a backslash as pointed out in the comments.

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
 

why i chose dokuwiki over junebug 10. Feb 2008

Okay, der Vergleich ist nicht ganz fair. Dokuwiki ist produktionstauglich und Junebug ist Alpha. Nichtsdestotrotz mußten sich diese beiden Kandidaten im Laufe der letzten Tage bei mir beweisen. Trotz derer Verschiedenheit bin ich je nach Laune zwischen den beiden hin- und hergeschwankt. Mal wieder ein nettes Feature dort entdeckt und gedacht es wäre ein Alleinstellungsmerkmal, dann gesehen, dass es das auch beim anderen Kandidaten gibt. Mal wieder einen Bug dort entdeckt und gedacht okay, das ist nun wirklich ein KO-Kriterium und dann doch sich dazu hinreißen zu lassen mal in den Quellcode zu schauen und dabei festzustellen, dass zwei Einzeiler das Problem lösen. Welcher Syntax ist nun besser lesbar, welches Defaultlayout hübscher anzusehen ?

Die Ausgangssituation beim Vergleich war ein installiertes DokuWiki und eine gewisse Zufriedenheit damit. Dann bin ich jedoch über sicherheitsrelevanten Beitrag in der Mailingliste gestolpert und da gingen bei mir gleich die PHP-Alarmglocken an. Im Anschluß dann die Frage: Gibt es nicht vielleicht doch eine brauchbare Ruby-Wiki-Lösung ? Nachdem Instiki und Pandora eingeschlafen waren, ergab sich bei mir erstmal eine gewisse Ratlosigkeit. Maurice stieß mich dann in den Kommentaren auf Junebug. Die offizielle Website ist zwar leider immer noch nicht erreichbar, aber es gibt einen Gem und ein Repository bei RubyForge.

Das einzige wirkliche Argument gegen DokuWiki war also, dass es in PHP entwickelt wird, womit sich auch gleichzeitig das größte Proargument für Junebug ergibt, nämlich dass es in Ruby entwickelt wird. Symphathiepunkte gibts bei Junebug außerdem für das Defaultlayout, was schön minimalistisch ist und Textile als Markupsprache ab Werk. Kommen wir aber nun mal zu Nachteilen von Junebug. Erstens es ist Alpha, solange man sich verhält wie der Entwickler das gerne hätte, dann ist es okay, aber sobald man etwas “falsches” macht gibt’s gerne mal eine Fehlermeldung. Man kann keine Unterwikis oder Namespaces anlegen. Es gibt kein Syntax-Highlighting. Es gibt Einschränkungen bei den Seitennamen. Das CSS ist noch nicht wirklich ausgefeilt, es gibt Darstellungsfehler im Safari. Die Überschriften sind keine Anchor. Junebug nutzt RedCloth mit all seinen Einschränkungen und Fehlern. Junebug basiert auf Camping, was eingeschlafen ist. Der aktuelle Gem-Release von Junebug ist nur unter zuhilfenahme des Repository lauffähig. Es gibt keine Mailingliste. Es gibt keine offizielle Website. Bug-Reports und Feature-Requests werden zur Zeit noch ignoriert. Es gibt eine verschwindend geringe aktive Nutzerschaft. Und was mich letzten Ende von der Benutzung abgebracht hat: Es gibt keine Möglichkeit Mediendateien hochzuladen.

Ich werde Junebug weiterhin beobachten um zu schauen, ob sich ein Reifeprozess abspielt, aber bis auf weiteres scheint mir DokuWiki die bessere Wahl zu sein.

 

1 ... 3 4 5 6