<?xml version="1.0"?>
<rss version="2.0">
  <channel>
  <title>Tammo Freese</title>
  <link>http://tammofreese.de/</link>
  <description>
  Im Themenspringer schreibt Tammo Freese über verschiedenste Themen,
  unter anderem OS X, XHTML/CSS, Agilität, Ruby on Rails und Eclipse.
  </description>
  <language>de-de</language>
  <copyright>Copyright 2005-2010 Tammo Freese</copyright>
  <managingEditor>business@tammofreese.de</managingEditor>
  <webMaster>business@tammofreese.de</webMaster>
  <pubDate></pubDate>
  <lastBuildDate>Tue, 11 May 2010 21:20:00 +0100</lastBuildDate>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  
  <item>
  <title>Was Apple anders macht</title>
  <link>http://tammofreese.de/2010/05/10/was-apple-anders-macht</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/was-apple-anders-macht.png" />
      <p>Apple ist zurzeit sehr erfolgreich, und wird es wohl auch auf absehbare Zeit bleiben. Das liegt vielleicht auch mit daran, dass Apple bestimmte Dinge anders macht als andere Unternehmen. Als erstes kommt einem natürlich das Design der Produkte in den Sinn, aber es gibt auch noch andere interessante Punkte.</p>


<h2>Konkurrenz vermeiden</h2>

<p>Als erstes scheinen viele Apple-Produkte einem bestimmten Muster zu folgen: Sie weisen Alleinstellungsmerkmale auf, die so sehr Teil des Produkts sind, dass Kunden bereit sind, mehr für die Produkte zu zahlen. Damit kann Apple einen Wettbewerb auf Preisbasis vermeiden (da es keine gut vergleichbaren Produkte gibt) und damit deutlich höhere Margen realisieren.</p>

<p>Das zurzeit heißeste Beispiel ist wohl das iPad. Das Alleinstellungsmerkmal: Es ist das erste Gerät dieser Kategorie, das überhaupt in einer großen Stückzahl verkauft wird. Auch das iPhone hatte zu seiner Veröffentlichung Alleinstellungsmerkmale, das wichtigste war der Verzicht auf eine physikalische Tastatur und die Bedienung über einen sehr genauen, mit dem Finger bedienbaren Touchscreen. Mittlerweile hat sich das Alleinstellungsmerkmal des iPhone geändert, es ist nun der App Store: Das iPhone hat wohl von allen Smartphones mit Abstand die größte Software-Auswahl.</p>

<p>Solche Alleinstellungsmerkmale haben auch noch einen anderen Vorteil für Apple: Die Wahrscheinlichkeit, dass Apple-Kunden zu einer anderen Marke zu wechseln, ist deutlich geringer.</p>


<h2>Bewährtes verbessern</h2>

<p>Im Entwicklungsbereich verbessert Apple bewährte Technologien lieber weiter, anstatt immer wieder auf neue Pferde zu setzen. Ein gutes Beispiel hierfür ist die Programmiersprache Objective-C. Diese Sprache wurde als Grundlage von NeXTStep gewählt, dem Vorläufer von Mac OS X. Schon Objective-C ist keine neue Sprache, sondern eine Erweiterung der Programmiersprache C.</p>

<p>Objective-C wurde von Apple in den letzten Jahren kontinuierlich weiterentwickelt: Mit Mac OS X 10.5 erschien Objective-C 2.0, indem bei der Sprache einige Konstrukte hinzukamen, und auch eine Garbage Collection ergänzt wurde. Mit Mac OS X 10.6 wurden Blöcke eingeführt – ein Sprachmittel, das deutlich kompakteren Code ermöglicht und auch die Anpassung auf Mehrkernprozessoren vereinfacht.</p>

<p>Diese Erweiterungen sind meiner Meinung nach bemerkenswert, weil gerade im Bereich von Programmiersprachen die Neigung zu bestehen scheint, lieber eine neue Sprache zu entwickeln als eine alte anzupassen.</p>


<h2>Altlasten loswerden</h2>

<p>Andererseits erlaubt sich Apple, sich von Altlasten zu befreien, anstatt sie über Jahre oder gar Jahrzente mit sich mitzuschleppen. Das alte Betriebssystem OS 9 wurde komplett aufgegeben, es gibt keine Abwärtskompatibilität. Ein anderes Beispiel ist die Umstellung auf Intel-Prozessoren: Sie begann im Januar 2006, und bereits im August 2009 erschien mit Snow Leopard ein Betriebssystem, das nicht mehr auf den Mac-Rechnern der Pre-Intel-Ära lauffähig ist.</p>

<p>Auch mit dem iPhone OS 4, das im Sommer 2010 erscheint, wird Apple Software anbieten, die auf älteren Geräten nicht mehr einsetzbar ist: Das neue Betriebssystem wird nicht auf iPhone und iPod touch der ersten Generation laufen. Dieses Beispiel ist besonders krass, denn das iPhone der ersten Generation war bis Mitte 2008 erhältlich. Nach gerade mal zwei Jahren kann man das aktuelle Betriebssystem nicht mehr einsetzen!</p>


<h2>Was ist gut, was ist schlecht?</h2>

<p>Alle drei Punkte – Konkurrenz vermeiden, Bewährtes verbessern und Altlasten loswerden – sind sicherlich gut für Apple. Als langjähriger überzeugter Apple-Benutzer finde ich es lediglich schade, dass Apple-Hardware immer früher von der aktuellen Software nicht mehr unterstützt wird. Ich habe unter anderem einen iMac G5 und einen iPod touch erster Generation. Den iMac kann ich schon heute nicht mehr zum Entwickeln für das aktuelle Mac OS X 10.6 einsetzen, und auf dem iPod touch kann ich ab Sommer keine Apps mehr verwenden, die das dann aktuelle iPhone OS 4 voraussetzen. Vielleicht ist aber auch das Teil der Apple-Strategie, denn so kaufen Apple-Kunden häufiger neue Hardware…</p>
  ]]>
  </description>
  <pubDate>Tue, 11 May 2010 21:20:00 +0100</pubDate>
  <guid>http://tammofreese.de/2010/05/10/was-apple-anders-macht</guid>
  </item>

  <item>
  <title>Goodbye Rails, Hello OS X (iPhone/Mac)</title>
  <link>http://tammofreese.de/2009/09/03/goodbye-rails-hello-os-x-iphone-mac</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/goodbye-rails-hello-os-x-iphone-mac.png" />
      <p>Ich habe mich vor einigen Monaten entschlossen, mit Ruby on Rails aufzuhören, um mich in Zukunft auf technischer Seite voll auf Beratung, Entwicklung und Schulung zum OS X-Betriebssystem (sowohl iPhone als auch Mac) zu konzentrieren. Aus meinem Netzwerk kamen Fragen, warum ich diesen Wechsel mache. Meine Antworten darauf habe ich hier zusammengefasst.
</p>

<h2>Warum iPhone/Mac?</h2>

<p>Anfang 2003 wechselte ich auf Mac OS X (damals 10.2 auf einem G3-iBook), weil mich das Gesamtkonzept überzeugte: Das Design der Rechner, die Kombination von Software und Hardware, ein BSD-basiertes Unix-Betriebssystem, vor allem aber die Entwicklung mit Cocoa und Objective-C. Damals gab es aber keine für mich wahrnehmbare Nachfrage nach Mac-Entwicklung, auch in meinem Netzwerk war ich mit dem Mac eher ein Exot. Daher verfolgte ich die Entwicklung für den Mac nur auf Sparflamme weiter. Meinen Switch habe ich aber nie bereut, im Gegenteil.</p>

<p>Mit dem Wechsel auf Intel-Macs wurde das allgemeine Interesse an Mac OS X deutlich größer, und mit dem iPhone SDK sah ich die Chance, endlich für die Plattform meiner Wahl zu entwickeln. Mac und iPhone sind meiner Meinung nach mit die interessantesten Entwicklungsplattformen: Die Sprache ist OK und Bibliotheken und Werkzeuge sind großartig, und durch die ständige, inkrementelle Weiterentwicklung seitens Apple gibt es auch immer etwas Neues zu entdecken.</p>


<h2>Warum kein Rails mehr?</h2>

<p>Rails ist ein hervorragendes Framework für die Entwicklung von dynamischen Webseiten. Mein Interesse geht jedoch mittlerweile wieder mehr in Richtung hin zu nativen Applikationen. Die einzigen Webapplikationen, für die ich mich noch begeistern kann, versuchen im Browser möglichst nah an native Applikationen heranzukommen, wie zum Beispiel <a href="http://280slides.com">280slides</a> (implementiert mit <a href="http://cappuccino.org/">Cappuccino</a>) und <a href="http://me.com">Mobile Me</a> (implementiert mit <a href="http://www.sproutcore.com/">SproutCore</a>). Für solche Applikationen ist Rails aber bestenfalls als Backend interessant.
</p>


<h2>Warum nicht beides?</h2>

<p>
  Ich kann nicht alles machen. Als ich 2005 auf Ruby on Rails umgeschwenkt bin, habe ich Java/Eclipse aus meinem Portfolio genommen, mit meinem technischen Wechsel auf OS X (iPhone/Mac) nehme ich Ruby on Rails aus meinem Portfolio. Meiner Meinung nach ist es besser, sich voll auf ein technisches Thema zu konzentrieren. Zudem möchte ich auch endlich mal meine Dissertation fertigstellen, und in zwei technischen Themen auf dem neusten Stand zu bleiben, würde mir dafür noch weniger Zeit lassen.
</p>

<h2>Warum nicht auch Blackberry, Android oder Palm Pre?</h2>

<p>Wie oben schon gesagt: Ich kann nicht alles machen. Blackberry, Android und Palm Pre sind Plattformen mit anderen Programmiersprachen, anderen Frameworks, anderen Werkzeugen, anderen Bedienkonzepten – also fundamental verschiedene Umgebungen im Mobile-Bereich, so wie Windows, Mac OS X und Linux fundamental verschiedene Umgebungen im Desktop-Bereich sind.
</p>

<h2>Was ist mit Ruby?</h2>

<p>Ich verwende zwar weiter Ruby, biete aber keine Beratung mehr dazu an. In 2000 bin durch <a href="http://www.frankwestphal.de">Frank Westphal</a> und das <a href="http://www.amazon.de/Programming-Ruby-Pragmatic-Programmers-Guide/dp/0201710897">Pickaxe-Buch</a> der Pragmatic Programmer auf Ruby gestoßen, und Ruby ist bis heute eine meiner Lieblingssprachen.
</p>

  ]]>
  </description>
  <pubDate>Thu, 03 Sep 2009 12:52:00 +0100</pubDate>
  <guid>http://tammofreese.de/2009/09/03/goodbye-rails-hello-os-x-iphone-mac</guid>
  </item>

  <item>
  <title>Tuning the Performance of the Ruby Builder Library</title>
  <link>http://tammofreese.de/2009/09/02/tuning-the-performance-of-the-ruby-builder-library</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/tuning-the-performance-of-the-ruby-builder-library.png" />
      <p>
