<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5321472228511037817</id><updated>2011-09-28T11:57:00.384-07:00</updated><category term='ruby'/><category term='linux'/><category term='gpg'/><category term='git svn'/><category term='ant'/><category term='scala'/><category term='eee'/><category term='java'/><category term='cygwin'/><category term='rsync'/><category term='quote'/><category term='mock'/><category term='maven'/><category term='oop'/><category term='regexp'/><category term='hal'/><category term='bash'/><category term='lift'/><category term='pdf'/><category term='iptable'/><category term='gnome'/><category term='tsp'/><category term='firefox'/><category term='test'/><category term='audio'/><category term='gradle'/><category term='android'/><category term='ivy'/><category term='groovy'/><category term='functional'/><category term='cruisecontrol'/><category term='script'/><category term='ci'/><category term='gimp'/><category term='design'/><category term='tdd'/><category term='network'/><category term='fun'/><category term='ubuntu'/><category term='mtp'/><category term='architecture'/><category term='exif'/><category term='hardware'/><category term='backup'/><category term='nautilus'/><title type='text'>My (Tech) User Stories</title><subtitle type='html'>&lt;p&gt;This tech blog does not claim to be interesting. It is only here to help me gather my ideas about subjects in which I'm interested.&lt;/p&gt;
&lt;i&gt;"Experience is not what happens to a man; it is what a man does with what happens to him."&lt;/i&gt; &lt;u&gt;Aldous Huxley&lt;/u&gt;</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>51</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3093392905404383111</id><published>2011-03-27T12:16:00.000-07:00</published><updated>2011-03-27T12:21:27.285-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gradle'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Tapestry conversion to Gradle</title><content type='html'>One more high profile open source project &lt;a href="http://tapestryjava.blogspot.com/2011/03/combining-gradle-with-antlr3.html"&gt;migrating&lt;/a&gt; from Maven to Gradle.&lt;br /&gt;&lt;br /&gt;Experience of &lt;a href="http://userstories.blogspot.com/2010/07/hibernate-build-infrastructure.html"&gt;Déjà vu&lt;/a&gt; ?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3093392905404383111?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3093392905404383111/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3093392905404383111' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3093392905404383111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3093392905404383111'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2011/03/tapestry-conversion-to-gradle.html' title='Tapestry conversion to Gradle'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-8967921370207339173</id><published>2010-12-30T10:47:00.000-08:00</published><updated>2010-12-30T10:49:39.088-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><title type='text'>Beyond Red/Green/Refactor</title><content type='html'>Michael Feathers' post &lt;a href="http://michaelfeathers.typepad.com/michael_feathers_blog/2010/12/making-too-much-of-tdd.html"&gt;Making Too Much Of TDD&lt;/a&gt;. A must read.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-8967921370207339173?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/8967921370207339173/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=8967921370207339173' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8967921370207339173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8967921370207339173'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2010/12/beyond-redgreenrefactor.html' title='Beyond Red/Green/Refactor'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-2062839043658534961</id><published>2010-11-08T13:25:00.000-08:00</published><updated>2010-11-08T13:29:38.684-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quote'/><title type='text'>Doubt</title><content type='html'>&lt;span style="font-style: italic;"&gt;"Someone's intelligence can be measured by the quantity of uncertainties that he can bear"&lt;/span&gt; &lt;u&gt;Immanuel Kant&lt;/u&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-2062839043658534961?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/2062839043658534961/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=2062839043658534961' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2062839043658534961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2062839043658534961'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2010/11/doubt.html' title='Doubt'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-7139849594430220173</id><published>2010-07-12T14:25:00.000-07:00</published><updated>2010-07-12T14:36:31.435-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lift'/><category scheme='http://www.blogger.com/atom/ns#' term='functional'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Java Posse Podcast #314 all about Scala</title><content type='html'>Once again, plenty of interesting things, this time &lt;a href="http://javaposse.com/java_posse_314_roundup_10_scala"&gt;all about Scala&lt;/a&gt; (not about Java)&lt;br /&gt;&lt;br /&gt;During the podcast several people said that it was a very bad idea to try to learn Scala using &lt;a href="http://liftweb.net/"&gt;Lift&lt;/a&gt; as a training material because it is way too functional for a beginner (in the&lt;a href="http://en.wikipedia.org/wiki/Functional_programming"&gt; functional programming&lt;/a&gt; acception of the term)&lt;br /&gt;&lt;br /&gt;Well, maybe I should try Lift now, to check if I begin to understand functional programming in general, and in especialy in Scala.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-7139849594430220173?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/7139849594430220173/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=7139849594430220173' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7139849594430220173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7139849594430220173'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2010/07/java-posse-podcast-314-all-about-scala.html' title='Java Posse Podcast #314 all about Scala'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-1832306656218912863</id><published>2010-07-12T14:06:00.000-07:00</published><updated>2010-07-13T04:34:48.678-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gradle'/><category scheme='http://www.blogger.com/atom/ns#' term='ivy'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Hibernate build infrastructure switching from Maven to Gradle</title><content type='html'>In the world of build tools this is big news : Steve Ebersole announced that &lt;a href="http://community.jboss.org/wiki/Gradlewhy"&gt;he wanted to migrate from Maven to Gradle&lt;/a&gt; for the Hibernate project. A &lt;a href="http://community.jboss.org/docs/DOC-13761"&gt;list of reasons&lt;/a&gt; is given for the switch (and I utterly understand what they mean).&lt;br /&gt;&lt;br /&gt;Well, it is a very good incentive to try Gradle (and really Groovy + Ivy is a very enticing combination).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-1832306656218912863?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/1832306656218912863/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=1832306656218912863' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1832306656218912863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1832306656218912863'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2010/07/hibernate-build-infrastructure.html' title='Hibernate build infrastructure switching from Maven to Gradle'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3739578443580259179</id><published>2010-06-19T14:04:00.000-07:00</published><updated>2010-06-19T14:10:21.424-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='audio'/><title type='text'>Convert M4A/AAC file to and from WAV file on Linux</title><content type='html'>May be useful to create M4A/ACC files for phone ringtones, use &lt;span style="font-style: italic;"&gt;faad&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;faac&lt;/span&gt; command line utilities :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;faad foo.aac&lt;/span&gt; creates a foo.wav file&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;faac foo.wav&lt;/span&gt; create a foo.acc file&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3739578443580259179?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3739578443580259179/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3739578443580259179' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3739578443580259179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3739578443580259179'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2010/06/convert-m4aaac-file-to-and-from-wav.html' title='Convert M4A/AAC file to and from WAV file on Linux'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-5292379125500427832</id><published>2010-06-04T14:11:00.000-07:00</published><updated>2010-06-22T16:15:50.057-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android'/><title type='text'>Writing zippy Android apps Google IO Video</title><content type='html'>A Must-see presentation for development on Android :  &lt;a href="http://code.google.com/intl/fr/events/io/2010/sessions/writing-zippy-android-apps.html"&gt;Writing zippy Android apps&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I really liked the part where Brad Fitzpatrick explains what the difference is between an &lt;span style="font-weight: bold;"&gt;AsyncTask&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;IntentService&lt;/span&gt; :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://developer.android.com/reference/android/os/AsyncTask.html"&gt;AsyncTask&lt;/a&gt; :  for non blocking "fire and forget" operations outside of the UI Thread  (the OS may kill the AsyncTask upon certain conditions and the task may not be able to finish its job)&lt;/li&gt;&lt;li&gt;&lt;a href="http://developer.android.com/reference/android/app/IntentService.html"&gt;IntentService&lt;/a&gt; : use for non blocking operations that must not be interrupted outside of the UI Thread&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-5292379125500427832?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/5292379125500427832/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=5292379125500427832' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/5292379125500427832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/5292379125500427832'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2010/06/writing-zippy-android-apps-google-io.html' title='Writing zippy Android apps Google IO Video'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-8018596681443955409</id><published>2010-05-25T14:23:00.000-07:00</published><updated>2010-05-27T00:05:34.113-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Checked exceptions : really, the debat is not over</title><content type='html'>Whao, I knew that &lt;a href="http://userstories.blogspot.com/2008/12/checked-exception-why-debat-is-not-over.html"&gt;it was not over&lt;/a&gt;, but I did not suspect how much.&lt;br /&gt;&lt;br /&gt;And even though I disagree with the conclusion of his article, we can be thankful to &lt;span style="font-style: italic;"&gt;Elliotte Rusty Harold&lt;/span&gt; for having provided us with &lt;a href="http://cafe.elharo.com/programming/bruce-eckel-is-wrong/"&gt;a thorough thread of discussion about &lt;span style="font-style: italic;"&gt;checked vs unchecked exception&lt;/span&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;(By the way, did I mentioned that I did not like checked exceptions ?)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-8018596681443955409?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/8018596681443955409/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=8018596681443955409' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8018596681443955409'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8018596681443955409'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2010/05/checked-exceptions-really-debat-is-not.html' title='Checked exceptions : really, the debat is not over'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-6487008617636679523</id><published>2009-12-27T14:30:00.000-08:00</published><updated>2009-12-29T14:49:50.454-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><category scheme='http://www.blogger.com/atom/ns#' term='oop'/><title type='text'>Barbara Liskov : The Power of Abstraction</title><content type='html'>Great, great &lt;a href="http://www.infoq.com/presentations/liskov-power-of-abstraction"&gt;talk&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ho ! By the way, didn't she sort of tell that Checked Exceptions was not a good idea ? (at around 37:30 during the talk). And it was known 30 years ago...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-6487008617636679523?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/6487008617636679523/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=6487008617636679523' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6487008617636679523'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6487008617636679523'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/12/barbara-liskov-power-of-abstraction.html' title='Barbara Liskov : The Power of Abstraction'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-7866352340686414987</id><published>2009-12-06T13:36:00.000-08:00</published><updated>2009-12-06T14:24:41.561-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hal'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Manages Linux devices with HAL</title><content type='html'>HAL, the &lt;a href="http://www.freedesktop.org/wiki/Software/hal"&gt;Hardware Abstraction Layer&lt;/a&gt; of most recent Linux distros really is an impressive piece of architecture.&lt;br /&gt;&lt;br /&gt;I'm impressed by how easy it is to take control of any low level parameter of any hardware.&lt;br /&gt;&lt;br /&gt;Two example I've recently used :&lt;br /&gt;&lt;br /&gt;A workaround to prevent nautilus in Ubuntu 9.04 from automatically mounting my MTP player :&lt;br /&gt;I've added a &lt;span style="font-style: italic;"&gt;no-automount.fdi&lt;/span&gt; file in directory &lt;span style="font-style: italic;"&gt;/etc/hal/fdi/policy&lt;/span&gt;&lt;textarea name="code" class="xml:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;!-- -*- SGML -*- --&gt;&lt;br /&gt;&lt;deviceinfo version="0.2"&gt;&lt;br /&gt; &lt;device&gt;&lt;br /&gt;  &lt;match key="info.product" string="ZEN V Plus"&gt;&lt;br /&gt;    &lt;remove key="info.capabilities" type="strlist"&gt;portable_audio_player&lt;/remove&gt;&lt;br /&gt;  &lt;/match&gt;&lt;br /&gt; &lt;/device&gt;&lt;br /&gt;&lt;/deviceinfo&gt;&lt;br /&gt;&lt;/textarea&gt;(See my answer "stephg  said on 2009-11-09" in &lt;a href="https://answers.launchpad.net/ubuntu/+question/69910"&gt;this post&lt;/a&gt; for more details)&lt;br /&gt;&lt;br /&gt;An other example to slow down the cursor speed of my high resolution mouse. As the xorg configuration file is not used anymore in Ubuntu, the X11 options can be set with a HAL configuration file. &lt;br /&gt;I've added a &lt;span style="font-style: italic;"&gt;10-x11-input.fdi&lt;/span&gt; file in directory &lt;span style="font-style: italic;"&gt;/etc/hal/fdi/policy&lt;/span&gt; : &lt;textarea name="code" class="xml:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;!-- -*- SGML -*- --&gt;&lt;br /&gt;&lt;deviceinfo version="0.2"&gt;&lt;br /&gt; &lt;device&gt;&lt;br /&gt;  &lt;match key="info.product" string="Logitech USB Laser Mouse"&gt;&lt;br /&gt;    &lt;merge key="input.x11_options.ConstantDeceleration" type="string"&gt;2&lt;/merge&gt;&lt;br /&gt;  &lt;/match&gt;&lt;br /&gt; &lt;/device&gt;&lt;br /&gt;&lt;/deviceinfo&gt;&lt;br /&gt;&lt;/textarea&gt;This "merge" scheme really is brilliant because it allows to go back to a default configuration by &lt;span style="font-style: italic;"&gt;deleting&lt;/span&gt; a file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-7866352340686414987?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/7866352340686414987/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=7866352340686414987' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7866352340686414987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7866352340686414987'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/12/manages-linux-devices-with-hal.html' title='Manages Linux devices with HAL'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-7509901686377152669</id><published>2009-11-25T13:45:00.000-08:00</published><updated>2009-11-25T13:47:54.191-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>My "Hello World" Scala script</title><content type='html'>My first Scala project, a simple Scala script to download podcasts and sync with an Mp3 player : &lt;a href="http://github.com/sgalles/jonction"&gt;Jonction&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-7509901686377152669?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/7509901686377152669/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=7509901686377152669' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7509901686377152669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7509901686377152669'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/11/my-hello-world-scala-script.html' title='My &quot;Hello World&quot; Scala script'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-9071503677132921016</id><published>2009-10-25T14:53:00.000-07:00</published><updated>2009-10-25T15:00:37.901-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='mtp'/><title type='text'>Delete all tracks from MTP device</title><content type='html'>My MTP MP3 player does not allow to delete all albums with one command (yes I really wanted to delete all tracks ! )&lt;br /&gt;&lt;br /&gt;Bash to the rescue :&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;mtp-tracks | grep 'Track ID' | awk '{print $3}' | xargs mtp-delfile -n&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Of course you must install the mtp-tools, on Ubuntu &lt;span style="font-style: italic;"&gt;:&lt;br /&gt;sudo apt-get install mtp-tools&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-9071503677132921016?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/9071503677132921016/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=9071503677132921016' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/9071503677132921016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/9071503677132921016'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/10/delete-all-tracks-from-mtp-device.html' title='Delete all tracks from MTP device'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-8205051212356202972</id><published>2009-10-06T21:14:00.000-07:00</published><updated>2009-10-06T21:18:31.054-07:00</updated><title type='text'>Cloned Git repository in DropBox folder</title><content type='html'>Nice and easy way to setup a Git server : &lt;a href="http://www.cimgf.com/2008/06/03/version-control-makes-you-a-better-programmer/"&gt;a cloned git repository in a DropBox folder&lt;/a&gt;. This is brilliant and It works very well (see the &lt;span style="font-style: italic;"&gt;Collaboration&lt;/span&gt; part of the article).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-8205051212356202972?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/8205051212356202972/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=8205051212356202972' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8205051212356202972'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8205051212356202972'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/10/cloned-git-repository-in-dropbox-folder.html' title='Cloned Git repository in DropBox folder'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3040133661839697008</id><published>2009-10-03T14:22:00.000-07:00</published><updated>2009-10-04T01:43:03.019-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mtp'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>File parsing in Scala with Pattern Matching and Tail-Call optimization</title><content type='html'>In the process of learning Scala, I had to find a subject for my very first "Non Hello World" program in Scala. It was a good opportunity to code a script I wanted to write a long time ago in order to automatize the sync of the podcasts I download for my Mp3 player (I have very specific use cases that are not addressed by the usual podcast downloader software).&lt;br /&gt;&lt;br /&gt;As my player is an MTP player,the script launches some &lt;span style="font-style: italic;"&gt;mtp-tools&lt;/span&gt; command line utilities of &lt;a href="http://libmtp.sourceforge.net/"&gt;libmtp&lt;/a&gt; to drive the player.&lt;br /&gt;&lt;br /&gt;In particular the &lt;span style="font-style: italic;"&gt;mtp-tracks&lt;/span&gt; command lists some information for the tracks stored into the player. The output of the command looks like :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Attempting to connect device(s)&lt;br /&gt;mtp-tracks: Successfully connected&lt;br /&gt;Friendly name: My Zen&lt;br /&gt;Track ID: 40337&lt;br /&gt;Title: Java Posse #273 - Roundup 09 - Managing Technical Debt&lt;br /&gt;Artist: Tor Norbye, Carl Quinn, Joe Nuxoll, Dick Wall&lt;br /&gt;Genre: Podcast&lt;br /&gt;Album: The Java Posse&lt;br /&gt;Origfilename: Tor Norbye, Carl Quinn, Joe Nuxoll, Dick Wall-Java Posse #273 - Roundup 09 - Managing Technical Debt.mp3&lt;br /&gt;Track number: 0&lt;br /&gt;Duration: 3809000 milliseconds&lt;br /&gt;File size 45766104 bytes&lt;br /&gt;Filetype: ISO MPEG-1 Audio Layer 3&lt;br /&gt;Use count: 9 times&lt;br /&gt;Track ID: 41737&lt;br /&gt;Title: Java Posse #272 - Newscast for July 30th 2009&lt;br /&gt;Artist: Tor Norbye, Carl Quinn, Joe Nuxoll, Dick Wall&lt;br /&gt;Genre: Podcast&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;So basically, in order to parse the file I must skip the header (there's a footer too) and find each track information. It looks like a job for Pattern Matching on list with a mix of RegExp matching. And as he goal is also to learn Scala (And improve my Functional Programming skills) I wanted to code the parsing without any mutable object (yes, I'm trying to follow the rules described in &lt;a href="http://www.artima.com/shop/programming_in_scala"&gt;Programming in Scala&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;So here is the part of the code that parses the &lt;span style="font-style: italic;"&gt;mtp-tracks&lt;/span&gt; output :&lt;br /&gt;&lt;textarea name="code" class="ruby:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;//let's define some RegExp&lt;br /&gt;Expval TrackId = """Track ID: ([0-9]+)""".r&lt;br /&gt;val Title = """ *Title: (.+)""".r&lt;br /&gt;val Album = """ *Album: (.+)""".r&lt;br /&gt;val Filename = """ *Origfilename: (.+)""".r&lt;br /&gt; &lt;br /&gt;//The recursive function&lt;br /&gt;def collect(lines: List[String],tracks: List[MtpTrack]): List[MtpTrack] = {&lt;br /&gt;  lines match{&lt;br /&gt;   case TrackId(id) :: Title(title) :: _ :: _:: Album(album) :: Filename(filename) :: rest =&gt; &lt;br /&gt;      collect(rest,new MtpTrack(id,album,title,filename) :: tracks)&lt;br /&gt;   case _ :: rest =&gt; collect(rest,tracks)&lt;br /&gt;   case Nil =&gt; tracks&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;The list of lines of the &lt;span style="font-style: italic;"&gt;mtp-tracks&lt;/span&gt; command output are stored in the &lt;span style="font-style: italic;"&gt;lines&lt;/span&gt; list. This recursive function can then be called with &lt;span style="font-style: italic;"&gt;collect(lines,Nil)&lt;/span&gt;, it returns a list of parsed MtpTrack object.&lt;br /&gt;&lt;br /&gt;To explain the three &lt;span style="font-style: italic;"&gt;pattern matching cases&lt;/span&gt; of the &lt;span style="font-style: italic;"&gt;collect&lt;/span&gt; function, I must first explain how pattern matching works for regexp.&lt;br /&gt;&lt;br /&gt;In this code, four matchers are defined for four regexp : &lt;span style="font-style: italic;"&gt;TrackId, Title, Album and Filename&lt;/span&gt;. For instance, if one line matches on the expression &lt;span style="font-style: italic;"&gt;Track(id)&lt;/span&gt; the first group of the regexp is captures in the &lt;span style="font-style: italic;"&gt;id&lt;/span&gt; variable.&lt;br /&gt;&lt;br /&gt;So, now, the description of the three cases after &lt;span style="font-weight: bold;"&gt;lines match&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;&lt;span style="font-weight: bold;" class="keyword"&gt;case&lt;/span&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt; TrackId(id) :: Title(title) :: _ :: _:: Album(album) :: Filename(filename) :: rest &lt;/span&gt;&lt;br /&gt;this one says "match on&lt;span style="font-style: italic;"&gt; list&lt;/span&gt; if &lt;span style="font-style: italic;"&gt;list&lt;/span&gt; first element matches the regexp TrackId, then regexp Title, then whatever twice, then regexp Album, then regexp Filename. And also, if it matches extract the rest of the list in the &lt;span style="font-weight: bold;"&gt;rest&lt;/span&gt; variable. Also, capture all regexp groups in the declared variables &lt;span style="font-weight: bold;"&gt;id,title,album and filename&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;. Rather powerfull.&lt;br /&gt;Then, with the matched information we create an MtpTrack object that is added to &lt;span style="font-style: italic;"&gt;tracks&lt;/span&gt;, the already collected list of MtpTrack (actually a new list is created as a List in Scala is immutable).&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;case _ :: rest&lt;/span&gt;&lt;br /&gt;this one is tried when the previous case does not match. It says "if &lt;span style="font-style: italic;"&gt;list&lt;/span&gt; starts with any element, extract the rest of the list after this element in a &lt;span style="font-weight: bold;"&gt;rest&lt;/span&gt; variable". &lt;span style="font-style: italic;"&gt;rest&lt;/span&gt; may also by empty.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;case Nil&lt;/span&gt;&lt;br /&gt;this one is the stop clause for the recursion, it matches when the list is empty.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;Thanks to the power of pattern matching composition (RexgExp in list), header and footer are automatically ignored. And as the function ends with a direct recursive call, the Scala compiler has compiled the code with &lt;a href="http://en.wikipedia.org/wiki/Tail_call"&gt;tail-call optimisation&lt;/a&gt;, so not stack overflow, even with very large command output.&lt;br /&gt;&lt;br /&gt;I must say that I was amazed by how much can be expressed in Scala with so little lines of code. And as I'm a beginner in Scala, there are surely even smaller and idiomatic ways to code the same functionnality.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3040133661839697008?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3040133661839697008/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3040133661839697008' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3040133661839697008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3040133661839697008'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/10/file-parsing-in-scala-with-pattern.html' title='File parsing in Scala with Pattern Matching and Tail-Call optimization'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-789264016167122665</id><published>2009-08-30T05:07:00.001-07:00</published><updated>2009-09-20T13:06:33.785-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='backup'/><category scheme='http://www.blogger.com/atom/ns#' term='rsync'/><category scheme='http://www.blogger.com/atom/ns#' term='cygwin'/><title type='text'>Backup with rsync and cygwin on Windows</title><content type='html'>I recently had to setup an automatic backup scheme for a desktop Windows PC used for office tasks. I usually try to avoid complicated backup software that tend to store the backups in a proprietary format. I want to be able to browse the files in the backup, and copy one file to recover the content.&lt;br /&gt;&lt;br /&gt;On UNIX/Linux I use rsync and thanks to Cygwin I was able to do the same thing on Windows. The goal for the script was :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Must backup two directories into one directory located on an USB key drive&lt;/li&gt;&lt;li&gt;Must not delete any file in the backup&lt;/li&gt;&lt;li&gt;Must always keep the old versions of any updated file&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Here is a bash script that backups two directories &lt;span style="font-style: italic;"&gt;foo1&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;foo2&lt;/span&gt;&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;SRCDIR="/cygdrive/c/links/foo1 /cygdrive/c/links/foo2"&lt;br /&gt;DESTDIR="/cygdrive/J/backupdir"&lt;br /&gt;&lt;br /&gt;OLDBDIR="old."`date +%F`&lt;br /&gt;&lt;br /&gt;rsync -avb  --backup-dir=$OLDBDIR --modify-window=2 $SRCDIR $DESTDIR&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;links&lt;/span&gt; actually is a symlink to the directory that contains &lt;span style="font-style: italic;"&gt;foo1&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;foo2. &lt;/span&gt;I created this symlink with the &lt;span style="font-style: italic;"&gt;ln -s&lt;/span&gt; command as the  path to the parent directory of &lt;span style="font-style: italic;"&gt;foo1&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;foo2&lt;/span&gt; had spaces and I didn't want to deal with path escaping issues.&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;--modify-window=2&lt;/span&gt; is necessary on windows, else the whole set of file is saved all the time.&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;--delete&lt;/span&gt; can be added to the rsync command if we want the deleted files to be removed from the backup&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Then a .bat file is created in order to run the script with a double click :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;set PATH=%PATH%;/cygdrive/c/cygwin/bin&lt;br /&gt;bash.exe backup.sh&lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-789264016167122665?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/789264016167122665/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=789264016167122665' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/789264016167122665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/789264016167122665'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/08/backup-with-rsync-and-cygwin-on-windows.html' title='Backup with rsync and cygwin on Windows'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3870867738074876664</id><published>2009-08-18T14:19:00.000-07:00</published><updated>2009-12-07T00:28:18.454-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Test Driven Wall Tiling</title><content type='html'>Last week I was tiling a kitchen backsplash and countertop and as I was using the usual tile alignment tricks, I was struck by the parallel with the Test-driven development techniques for application development.&lt;br /&gt;&lt;br /&gt;This picture shows the wooden cleat (the dark brown area on the picture) that was temporarily screwed to the wall to obtain a perfect alignment for the second row of tiles above the countertop.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Jvq2hRnIVmc/SosiQ3c-VCI/AAAAAAAAACk/PpuacHOa2Rw/s1600-h/Photo006.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_Jvq2hRnIVmc/SosiQ3c-VCI/AAAAAAAAACk/PpuacHOa2Rw/s320/Photo006.jpg" alt="" id="BLOGGER_PHOTO_ID_5371424653728175138" border="0" /&gt;&lt;/a&gt;It made me think of TDD because :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The screwed wooden cleat is a temporary built artifact that has nothing to do with what we want to construct (that's to say, a tiled backsplash), but we built it nonetheless, and with care, as it's going to direct the alignment of the tile. &lt;span style="font-style: italic;"&gt;That's what we do with unit test, they are a programs designed to unit test other programs.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The wooden cleat is attached to the wall using the same tools and technique that are used to build the main "noble" artifacts (wood, screw, you name it).&lt;span style="font-style: italic;"&gt; A unit test is build with the same skills than the tested program&lt;/span&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;When the cleat is fixed we do not have to think anymore about the tile alignment. The alignment is forced by the wooden rule, we can forget about it and concentrate on the quality of the tiling. &lt;span style="font-style: italic;"&gt;The unit test creates design or behavior constraints on the code that is written. You can then forget about these constraints (you'll get a red test anyway if you don't respect these constraints)&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The wooden cleat is set at the beginning of the tiling for the row, you do not verify the alignment at the end. &lt;span style="font-style: italic;"&gt;Test first&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Nobody told you to screw a wooden cleat on the wall. Its an act of craftsmanship. We do it because we think that it will improve the quality of the tiling. &lt;span style="font-style: italic;"&gt;You have to write unit tests, even when nobody tells you to do so.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Sometimes, it is not perfectly clear where the tiling must start, so you just set the tiles on the countertop without adhesive, and then you know where the wooden cleat must be fixed. &lt;span style="font-style: italic;"&gt;Can be seen as exploratory testing.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;I need the second row to be perfectly aligned since the first row above the contertop can not be aligned on the countertop itself (tiles thickness may vary). &lt;span style="font-style: italic;"&gt;When parts of a system have good unit tests, the integration of these parts is easier.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;So why do we still see large software systems that are written without any sort of unit tests whereas this seems to be a widespread technique for building physical artifacts (tiling, walls, etc...) ?&lt;br /&gt;&lt;br /&gt;I think it is because programming is like tiling without adhesive. You may always be able to move the tiles. But even though we may try to align all the tiles at the end of the project, we would not completely succeed, and the quality of the alignment would suffer.&lt;br /&gt;&lt;br /&gt;We should rather use all the available engineering techniques during the life of the project in order to be sure that the alignment is perfect, all the time. From start to end.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3870867738074876664?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3870867738074876664/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3870867738074876664' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3870867738074876664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3870867738074876664'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/08/test-driven-wall-tiling.html' title='Test Driven Wall Tiling'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Jvq2hRnIVmc/SosiQ3c-VCI/AAAAAAAAACk/PpuacHOa2Rw/s72-c/Photo006.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-1756958943913418734</id><published>2009-05-13T14:06:00.000-07:00</published><updated>2009-09-20T13:07:48.269-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>OPML import for Rhythmbox, kind of</title><content type='html'>&lt;i&gt;&lt;b&gt;Update&lt;/b&gt; : it seems that Rhythmbox now has support for OPML import, you may type the path to the OPML import in the new feed dialog box. I didn't try it, though&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I've just installed the brand new Ubuntu 9.04 to replace my good old 6.06, and now I must find a way to import all my podcast feeds exported in opml format from Juice, to Rhythmbox, the official Ubuntu music manager.&lt;br /&gt;&lt;br /&gt;However, it seems that Rhythmbox does not manage opml imports. I don't feel like copying manually 70 rss feeds...&lt;br /&gt;&lt;br /&gt;An opportunity to learn the XML library of Groovy !&lt;br /&gt;&lt;br /&gt;Save this groovy script as &lt;span style="font-style: italic;"&gt;opml2rhythmbox.groovy&lt;/span&gt;&lt;br /&gt;&lt;textarea name="code" class="ruby:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;#!/usr/bin/groovy&lt;br /&gt;&lt;br /&gt;if(args.size() != 1){&lt;br /&gt;    println("Usage : opml2rhythmbox.groovy myfile.opml")&lt;br /&gt;    System.exit 1&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;File opmlFile = new File(args[0])&lt;br /&gt;def opml = new XmlSlurper().parse(opmlFile)&lt;br /&gt;&lt;br /&gt;def builder = new groovy.xml.MarkupBuilder()&lt;br /&gt;opml.body.depthFirst().findAll { it.@type == "rss"}.each { rss -&gt;&lt;br /&gt;    builder.entry(type:'podcast-feed') {&lt;br /&gt; title rss.@title.text()&lt;br /&gt;        location rss.@xmlUrl.text()&lt;br /&gt;        status '1'&lt;br /&gt;    } &lt;br /&gt;}&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Then if it is not already done, install Groovy&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;sudo apt-get install groovy&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Execute the script on your exported opml file&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;chmod +x opml2rhythmbox.groovy&lt;br /&gt;./opml2rhythmbox.groovy exported.opml &gt; generated.xml&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Now, you must manually copy the content of the generated &lt;span style="font-style: italic;"&gt;generated.xml&lt;/span&gt; file, into the &lt;span style="font-style: italic;"&gt;~/local/share/rhythmbox/rhythmdb.xml&lt;/span&gt; db file of Rhythmbox &lt;span style="font-weight: bold;"&gt;(make a backup of the rhythmdb.xml file before !)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The generated file content must be inserted after the rhythmdb opening tag at the begining of the rhythmdb.xml file&lt;br /&gt;&lt;textarea name="code" class="xml:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;&lt;?xml version="1.0" standalone="yes"?&gt;&lt;br /&gt;&lt;rhythmdb version="1.6"&gt;&lt;br /&gt;&lt;!-- Insert here  --&gt;&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Then start Rhythmbox and run &lt;span style="font-style: italic;"&gt;"Update all Feeds"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You're done !&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-1756958943913418734?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/1756958943913418734/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=1756958943913418734' title='1 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1756958943913418734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1756958943913418734'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/05/opml-import-for-rhythmbox-kind-of.html' title='OPML import for Rhythmbox, kind of'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3867556791097880184</id><published>2009-01-24T07:03:00.000-08:00</published><updated>2009-01-24T07:14:15.654-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='oop'/><title type='text'>OOP vs Procedural : a definition</title><content type='html'>In my opinion one of the best definition :&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Procedural code gets information then makes decisions.  Object-oriented code tells objects to do things.&lt;/i&gt; &lt;u&gt;Alec Sharp&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;That's an explanation for the&lt;a href="http://tech.groups.yahoo.com/group/testdrivendevelopment/message/29713"&gt; Tell, don't ask&lt;/a&gt; advice for OOP.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3867556791097880184?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3867556791097880184/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3867556791097880184' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3867556791097880184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3867556791097880184'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2009/01/oop-vs-procedural-definition.html' title='OOP vs Procedural : a definition'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-7353755144237247852</id><published>2008-12-24T00:18:00.000-08:00</published><updated>2010-10-22T23:34:56.149-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Checked exception : why the debat is not over for everyone</title><content type='html'>I've just read &lt;a href="http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882"&gt;Clean Code&lt;/a&gt; by Bob Martin.&lt;br /&gt;&lt;br /&gt;In this book uncle Bob says :&lt;br /&gt;&lt;i&gt;"The debate is over [...] At the time, we thought that checked exceptions were a great idea; and yes, they can yield some benefit. However, it is clear now that they aren’t necessary for the production of robust software"&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I do agree, but as I was reading these lines I was wondering why, for plenty of people, checked exception is still the way to go when writing a java app.&lt;br /&gt;&lt;br /&gt;Here is my humble hypothesis.&lt;br /&gt;&lt;br /&gt;First, I need two acronyms :&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;SLJA&lt;/i&gt; : "Significantly Large JAVA Application". let's say a 500+ java classes application with at least two layers.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;BTDT&lt;/i&gt; : "Been There, Done That"&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;So, in the checked vs unchecked exception debate I think that we may find at least 5 categories of contestant :&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;Category 1&lt;/i&gt; : programmers who have never worked on any SLJA and just didn't experience what the real difficulties are with checked exceptions, so these programmers think that checked exceptions must be systematically used. BTDT (remember my second acronym ?)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;Category 2&lt;/i&gt; : programmers who have already worked on one or two SLJA and who also think that checked exceptions must be systematically used. Still they wonder why they need to use so many try/catch blocks and so many throws directives when the number of classes increases and the layers stack. The bigger the SLJA the more painful it becomes. BTDT.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;Category 3a&lt;/i&gt; : programmers who have aleady worked on some SLJA and who know that exception hierarchy in an application must be thoroughly designed in order to keep the verbosity of the try/catch/throws at a manageable level and to keep a coherence in the abstraction levels.They are sure that checked exception must be used, granted that a &lt;i&gt;MyApplicationException&lt;/i&gt; is used as the top most exception in the exception hierarchy of their application. They use &lt;i&gt;"throws MyApplicationException"&lt;/i&gt; in every methods, to please the compiler and allow most of their exceptions to bubble up. Most of their try/catch/finally became try/finally as they don't need to rethrow most exceptions anymore, thanks to the "throws MyApplicationException" directive in almost all methods signature. BTDT.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;Category 3b&lt;/i&gt; : programmers who have aleady worked on some SLJA and who are fed up with the &lt;i&gt;MyApplication&lt;/i&gt; exception trick. They understand that's only a symptom of the fact that in any SLJA most exceptions must bubble up and unchecked exceptions must be used instead of the &lt;i&gt;"throws MyApplicationException"&lt;/i&gt; everywhere. Still, they think that checked exceptions could be useful sometimes for the recoverable errors. BTDT.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;i&gt;Category 3c&lt;/i&gt; : programmers who have aleady worked on some SLJA. They never use checked exceptions anymore. they have a few unchecked exceptions at the top most level of their exception hierarchy. They declare as many unchecked exceptions as necessary in the packages of the classes that might need to throw specific exceptions. As soon as they have to work with an API that throws checked exceptions, they wrap them with unchecked exceptions.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;So, actually, the debate is only over for category 3c (I consider that I'm in 3c now).&lt;br /&gt;&lt;br /&gt;It's interesting to understand how you move &lt;i&gt;from 3b to 3c&lt;/i&gt; (You're in 3b because you think that checked exception might sometimes be used for recoverable errors).&lt;br /&gt;&lt;br /&gt;Let's say you're in 3b and one day you have to design a factory method that parses a string for a credit card number and transform it into a CreditCardNumber object.&lt;br /&gt;&lt;br /&gt;The first client of this factory method is the GUI layer, so you think : "hey, the user may have made a mistake when entering the credit card number, it is a recoverable error because the GUI must warn him and retry, so I'm going to use a checked exception". So your factory method throws a BadCreditCardNumberException checked exception that inherit from a more general BadFormatException checked exception.&lt;br /&gt;&lt;br /&gt;Two weeks later you have to store the credit card number as a string into a database, and when you reread the record, you must transform the string back into a CreditCardNumber object. So you obviously reuse the factory method that was created. But now you have to catch the BadCreditCardNumber exception in your DAO, and you don't want to do that, because it is really not a recoverable error in this layer. It is a fatal error (data corruption ?) and you just want the application to bubble up and hit the fault barrier of your application as a BadFormatException.&lt;br /&gt;&lt;br /&gt;That's the exact moment you move from 3b to 3c because you see that an error that's recoverable in a layer may not be recoverable in an other layer and you really don't want to be forced to artificially chain a checked exception that is not recoverable. You prefer to choose to catch an unchecked exception when you think that it is recoverable for you in your layer.&lt;br /&gt;&lt;br /&gt;When you choose to use checked exception for errors that you think are recoverable, you try to decide for the client of you API, whereas only he may know what's recoverable and what isn't in his own context.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-weight: bold;"&gt;Update&lt;/span&gt; : Everething was already &lt;a href="http://www.artima.com/intv/handcuffs.html"&gt;there&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-7353755144237247852?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/7353755144237247852/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=7353755144237247852' title='3 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7353755144237247852'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7353755144237247852'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/12/checked-exception-why-debat-is-not-over.html' title='Checked exception : why the debat is not over for everyone'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-2804083482476450908</id><published>2008-12-23T14:12:00.000-08:00</published><updated>2008-12-23T14:41:30.629-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='regexp'/><title type='text'>How to negate a regexp with a negative lookahead</title><content type='html'>When I need it, I tend to forget how to create a regular expression that matches a large set of strings but does not match some specific strings. It's tricky because it involves the negative lookahead syntax.&lt;br /&gt;&lt;br /&gt;For exemple the regexp&lt;br /&gt;&lt;br /&gt;&lt;b&gt;.+\.java&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;matches any java filename.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;But the following regexp&lt;br /&gt;&lt;br /&gt;&lt;b&gt;(?!Foo\.java).+\.java&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;matches any java filename except &lt;i&gt;Foo.java&lt;/i&gt; because the negative lookahead of &lt;i&gt;Foo\.java&lt;/i&gt; will prevent &lt;i&gt;.+\.java&lt;/i&gt; from matching.&lt;br /&gt;&lt;br /&gt;The negative lookahead syntax is&lt;br /&gt;&lt;b&gt;(?!X)&lt;/b&gt; where X is the string that must not be matched.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-2804083482476450908?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/2804083482476450908/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=2804083482476450908' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2804083482476450908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2804083482476450908'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/12/how-to-negate-regexp-with-negative.html' title='How to negate a regexp with a negative lookahead'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3705617041259285578</id><published>2008-11-09T13:44:00.000-08:00</published><updated>2009-08-31T01:19:17.022-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>Sepia GFA Basic code</title><content type='html'>Internet is such a strange place. I've found by chance some lines of GFA Basic code I wrote something like 20 years ago.&lt;br /&gt;&lt;br /&gt;Actually in the late 80s the French magazine &lt;span style="font-weight: bold;"&gt;ST Mag&lt;/span&gt; used to publish short GFA Basic programs written by the readers. These were called &lt;span style="font-style: italic;"&gt;GFA Punch&lt;/span&gt;. They had to have at most 20 lines of code, and had to do something funny or useful. Some GFA Punch I had written were published. Someone kept a floppy disk of a bunch of GFA Punch that were published and made it available.&lt;a href="http://www.chez.com/freddo/GfaBasic/GfaBasic.html"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now, among all these programs I've found some of my old GFA Punch. For me its like finding an old sepia picture in a box. It's weird. I've decided to post two of these programs just for archeological reason. There isn't any technological interest as the quality of the code is poor.&lt;br /&gt;&lt;br /&gt;This one draw colored frames :&lt;br /&gt;&lt;br /&gt;&lt;textarea name="code" class="ruby:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;BOX 0,0,319,199&lt;br /&gt;DIM b(1,3)&lt;br /&gt;READ b(0,0),b(0,1),b(1,0),b(1,1),b(0,2),b(0,3),b(1,2),b(1,3)&lt;br /&gt;DATA 30,30,40,21,1,1,1,1&lt;br /&gt;COLOR 11&lt;br /&gt;DO&lt;br /&gt;  FOR t=0 TO 1&lt;br /&gt;    ADD b(t,0),b(t,2)&lt;br /&gt;    ADD b(t,1),b(t,3)&lt;br /&gt;    IF POINT(b(t,0)+b(t,2),b(t,1))=1&lt;br /&gt;      COLOR RANDOM(13)+2&lt;br /&gt;      MUL b(t,2),-1&lt;br /&gt;    ENDIF&lt;br /&gt;    IF POINT(b(t,0),b(t,1)+b(t,3))=1&lt;br /&gt;      COLOR RANDOM(13)+2&lt;br /&gt;      MUL b(t,3),-1&lt;br /&gt;    ENDIF&lt;br /&gt;    LINE b(0,0),b(0,1),b(1,0),b(1,1)&lt;br /&gt;  NEXT t&lt;br /&gt;LOOP&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;This one draw a big ugly and slow scroll text :&lt;br /&gt;&lt;br /&gt;&lt;textarea name="code" class="ruby:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;Dim Big$(20)&lt;br /&gt;For T%=1 To 20&lt;br /&gt;  Print Mid$("SCROLLING, TRES LENT!",T%,1);Spc(10);"VEUILLEZ PATIENTER"&lt;br /&gt;  For Y%=0 To 13          ! Monochrome: 13&lt;br /&gt;    For X%=0 To 8&lt;br /&gt;      If Point(X%,Y%)&lt;&gt;0&lt;br /&gt;        Pcircle X%*10+5,Y%*10+5,5&lt;br /&gt;      Endif&lt;br /&gt;    Next X%&lt;br /&gt;  Next Y%&lt;br /&gt;  Get 9,0,71,80,Big$(T%)  ! A modifier aussi pour le monochrome&lt;br /&gt;  Cls&lt;br /&gt;Next T%&lt;br /&gt;For T%=1 To 20&lt;br /&gt;  Put 240,50,Big$(T%)&lt;br /&gt;  Get 0,50,319,130,A$&lt;br /&gt;  For R%=0 Downto -70&lt;br /&gt;    Put R%,50,A$&lt;br /&gt;  Next R%&lt;br /&gt;Next T%&lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3705617041259285578?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3705617041259285578/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3705617041259285578' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3705617041259285578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3705617041259285578'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/11/sepia-gfa-basic-code.html' title='Sepia GFA Basic code'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-394555169971815942</id><published>2008-11-06T14:30:00.000-08:00</published><updated>2008-11-06T14:43:54.257-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>TDD and tomatoes</title><content type='html'>&lt;i&gt;Tomatoes are like tests, it is easier to make a tomato go from green to red, than from red to green.&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-394555169971815942?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/394555169971815942/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=394555169971815942' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/394555169971815942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/394555169971815942'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/11/tdd-and-tomatoes.html' title='TDD and tomatoes'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-2949191103078675611</id><published>2008-10-14T15:03:00.000-07:00</published><updated>2008-10-17T14:53:05.458-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='groovy'/><title type='text'>Sanitize dates in podcast title with groovy + jid3lib</title><content type='html'>When I sort the podcasts I download by their ID3 titles, I want to see an older episode sorted after a newer episode.&lt;br /&gt;&lt;br /&gt;Here is a groovy script that modifies the MP3 title of an MP3 file passed as first parameter of the script, and replaces any dd.mm.20yy date by a 20yy.mm.dd date. It also moves the date at the beginning of the title.&lt;br /&gt;&lt;br /&gt;You need the &lt;a href="http://javamusictag.sourceforge.net/"&gt;Java ID3 Tag Library (jid3lib)&lt;/a&gt; and an embeddable Groovy (&lt;span style="font-style:italic;"&gt;groovy-all.jar&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;Now in order to run the script after each download, your podcast receiver must be able to run a custom command after each download. You can use &lt;a href="http://juicereceiver.sourceforge.net/"&gt;Juice&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So here is a quick and dirty script &lt;i&gt;sanitize_dates.groovy&lt;/i&gt; :&lt;br /&gt;&lt;br /&gt;&lt;textarea name="code" class="ruby" cols="60" rows="10"&gt;&lt;br /&gt;import org.farng.mp3.*&lt;br /&gt;&lt;br /&gt;TagOptionSingleton.getInstance().setOriginalSavedAfterAdjustingID3v2Padding(false)&lt;br /&gt;&lt;br /&gt;def readMp3Title(filepath) {&lt;br /&gt;    def mp3file = new MP3File(filepath) &lt;br /&gt;    mp3file.getID3v2Tag().getSongTitle()&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def writeMp3Title(filepath,title) {&lt;br /&gt;    def mp3file = new MP3File(filepath) &lt;br /&gt;    mp3file.getID3v2Tag().setSongTitle(title)&lt;br /&gt;    mp3file.save()&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def sanitizeTitle(title,action) {&lt;br /&gt;    def m = (title =~ /^(.*)([0-9][0-9])\.([0-9][0-9])\.(20[0-9][0-9])(.*)$/)&lt;br /&gt;    if(m){&lt;br /&gt;       def g = m[0]&lt;br /&gt;       action("" + g[4] + "." + g[3] + "." + g[2] + " " + g[1] + g[5])&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def filepath = args[0]&lt;br /&gt;def title = readMp3Title(filepath)&lt;br /&gt;&lt;br /&gt;println "Original title:${title}"&lt;br /&gt;sanitizeTitle(title) { sanitizedTitle -&gt;&lt;br /&gt;     writeMp3Title(filepath,sanitizedTitle)&lt;br /&gt;     println "Title after sanitization:${readMp3Title(filepath)}"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;&lt;br /&gt;In the preferences of Juice you can use &lt;i&gt;Run this command after each download&lt;/i&gt; with for exemple :&lt;br /&gt;&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;/usr/bin/java -cp /home/me/javalibs/groovy-all.jar:/home/me/javalibs/jid3lib.jar groovy.lang.GroovyShell /home/me/bin/sanitize_dates.groovy  "%f"&lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-2949191103078675611?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/2949191103078675611/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=2949191103078675611' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2949191103078675611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2949191103078675611'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/10/sanitize-dates-in-podcast-title-with.html' title='Sanitize dates in podcast title with groovy + jid3lib'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-6167079874663457000</id><published>2008-10-11T13:36:00.000-07:00</published><updated>2008-10-12T12:42:05.434-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mtp'/><category scheme='http://www.blogger.com/atom/ns#' term='eee'/><title type='text'>MTP Mp3 players and Asus eee</title><content type='html'>Works like a charm :&lt;br /&gt;&lt;br /&gt;Open a console : CTRL+ALT+T then&lt;br /&gt;&lt;span style="font-style:italic;"&gt;sudo apt-get install libmtp libnjb gnomad2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;./gnomad2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Does not work with Amarok though, as the MTP support does not seem to be compiled in the version provided with the eee.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-6167079874663457000?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/6167079874663457000/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=6167079874663457000' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6167079874663457000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6167079874663457000'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/10/mtp-mp3-players-and-asus-eee.html' title='MTP Mp3 players and Asus eee'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-2093896714530344806</id><published>2008-10-09T13:05:00.000-07:00</published><updated>2008-10-09T13:36:35.293-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mtp'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>MTP devices, udev and permissions in Ubuntu Dapper</title><content type='html'>I had to install Gnomad2 in Ubuntu Dapper in order to upload files to my MTP Mp3 player.&lt;br /&gt;&lt;span style="display: block;" id="formatbar_Buttons"&gt;&lt;span class="down" style="display: block;" id="formatbar_CreateLink" title="Lien" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;I followed this great &lt;a href="http://ubuntuforums.org/showthread.php?t=199250"&gt;How To&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;However Gnomad2 could not open the device unless I was root (same thing for mtp-detect)&lt;br /&gt;&lt;br /&gt;It looked like a permission problem in &lt;span style="font-style: italic;"&gt;/dev/bus/usb&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;This worked for me :&lt;br /&gt;&lt;br /&gt;In &lt;span style="font-style: italic;"&gt;/etc/udev/rules.d/40-permissions.rules&lt;/span&gt; I changed&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;# USB devices (usbfs replacement)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;SUBSYSTEM=="usb_device", MODE="0664"&lt;/span&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;to&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;# USB devices (usbfs replacement)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;SUBSYSTEM=="usb_device", MODE="0664"&lt;span style="font-weight: bold;"&gt;, GROUP="plugdev"&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;Then&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;sudo /etc/init.d/udev restart&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-2093896714530344806?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/2093896714530344806/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=2093896714530344806' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2093896714530344806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2093896714530344806'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/10/mtp-devices-udev-and-permissions-in.html' title='MTP devices, udev and permissions in Ubuntu Dapper'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3437250014293843254</id><published>2008-09-05T23:49:00.000-07:00</published><updated>2008-09-05T23:52:33.205-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional'/><title type='text'>Closure, Functors, Command Pattern</title><content type='html'>Closure, Functors, Command Pattern: &lt;a href="http://en.wikipedia.org/wiki/Function_object"&gt;rougly the same thing&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3437250014293843254?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3437250014293843254/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3437250014293843254' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3437250014293843254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3437250014293843254'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/09/closure-functors-command-pattern.html' title='Closure, Functors, Command Pattern'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3421055369006348575</id><published>2008-08-30T02:59:00.000-07:00</published><updated>2008-09-10T22:47:35.598-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>TDD : Can Shared Fixture test unexpected statefulness ?</title><content type='html'>When praticing TDD, the impact of the different types of fixture that can be used to setup the tests must be known in order to avoid to create &lt;a href="http://xunitpatterns.com/Obscure%20Test.html"&gt;Obscure Tests&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The usual default fixture type is the &lt;a href="http://xunitpatterns.com/Fresh%20Fixture.html"&gt;Fresh Fixture&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When the fresh fixture takes too much time a &lt;a href="http://xunitpatterns.com/Shared%20Fixture.html"&gt;Shared Fixture&lt;/a&gt; can be used. Now, that being said, the shared fixture should usually be avoided (there's a risk of &lt;a href="http://xunitpatterns.com/Erratic%20Test.html"&gt;Erratic Test&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;A few days ago, during a discussion about test fixture, some colleagues of mine and I were wondering if shared fixture could be used in order to test the statelessness of the objects used in the fixture as a side effect of the sharing.&lt;br /&gt;&lt;br /&gt;Here is the initial reasoning : when an object is supposed to be stateless, but is buggy and not actually stateless , the use of this object will lead to some failing test, since the state left by some tests will make some subsequent tests fail. The statelessness is tested "for free" with a lot of different combination. Actually, if the object happens to be stateful it is a case of &lt;a href="http://xunitpatterns.com/Erratic%20Test.html#Interacting%20Tests"&gt;Interacting Tests&lt;/a&gt; and the reason why some tests fail could be quite hard to find.&lt;br /&gt;&lt;br /&gt;I must admit that it may help to find some obscure statefulness interaction, but I do not really like this idea :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;I fear &lt;a href="http://xunitpatterns.com/Erratic%20Test.html#Interacting%20Tests"&gt;Interacting Tests&lt;/a&gt; far more than a single bug, as &lt;a href="http://xunitpatterns.com/Erratic%20Test.html#Interacting%20Tests"&gt;Interacting Tests&lt;/a&gt; in a test suite is hell on earth,&lt;/li&gt;&lt;li&gt;A test is supposed to have  &lt;a href="http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html"&gt;One Assertion Per Test&lt;/a&gt;. Or, at most a few assertions. In this case, the statefulness test is spread in the whole test class.&lt;/li&gt;&lt;/ul&gt;Actually, the statefulness should be tested in its own test. Of course it means that only a few method call combination will be tested but it should be enough to prove the statelessness (or else it means that the code of the class under test must be refactored in order to make the stalessness explicit in the code structure)&lt;br /&gt;&lt;br /&gt;To try to use the side effect of the shared fixture to test statelessness may seem to be worthwhile at first, but it creates &lt;a href="http://www.martinfowler.com/bliki/TechnicalDebt.html"&gt;Technical Debt&lt;/a&gt;. And in this case I think that the financial interest of the debt is pretty high.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3421055369006348575?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3421055369006348575/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3421055369006348575' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3421055369006348575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3421055369006348575'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/08/tdd-can-shared-fixture-test-unexpected.html' title='TDD : Can Shared Fixture test unexpected statefulness ?'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-827315542005899331</id><published>2008-04-13T02:18:00.000-07:00</published><updated>2008-04-13T11:20:59.583-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quote'/><title type='text'>Albert Einstein quote</title><content type='html'>&lt;i&gt;“Insanity: doing the same thing over and over again and expecting different results.”&lt;/i&gt;&lt;u&gt;Albert Einstein&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;Closely related to the quotation in the header of this blog&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-827315542005899331?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/827315542005899331/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=827315542005899331' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/827315542005899331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/827315542005899331'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/04/albert-einstein-quote.html' title='Albert Einstein quote'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-5029460877790500944</id><published>2008-03-05T23:07:00.000-08:00</published><updated>2008-11-06T14:46:28.551-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><title type='text'>90s geek flashback</title><content type='html'>Wooow...Internet still surprising me.&lt;br /&gt;&lt;br /&gt;(Just skip this post if terms Atari ST + Demo do no ring a bell)&lt;br /&gt;&lt;br /&gt;I never imagined I would have the opportunity to listen to this track again : &lt;a href="http://www.youtube.com/watch?v=YJ6Pbtpsfhs"&gt;the Union Demo main menu&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-5029460877790500944?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/5029460877790500944/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=5029460877790500944' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/5029460877790500944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/5029460877790500944'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/03/90s-geek-flashback.html' title='90s geek flashback'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-4448957677309630623</id><published>2008-02-23T05:04:00.000-08:00</published><updated>2008-02-23T11:19:21.254-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mock'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>"Unit Integrated Tests" for integrator objects</title><content type='html'>Whether I use interaction based testing or state based testing I usually write "real" unit tests in the sense that only one real object is involved (the SUT, System Under Test) and only stubs and mocks are used for the fixture. It usually is the only way to easily get a good code coverage (or at least to easily specifically cover some parts of the code )&lt;br /&gt;&lt;br /&gt;However, Lately, when testing Facade or Adapter objects he seemed to me that the unit tests for these objects were :&lt;br /&gt;- too complicated&lt;br /&gt;- too...useless&lt;br /&gt;&lt;br /&gt;For most Facade or Adapter objects :&lt;br /&gt;- They're stateless, so let's forget state based testing, we must use mocks&lt;br /&gt;- They're highly coupled to the contract of the adapted objects (by definition of a Facade or of an Adapter)&lt;br /&gt;&lt;br /&gt;This kind of class does nothing clever, they basically chain one or multiples calls. They may have bug though if they do not respect the contracts of the adapted classes.&lt;br /&gt;&lt;br /&gt;So a unit test for this kind of class depends more on the contract of the mocked objects, than on the behavior of the class under test. A very clear test smell.&lt;br /&gt;&lt;br /&gt;Even worse, the fixtures for the mocks are going to closely mimick the code of the SUT (if a method of a Facade calls an object A and then an object B, the fixture for the test on mocks and stubs will tell the exact same thing).&lt;br /&gt;&lt;br /&gt;In this specific case the unit test does not provide an orthogonal view of the behavior implemented by the code (the usual value of a unit test) ; it is the exact same view. If I make a wrong assumption on the contract of the adapted objects, I'm going to make the same exact mistake both in the test and then in the code (TDDly speaking)&lt;br /&gt;&lt;br /&gt;The problem basically is that the job of this kind of objects is to integrate other objects in the system. So he seemed to me that it is very hard to try to test an integration behavior in isolation.&lt;br /&gt;&lt;br /&gt;That's why, now, when I must deal with objects that heavily depend on the contract of the collaborating objects but do not do anything complicated, "integrator objects", I use integrated tests. &lt;br /&gt;&lt;br /&gt;However when I encounter an object that heavily depend on the contract of the collaborating objects and does many complicated things, it just means that it is too hard to test, I refactor.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-4448957677309630623?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/4448957677309630623/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=4448957677309630623' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/4448957677309630623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/4448957677309630623'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/02/unit-integrated-tests-for-integrator.html' title='&quot;Unit Integrated Tests&quot; for integrator objects'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-7096653602049926830</id><published>2008-02-03T10:34:00.001-08:00</published><updated>2008-02-03T12:04:07.349-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>TDD : How I was overusing mocks</title><content type='html'>The path to TDD is hard. When you think that you begin to understand how to apply it, you read this great &lt;a href="http://blog.jayfields.com/2008/01/testing-one-expectation-per-test.html"&gt;Jay Fields post&lt;/a&gt;, and you understand that you missed the point about stubs...&lt;br /&gt;&lt;br /&gt;As explained in his post, I was overusing mocks. I knew that I had to improve a lot in my way of using mocks, but what I really had to do was to use fewer mocks and more stubs. Basically I was missing the point that stubs are still of great use and are not superseded by mocks.&lt;br /&gt;&lt;br /&gt;It's been a long time since I learned such an important thing in a post about TDD. Now I'm really going to try to stick to "one assert per test" (including "one mock assert per test")&lt;br /&gt;&lt;br /&gt;And trying to do that, I'm also going to try "one test class per test setup". See this &lt;a href="http://video.google.fr/url?docid=8135690990081075324&amp;esrc=sr1&amp;ev=v&amp;len=2861&amp;q=bdd&amp;srcurl=http%3A%2F%2Fvideo.google.fr%2Fvideoplay%3Fdocid%3D8135690990081075324&amp;vidurl=%2Fvideoplay%3Fdocid%3D8135690990081075324%26q%3Dbdd%26total%3D535%26start%3D0%26num%3D10%26so%3D0%26type%3Dsearch%26plindex%3D0&amp;usg=AL29H229TyQhHRBiOgjNcReKfHnSF5p6Ew"&gt;presentation about BDD&lt;/a&gt; by Dave Astels&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-7096653602049926830?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/7096653602049926830/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=7096653602049926830' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7096653602049926830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7096653602049926830'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/02/tdd-how-i-was-overusing-mocks.html' title='TDD : How I was overusing mocks'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-244792814133269427</id><published>2008-02-03T01:28:00.000-08:00</published><updated>2008-04-04T15:45:05.398-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Asus eee and FullerScreen for Firefox</title><content type='html'>The &lt;a href="http://eeepc.asus.com/global/"&gt;Asus eee PC&lt;/a&gt; is a terrific machine. Now, the screen resolution being what it is you must find ways to use as much as possible the available area in Firefox.&lt;br /&gt;&lt;br /&gt;Enters &lt;a href="https://addons.mozilla.org/fr/firefox/addon/4650"&gt;FullerScreen&lt;/a&gt; an extension for Firefox. After the install, F11 gives you a real fullscreen (even for a tab)&lt;br /&gt;&lt;br /&gt;In full screen mode, you may need the following shorcuts of Firefox :&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Ctr + T : open tab&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ctrl + W : close tab&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ctrl + Tab : next tab (yeah, I know)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ctrl + L : go to location dialog&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ctrl + K : go to search page&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-244792814133269427?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/244792814133269427/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=244792814133269427' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/244792814133269427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/244792814133269427'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2008/02/asus-eee-and-fullerscreen-for-firefox.html' title='Asus eee and FullerScreen for Firefox'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3430532845191951796</id><published>2007-12-28T15:08:00.000-08:00</published><updated>2007-12-29T01:33:46.878-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mock'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>EasyMock and methods with side effects</title><content type='html'>I really like EasyMock, but was disappointed to not see any explanation in the documention about how to simulate the modification of an argument of a method with side effects.&lt;br /&gt;&lt;br /&gt;I thought that maybe I could write a custom argument matcher that modifies the argument (even if it seems weird to implement a side effect on the &lt;i&gt;matches&lt;/i&gt; method of the &lt;i&gt;IArgumentMatcher&lt;/i&gt;)&lt;br /&gt;&lt;br /&gt;Actually, it was confirmed by this brilliant article about a &lt;a href="http://www.carbonfive.com/community/archives/2007/05/generic_custom.html"&gt;Generic Custom Argument Matching in EasyMock&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3430532845191951796?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3430532845191951796/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3430532845191951796' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3430532845191951796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3430532845191951796'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/12/easymock-and-methods-with-side-effects.html' title='EasyMock and methods with side effects'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-7682937568123948282</id><published>2007-12-12T15:46:00.000-08:00</published><updated>2007-12-29T01:30:16.068-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ci'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>Continuous Integration and test environment availability</title><content type='html'>When a developper does not have an environment to run the unit tests of a projet, never allows this team member to check in, even if you have a continuous integration server running.&lt;br /&gt;&lt;br /&gt;If the build is broken, this programmer will have to perform several check in with the new code and wait for the next build in order to know if the build was fixed. Et voilà, the continuous integration server has become the test environment for this person...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-7682937568123948282?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/7682937568123948282/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=7682937568123948282' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7682937568123948282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/7682937568123948282'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/12/continuous-integration-and-test.html' title='Continuous Integration and test environment availability'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3224554101434590858</id><published>2007-11-22T03:09:00.000-08:00</published><updated>2008-10-11T11:39:10.832-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pdf'/><title type='text'>Convert an image to high quality PDF, Redux</title><content type='html'>In a previous post I explained how to &lt;a href="http://userstories.blogspot.com/2007/11/convert-image-to-pdf-with-gimp.html"&gt;Convert an image to PDF with Gimp&lt;/a&gt;, but the quality of the picture in the PDF turned out to be rather poor for printing purpose.&lt;br /&gt;&lt;br /&gt;So here is an other way, install &lt;i&gt;tiff2pdf&lt;/i&gt;.&lt;br /&gt;On Ubuntu for instance :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;sudo apt-get install libtiff-tools&lt;/textarea&gt;Then save your image as a tiff image, then use &lt;i&gt;tiff2pdf&lt;/i&gt; to convert it to a PDF. For example, for an A4 sized PDF :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;tiff2pdf -p A4  -o foo.pdf foo.tiff&lt;/textarea&gt;&lt;br /&gt;For a multiple pages PDF, preprocess your TIFF files with &lt;i&gt;tiffcp&lt;/i&gt;&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;tiffcp foo1.tiff foo2.tiff foo3.tiff totalfoo.tiff&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3224554101434590858?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3224554101434590858/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3224554101434590858' title='4 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3224554101434590858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3224554101434590858'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/11/convert-image-to-high-quality-pdf-redux.html' title='Convert an image to high quality PDF, Redux'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3202338129661028733</id><published>2007-11-18T21:20:00.000-08:00</published><updated>2008-10-11T11:40:03.374-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gimp'/><category scheme='http://www.blogger.com/atom/ns#' term='pdf'/><title type='text'>Convert an image to PDF with Gimp</title><content type='html'>In order to have an image printed by a professional printing service, I had to export the image created with Gimp to a PDF file. Here is how to do that.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;With Gimp save the image as a PS file, with the right size in the option dialog box (for instance, for an A5 dimension choose 148x210 mm)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In a terminal convert the ps file to a pdf file, with the matching size&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;ps2pdf -dPDFSETTINGS=/prepress  sPAPERSIZE=a5 foo.ps foo.pdf&lt;/textarea&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt; : There's an other another way that gives much better results regarding the compression ratio of the picture in the PDF, see &lt;a href="http://userstories.blogspot.com/2007/11/convert-image-to-high-quality-pdf-redux.html"&gt;Convert an image to high quality PDF, Redux&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3202338129661028733?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3202338129661028733/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3202338129661028733' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3202338129661028733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3202338129661028733'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/11/convert-image-to-pdf-with-gimp.html' title='Convert an image to PDF with Gimp'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-4841527838939778753</id><published>2007-11-02T04:33:00.000-07:00</published><updated>2008-04-13T02:25:37.599-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='quote'/><title type='text'>Karl Weick quote</title><content type='html'>&lt;i&gt;“Fight as if you are right; listen as if you are wrong.”&lt;/i&gt;&lt;u&gt;Karl Weick&lt;/u&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-4841527838939778753?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/4841527838939778753/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=4841527838939778753' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/4841527838939778753'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/4841527838939778753'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/11/karl-weick-quote.html' title='Karl Weick quote'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-8365118416170413261</id><published>2007-10-09T13:03:00.000-07:00</published><updated>2007-12-29T01:34:43.290-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>I want closures in Java !</title><content type='html'>It's true, trying other programming languages, different enough from your main programming language make you a better programmer/designer in general.&lt;br /&gt;&lt;br /&gt;My main programming language is Java, but I also do a lot of Ruby, and it definitely helps me to find new technics in Java.&lt;br /&gt;&lt;br /&gt;Now, the problem is : &lt;b&gt;the more I use closures in Ruby, the more I miss closures in Java...&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;However sometimes, even in Java, the only answer to some OOP problem is some sort of closure, implemented via a callback interface with an anonymous inner class. But the syntax cost of such a construct is very high. All the more so since only final local variables can be used in the anonymous inner class.&lt;br /&gt;&lt;br /&gt;So. I do hope closure will make it in the next release of Java.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-8365118416170413261?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/8365118416170413261/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=8365118416170413261' title='1 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8365118416170413261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/8365118416170413261'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/10/i-want-closures-in-java.html' title='I want closures in Java !'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-649477075820171857</id><published>2007-10-07T23:12:00.000-07:00</published><updated>2007-12-29T01:35:41.234-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tsp'/><title type='text'>XMLRPC/Ruby presentation for TSP</title><content type='html'>The slides of my presentation on the implementation of a &lt;a href="http://cvs.savannah.nongnu.org/viewvc/rubytsp/?root=tsp"&gt;TSP consumer in Ruby&lt;/a&gt; for the first &lt;a href="http://savannah.nongnu.org/projects/tsp"&gt;TSP&lt;/a&gt; workshop are archived &lt;a href="http://ftp.cc.uoc.gr/mirrors/nongnu.org/tsp/events/First_TSP_Workshop_27march2007/rubytsp.pdf"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-649477075820171857?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/649477075820171857/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=649477075820171857' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/649477075820171857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/649477075820171857'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/10/xmlrpcruby-presentation-for-tsp.html' title='XMLRPC/Ruby presentation for TSP'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-6196388653800852062</id><published>2007-10-06T12:28:00.000-07:00</published><updated>2007-10-07T12:13:01.317-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mock'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>TDD : state-based testings VS behavior-base testing</title><content type='html'>Using TDD to write my code I'm still trying to find when I should use mock objects or just test against the whole cluster of objects. That's the difference between state-based testing (no mocks) and behavior-based testing (using mocks) explained in this brilliant article by Martin Fowler : &lt;a href="http://martinfowler.com/articles/mocksArentStubs.html"&gt;Mocks Aren't Stubs&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'm sure that both technics are useful and I switch from one technic to an other depending on the complexity of the objects I'm testing.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;State-based testing (no mock) tend to increase the amount of integration test between objects, so is better suited to tests clusters of object with complex interaction&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Behaviour-based testing (with mocks) tend to increase the test coverage on a given object as it is easier to control the test environment of the object under test&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Using mocks objects tend to create more work, but the more I use them the more I detect bugs in my code that would very hard, or impossible to detect with state-based testing.&lt;br /&gt;&lt;br /&gt;Last time it happened it allowed me to see that an external object was called and was not supposed to be called (because the mock was called and warned me that it was an unexpected call)&lt;br /&gt;&lt;br /&gt;I would have never been able to detect this call with state base testing because :&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;It did not change the state of the object under test&lt;/li&gt;&lt;br /&gt;&lt;li&gt;It did not change the state of the external object&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;However the call to the real external object is very CPU intensive and would have harmed the performance. Behavior based testing allowed me to see this, it would have been undetected with state-based testing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-6196388653800852062?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/6196388653800852062/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=6196388653800852062' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6196388653800852062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6196388653800852062'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/10/tdd-state-based-testings-vs-behavior.html' title='TDD : state-based testings VS behavior-base testing'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-1110818417546103549</id><published>2007-10-04T12:48:00.000-07:00</published><updated>2007-10-04T13:23:33.767-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='test'/><title type='text'>TDD, red-green-refactor :  red as important as green</title><content type='html'>When I got started with &lt;a href="http://www.agiledata.org/essays/tdd.html"&gt;TDD,&lt;/a&gt; I tended to often forget the first step : &lt;i&gt;red&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;At first, it can seem silly to try to run a test when you did not write the matching functional code. You may think &lt;i&gt;"Of course, it is going to be red ! There's nothing in my application for this feature !"&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Of course... Unless there's a bug in your test. And it could be green and you're not going to see it. Then, you write the application code, with a bug too, you run your buggy test, and it is green even with your buggy functional code...&lt;br /&gt;&lt;br /&gt;Basically the red step of red-green-refactor helps you debug your test before your code. The red step really is some sort of TDD applied to the test itself. When there is no functional code, a test must be red. It does not mean that the test is correct, but if it is green, surely, it must be fixed.&lt;br /&gt;&lt;br /&gt;And even if your test is red and buggy, it is not a big deal, because when your functional code will be written, the test will be red too. The problem could come from your functional code, from your test, or both, but at least you will have to fix something to make the test pass.&lt;br /&gt;&lt;br /&gt;TDD like all Agile techniques, is all about rapid feedback. Red gives you feedback about your test, green feedback about your functional code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-1110818417546103549?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/1110818417546103549/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=1110818417546103549' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1110818417546103549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1110818417546103549'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/10/tdd-red-green-refactor-red-as-important.html' title='TDD, red-green-refactor :  red as important as green'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-6413372131013556144</id><published>2007-09-24T13:25:00.000-07:00</published><updated>2007-09-24T14:06:49.274-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git svn'/><title type='text'>Unusual Git usage : temporary commit into a local SVN checkout</title><content type='html'>I really love &lt;a href="http://git.or.cz/"&gt;Git&lt;/a&gt;, the distributed source code management designed by Linus Torvald. It is VERY easy to setup a new local repository (just type &lt;i&gt;git init&lt;/i&gt; in the root of your working directory). I do it all the time, even for a simple bash script creation. Even for the simple text files I use as TODO lists.&lt;br /&gt;&lt;br /&gt;A few days ago, I add to work offline in an old SVN checkout. I wish I could have used &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-svn.html"&gt;git-svn&lt;/a&gt; but it was an old checkout, and I was offline now. So much for git-svn for now...&lt;br /&gt;&lt;br /&gt;I had to find something to be able to undo my changes while I was offline. So why not try Git in a local SVN checkout ? After all, the only thing that Git does is to create a .git file, it does not modify the local files if I don't want to.&lt;br /&gt;&lt;br /&gt;So, at the root of my SVN checkout directory :&lt;br /&gt;&lt;i&gt;git init&lt;/i&gt;&lt;br /&gt;Then, I must hide the &lt;i&gt;.svn&lt;/i&gt; metadata directory, so I modify &lt;i&gt;.git/info/exclude&lt;/i&gt; and I add the line &lt;i&gt;:&lt;br /&gt;.svn&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Now Git will ignore the .svn directories and I can type :&lt;br /&gt;&lt;i&gt;git add .&lt;/i&gt;&lt;br /&gt;&lt;i&gt;git commit&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Now at least I can regularly commit my changes into Git and go back in time if&lt;br /&gt;I badly break my unit tests during my TDD session. Of course when I'll be back online&lt;br /&gt;and I'll commit into the SVN repository I'll lose the commit history in Git, but as I&lt;br /&gt;was offline anyway, at least I could work with a safety net.&lt;br /&gt;&lt;br /&gt;Even used in this unusual way, Git is still useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-6413372131013556144?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/6413372131013556144/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=6413372131013556144' title='2 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6413372131013556144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6413372131013556144'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/unusual-git-usage-temporary-commit-into.html' title='Unusual Git usage : temporary commit into a local SVN checkout'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-705468610873591772</id><published>2007-09-20T22:18:00.000-07:00</published><updated>2007-09-20T22:21:52.913-07:00</updated><title type='text'>JAgraffe : quickly send your pictures by email</title><content type='html'>I had coded &lt;a href="http://stephane.galles.free.fr/jagraffe"&gt;JAgraffe&lt;/a&gt; two years ago for my own use. I still use it almost on a weekly basis.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-705468610873591772?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/705468610873591772/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=705468610873591772' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/705468610873591772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/705468610873591772'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/jagraffe-quickly-send-your-pictures-by.html' title='JAgraffe : quickly send your pictures by email'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-3859830476727838964</id><published>2007-09-18T22:43:00.000-07:00</published><updated>2007-10-23T15:05:46.991-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gpg'/><title type='text'>Usual GnuPG commands</title><content type='html'>I tend to forget these very usual GnGPG commands as I use them only once in while. Now I won't forget them, or at least I'll known how to quickly find them :&lt;br /&gt;&lt;br /&gt;Sign a file :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;gpg --detach-sign --armor myfile.tar.gz&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Encrypt and decrypt a file for my own use :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;gpg --encrypt --recipient 'MyName' foo.txt&lt;br /&gt;gpg --output foo.txt --decrypt foo.txt.gpg&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Encrypt a free text for someone :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;echo 'Hello world' | gpg --output encrypted_hello.txt --encrypt --armor -r myfriend@whatever.com&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Get my own fingerprint :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;gpg  --fingerprint  myaddress@whatever.com&lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-3859830476727838964?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/3859830476727838964/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=3859830476727838964' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3859830476727838964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/3859830476727838964'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/usual-gnupg-commands.html' title='Usual GnuPG commands'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-2200581467841911593</id><published>2007-09-15T04:17:00.000-07:00</published><updated>2008-12-10T18:00:46.151-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='network'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Mix RJ45 and coaxial (10Base-2) ethernet cables</title><content type='html'>Problem : how do you add a WIFI AP (Access Point) to an already existing network wired with 10Base-2 coaxial ethernet cables ?&lt;br /&gt;&lt;br /&gt;Of course this is an old network with old PC, and you can not replace the 10Base-2 network adapters of the PC in order to rewire with RJ45 cables.&lt;br /&gt;&lt;br /&gt;Answer : just find one more 10Base-2 network adapter in a drawer, and add it in an old Linux box that already has a RJ45 network adapter. &lt;br /&gt;&lt;br /&gt;Setup two subnetworks, one on each adapter of the Linux box, with routing of the traffic from one subnetwork, to another (or even better, build a bridge, in order to have one single subnetwork).&lt;br /&gt;&lt;br /&gt;Plug in the AP to the linux box with a crossover RJ45 ethernet cable.&lt;br /&gt;&lt;br /&gt;This linux box can even be used as a gateway both for the LAN and for the WLAN !&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Jvq2hRnIVmc/RuvdaB72_CI/AAAAAAAAAAM/Yp3YKcC98DE/s1600-h/coax.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_Jvq2hRnIVmc/RuvdaB72_CI/AAAAAAAAAAM/Yp3YKcC98DE/s320/coax.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5110421641447799842" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-2200581467841911593?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/2200581467841911593/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=2200581467841911593' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2200581467841911593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2200581467841911593'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/mix-rj45-and-coaxial-10base-2-ethernet.html' title='Mix RJ45 and coaxial (10Base-2) ethernet cables'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_Jvq2hRnIVmc/RuvdaB72_CI/AAAAAAAAAAM/Yp3YKcC98DE/s72-c/coax.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-2375298541574273665</id><published>2007-09-14T23:56:00.000-07:00</published><updated>2008-02-03T04:29:52.823-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iptable'/><category scheme='http://www.blogger.com/atom/ns#' term='network'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Simple iptable gateway script</title><content type='html'>It's the second time I must setup a Linux gateway connected to a DSL line. The DSL modem interface is ppp0 and the LAN interface is eth0. Here is the script I use to setup the iptables :&lt;br /&gt;&lt;br /&gt;&lt;textarea name="code" class="bash" cols="60" rows="10"&gt;&lt;br /&gt;echo "1" &gt; /proc/sys/net/ipv4/ip_forward&lt;br /&gt;&lt;br /&gt;modprobe ip_conntrack_ftp&lt;br /&gt;&lt;br /&gt;export EXT=ppp0&lt;br /&gt;&lt;br /&gt;/sbin/iptables -F&lt;br /&gt;/sbin/iptables -t nat -F&lt;br /&gt;/sbin/iptables -t mangle -F &lt;br /&gt;/sbin/iptables -X &lt;br /&gt;&lt;br /&gt;/sbin/iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT&lt;br /&gt;/sbin/iptables -A INPUT -m state --state NEW -i ! $EXT -j ACCEPT&lt;br /&gt;/sbin/iptables -P INPUT DROP&lt;br /&gt;&lt;br /&gt;/sbin/iptables -A FORWARD -i $EXT -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT&lt;br /&gt;/sbin/iptables -A FORWARD -i eth0 -o $EXT -j ACCEPT&lt;br /&gt;&lt;br /&gt;/sbin/iptables -t nat -A POSTROUTING -o $EXT -j MASQUERADE&lt;br /&gt;&lt;br /&gt;/sbin/iptables -A FORWARD -i $EXT -o $EXT -j REJECT&lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-2375298541574273665?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/2375298541574273665/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=2375298541574273665' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2375298541574273665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/2375298541574273665'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/simple-iptable-gateway-script.html' title='Simple iptable gateway script'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-1077391849643177064</id><published>2007-09-10T13:26:00.000-07:00</published><updated>2007-09-10T14:40:26.337-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='exif'/><category scheme='http://www.blogger.com/atom/ns#' term='script'/><title type='text'>Sort JPG images by EXIF date in Ruby</title><content type='html'>Everybody now has a digital camera, and, during special events everybody takes plenty of pictures, and you end up with all these JPG images from different cameras. Each set of pictures tells the same story from the beginning, so it would be great to be able to reinterleave all of them in order to tell one single story. &lt;br /&gt;&lt;br /&gt;That what the folllowing little Ruby script does, thanks to the libexif-ruby library. If you run it in a directory with JPG images, it will create a subdirectory and copy all images, renaming them, prefixing with the EXIF date set by the camera.&lt;br /&gt;&lt;br /&gt;First, install the libexif-ruby library. On Ubuntu you can type :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;sudo apt-get install libexif-ruby&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;Then copy the following script in a ruby script file :&lt;br /&gt;&lt;textarea name="code" class="ruby:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;#!/usr/bin/ruby&lt;br /&gt;&lt;br /&gt;require 'exif'&lt;br /&gt;require 'ostruct'&lt;br /&gt;require 'fileutils'&lt;br /&gt;include FileUtils::Verbose&lt;br /&gt;&lt;br /&gt;SUBDIR='exifsorted'&lt;br /&gt;&lt;br /&gt;files = Dir.glob('*.{JPG,jpg}').entries.map { |name| OpenStruct.new({"name"=&gt;name, "date"=&gt;Exif.new(name)['Date and Time'].gsub(/ |:/,'')})}&lt;br /&gt;if(File.exist?(SUBDIR))&lt;br /&gt;    puts("Error : #{SUBDIR} directory already exist, delete it")&lt;br /&gt;    exit(1)&lt;br /&gt;end&lt;br /&gt;mkdir SUBDIR&lt;br /&gt;files.each do |file|&lt;br /&gt;    cp(file.name,File.join(SUBDIR,file.date + '_' + file.name))&lt;br /&gt;end &lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-1077391849643177064?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/1077391849643177064/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=1077391849643177064' title='2 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1077391849643177064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1077391849643177064'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/sort-jpg-images-by-exif-date.html' title='Sort JPG images by EXIF date in Ruby'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-1652923527783274724</id><published>2007-09-08T23:04:00.000-07:00</published><updated>2007-09-10T23:08:44.793-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Linux and Logitech MX mouses resolution</title><content type='html'>Without its real driver, my Logitech MX 510 mouse is seen by Linux as a 400 cpi resolution mouse. Actually it can be used in 800 cpi thanks to &lt;a href="http://www.bedroomlan.org/~alexios/coding_lmctl.html"&gt;lmctl&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've added this script to the 'Startup Program' list of Gnome :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;lmctl -8&lt;br /&gt;xmodmap -e "pointer = 1 2 3 4 5 8 9 6 7"&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;The xmodmap call allows to properly configure the thumb buttons.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-1652923527783274724?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/1652923527783274724/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=1652923527783274724' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1652923527783274724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/1652923527783274724'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/linux-and-logitech-mx-mouses-resolution.html' title='Linux and Logitech MX mouses resolution'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-6506766584743427330</id><published>2007-09-08T14:09:00.000-07:00</published><updated>2007-09-08T14:27:42.914-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='script'/><category scheme='http://www.blogger.com/atom/ns#' term='gnome'/><category scheme='http://www.blogger.com/atom/ns#' term='nautilus'/><title type='text'>An other Nautilus script : create directory with date</title><content type='html'>I've &lt;a href="http://userstories.blogspot.com/2007/09/nautilus-scripts.html"&gt;already described&lt;/a&gt; how to add custom scripts for Gnome file manager, Nautilus. So here is a an other simple script : it allows to create a directory with the name prefixed by the current date, via the contextual menu (right click) of Nautilus in the window of an other directory.&lt;br /&gt;Just type the name of the directory in the popup dialogue ; spaces will automatically replaced by underscores. &lt;br /&gt;&lt;br /&gt;You must install &lt;a href="http://xdialog.dyns.net/"&gt;XDialog&lt;/a&gt; to be able to use this script.&lt;br /&gt;On Ubuntu :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;sudo apt-get install xdialog&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;And here is the script :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;CURRENTDATE=`date +%y.%m.%d`&lt;br /&gt;DIRNAME=`Xdialog --title "Directory with date creation" --inputbox "Date : $CURRENTDATE\nDirectory name ?" 8 45 2&gt;&amp;1`&lt;br /&gt;case $? in&lt;br /&gt;0) mkdir $CURRENTDATE.${DIRNAME// /_};;&lt;br /&gt;1) exit;;&lt;br /&gt;esac&lt;br /&gt;&lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-6506766584743427330?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/6506766584743427330/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=6506766584743427330' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6506766584743427330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6506766584743427330'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/other-nautilus-script-create-directory.html' title='An other Nautilus script : create directory with date'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-26449904928757734</id><published>2007-09-06T12:26:00.000-07:00</published><updated>2007-09-08T14:27:28.278-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='script'/><category scheme='http://www.blogger.com/atom/ns#' term='gnome'/><category scheme='http://www.blogger.com/atom/ns#' term='nautilus'/><title type='text'>Nautilus File Manager scripts : Scale JPG images</title><content type='html'>I really appreciate the feature of Nautilus (Gnome file manager) that allows the use of custom scripts via the right click contextual menu. To be seen by Nautilus, the scripts must be copied in a specific directory ; you can reach this directory with :&lt;br /&gt;&lt;i&gt;Right clic in any directory window --&gt; Scripts --&gt; Open Scripts Folder&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;For example here is a bash script that can be used to scale all JPG images in a directory (a directory is created and the scaled images are copied into this directory). &lt;br /&gt;&lt;br /&gt;You must install &lt;a href="http://xdialog.dyns.net/"&gt;XDialog&lt;/a&gt; and &lt;a href="http://www.imagemagick.org/script/index.php"&gt;ImageMagick&lt;/a&gt; to be able to use this script.&lt;br /&gt;On Ubuntu :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;sudo apt-get install xdialog&lt;br /&gt;sudo apt-get install imagemagick&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;&lt;br /&gt;Then copy the script in the script directory of nautilus :&lt;br /&gt;&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;CURRENTDIR=`pwd`&lt;br /&gt;&lt;br /&gt;SIZE=`Xdialog --stdout --title "Image Scaling" --radiolist "Scaling images in \n$CURRENTDIR\npick a size !" 0 0 0 640 "640 pixels" 0 800 "800 pixels" 0 1024 "1024 pixels" 0 1920 "1920 pixels" 0 2&gt;&amp;1`&lt;br /&gt;&lt;br /&gt;if [ "$?" = "1" ] ; then&lt;br /&gt;    exit 0&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;TARGET="R"$SIZE&lt;br /&gt;mkdir $TARGET&lt;br /&gt;&lt;br /&gt;for j in '*.jpg' '*.JPG' ;do&lt;br /&gt;    for f in $j ;do&lt;br /&gt;        convert -quality 88 -geometry $SIZE "$f" $TARGET/"${TARGET}_$f"&lt;br /&gt;    done&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;Xdialog --title "Image Scaling" --msgbox "Image scaling done in :\n"$CURRENTDIR/$TARGET 0 0&lt;br /&gt;&lt;br /&gt;exit 0&lt;br /&gt;&lt;/textarea&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-26449904928757734?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/26449904928757734/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=26449904928757734' title='1 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/26449904928757734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/26449904928757734'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/nautilus-scripts.html' title='Nautilus File Manager scripts : Scale JPG images'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5321472228511037817.post-6793660322853357596</id><published>2007-09-04T12:08:00.000-07:00</published><updated>2007-10-07T03:37:26.324-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ci'/><category scheme='http://www.blogger.com/atom/ns#' term='cruisecontrol'/><category scheme='http://www.blogger.com/atom/ns#' term='ant'/><title type='text'>Simple CruiseControl + Ant example</title><content type='html'>I always have a hard time finding online a simple example of CruiseControl + Ant file whenever I need to quickly setup a Continuous Integration server for a project. So here is the configuration I had set up for the open source project &lt;a href="http://savannah.nongnu.org/projects/tsp/"&gt;TSP (Transport Sample Protocol)&lt;/a&gt; :&lt;br /&gt;&lt;br /&gt;&lt;textarea name="code" class="xml:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;&lt;cruisecontrol&gt;&lt;br /&gt;    &lt;project name="tsp" buildafterfailed="false"&gt;&lt;br /&gt;&lt;br /&gt;        &lt;listeners&gt;&lt;br /&gt;            &lt;currentbuildstatuslistener file="logs/${project.name}/status.txt"&gt;&lt;br /&gt;        &lt;/listeners&gt;&lt;br /&gt;&lt;br /&gt;        &lt;bootstrappers&gt;&lt;br /&gt;            &lt;cvsbootstrapper localworkingcopy="projects/${project.name}/make" file="cc-build.xml"&gt;&lt;br /&gt;        &lt;/bootstrappers&gt;&lt;br /&gt;&lt;br /&gt;        &lt;modificationset quietperiod="30"&gt;&lt;br /&gt;            &lt;cvs localworkingcopy="projects/${project.name}"&gt;&lt;br /&gt;        &lt;/modificationset&gt;&lt;br /&gt;&lt;br /&gt;        &lt;schedule interval="300"&gt;&lt;br /&gt;            &lt;ant anthome="apache-ant-1.6.5" buildfile="projects/${project.name}/make/cc-build.xml"&gt;&lt;br /&gt;        &lt;/schedule&gt;&lt;br /&gt;&lt;br /&gt;        &lt;log&gt;&lt;br /&gt;            &lt;merge dir="projects/${project.name}/target/test-results"&gt;&lt;br /&gt;        &lt;/log&gt;&lt;br /&gt;&lt;br /&gt;        &lt;publishers&gt;&lt;br /&gt;            &lt;htmlemail mailhost="smtp.blablablablabla.fr" reportsuccess="fixes" returnaddress="foo@blablablablabla.fr" subjectprefix="[CruiseControl]" skipusers="true" spamwhilebroken="true" xsldir="webapps/cruisecontrol/xsl" css="webapps/cruisecontrol/css/cruisecontrol.css"&gt;&lt;br /&gt;                &lt;always address="foo@blablablablabla.fr"&gt;&lt;br /&gt;                &lt;always address="bar@blablablablabla.fr"&gt;&lt;br /&gt;           &lt;/htmlemail&gt;&lt;br /&gt;        &lt;/publishers&gt;&lt;br /&gt;        &lt;br /&gt;    &lt;/project&gt;&lt;br /&gt;&lt;/cruisecontrol&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;For TSP the Ant build file was there to trigger the C build :&lt;br /&gt;&lt;textarea name="code" class="xml:nogutter" cols="60" rows="10"&gt;&lt;br /&gt;&lt;project name="tsp" default="build" basedir=".."&gt;&lt;br /&gt;&lt;br /&gt;    &lt;property name="build.dir" value="tsp_cc_build"&gt;&lt;br /&gt;    &lt;property name="tsp.dir" value=".."&gt;&lt;br /&gt;&lt;br /&gt;    &lt;macrodef name="cmake"&gt;&lt;br /&gt;                &lt;attribute name="flags" default=""&gt;&lt;br /&gt;                &lt;sequential&gt;&lt;br /&gt;                        &lt;exec dir="${build.dir}" executable="cmake" failonerror="true"&gt;&lt;br /&gt;                            &lt;arg line="@{flags} ${tsp.dir}"&gt;&lt;br /&gt;                        &lt;/exec&gt;&lt;br /&gt;                &lt;/sequential&gt;&lt;br /&gt;    &lt;/macrodef&gt;&lt;br /&gt;&lt;br /&gt;    &lt;macrodef name="make"&gt;                &lt;br /&gt;                &lt;sequential&gt;&lt;br /&gt;                        &lt;exec dir="${build.dir}" executable="make" failonerror="true"&gt;&lt;br /&gt;                &lt;/sequential&gt;&lt;br /&gt;    &lt;/macrodef&gt; &lt;br /&gt;&lt;br /&gt;    &lt;macrodef name="cvs-update"&gt;&lt;br /&gt;                &lt;attribute name="workDir"&gt;&lt;br /&gt;                &lt;sequential&gt;&lt;br /&gt;                        &lt;exec dir="@{workDir}" executable="cvs" failonerror="true"&gt;&lt;br /&gt;                                &lt;arg line="update -dP "&gt;&lt;br /&gt;                        &lt;/exec&gt;&lt;br /&gt;                &lt;/sequential&gt;&lt;br /&gt;    &lt;/macrodef&gt;&lt;br /&gt;&lt;br /&gt;    &lt;target name="build" depends="update"&gt;&lt;br /&gt;        &lt;antcall target="vanilla"&gt;&lt;br /&gt;        &lt;antcall target="xmlrpc"&gt;&lt;br /&gt;    &lt;/target&gt;&lt;br /&gt;&lt;br /&gt;    &lt;target name="vanilla" depends="clean"&gt;&lt;br /&gt;        &lt;cmake/&gt;&lt;br /&gt;        &lt;make/&gt;&lt;br /&gt;    &lt;/target&gt;&lt;br /&gt;&lt;br /&gt;    &lt;target name="xmlrpc" depends="clean"&gt;&lt;br /&gt;        &lt;cmake flags="-DBUILD_XMLRPC=ON"&gt;&lt;br /&gt;        &lt;make/&gt;&lt;br /&gt;    &lt;/target&gt;&lt;br /&gt;&lt;br /&gt;    &lt;target name="clean"&gt;&lt;br /&gt;        &lt;delete dir="${build.dir}"&gt;&lt;br /&gt;        &lt;mkdir dir="${build.dir}"&gt;&lt;br /&gt;    &lt;/target&gt;&lt;br /&gt;&lt;br /&gt;    &lt;target name="update"&gt;&lt;br /&gt;        &lt;cvs-update workdir="${basedir}"&gt;&lt;br /&gt;    &lt;/target&gt;&lt;br /&gt;&lt;br /&gt;&lt;/project&gt;&lt;br /&gt;&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;For TSP the setup instruction were :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;install a binary distribution of cruise control&lt;/li&gt;&lt;li&gt;replace the config.xml file with the previous file. Change the email addresses of the 'returnaddress', and the 'always address'. You can add as many 'always address' as you want&lt;/li&gt;&lt;li&gt;go to the 'projects' directory of the cruisecontrol install and perform an anonymous check out of tsp :&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;cd projects&lt;br /&gt;cvs -z3 -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/tsp co tsp&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;you can test the build without cruise control if Ant is available :&lt;br /&gt;go to projects/tsp/make and launch :&lt;br /&gt;&lt;textarea name="code" class="bash:nogutter:nocontrols" cols="60" rows="10"&gt;&lt;br /&gt;ant -f cc-build.xml&lt;br /&gt;&lt;/textarea&gt;&lt;br /&gt;If everything is OK, with the build, run the cruisecontrol.sh script. you're done,&lt;br /&gt;cruisecontrol is up and running.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5321472228511037817-6793660322853357596?l=userstories.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://userstories.blogspot.com/feeds/6793660322853357596/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5321472228511037817&amp;postID=6793660322853357596' title='0 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6793660322853357596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5321472228511037817/posts/default/6793660322853357596'/><link rel='alternate' type='text/html' href='http://userstories.blogspot.com/2007/09/cruisecontrol-ant-example.html' title='Simple CruiseControl + Ant example'/><author><name>Stephane</name><uri>http://www.blogger.com/profile/14321270550425216125</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