In <a href="http://tammofreese.de/2008/3/22/handedict-fuer-apple-dictionary">a previous article</a>, I have shown how to convert HanDeDict (a free chinese-german dictionary) for using it in Apple&#x27;s Dictionary.
The main part was the generation of a big XML file using the <a href="http://builder.rubyforge.org/">Builder Library</a>.
</p>
<p>
Generating the XML file now takes more than 4 minutes, almost all of the time spent in
the XML generation. In this article, I show how to improve the performance of the Builder Library.
Generating the XML file got up to 36% faster. If you would like to get the code, you may clone it from <a href="https://github.com/tammofreese/builder">GitHub</a>.</p>


<h2>Finding the Bottleneck</h2>

<p>To test for performance, we use five scenarios. The first one is the
  XML generation for HanDeDict. In the second one, we generate an in-memory xml string with one million 
  tags, each containing a short text:
</p>    

<pre>
require &#x27;builder/xmlmarkup&#x27;
require &#x27;benchmark&#x27;

result = Benchmark.measure do
  xm = Builder::XmlMarkup.new
  xm.instruct!
  xm.links do
    1000000.times do |i|
      xm.link &quot;Item #{i.to_s}&quot;
    end
  end
  x = xm.target!
end
puts result
</pre>

<p>
  The third example is <code>test/performance.rb</code> from the Builder library
  itself. It renders XML with very few tags, but a lot of text:
</ul>

<pre>
#!/usr/bin/env ruby

require &#x27;builder/xmlmarkup&#x27;
require &#x27;benchmark&#x27;

text = "This is a test of the new xml markup. Iñtërnâtiônàlizætiøn" * 10000

include Benchmark          # we need the CAPTION and FMTSTR constants
include Builder
n = 50
Benchmark.benchmark do |bm|
  tf = bm.report(&quot;base&quot;)   {
    n.times do
      x = XmlMarkup.new
      x.text(text)
      x.target!
    end
  }
  def XmlMarkup._escape(text)
    text.to_xs
  end
  tf = bm.report(&quot;to_xs&quot;)   {
    n.times do
      x = XmlMarkup.new
      x.text(text)
      x.target!
    end
  }
end
</pre>

<p>It should be noted here that performance.rb actually tests a special case: The term <em>Iñtërnâtiônàlizætiøn</em> is in CP1252 (Windows) encoding, for which Builder provides
a workaround. So as fourth scenario, we use the same file saved as UTF8. In the fifth and last
scenario, we replace <em>Iñtërnâtiônàlizætiøn</em> with <em>Internationalization</em> to test ASCII performance.
</p>

<p>The initial results are:</p>  

<pre>
Example 1:      251.330000   1.280000 252.610000 (256.420148)
Example 2:       48.910000   0.240000  49.150000 ( 49.680870)
Example 3:  base 97.400000   0.760000  98.160000 ( 98.609859)
           to_xs 98.230000   0.730000  98.960000 ( 99.363800)
Example 4:  base 98.250000   0.790000  99.040000 ( 99.441164)
           to_xs 99.100000   0.760000  99.860000 (100.257412)
Example 5:  base 88.380000   0.540000  88.920000 ( 89.046175)
           to_xs 88.240000   0.630000  88.870000 ( 89.225532)
</pre>


<p>To get some hints where the time is spent, we install the <code>ruby-perf</code> 
  gem and change a copy of the second scenario to print out a report.</p>
  
<pre>
require 'builder'
require 'rubygems'
require 'ruby-prof'

result = RubyProf.profile do
  xm = Builder::XmlMarkup.new
  xm.instruct!
  xm.links do
    100000.times do |i|
      xm.link i.to_s
    end
  end
  x = xm.target!
end
RubyProf::FlatPrinter.new(result).print(STDOUT, 0)
</pre>

<p>Here are the first lines of the report. It mainly consists of a table
  listing how much time was spent in each method, and how much in child (read: called) methods. 
  Note that due to the profiling, the scenario runs a lot slower, so we only write 100,000 nodes.</p>

<pre>
Thread ID: 136060
Total: 62.546996

 %self     total     self     wait    child    calls  name
at top level in 24.01 37.94 15.02 0.00 22.92 488898 Fixnum#xchr (/Users/tammofreese/Documents/builder/trunk/test/../lib/builder/xchar.rb at line 95
at top level in 9.73 9.18 6.08 0.00 3.10 1466694 Kernel#=== (ruby_runtime at line 0
at top level in 8.07 61.54 5.05 0.00 56.50 100000 Builder::XmlBase#method_missing-1 (/Users/tammofreese/Documents/builder/trunk/test/../lib/builder/xmlbase.rb at line 40
at top level in 6.60 6.24 4.13 0.00 2.11 977803 Hash#[] (ruby_runtime at line 0
at top level in 5.65 3.53 3.53 0.00 0.00 1666700 Fixnum#== (ruby_runtime at line 0
at top level in 5.06 5.23 3.16 0.00 2.06 488898 Range#=== (ruby_runtime at line 0
at top level in 4.13 40.52 2.58 0.00 37.94 100002 Array#map (ruby_runtime at line 0
at top level in 3.48 3.94 2.18 0.00 1.76 100001 Builder::XmlMarkup#_start_tag (/Users/tammofreese/Documents/builder/trunk/test/../lib/builder/xmlmarkup.rb at line 290
at top level in 3.38 2.11 2.11 0.00 0.00 977800 Hash#default (ruby_runtime at line 0
at top level in 3.30 2.06 2.06 0.00 0.00 977796 Fixnum#&lt;=&gt; (ruby_runtime at line 0</pre>  

<p>As we see, most of the time is spent in the <code>Fixnum#xchr</code> method, which is called almost
  half a million times. So we target this method for performance improvement. 
  What's more, we see a huge number of calls to <code>Kernel#===</code>, <code>Hash#[]</code>, <code>Fixnum#==</code>,
  and <code>Range#===</code>. 
</p>


<h2>Improving the Performance at the Bottleneck</h2>

Let's have a look at the <code>Fixnum#xchr</code> method:

<pre>
  def xchr(escape=true)
    n = XChar::CP1252[self] || self
    case n when *XChar::VALID
      XChar::PREDEFINED[n] or (n&lt;128 ? n.chr : (escape ? &quot;&amp;##{n};&quot; : [n].pack(&#x27;U*&#x27;)))
    else
      &#x27;*&#x27;
    end
  end
</pre>

<p>It contains a fix for CP1252-encoded files from the windows world, then it decides whether 
  <code>n</code> is a valid unicode code point. Invalid numbers are mapped to a string containing an asterisk. 
  Valid numbers are mapped to strings containing the respective UTF-8 characters. Characters outside the ASCII range are optionally escaped.</p>
  
<p>The <code>case n when *XChar::VALID</code> may be the source of all the case equality operator and <code>Fixnum#==</code> calls. 
  Let's look at the definition of VALID:
</p>

<pre>
  VALID = [
    0x9, 0xA, 0xD,
    (0x20..0xD7FF), 
    (0xE000..0xFFFD),
    (0x10000..0x10FFFF)
  ]
</pre>

<p>The first three elements of the array are the unicode codepoints of tab, linefeed and carriage return. Then, three ranges are used that contain a lot of unicode code points. A typical text will contain many characters in the first two ranges, and not many tabs, linefeeds, carriage returns, or characters from the third range (the unicode codepoints above 0xFFFF are not that often used).
We change the array so that the common cases are checked first:</p>

<pre>
  VALID = [
    (0x20..0xD7FF), 
    (0xE000..0xFFFD),
    0x9, 0xA, 0xD,
    (0x10000..0x10FFFF)
  ]
</pre>

<p>After this change, the performance improves by about 3-16%:</p>

<pre>
Example 1:      244.080000   1.250000 245.330000 (249.456791)
Example 2:       43.220000   0.150000  43.370000 ( 43.529551)
Example 3:  base 81.560000   0.560000  82.120000 ( 82.182873)
           to_xs 82.370000   0.510000  82.880000 ( 82.938198)
Example 4:  base 81.750000   0.670000  82.420000 ( 82.601055)
           to_xs 82.400000   0.620000  83.020000 ( 83.276765)
Example 5:  base 74.350000   0.620000  74.970000 ( 75.106275)
           to_xs 74.410000   0.530000  74.940000 ( 75.092212)

</pre>

<p>The next change is to cache all three constants <code>CP1252</code>, <code>VALID</code>
  and <code>PREDEFINED</code> directly in <code>Fixnum</code>:</p>

<pre>
VALID = Builder::XChar::VALID if ! defined?(VALID)
PREDEFINED = Builder::XChar::PREDEFINED if ! defined?(PREDEFINED)
CP1252 = Builder::XChar::CP1252 if ! defined?(CP1252)

def xchr(escape=true)
  n = CP1252[self] || self
  case n when *VALID
    PREDEFINED[n] or (n&lt;128 ? n.chr : (escape ? &quot;&amp;##{n};&quot; : [n].pack(&#x27;U*&#x27;)))
  else
    &#x27;*&#x27;
  end
end
</pre>

<p>Now the scenarios run faster by about 5-23%:</p>

<pre>
Example 1:      237.950000   1.200000 239.150000 (243.143989)
Example 2:       41.710000   0.260000  41.970000 ( 46.755385)
Example 3:  base 76.360000   0.670000  77.030000 ( 77.289001)
           to_xs 77.210000   0.600000  77.810000 ( 78.047175)
Example 4:  base 75.260000   0.760000  76.020000 ( 76.714416)
           to_xs 76.090000   0.670000  76.760000 ( 77.237976)
Example 5:  base 67.790000   0.690000  68.480000 ( 68.834157)
           to_xs 67.740000   0.510000  68.250000 ( 68.404581)

</pre>
            
<p>Then, we eliminate the local variable <code>n</code>:</p>

<pre>
def xchr(escape=true)
  return CP1252[self].xchr(escape) if CP1252.key?(self)
  case self when *VALID
    PREDEFINED[self] or (self&lt;128 ? self.chr : (escape ? &quot;&amp;##{self};&quot; : [self].pack(&#x27;U*&#x27;)))
  else
    &#x27;*&#x27;
  end
end
</pre>

<p>Again, the scenarios run faster. We have now improvements of about 6-29%
  compared to the original implementation:</p>

<pre>
Example 1:      234.610000   1.350000 235.960000 (240.837129)
Example 2:       40.040000   0.180000  40.220000 ( 48.501536)
Example 3:  base 71.440000   0.670000  72.110000 ( 72.325842)
           to_xs 72.300000   0.600000  72.900000 ( 73.041512)
Example 4:  base 70.460000   0.700000  71.160000 ( 71.500979)
           to_xs 71.080000   0.570000  71.650000 ( 71.827479)
Example 5:  base 62.980000   0.630000  63.610000 ( 63.944886)
           to_xs 62.910000   0.530000  63.440000 ( 63.588532)

</pre>
            

<h2>Intermezzo: Fixing two Bugs along the Way</h2>

<p>In <code>Fixnum#xchr</code>, the CP1252 fix is always applied. It maps CP1252 codes to their unicode counterparts:</p>

<pre>
CP1252 = {        # :nodoc:
  128 => 8364,    # euro sign
  130 => 8218,    # single low-9 quotation mark
  131 =>  402,    # latin small letter f with hook
  132 => 8222,    # double low-9 quotation mark
  133 => 8230,    # horizontal ellipsis
  134 => 8224,    # dagger
  135 => 8225,    # double dagger
  136 =>  710,    # modifier letter circumflex accent
  137 => 8240,    # per mille sign
  138 =>  352,    # latin capital letter s with caron
  139 => 8249,    # single left-pointing angle quotation mark
  140 =>  338,    # latin capital ligature oe
  142 =>  381,    # latin capital letter z with caron
  145 => 8216,    # left single quotation mark
  146 => 8217,    # right single quotation mark
  147 => 8220,    # left double quotation mark
  148 => 8221,    # right double quotation mark
  149 => 8226,    # bullet
  150 => 8211,    # en dash
  151 => 8212,    # em dash
  152 =>  732,    # small tilde
  153 => 8482,    # trade mark sign
  154 =>  353,    # latin small letter s with caron
  155 => 8250,    # single right-pointing angle quotation mark
  156 =>  339,    # latin small ligature oe
  158 =>  382,    # latin small letter z with caron
  159 =>  376,    # latin capital letter y with diaeresis
}
</pre>

<p>But this does not always make sense. As an example, let us look at the number 134.
  A string containing only this number in one byte should be regarded as CP1252-encoded 
  as it is invalid UTF-8. Therefore it should be mapped to the corresponding unicode code point 8224.
  However, a string that contains the UTF-8 bytes for codepoint 134 must not be changed.
  We add a test to check whether this is a problem in the current implementation:</p>

<pre>
def test_do_not_fix_utf8_as_win_1252
  assert_equal &#x27;&amp;#8224;&#x27;, &quot;\x86&quot;.to_xs         # CP1252 dagger
  assert_equal &#x27;&amp;#134;&#x27;,  &quot;\xC2\x86&quot;.to_xs     # UTF-8 reverse line feed
end
</pre>

<p>Turns out it is a problem: the test fails. The method <code>String#to_xs</code> which calls <code>Fixnum#xchr</code> looks like this:</p>

<pre>
def to_xs(escape=true)
  unpack(&#x27;U*&#x27;).map {|n| n.xchr(escape)}.join # ASCII, UTF-8
rescue
  unpack(&#x27;C*&#x27;).map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
end
</pre>

<p>CP1252 encoding does only make sense if the UTF-8 unpack fails. So we change the code to</p>

<pre>
CP1252 = Builder::XChar::CP1252 if ! defined?(CP1252)

def to_xs(escape=true)
  unpack(&#x27;U*&#x27;).map {|n| n.xchr(escape)}.join # ASCII, UTF-8
rescue
  unpack(&#x27;C*&#x27;).map {|n| (CP1252[n] || n).xchr}.join # ISO-8859-1, WIN-1252
end
</pre>

<p>Then, we delete the CP1252 constant and the first line of <code>Fixnum#xchar</code>:</p>

<pre>
class Fixnum
  VALID = Builder::XChar::VALID if ! defined?(VALID)
  PREDEFINED = Builder::XChar::PREDEFINED if ! defined?(PREDEFINED)

  def xchr(escape=true)
    case self when *VALID
      PREDEFINED[self] or (self&lt;128 ? self.chr : (escape ? &quot;&amp;##{self};&quot; : [self].pack(&#x27;U*&#x27;)))
    else
      &#x27;*&#x27;
    end
  end
end
</pre>

<p>Now the test passes. As the CP1252 fix is not applied for UTF-8 anymore, 
  we get quite a performance boost. Our version now needs 10-37% less time than the original implementation:</p>

<pre>
Example 1:      225.130000   1.230000 226.360000 (229.601577)
Example 2:       36.010000   0.110000  36.120000 ( 36.246593)
Example 3:  base 76.140000   0.680000  76.820000 ( 77.060833)
           to_xs 77.080000   0.630000  77.710000 ( 77.986746)
Example 4:  base 62.260000   0.580000  62.840000 ( 63.280986)
           to_xs 62.710000   0.560000  63.270000 ( 63.408446)
Example 5:  base 55.040000   0.620000  55.660000 ( 55.865656)
           to_xs 54.780000   0.510000  55.290000 ( 55.367186)

</pre>

<p>Another bug we have spotted in <code>String#to_xs</code> is that escaping is ignored if the CP1252 fix is applied.
  We add a test to confirm that:
</p>

<pre>
def test_win_1252_escaping
  assert_equal &#x27;&amp;#8364;&#x27;, &quot;\x80&quot;.to_xs(true)   # euro with escaping
  assert_equal &#x27;&#8364;&#x27;, &quot;\x80&quot;.to_xs(false)        # euro without escaping
end
</pre>

<p>The test fails as expected. Now we pass the <code>escape</code> parameter to <code>Fixnum#to_xs</code>:</p>

<pre>
def to_xs(escape=true)
  unpack(&#x27;U*&#x27;).map {|n| n.xchr(escape)}.join # ASCII, UTF-8
rescue
  unpack(&#x27;C*&#x27;).map {|n| (CP1252[n] || n).xchr(escape)}.join # ISO-8859-1, WIN-1252
end
</pre>


<h2>Avoiding the Bottleneck</h2>

<p>Here is what is left of <code>Fixnum#xchr</code>:</p>

<pre>
def xchr(escape=true)
  case self when *VALID
    PREDEFINED[self] or (self&lt;128 ? self.chr : (escape ? &quot;&amp;##{self};&quot; : [self].pack(&#x27;U*&#x27;)))
  else
    &#x27;*&#x27;
  end
end
</pre>

<p>So far, we tried to make <code>Fixnum#xchr</code> faster. But maybe it is possible to do move the whole <code>Fixnum#xchr</code> functionality directly inside the <code>String#to_xs method</code>? We would not need a method call per character anymore, which should give us a huge performance boost. So what has to be done in <code>String#to_xs</code>?</p>
  
<ol>
  <li>Apply the CP1252 fix if needed.</li>
  <li>Replace invalid characters with &#x27;*&#x27;.</li>
  <li>Replace the characters for the code points in <code>PREDEFINED</code> with the mapped strings.</li>
  <li>Escape if needed.</li>
</ol>

<p>Here is the new implementation:</p>

<pre>
def to_xs(escape=true)
  result = begin
    unpack(&#x27;U*&#x27;)
    dup
  rescue
    unpack(&#x27;C*&#x27;).map! {|n| CP1252[n] || n }.pack(&quot;U*&quot;)
  end
  result.gsub!(INVALID_UTF8_MATCHER, &quot;*&quot;)
  result.gsub!(PREDEFINED_UTF_MATCHER) { |match| PREDEFINED[match] }
  result.gsub!(NON_ASCII_UTF8_MATCHER) { |match| &quot;&amp;##{match.unpack(&#x27;U&#x27;).first};&quot;} if escape
  result
end
</pre>

<p>What we still need to do is to define the constants. First, we define a helper lambda in <code>String</code>
  that converts a given Integer or Range to a character class entry. As an example, 0x41 will be converted to &quot;A&quot;
  and (0x41..0x5A) to &quot;A-Z&quot;:
</p>  
  
<pre>
to_character_class_entry = lambda do |entry|
  next &quot;#{[entry].pack(&#x27;U&#x27;)}&quot; if entry.is_a? Integer
  next &quot;#{[entry.first].pack(&#x27;U&#x27;)}-#{[entry.last].pack(&#x27;U&#x27;)}&quot; if entry.is_a? Range
end 
</pre>

<p>Using this helper, we define <code>INVALID_UTF8_MATCHER</code>:
</p>

<pre>
INVALID_UTF8_MATCHER = /[^#{Builder::XChar::VALID.map(&amp;to_character_class_entry).join}]/u
</pre>

<p>For <code>NON_ASCII_UTF8_MATCHER</code>, we need all the values from <code>VALID</code>
  which are greater than 0x7F. To get those, we roll back <code>VALID</code> and split the first range:</p>

<pre>
  VALID = [
    0x9, 0xA, 0xD,
    (0x20..0x7F), 
    (0x80..0xD7FF), 
    (0xE000..0xFFFD),
    (0x10000..0x10FFFF)
  ]
</pre>

<p>Now we can define <code>NON_ASCII_UTF8_MATCHER</code>:
</p>

<pre>
NON_ASCII_UTF8_MATCHER = /[#{Builder::XChar::VALID[4..-1].map(&amp;to_character_class_entry).join}]/u
</pre>

<p>The <code>PREDEFINED</code> that we need in <code>String#to_xs</code> is like the one in <code>Builder::XChar</code>,
  except that it needs Strings instead of Fixnums as keys:</p>

<pre>
PREDEFINED = Builder::XChar::PREDEFINED.inject({}) { |sum, (key, val)| sum[[key].pack(&#x27;U&#x27;)] = val; sum }
</pre>

<p>The <code>PREDEFINED_UTF8_MATCHER</code> constant references a regular expression that matches all the characters
  with unicode codepoints contained in <code>Builder::XChar::PREDEFINED.keys</code>:</p>
  
<pre>
PREDEFINED_UTF_MATCHER = /[#{Builder::XChar::PREDEFINED.keys.map(&amp;to_character_class_entry).join}]/u
</pre>

<h2>Results</h2>

<p>Here are the performance results for the bootleneck-avoiding version of <code>String#to_xs</code>:</p>

<pre>
Example 1:      159.390000   0.970000 160.360000 (163.721333)
Example 2:       17.000000   0.080000  17.080000 ( 17.265984)
Example 3:  base 47.410000   0.470000  47.880000 ( 48.071752)
           to_xs 47.390000   0.470000  47.860000 ( 47.995763)
Example 4:  base 13.910000   0.350000  14.260000 ( 14.326577)
           to_xs 14.510000   0.360000  14.870000 ( 14.921875)
Example 5:  base  1.550000   0.230000   1.780000 (  1.788842)
           to_xs  1.470000   0.230000   1.700000 (  1.703043)
</pre>

<p>The final version needs 36-98% less time than the original version (ruby1.8.6p114). The last number is somewhat misleading, as the corresonding test tests the to_xs performance for huge ASCII texts, which may not be the typical case. However, even for the second example, which builds a structure with small nodes, the execution is almost three times as fast as before.
</p>  
<p>Patches for the bug fixes and the performance fixes were sent to the maintainers of the builder library, unfortunately not much of it has shown up in their repository. So if you would like to use the improved version, grab a copy of the source from <a href="https://github.com/tammofreese/builder">GitHub</a>.
</p>  

  ]]>
  </description>
  <pubDate>Wed, 02 Sep 2009 21:00:00 +0100</pubDate>
  <guid>http://tammofreese.de/2009/09/02/tuning-the-performance-of-the-ruby-builder-library</guid>
  </item>

  <item>
  <title>Das Stomp-Protokoll, Telnet und ASCII NUL</title>
  <link>http://tammofreese.de/2008/08/14/stomp-protokoll-telnet-und-ascii-nul</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/stomp-protokoll-telnet-und-ascii-nul.png" />
      <p>
Das <a href="http://stomp.codehaus.org/">Stomp</a>-Protokoll gibt ein Format vor,
mit sehr einfach Clients für Message Broker wie <a href="http://activemq.apache.org/">ActiveMQ</a> geschrieben werden können. 
</p>
<p>Um mit dem Stomp-Protokoll zu Testzwecken auf einen ActiveMQ-Server zuzugreifen, kann <a href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/telnet.1.html">telnet</a> verwendet werden:
</p>
<pre>
telnet localhost 61613
</pre>
<p>
Um nun eine Verbindung gemäß dem <a href="http://stomp.codehaus.org/Protocol">Stomp-Protkoll</a> aufzubauen, soll folgendes eingegeben werden:
</p>
<pre>
CONNECT
login:john
passcode:doe

^@
</pre>
<p>Hier bekommt man aber ein Problem, zumindest mit einer deutschen Tastatur und einem Mac: Am Ende soll ^@ eingegeben werden, also die Control-Taste und das @-Zeichen, um ein ASCII NUL-Steuerzeichen zu erzeugen. Bei der amerikanischen Tastaturbelegung funktioniert dies: ^@ ist dort ctrl-shift-2. Auf der deutschen
Tastatur wäre ^@ der Tastendruck ctrl-alt-L, der aber leider kein ASCII NUL erzeugt.
Auch die Tastenkombination der englischen Tastatur hilft bei der deutschen Belegung nicht weiter.</p>
<p>
Nach einiger Zeit der Suche fand ich eine Lösung: Sowohl auf der deutschen als auch auf der englischen Tastaturbelegung kann man alternativ ctrl-space eingeben, um das ASCII NUL-Steuerzeichen einzugeben. Es wird dann tatsächlich auch als ^@ dargestellt.
</p>


  ]]>
  </description>
  <pubDate>Thu, 14 Aug 2008 20:00:00 +0100</pubDate>
  <guid>http://tammofreese.de/2008/08/14/stomp-protokoll-telnet-und-ascii-nul</guid>
  </item>

  <item>
  <title>HanDeDict für Apple Dictionary</title>
  <link>http://tammofreese.de/2008/03/22/handedict-fuer-apple-dictionary</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/handedict-fuer-apple-dictionary.png" />
      <p>
Seit Mac OS X 10.5 Leopard kann man mit wenig Aufwand
  selbst Lexika erstellen, die dann genauso verwendet werden können wie die
  von Apple mitgelieferten. 
</p>
<p>
  Dieser Artikel zeigt, wie ein bestehendes Lexikons für 
  Leopard konvertiert wird. Als Beispiel konvertieren wir das chinesisch-deutsche Lexikon <a href="http://www.chinaboard.de/chinesisch_deutsch.php?mode=dl">HanDeDict</a>. Der eigentliche Konvertierungsteil wird in Ruby implementiert.
</p>
<p>
Diejenigen, die lediglich das Lexikon verwenden wollen, können es hier herunterladen:
<a href="http://tammofreese.de/assets/2008/3/21/HanDeDict.dictionary.zip">HanDeDict.dictionary.zip</a> (33,8 MB). 
(<strong>Update 20.04.2008:</strong>: Das jeweils aktuelle Download steht nun auf der <a href="http://chdw.de/dl">HanDeDict-Downloadseite</a> zur Verfügung!) Wer sich für die Erzeugung von Lexika für 
Apple Dictionary interessiert, sollte weiterlesen. :)
</p>

<h2>Projektsetup</h2>

<p>
  Ausgangsbasis ist Mac OS X 10.5 mit installierten
  <a href="http://developer.apple.com/tools/">Development Tools</a>.
  Zuerst kopieren wir uns das Projekt-Template aus dem Dictionary Development Kit:
</p>  
<pre>cp /Developer/Extras/Dictionary\ Development\ Kit/project_templates/ ~/Desktop/HanDeDict
</pre>
<p>
  Von dem Inhalt des Verzeichnisses <code>~/Desktop/HanDeDict</code> benötigen wir nur 
  vier Dateien:
</p>  
<dl>
  <dt><code>MyDictionary.xml</code></dt>
  <dd>Diese Datei gibt ein Beispiel dafür, wie das XML-Format aussieht, aus dem 
    ein Lexikon erstellt werden kann. Eine solche XML-Datei erzeugen wir später aus
    dem HanDeDict. Um das Projektsetup zu prüfen, lassen wir sie erst einmal
    bestehen. Allerdings geben wir ihr den Namen <code>HanDeDict.xml</code>.</dd>
  <dt><code>MyDictionary.css</code></dt>
  <dd>Für die Formatierung der Einträge im Lexikon wird CSS verwendet. Wir benennen
    diese Datei in <code>HanDeDict.css</code> um.</dd>
  <dt><code>MyInfo.plist</code></dt>
  <dd>In der Property List werden verschiedene Attribute unseres Lexikons beschrieben. Wir benennen diese Datei in <code>HanDeDict.plist</code> um.</dd>
  <dt><code>Makefile</code></dt>
  <dd>Das Makefile, mit dem wir unser Dictionary erstellen können. Innerhalb der Datei
    ändern wir die Dateinamen zu unseren Dateinamen, und geben den Namen unseres Lexikons an.
    Dazu ersetzen wir
<pre>DICT_NAME		=	"My Dictionary"
DICT_SRC_PATH		=	MyDictionary.xml
CSS_PATH		=	MyDictionary.css
PLIST_PATH		=	MyInfo.plist</pre>    
    durch
<pre>DICT_NAME		=	"HanDeDict"
DICT_SRC_PATH		=	HanDeDict.xml
CSS_PATH		=	HanDeDict.css
PLIST_PATH		=	HanDeDict.plist</pre>    
</dd>
</dl>  

<p>Nun können wir das Lexikon testweise erstellen und installieren. Dazu
verwenden wir <code>make</code> im Terminal. Mittels <code>make install</code> kopieren
wir das erstellte Lexikon nach <code>~/Library/Dictionaries</code>:</p>

<pre>make &amp;&amp; make install
</pre>

<p>In den Beispieldaten gibt es den Eintrag <em>make</em>, der nun im Lexikon
(<code>Dictionary.app</code>) gefunden werden kann. Wird das Lexikon nicht angezeigt,
muss es in den Einstellungen aktiviert werden.</p>


<h2>Datenimport aus HanDeDict</h2>

<p>In einem <code>importer</code>-Skript beginnen wir mit der Shebang-Zeile,
 die angibt, dass das Skript ein Ruby-Skript ist. danach schalten wir den
 UTF-8 Support von Ruby ein:</p>

<pre>#!/usr/bin/env ruby
$KCODE = 'u'
</pre>

<p>Vom Terminal aus machen wir die Datei ausführbar:</p>

<pre>chmod u+x importer
</pre>

<p>Zur Erzeugung der XML-Datei, aus der das Dictionary Development Kit den Inhalt
  des Lexikons liest, verwenden wir die <a href="http://builder.rubyforge.org/">Builder</a>-Library von Jim Weirich. Sollte diese
noch nicht installiert sein, hilft
</p>

<pre>sudo gem install builder</pre>

<p>Dann importieren wir RubyGems und laden die Builder-Library:</p>

<pre>require 'rubygems'
require 'builder'
</pre>

Anschließend kopieren wir die UTF8-Version des HanDeDict
namens <code>handedict_nb.u8</code> in unser Projektverzeichnis, und lesen sie
mittels Ruby-Code ein:

<pre>vocabulary = []

File.new(File.dirname(__FILE__) + &#x27;/handedict_nb.u8&#x27;, &#x27;r&#x27;).each do |line|
  next if line !~ /^([^\s]+)\s+([^\s]+)\s+\[([^\]]+)\]\s+\/(.+)\/$/

  vocabulary &lt;&lt; {:traditional =&gt; $1, 
    :simplified =&gt; $2, :pinyin =&gt; $3, :translations =&gt; $4.split(&#x27;/&#x27;)}
  end  
</pre>

Um die XML-Datei zu erzeugen, verwenden wir die Builder-Library.
Das Format schauen wir uns aus der umbenannten <code>HanDeDict.xml</code> ab:

<pre>File.open(File.dirname(__FILE__) + &#x27;/HanDeDict.xml&#x27;, &#x27;w&#x27;) do |file|
  b = Builder::XmlMarkup.new(:target =&gt; file, :indent =&gt; 2)
  b.instruct!
  b.d :dictionary, :xmlns =&gt; &quot;http://www.w3.org/1999/xhtml&quot;, 
      :&#x27;xmlns:d&#x27; =&gt; &quot;http://www.apple.com/DTDs/DictionaryService-1.0.rng&quot; do
    vocabulary.each_with_index do |entry, index|
      b.d :entry, :id =&gt; &quot;handedict_#{index}&quot;, 
          :&#x27;d:title&#x27; =&gt; entry[:simplified] do
        b.d :index, :&#x27;d:value&#x27; =&gt; entry[:simplified]
        b.p do
          b.span entry[:simplified], :class =&gt; &quot;entry_heading&quot;
          b.text! &quot; [#{entry[:pinyin]}]&quot;
        end
        b.ol do
          entry[:translations].each do |translation|
            b.li translation
          end
        end
      end
    end
  end
end
</pre>

<p>Die Struktur ist recht einfach: Ein Lexikon <code>dictionary</code> enhält Einträge <code>entry</code> mit eindeutiger ID (Attribut <code>id</code>) 
und Titel (Attribut <code>title</code>). Innerhalb eines <code>entry</code>
befinden sich ein oder mehrere Index-Einträge <code>index</code> (mit Attribut <code>value</code>) sowie der Inhalt des Eintrags. Das <a href="http://developer.apple.com/documentation/UserExperience/Conceptual/DictionaryServicesProgGuide/Introduction/chapter_1_section_1.html">Dictionary Services Programming Guide</a>
 gibt einen kompletten Überblick über das XML-Format.</p>

<p>Nun können wir die XML-Datei durch Aufruf des Skripts aufbauen und anschließend installieren:</p>

<pre>./importer &amp;&amp; make &amp;&amp; make install</pre>

<p>Leider hat diese Lösung noch ein Problem: UTF-8 Zeichen werden von der Builder-Library
codiert (z.B. &amp;#21621; statt 呵). Damit kann das Dictionary Development Kit nicht umgehen.
Abhilfe schafft die neuste Version der Builder-Library aus dem Repository, die UTF-8 Zeichen
unverändert lässt:
</p>

<pre>svn export svn://rubyforge.org/var/svn/builder/trunk builder
cd builder
rake builder:gem
sudo gem install pkg/builder-2.2.0.gem
cd ..
</pre>

<p>Nach einem erneuten <code>./importer &amp;&amp; make &amp;&amp; make install</code>
ist das Lexikon einsatzbereit.  
</p>

<h2>Feinschliff</h2>

<p>Vor der Veröffentlichung des Lexikons sollten wir noch einige Korrekturen durchführen:
  Die Einträge werden zu klein angezeigt, und wir müssen noch das Copyright angeben.
</p>

<p>Um die Einträge größer anzuzeigen, ändern wir <code>HanDeDict.css</code> ab, 
  so dass die Bereiche mit der CSS-Klasse <code>entry_heading</code> größer angezeigt werden.
  Mit dieser Klasse haben wir die Einträge im XML markiert.</p>

<pre>@charset &quot;UTF-8&quot;;
@namespace d url(http://www.apple.com/DTDs/DictionaryService-1.0.rng);

span.entry_heading {
  font-size: 150%;
  font-weight: bold;
}
</pre>  

<p>In der Property List <code>HanDeDict.plist</code> setzen wir Attribute wie den Bundle Identifier, den Bundle Name, 
  die Version und die Copyright-Information, und löschen nicht verwendete
    Attribute:</p>
  
<pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
&lt;dict&gt;
	&lt;key&gt;CFBundleDevelopmentRegion&lt;/key&gt;
	&lt;string&gt;Germany&lt;/string&gt;
	&lt;key&gt;CFBundleIdentifier&lt;/key&gt;
	&lt;string&gt;de.tammofreese.dictionary.HanDeDict&lt;/string&gt;
	&lt;key&gt;CFBundleName&lt;/key&gt;
	&lt;string&gt;HanDeDict&lt;/string&gt;
	&lt;key&gt;CFBundleShortVersionString&lt;/key&gt;
	&lt;string&gt;1.0&lt;/string&gt;
	&lt;key&gt;DCSDictionaryCopyright&lt;/key&gt;
	&lt;string&gt;&copy; 2003-2008 Chinesisch-Deutsche Gesellschaft e.V. Hamburg, Tammo Freese. Some rights reserved. See http://creativecommons.org/licenses/by-sa/2.0/de/&lt;/string&gt;
	&lt;key&gt;DCSDictionaryManufacturerName&lt;/key&gt;
	&lt;string&gt;Chinesisch-Deutsche Gesellschaft e.V. Hamburg, Tammo Freese&lt;/string&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</pre>


<p>Weitere, hier nicht im Detail gezeigte Korrekturen sind die Darstellung
  der Tonangaben mit Tonzeichen statt Zahlen (z.B. guó statt guo2),
  und die Indizierung und Anzeige der traditionellen Schreibweise der
  chinesischen Zeichen (falls vorhanden).</p>

<p>Jetzt erzeugen wir das Lexikon neu und haben eine zur Veröffentlichung
  geeignete Version.</p>

<h2>Download</h2>

<p>Das HanDeDict für Apple Dictionary kann hier heruntergeladen werden: 
<a href="http://tammofreese.de/assets/2008/3/21/HanDeDict.dictionary.zip">HanDeDict.dictionary.zip</a>
(33,8 MB). Zur
Installation muss das Verzeichnis <code>HanDeDict.dictionary</code> nach 
<code>~/Library/Dictionaries</code> oder nach <code>/Library/Dictionaries</code> 
kopiert werden. Wird es nach <code>~/Library/Dictionaries</code> kopiert, ist das Lexikon
nur für den aktuellen Benutzer verfügbar. Das Kopieren in das Verzeichnis <code>/Library/Dictionaries</code> stellt das Lexikon für alle Benutzer zur Verfügung,
allerdings sind für den Kopiervorgang Administratorrechte erforderlich. Wird das Lexikon nicht angezeigt, muss man es noch in den Einstellungen von <code>Dictionary.app</code> aktivieren.</p>

<h2>Verwendung des HanDeDict für Apple Dictionary</h2>

<p>Lexika sind ein schönes Beispiel dafür, wie weit Apple die Integration
von Daten innerhalb des Systems treibt:
</p>

<ol>
<li>Das Lexikon kann in der Lexikon-Applikation (<code>Dictionary.app</code>) verwendet werden: <div><img src="http://tammofreese.de/assets/2008/3/15/handedict-in-dictionary.png" /></div></li>
<li>Auch im Dashboard-Widget steht es zur Verfügung: <div><img src="http://tammofreese.de/assets/2008/3/15/handedict-in-widget.png" /></div></li>
<li>Über die Spotlight-Suche werden die Einträge im Lexikon ebenfalls gefunden: <div><img src="http://tammofreese.de/assets/2008/3/15/handedict-in-spotlight.png" /></div></li>
<li>Über das Kontextmenü in Safari und vielen anderen Anwendungen kann mittels <em>Look Up in Dictionary</em> in den Lexika gesucht werden: <div><img src="http://tammofreese.de/assets/2008/3/15/handedict-in-contextmenu.png" /></div></li>
<li>Schließlich kann über das Tastaturkürzel ctrl-command-d das Wort unter dem Mauszeiger
  im Lexikon gesucht werden. Zur Anzeige dient hier nicht <code>Dictionary.app</code>,
  sondern ein Popup. Das Kürzel funktioniert auch in Anwendungen, in denen dem Kontextmenü
  der Menüpunkt <em>Look Up in Dictionary</em> fehlt: <div><img src="http://tammofreese.de/assets/2008/3/15/handedict-in-popup.png" /></div></li>
</ol>
  ]]>
  </description>
  <pubDate>Sat, 22 Mar 2008 13:30:00 +0100</pubDate>
  <guid>http://tammofreese.de/2008/03/22/handedict-fuer-apple-dictionary</guid>
  </item>

  <item>
  <title>Fortgeschrittene Techniken der testgetriebenen Entwicklung</title>
  <link>http://tammofreese.de/2007/05/30/fortgeschrittene-techniken-der-testgetriebenen-entwicklung</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/fortgeschrittene-techniken-der-testgetriebenen-entwicklung.png" />
      <p>
Im letzten Monat fand die diesjährige <a href="http://jax.de/">JAX</a> statt. <a href="http://www.johanneslink.net/">Johannes Link</a>
und ich hatten einen Power Workshop zu fortgeschrittenen Techniken der
testgetriebenen Entwicklung.</p>


<h2>Umbesetzung am frühen Morgen</h2>

<p>
Leider musste mir Johannes erkältungsbedingt am Morgen des Workshops
absagen. Prinzipiell kein Problem, einer der Gründe dafür, einen 
Workshop zu zweit einzureichen, ist schließlich Ausfallsicherheit.
Nur wurden uns wenige Tage vor dem Workshop voraussichtliche 70 Teilnehmer angekündigt, und bei dem hohen Anteil von praktischen Übungen wäre das für mich alleine doch recht hektisch geworden.</p>

<p>
Glücklicherweise traf ich beim Frühstück <a href="http://stefanroock.blogspot.com/">Stefan Roock</a>, einen meiner Freunde
von <a href="http://it-agile.de/">it-agile</a> aus Hamburg. Er meldete sich freiwillig, mir bei den Übungen auszuhelfen. 
</p>

<h2>Warum testgetriebene Entwicklung?</h2>

<p>Meine Lieblingsfrage bei Testworkshops ist immer, warum wir eigentlich testgetrieben entwickeln sollen. Tests sieht jeder ein, automatisierte Tests auch, warum aber testgetrieben?
</p>  
<p>Meine Antwort darauf: Die Relevanz von Testergebnissen hängt unter anderem von zwei Dingen ab &mdash; sie müssen möglichst aktuell sein, und möglichst viele Teile des Codes erreichen. Sind die Testergebnisse alt, machen sie nur noch eine Aussage über das System zum Zeitpunkt
der Testausführung, und dieses System kann stark vom aktuellen System abweichen. Decken die Tests nur einen geringen Teil des Systems ab,
haben die Ergebnisse nur wenig Aussagekraft über das Gesamtsystem.
</p>

<p>Entwickeln wir testgetrieben, dann führen wir die Tests sehr häufig aus, damit haben wir immer aktuelle Ergebnisse. Und das testgetriebene
Vorgehen führt zu einer hohen Testabdeckung: Halten wir sie komplett durch, werden 100% des Systems durch die Tests erreicht.</p>

<p>
  Eine Folgefrage ist oft, ob testgetriebene Entwicklung nicht zu teuer ist. Schließlich kostet die Testerstellung Zeit. Als Antwort darauf wird oft argumentiert,
  die Investition mache sich später durch höhere Qualität bezahlt. Das denke ich auch, aber empirische Untersuchungen dazu 
  sind rar. Andererseits sollten wir lieber erst einmal in Tests investieren und sehen, wann wir zu dem Punkt kommen, an dem wir denken: Unsere Qualität ist zu hoch, wir sollten weniger Tests schreiben.</p>

<h2>Groovy</h2>

<p>Um den Aufwand für die Erstellung der Tests in Java zu verringern, 
kann man die Tests in <a href="http://groovy.codehaus.org/">Groovy</a>, einer dynamisch typisierten Skriptsprache für die Java Virtual Machine, implementieren.
</p>
<p>Natürlich können wir auch Systemteile oder das gesamte System in Groovy statt Java schreiben. Oftmals wird der Code dadurch kürzer und besser verständlich. Ein Beispiel: Nehmen wir an, wir haben in der Variablen <code>users</code> eine Liste von Objekten der Klasse <code>User</code>. Bei User-Objekten gibt der Getter <code>getFirstName()</code> den Vornamen zurück. Wir wollen nun 
alle Vornamen alphabetisch sortiert durch Kommata getrennt
der Variablen <code>firstNamesForDisplay</code> zuweisen.
</p>
<p>
  In Java kann das zum Beispiel so aussehen:
</p>
<pre>List&lt;String&gt; firstNames = new ArrayList&lt;String&gt;();
for (User user : users) {
    firstNames.add(user.getFirstName());
}  
Collections.sort(firstNames);
StringBuffer result = new StringBuffer();
for (Iterator&lt;String&gt; it = firstNames.iterator(); it.hasNext();) {
  result.append(it.next());
  if (it.hasNext()) result.append(&quot;, &quot;);
}
String firstNamesForDisplay = result.toString();
</pre>
<p>
  Sicherlich kann man den Code noch ein wenig reduzieren, aber
  der Groovy-Code wird immer schöner bleiben:
</p> 
<pre>String firstNamesForDisplay = users.firstName.sort().join(', ')
</pre>
<p>
  Im Workshop kam die Frage auf, Groovy der Nachfolger von Java sein wird. Natürlich ist jede Antwort auf
  so eine Frage ein Blick in die Kristallkugel. Meiner Meinung nach
  hat Groovy das Potenzial, einen großen Teil der Java-Entwicklung zu  ersetzen, da man mit viel weniger Code viel mehr erreichen kann.</p>
  
<p>Leider hat eine dynamisch getypte Sprache Nachteile,
  wenn es um die Werkzeugunterstützung geht. Eine gute Code
  Completion zu implementieren, ist schwierig, da die Typen von Variablen zur Übersetzungszeit nicht bekannt sind. Auch automatisierte Refactorings sind durch die dynamischen Eigenschaften der Sprache
schwieriger zu implementieren, und bleiben wohl am Ende immer unzuverlässiger als für Java.
</p>

<h2>Infos zum Workshop</h2>

<p>
Johannes hat <a href="http://www.slideshare.net/jlink/advancedtdd/">unsere Folien zum Workshop</a> auf SlideShare
veröffentlicht. Stefan ließ es sich nicht nehmen, über die
Inhalte des Workshops zu bloggen (<a href="http://jax2007blog.it-agile.de/2007/04/tdd-fr-fortgeschrittene-teil-1.html">Teil 1</a>, <a href="http://jax2007blog.it-agile.de/2007/04/tdd-fr-fortgeschrittene-teil-2.html">Teil 2</a>, <a href="http://jax2007blog.it-agile.de/2007/04/tdd-fr-fortgeschrittene-teil-3-legacy.html">Teil 3</a>).
Auch einer der Teilnehmer fasste den Workshop <a href="http://skiingorblogging.blogspot.com/2007/04/first-jax-day-workshop-day.html">in einem Blog-Eintrag</a> zusammen.
</p>


  ]]>
  </description>
  <pubDate>Wed, 30 May 2007 21:07:00 +0100</pubDate>
  <guid>http://tammofreese.de/2007/05/30/fortgeschrittene-techniken-der-testgetriebenen-entwicklung</guid>
  </item>

  <item>
  <title>Tracking zur einfachen Aufwandsschätzung</title>
  <link>http://tammofreese.de/2007/05/28/tracking-zur-einfachen-aufwandsschaetzung</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/tracking-zur-einfachen-aufwandsschaetzung.png" />
      <p>Für eine zuverlässige Projektplanung müssen wir zuverlässige
Schätzungen abgeben. Im Extreme Programming wollen wir uns
dabei nicht nur auf unser Bauchgefühl verlassen, sondern
Aufwände für zukünftige Aufgaben aus den Aufwänden für bereits
erfüllte Aufgaben ableiten.
</p>

<h2>Warum überhaupt Tracking?
</h2>
<p>Im Tracking erfassen wir die geschätzten und tatsächlichen 
Aufwände für die Erfüllung von Aufgaben, und messen damit die 
Entwicklungsgeschwindigkeit. Sie ist sowohl für den Kunden 
als auch für die Entwicklung eine wichtige Information: 
</p>
<ul><li>Der Kunde
erfährt, wie viele Aufgaben in den nächsten Entwicklungszyklen 
voraussichtlich erfüllt werden können. 
</li><li>Die Entwickler erhalten Informationen,
ob und wie sich die Entwicklungsgeschwindigkeit ändert, woraus sie
auf Probleme im Entwicklungsprozess schließen können.</li></ul>

<h2>Das einfachste Tracking, das funktionieren könnte
</h2>

<p>Das einfachste Maß für die Entwicklungsgeschwindigkeit ist die
Anzahl von Aufgaben, die pro Entwicklungszyklus realisiert
werden. Für den Kunden ist dieses Maß ebenfalls geeignet, da es
den für ihn sichtbaren Projektfortschritt abbildet.
</p>
<p>Für den folgenden Entwicklungszyklus nehmen wir dann die gleiche
Geschwindigkeit an, die wir im letzten Durchlauf erreicht
haben. Das ist die ehrlichste Abschätzung, die wir bei diesem Maß
machen können.
</p>
<p>Im Extreme Programming hat sich dafür der Begriff
Yesterday's Weather eingebürgert: Die Vorhersage, dass das Wetter heute
genauso sein wird wie gestern, erreicht eine recht hohe Genauigkeit.</p>


<h2>Verschiedene Aufgabengrößen</h2>

<p>Variieren die Aufwände pro Aufgabe stark und lassen sich die Aufgaben partout
nicht in annähernd gleichgroße, für den Kunden bedeutsame Teile zerschneiden,
führen wir ein Maß für die Größe von Aufgaben ein.
Wir geben zuerst einer kleinen Aufgabe 1 Punkt und schätzen dann größere Aufgaben
im Vergleich damit ab: Schätzen wir eine andere Aufgabe doppelt so groß,
bekommt sie 2 Punkte, schätzen wir eine andere wiederum doppelt so groß, 
geben wir ihr 4 Punkte.
</p>
<p>Die Geschwindigkeit messen wir dann in Punkten pro Entwicklungszyklus.
Für den folgenden Durchlauf nehmen wir die Geschwindigkeit des 
letzten Durchlaufs an. 
</p>

<h2>Schätzungen und Realdaten
</h2>
<p>Der Anteil am tatsächlichen Aufwand einer Aufgabe am Gesamtaufwand
eines Entwicklungsdurchlaufs kann stark von dem Punktanteil an der 
Gesamtpunktzahl abweichen. Zum Beispiel kann eine Aufgabe mit nur 10% 
der Punkte abgeschätzt worden sein, aber tatsächlich 30% der Zeit gekostet
haben.
</p>
<p>Treten solche Abweichungen häufig auf, wird das Tracking unzuverlässig.
Um das Problem zu lösen, erfassen wir pro Aufgabe, wieviel Zeit tatsächlich darauf
entfallen sind, und verteilen für zukünftige Vergleiche die geleisteten
Punkte nach der tatsächlich verwendeten Zeit auf die Aufgaben.
</p>
<p>Zur Vermeidung von Pseudogenauigkeiten wollen wir weiterhin
mit vollen Punktzahlen arbeiten. Daher verwenden wir ein einfaches 
Verfahren, um die geleisteten Punkte auf die Aufgaben umzurechnen:
</p>
<ol>
  <li>Jede Aufgabe bekommt den ganzzahligen Anteil.</li>
  <li>Verbleibende Punkte werden den Aufgaben mit den höchsten
   Teilungsresten zugeschlagen.</li>
</ol>
<p>Als Beispiel nehmen wir an, dass im letzten Entwicklungszyklus
die Aufgaben A, B, C, D und E mit jeweils 2 Punkten geplant waren.
Tatsächlich war noch etwas Zeit übrig, so dass die 1-Punkt-Aufgabe F
ebenfalls erfüllt werden konnte. Insgesamt wurden also 11 Punkte 
geliefert. Mit den realen Aufwänden verteilen wir diese nun auf
die Aufgaben:
</p>
<pre>Aufgabe A: 11 Punkte * 23% = 2,53 Punkte
Aufgabe B: 11 Punkte * 16% = 1,76 Punkte
Aufgabe C: 11 Punkte * 25% = 2,75 Punkte
Aufgabe D: 11 Punkte * 16% = 1,76 Punkte
Aufgabe E: 11 Punkte *  5% = 0,55 Punkte
Aufgabe F: 11 Punkte * 15% = 1,65 Punkte
</pre>
<p>Jede Aufgabe bekommt die ganzzahligen Punkte, das sind insgesamt 7 Punkte.
Die restlichen 4 Punkte bekommen die Aufgaben mit den höchsten 
Teilungsresten, also B, C, D und F:
</p>
<pre>Aufgabe A: 2 + 0 Punkte = 2 Punkte
Aufgabe B: 1 + 1 Punkt  = 2 Punkte
Aufgabe C: 2 + 1 Punkt  = 3 Punkte
Aufgabe D: 1 + 1 Punkt  = 2 Punkte
Aufgabe E: 0 + 0 Punkte = 0 Punkte
Aufgabe F: 1 + 1 Punkt  = 2 Punkte
</pre>

<p>Stellen wir die geschätzten Punkte den nach realen Aufwänden verteilten 
gegenüber, sehen wir, wie gut unsere Schätzungen im Einzelnen waren.
Hier waren die Schätzungen für A, B und D recht gut, C und F haben
wir unterschätzt und E völlig überbewertet.
</p>
<p>Das hier verwendete Vorgehen, um Prozentwerte auf eine 
feste Anzahl von Punkten zu verteilen, nennt sich <a href="http://de.wikipedia.org/wiki/Hare-Niemeyer-Verfahren">Hare-Niemeyer-Verfahren</a> und
wird bei Parlamentswahlen dazu verwendet, Sitze auf Parteien zu verteilen.</p>


<h2>Einfache Ausnahmebehandlung
</h2>
<p>Zahlreiche geplante und ungeplante Ereignisse können die pro Durchlauf 
verfügbare Zeit verändern (Krankheit, Urlaub, neue Teammitglieder, …). 
Natürlich könnten wir diese Daten verwenden, um zu versuchen, die
zukünftige Geschwindigkeit genauer einzuschätzen.
</p>
<p>Viel einfacher ist aber, einfach einen Entwicklungsdurchlauf zu warten
und damit festzustellen, ob sich die Geschwindigkeit tatsächlich ändert.
</p>
<p>Analog gehen wir mit Aufwänden um, die wir nicht direkt den bearbeiteten
Aufgaben zuweisen können. Sagen wir mal, wir
haben 10 Punkte geschätzt, eine der letzten Lieferungen war aber 
fehlerhaft und 20% der Zeit sind in ungeplante Fehlerkorrekturen
geflossen. Außerdem haben wir 10% der Zeit in Meetings verbracht.
Tatsächlich haben wir 7 Punkte geschafft. Natürlich können wir nun
zusätzliche 'Aufgaben' für die Fehlerkorrekturen (2 Punkte) und die Meetings
(1 Punkt) erfassen und damit unsere tatsächliche Geschwindigkeit 
auf die geschätzten 10 Punkte bringen.
</p>
<p>Tatsächlich ist so eine Vorgehensweise fatal: Wer sagt uns, dass wir
im nächsten Durchlauf nicht wieder 20% der Zeit für ungeplante Fehlerkorrekturen
brauchen? Und wer sagt uns, dass wir mit weniger Meetings nicht noch weniger
Punkte geschafft hätten, da zu wenig Abstimmung vorlag?
</p>
<p>Wir erfassen unsere Geschwindigkeit also mit ehrlichen 7 Punkten. 
Die Zeiten für nicht aufgabenrelevante Aufwände schlagen wir einfach 
den Aufgaben zu, von denen uns die Aufwände abgehalten haben. 
</p>

<h2>Anpassung der Planung
</h2>
<p>Natürlich eignet sich die Ausnahmebehandlung nur für Ausnahmefälle.
Haben wir ständig wiederkehrende Aufwände für ungeplante Aufwände
(zum Beipiel Serveradministration und Support), bietet es sich an,
diese Aufgaben entweder aus der Entwicklung herauszulösen, oder sie explizit
einzuplanen.
</p>

<h2>Geschwindigkeitsänderungen
</h2>
<p>Bei der Geschwindigkeit sollte keine Konstante erwartet werden, 
kleine Schwankungen sind völlig normal. Bei starken Geschwindigkeitsänderungen
sollten wir versuchen, den Ursachen auf den Grund zu gehen. Zum Beispiel
mag eine plötzliche Geschwindigkeitssteigerung aus Kundensicht
schön aussehen, sie kann aber ebensogut bedeuten, dass zu viel Druck auf das
Team ausgeübt wurde, und dass darunter die strukturelle Qualität des Systems
und die Testabdeckung gelitten haben. 
</p>

<h2>Lieber wenig als überhaupt nicht</h2>

<p>Sicher, das hier vorgestellte Tracking ist bewusst einfach und ungenau.
Andererseits können komplexere Lösungen leicht zu bürokratischen Monstern
ausarten, die vom Team nicht akzeptiert werden und daran scheitern.
</p>

<p>Meine Empfehlung ist daher: Starten Sie lieber mit dem einfachsten
Tracking, das funktionieren könnte. Erweitern Sie es dann, wenn es tatsächlich
nötig ist. Und prüfen Sie, ob sie es später nicht wieder vereinfachen können.
</p>
  ]]>
  </description>
  <pubDate>Mon, 28 May 2007 13:55:00 +0100</pubDate>
  <guid>http://tammofreese.de/2007/05/28/tracking-zur-einfachen-aufwandsschaetzung</guid>
  </item>

  <item>
  <title>Ruby on Rails Fixtures – Fluch oder Segen?</title>
  <link>http://tammofreese.de/2006/10/29/ruby-on-rails-fixtures-fluch-oder-segen</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/ruby-on-rails-fixtures-fluch-oder-segen.png" />
      <p>
	Ruby on Rails baut auf einer Model-View-Controller (MVC)-Architektur auf. 
	Die Modellschicht ActiveRecord verwendet das gleichnamige Pattern Active Record, 
	das in Martin Fowlers Buch <a href="http://www.amazon.com/dp/0321127420">Patterns of Enterprise Application Architecture</a> dokumentiert ist. Hierbei
	wird eine Datenbankrelation durch eine Klasse abgebildet, eine 
	Zeile der Relation entspricht einer Instanz der Klasse. Eine Ausnahme von dieser
	Regel sind die Tabellen, die m:n-Abbildungen abbilden.
</p>

<h2>Test-Fixtures in Rails</h2>

<p>
	Rails bietet Tests auf drei Ebenen an:
</p>

<ul>
	<li><em>Unit Tests</em> testen die Modellobjekte, also die ActiveRecord-Schicht.</li>
	<li><em>Funktionale Tests</em> testen einzelne Controller. Dabei verwenden diese 
		die Modellobjekte. In den Tests können auch die Views mit abgedeckt werden.</li>
	<li><em>Integrationstests</em> testen das System Controller-übergreifend.</li>
</ul>

<p>
	Alle drei Testarten verwenden also die Modellobjekte. Die meisten sinnvollen Tests
	benötigen Daten, auf denen sie operieren. Die Standardlösung dafür in Rails sind
	<em>Fixtures</em>. In diesen Dateien werden Objekte typischerweise im YAML-Format
	abgelegt:
</p>
<pre>
john:
  id: 1
  name: John Doe
  login: johndoe
  password: nosecret
</pre>
<p>
	In den Testklassen werden dann die benötigten Fixtures über die <code>fixtures</code>-Methode eingebunden. Sie stellt sicher, dass die Daten aus den übergebenen Fixtures jedem Test der Testfallklasse in der Datenbank zur Verfügung stehen. Die Datensätze können dann im Test über eine Methode mit dem Namen der Fixture zugegriffen werden:
</p>
<pre>
  fixtures :users	
	
  def setup
    @john = users(:john)	
  end  
</pre>

<h2>Probleme durch Fixtures</h2>

<p>
	Die Fixtures können zu zahlreichen Problemen führen:
</p>
<ul>
<li>Tests können ohne die Kenntnis der Fixture-Daten unverständlich sein.</li>
<li>Die Fixtures sind global, enthalten also Daten für alle Tests. Das kann dazu führen, dass Abhängigkeiten zwischen Tests entstehen.</li>
<li>m:n-Beziehungen in Fixtures zu definieren erfordert eine Menge an Datensätzen.</li>
<li>Die Daten aus den Fixtures werden an den Rails-Mechanismen vorbei in die Datenbank geschrieben. Damit können Testdaten ungültige Datensätze sein.</li>
</ul>

<h2>Best Practices</h2>

Um die Probleme mit Fixtures in den Griff zu bekommen,
bieten sich einige Techniken an:

<h3>Minimale Fixture</h3>

<p>In den Fixtures sollten nur diejenigen Datensätze
definiert werden, die für einen Großteil der Tests relevant sind. Wird zum Beispiel
mit Authentifizierung gearbeitet, könnte ein Benutzerkonto zu diesen Daten gehören.
</p>
<p>Damit wird die Menge an Fixture-Daten drastisch reduziert, und deutlich überschaubarer.
</p>

<h3>Echte Unit Tests schreiben</h3>

<p>Für viele Tests ist es nicht notwendig, Datenbankobjekte in die Datenbank zu speichern.
Nehmen wir als Beispiel diesen (durchwachsenen) Unit Test:
</p>

<pre>
def test_card_validates_presence_of_front_and_back
  card = Card.new
  assert !card.save

  card.front = '1 + 3 = ?'
  assert !card.save

  card.front = ''
  card.back = '4'
  assert !card.save
  
  card.front = '1 + 3 = ?'
  assert card.save
end
</pre>
<p>Die Validations können hier ebensogut über den Aufruf der Methode valid? 
getestet werden, was auch erlaubt, den Test in zwei Fälle aufzuteilen:
</p>

<pre>
def test_card_validates_presence_of_front
  card = Card.new
  card.valid?
  assert card.errors.include?(:front)
  card.front = 'front' 
  card.valid?
  assert !card.errors.include?(:front)
end

def test_card_validates_presence_of_back
  card = Card.new
  card.valid?
  assert card.errors.include?(:back)
  card.back = 'back' 
  card.valid?
  assert !card.errors.include?(:back)
end
</pre>



<h3>Test Helper</h3>

<p>Ist das Anlegen von testspezifischen Daten nicht vermeidbar, werden 
sie mit Hilfsmethoden im Testsetup oder direkt im Test angelegt. Ein gutes Schema 
für die Hilfsmethoden ist:
</p>

<ul><li>Hilfsmethoden heißen <code>create_modellname(options = {})</code>. Sie erzeugen ein Objekt des Typs Modellname in der Datenbank und geben es zurück.</li>
<li>Bei Aufruf ohne Optionen wird durch die Methode ein neues Objekt angelegt. Attribute werden so mit Standardwerten belegt, dass alle Nebenbedingungen (validations) eingehalten werden. Beziehungsobjekte werden entweder aus einer Fixture referenziert, oder aber durch den entsprechenden Helper neu angelegt.</li>
<li>Attribute und Beziehungsobjekte können alternativ über das <code>options</code>-Hash mitgegeben werden.</li>
</ul>

<p>
  Mit den Hilfsmethoden können wir nun Tests schreiben, bei denen die Fixture in der <code>setup</code>-Methode der Testklasse oder in den einzelnen Testmethoden aufgebaut wird.
</p>


<h2>Fluch oder Segen?</h2>

<p>Rails Fixtures sind ein Segen, da sie uns erlauben, häufig in Tests benötigte 
Datensätze einmalig anzulegen. Sie werden dann zum Fluch, wenn sich testspezifische Daten in die Fixtures verirren.
</p>

  ]]>
  </description>
  <pubDate>Sun, 29 Oct 2006 09:00:00 +0100</pubDate>
  <guid>http://tammofreese.de/2006/10/29/ruby-on-rails-fixtures-fluch-oder-segen</guid>
  </item>

  <item>
  <title>QYPE ist live</title>
  <link>http://tammofreese.de/2006/05/01/qype-ist-live</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/qype-ist-live.png" />
      <p>
Seit letzten Dienstag (25.04.2006) ist <a href="http://www.qype.com">QYPE</a> [&#107;&#119;&#97;&#618;&#112;] online,
die Plattform, an der ich seit drei Monaten mitarbeite. Auf QYPE kann jeder Benutzer die Adressen seiner Stadt bewerten 
und mit Stichworten (Tags) versehen, und über die Tags, Städte und Menschen auf QYPE neue Adressen entdecken. 
</p>

<p>Über QYPE berichteten unter anderem <a href="http://www.spiegel.de/netzwelt/technologie/0,1518,413256,00.html">Spiegel Online</a> 
und <a href="http://www.n-tv.de/661902.html">n-tv.de</a>. Als Projekt ist QYPE für mich interessant, 
weil ich dort mit guten Leuten in einem echten Team nach <a href="http://www.frankwestphal.de/ExtremeProgramming.html">Extreme Programming</a>-Prinzipien arbeiten kann. Und weil wir auf <a href="http://www.rubyonrails.org/">Ruby on Rails</a> 
und <a href="http://de.wikipedia.org/wiki/Ajax_(Programmierung)">AJAX</a> setzen,
um eine hohe Entwicklungsgeschwindigkeit zu erreichen. Und weil wir auf <a href="http://www.apple.com/de/macosx/">Mac OS X</a> entwickeln.
</p>	
	




  ]]>
  </description>
  <pubDate>Mon, 01 May 2006 20:00:00 +0100</pubDate>
  <guid>http://tammofreese.de/2006/05/01/qype-ist-live</guid>
  </item>

  <item>
  <title>CSS Min-Height Simulation für Internetseiten</title>
  <link>http://tammofreese.de/2005/12/31/css-min-height-simulation-fuer-internetseiten</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/css-min-height-simulation-fuer-internetseiten.png" />
      <p>
	Um für Elemente einer HTML-Seite eine minimale Höhe anzugeben, 
	gibt es in CSS 2.0 die <code>min-height</code>-Eigenschaft. Leider wird diese
    nicht vom Microsoft Internet Explorer 6.0 unterstützt. 
</p>

<h2>CSS Min-Height Simulation für feste Pixelwerte</h2>

<p>
Auf <a href="http://www.greywyvern.com/code/min-height-hack.html">http://www.greywyvern.com/code/min-height-hack.html</a> 
wird der <em>CSS min-height Hack</em> beschrieben, der erlaubt, für ein <code>div</code>-Element 
auch ohne Verwendung der <code>min-height</code>-Eigenschaft eine minimale Höhe in Pixeln anzugeben.
</p>

<p>Als Beispiel dient ein <code>div</code>-Element, für das eine minimale Höhe von 50 Pixeln angegeben werden soll:
</p>

<pre>
&lt;div&gt;
  Inhalt
&lt;/div&gt;
</pre>

<p>Die minimale Höhe wird über zwei zusätzliche <code>div</code>-Element und CSS-Code erzwungen.
</p>

<pre>
&lt;div&gt;
  &lt;div class="prop"&gt;&lt;/div&gt;
  Inhalt
  &lt;div class="clear"&gt;&lt;/div&gt;
&lt;/div&gt;
</pre>

<p>
Das Element mit der Klasse <code>prop</code> bekommt die gewünschte Höhe zugewiesen und wird nach rechts
verschoben.
</p>

<pre>
.prop {
  height: 50px;
  float: right;
  width: 1px;
}
</pre>

<p>
Das Element mit der Klasse <code>clear</code> wird nach dem Originalartikel für ältere Mozilla- und Firefox-Versionen
benötigt, damit der Inhalt durch das <code>prop</code>-Element bis nach unten reicht. Die <code>overflow</code>-Eigenschaft
soll gesetzt werden, da der Microsoft Internet Explorer 6.0 im Standard-kompatiblen Modus <code>div</code>-Elemente
mit einer Höhe von weniger als <code>1em</code> nicht automatisch erzeugen soll.
</p>

<pre>
.clear {
  clear:both;
  height:1px;
  overflow:hidden;
}
</pre>

<p>Hier sind drei Beispiele für den <em>CSS min-height Hack</em>, eine leere Box, eine Box, bei die niedriger als 50 Pixel
	sein sollte, und eine, die höher sein sollte. Zur Verdeutlichung ist die <code>prop</code>-Klasse rechts und die
	<code>clear</code>-Klasse unten in dunkelgrau dargestellt:
</p>

<div style="float:left; width: 100px; margin: 20px;background:#e6e6e6;">
	<div style="height: 50px;float: right; width: 1px; background:#aaa;"></div>
	
	<div style="clear:both;height:1px;overflow:hidden; background:#aaa;"></div>
</div>	
		
<div style="float:left; width: 100px; margin: 20px;background:#e6e6e6;">
	<div style="height: 50px;float: right; width: 1px; background:#aaa;"></div>
    Weniger als 50px Höhe
	<div style="clear:both;height:1px;overflow:hidden; background:#aaa;"></div>
</div>	
		
<div style="float:left; width: 100px; margin: 20px;background:#e6e6e6;">
	<div style="height: 50px;float: right; width: 1px; background:#aaa;"></div>
	Das ist hoffentlich genug Text für mehr als 50px Höhe
	<div style="clear:both;height:1px;overflow:hidden; background:#aaa;"></div>
</div>	
		
		
<div style="clear: both;"></div>		
	

<h2>Anpassung der Simulation für Internetseiten</h2>

<p>Um für eine komplette HTML-Seite zu erreichen, dass sie mindestens die Höhe des Browserfensters einnimmt
(dass also die Fußzeile auch für Seiten mit kurzem Inhalt immer unten im Browserfenster erscheint),
muss der <em>CSS min-height Hack</em> modifiziert werden.
</p>

<h3>Ausgangspunkt</h3>

<p>
Statt eines <code>div</code>-Elements ist der Inhalt einer HTML-Seite in ein <code>body</code>-Element eingeschlossen:
</p>

<pre>
&lt;body&gt;
  Inhalt
&lt;/body&gt;
</pre>

<p>
Der Text einer HTML-Seite darf nicht direkt im <code>body</code>-Element stehen, daher wird er in ein <code>div</code>-Element
eingeschlossen. Zusätzlich nehmen wir ein <code>div</code>-Element für die Fußzeile auf:
</p>

<pre>
&lt;body&gt;
  &lt;div&gt;
    Inhalt
  &lt;/div&gt;
  &lt;div&gt;Fu&szlig;zeile&lt;/div&gt;
&lt;/body&gt;
</pre>

<p>
Um einen Ausgangspunkt zu haben, der dem des <em>CSS min-height Hack</em> ähnelt, führen wir ein <code>div</code>-Element
mit der Klasse <code>prop</code> ein, und versehen das <code>div</code>-Element der Fußzeile mit der Klasse <code>clear</code>.
Da die Elemente auf der Seite eindeutig sein sollten, ist es sinnvoll, statt dem <code>class</code>-Attribut das 
<code>id</code>-Attribut zu verwenden. Um uns am Original zu halten, lassen wir es in diesem Beispiel aber sein.
</p>

<pre>
&lt;body&gt;
  &lt;div class="prop"&gt;&lt;/div&gt;
  &lt;div&gt;
    Inhalt
  &lt;/div&gt;
  &lt;div class="clear"&gt;Fu&szlig;zeile&lt;/div&gt;
&lt;/body&gt;
</pre>

<p>
Im CSS passen wir die Höhe für die <code>prop</code>-Klasse die Höhe auf 100% an. Für die <code>clear</code>-Klasse setzen
wir die Höhe der Fußzeile (hier auf 100px), und löschen die <code>overflow</code>-Eigenschaft, da diese mit der
neuen Höhe nicht mehr benötigt wird:
</p>

<pre>
.prop {
  height: 100%;
  float: right;
  width: 1px;
}

.clear {
  clear:both;
  height:100px;
}
</pre>

<h3>Problem 1: Zu niedrig</h3>

<p>
Am <a href="http://tammofreese.de/assets/2005/12/31/min-height-1-zu-niedrig.html">Ausgangspunkt</a> hängt die Fußzeile direkt unter dem Inhalt und nicht am unteren Bereich des Fensters.
Das liegt daran, dass <a href="http://www.w3.org/TR/REC-CSS2/visudet.html#the-height-property">nach der CSS2-Spezifikation</a> das <code>prop</code>-Element nun 100% der Höhe 
des umschließenden Blocks, also des <code>body</code>-Elements bekommt. Das hat standardmäßig
die Höhe <code>auto</code>, nimmt also nur soviel Platz ein, wie nötig. Wir müssen also für das 
<code>body</code>-Element eine Höhe von 100% zuweisen. Für die Kompatibilität zu Mozilla und Safari
muss auch das umschließende <code>html</code>-Element die Höhe 100% zugewiesen bekommen:
</p>

<pre>
html, body {
   height: 100%;
}   
</pre>

<h3>Problem 2: Zu hoch</h3>

<p>Vom Regen in die Traufe: <a href="http://tammofreese.de/assets/2005/12/31/min-height-2-zu-hoch.html">Jetzt</a> ist die Fußzeile zwar unten, aber zu weit: Die Seite ist immer höher als der zur Verfügung stehende Platz.
</p>

<p>Ein Teil der zu großen Höhe liegt an den Standardwerten für Innen- und Außenabstand des <code>body</code>-Elements.
Also setzen wir Innen- und Außenabstand auf 0:
</p>

<pre>
body {
  margin: 0;
  padding: 0;
}
</pre>

<p>
Das Problem ist <a href="http://tammofreese.de/assets/2005/12/31/min-height-3-immer-noch-zu-hoch.html">kleiner</a> geworden, die Seite ist aber immer noch zu hoch. Das liegt daran, dass unser
<code>prop</code>-Element 100% der Seitenhöhe bekommt, und zusätzlich noch die Fußzeile mit 100 Pixel zu Buche schlägt.
Die Seite ist also um 100 Pixel zu hoch. 
</p>

<p>Über die Höhe des <code>prop</code>-Elements können wir das Problem nicht lösen, da wir eine relative Größe in Prozent
von einer absoluten Größe in Pixeln subtrahieren müssten. Wollen wir die absolute Höhe der Fußzeile beibehalten,
müssen wir die Subtraktion in einer anderen Weise vornehmen. Das erreichen wir mit dem Außenabstand des <code>prop</code>-Elements: 
Indem wir ihn unten auf -100 Pixel setzen, werden unten anliegende Elemente beim Layout, also in unserem Fall die Fußzeile, 100 Pixel höher angesetzt:
</p>

<pre>
.prop {
  height: 100%;
  float: right;
  width: 1px;
  margin: 0 0 -100px 0;
}
</pre>


<h3>Die Min-height Simulation ist fertig</h3>

<p>
Damit ist <a href="http://tammofreese.de/assets/2005/12/31/min-height-4-ziel-erreicht.html">das Ziel</a> erreicht: Solange das Browserfenster groß genug ist, bleibt die Fußzeile am unteren Rand. Wird das Browserfenster
zu klein für die Seite, erscheinen Scrollbalken. Diese <code>min-height</code> Simulation wurde mit 
Microsoft Internet Explorer 6 auf Windows XP SP 2 sowie mit Safari 2.0.2 und Firefox 1.0.6 auf Mac OS X 10.4.3 erfolgreich getestet.
</p>

<p>
Auf meiner Internetpräsenz <a href="http://tammofreese.de">http://tammofreese.de</a> ist eine 
Variante der <code>min-height</code> Simulation eingesetzt, die auch eine Kopfzeile berücksichtigt. 
Beim
<code>div</code>-Element, das die minimale Höhe garantiert, wird dafür auch oben ein negativer Außenabstand verwendet.
</p>


  ]]>
  </description>
  <pubDate>Sat, 31 Dec 2005 14:00:00 +0100</pubDate>
  <guid>http://tammofreese.de/2005/12/31/css-min-height-simulation-fuer-internetseiten</guid>
  </item>

  <item>
  <title>EasyMock 2.0</title>
  <link>http://tammofreese.de/2005/12/24/easymock-2-0</link>
  <description>
  <![CDATA[
      <img src="http://tammofreese.de/images/easymock-2-0.png" />
      <p>
	Mit EasyMock können Mock-Objekte für automatisierte Tests
	dynamisch generiert werden. Die neue Version EasyMock 2.0 setzt Java 5.0 voraus und kann unter
	<a href="http://www.easymock.org/Downloads.html">http://www.easymock.org/Downloads.html</a> heruntergeladen werden.
</p>

<h2>Unit Tests und Mock-Objekte</h2>

<p>
In Unit Tests sollen Elemente der Software isoliert getestet werden. Ein Element
funktioniert oft nur in Zusammenarbeit mit anderen Elementen. 
Um es in Isolation zu testen, müssen diese Mitarbeiter simuliert werden.
</p>

<p>Ein Mock-Objekt ersetzt einen Mitarbeiter im Test. 
Es wird mit erwarteten Methodenaufrufen konfiguriert.
Im Test überprüft es, ob die erwarteten Methodenaufrufe auftreten.
Bei Abweichungen vom erwarteten Verhalten schlägt es so früh wie möglich fehl.
</p>

<h2>Mock-Objekte mit EasyMock 2.0</h2>

<p>Um EasyMock 2.0 in Tests zu verwenden, müssen die Methoden einer Klasse importiert werden:
</p>

<pre>
import static org.easymock.EasyMock.*;	
</pre>	

<p>
    Ein Mock-Objekt für eine Schnittstelle kann dann mit einer Programmzeile dynamisch erzeugt
    werden:
</p>

<pre>
IWebSearch mock = createMock(IWebSearch.class);
</pre>

<p>
	Nach der Erzeugung des Mock-Objekts befindet es sich in der Aufnahmephase. Methodenaufrufe auf
	dem Mock-Objekt werden als Erwartungen interpretiert:
</p>

<pre>
mock.registerURL("http://www.fscklog.com");
</pre>

<p>
Rückgabewerte können mit einer einfachen Syntax definiert werden:
</p>			

<pre>
expect(mock.search("ruby rails")).andReturn("http://www.rubyonrails.org");
</pre>	

<p>
Ist das Mock-Objekt mit dem erwarteten Verhalten gefüttert, wird es in die Wiedergabephase
umgeschaltet:
</p>

<pre>
replay(mock);
</pre>

<p>Nun kann das Mock-Objekt im Test verwendet werden. Wird eine nicht erwartete Methode oder
eine Methode mit anderen Argumenten als den erwarteten aufgerufen, wird eine Exception geworfen.
Am Ende des Tests sollte noch geprüft werden, ob die erwarteten Methodenaufrufe tatsächlich
stattgefunden haben. Das erledigt der Aufruf
</p>    

<pre>
verify(mock);
</pre>

<h3>Komplexe Mock-Objekte</h3>

<p>Standardmäßig werden die Argumente der erwarteten Methodenaufrufe mit <em>equals()</em> verglichen.
Falls es gewünscht ist, können diese Bedingungen abgeschwächt werden. Auch die Anzahl der erlaubten
Aufrufe kann geändert werden. Das folgende Programmstück definiert die Erwartung, das die Methode
<code>search()</code> mindestens einmal mit einem Argument aufgerufen wird, das die Begriffe 
<em>ruby</em> und <em>rails</em> enthält. Für alle Aufrufe wird <em>http://www.rubyonrails.org</em>
zurückgegeben:
</p>

<pre>
expect(mock.search(and(contains("ruby"), contains("rails"))))
    .andReturn("http://www.rubyonrails.org").atLeastOnce();
</pre>

<h2>Weitere Informationen</h2>

<p>
Die Dokumentation von EasyMock demonstriert alle Möglichkeiten von EasyMock. Sie ist im 
Download enthalten und online verfügbar unter <a href="http://easymock.org/EasyMock2_0_Documentation.html">http://easymock.org/EasyMock2_0_Documentation.html</a>.
</p>

<p>
Im Buch <a href="http://www.amazon.de/exec/obidos/ASIN/3898642208">Testgetriebene Entwicklung mit JUnit &amp; FIT</a> beschreibt Frank Westphal
ausführlich, wie Mock-Objekte helfen können, Teile eines Systems im Unit Test isoliert zu testen.
</p>






  ]]>
  </description>
  <pubDate>Sat, 24 Dec 2005 15:00:00 +0100</pubDate>
  <guid>http://tammofreese.de/2005/12/24/easymock-2-0</guid>
  </item>

  </channel>
</rss>
