<?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-3844230745666800015</id><updated>2011-11-28T01:18:54.186+01:00</updated><category term='Ruby Parser Dependencies Boost'/><category term='Setup'/><category term='Code'/><category term='Development'/><category term='Git'/><category term='Ruby'/><category term='Metaprogramming'/><category term='Infrastructure'/><category term='Image'/><category term='Web'/><category term='Music'/><title type='text'>Christoph Heindl</title><subtitle type='html'>def why?
  fun!
end</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>9</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-3038517335187630107</id><published>2009-12-05T11:08:00.002+01:00</published><updated>2009-12-05T11:10:19.061+01:00</updated><title type='text'>This Blog Has Moved!</title><content type='html'>My blog has moved over to

&lt;a href="http://cheind.wordpress.com"&gt;http://cheind.wordpress.com&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-3038517335187630107?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/3038517335187630107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2009/12/this-blog-has-moved.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/3038517335187630107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/3038517335187630107'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2009/12/this-blog-has-moved.html' title='This Blog Has Moved!'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-5392556689578656333</id><published>2008-12-19T16:13:00.007+01:00</published><updated>2008-12-21T13:13:25.544+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby Parser Dependencies Boost'/><title type='text'>Generic Dependency Parser : Boost</title><content type='html'>&lt;p&gt; I've added a dependency parser &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/dependencies"&gt;project&lt;/a&gt; to my github repository to parse dependencies between arbitrary files.&lt;/p&gt;

&lt;p&gt; The parser is generic and can work on arbitrary files and dependency rules. The &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/tests/dependencies/test_walker.rb"&gt;unit tests&lt;/a&gt; contain a contrived example.&lt;/p&gt;

&lt;p&gt; As a demonstration I've recorded inter-project dependencies of &lt;a href="http://www.boost.org/users/news/version_1_37_0"&gt;boost 1.37.0&lt;/a&gt; by parsing over 6000 boost header files. &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/dependencies/boost.rb"&gt;boost.rb&lt;/a&gt; is the ruby script that generated the &lt;a href="http://picasaweb.google.com/christoph.heindl/BoostDependencies1370#"&gt;dependencies&lt;/a&gt;.&lt;/p&gt;

&lt;a href="http://picasaweb.google.com/lh/photo/bXxu0fgQ2kalxE08YHE4hQ?feat=embedwebsite"&gt;&lt;img style="float:center" src="http://lh6.ggpht.com/_ZrtfMj01lnc/SUunV29XvnI/AAAAAAAABGs/DESlrW1KsX8/s288/archive.jpg" /&gt;&lt;/a&gt;

&lt;p&gt; Above image is the dependency graph for the archive library. &lt;/p&gt;

&lt;p&gt;The following section describe the parametrization for parsing boost.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;How is Boost parsed?&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The script records all header files within the boost include directory. Each
file path is assigned a vertex name: if the file path contains a nested directory
inside boost include directory, then the first nested directory name is used
as a vertex name in the graph. If the file path points directly to the boost
include directory the vertex name is derived as follows: if a directory with the same name exists (except for the file extension '.hpp') then the directory name is used. Else, the basename of the file including the extension is used (considered as mini-library).&lt;/p&gt;

&lt;p&gt;Next, each recorded file is parsed for matching &lt;tt&gt;#include&lt;/tt&gt; preprocessor statements. On every such statement the script tries to lookup the file inside boost directory and its vertex name. A dependency is then generated between the vertex name of the file parsed and the vertex name from the resolved include statement. If the dependency causes a cycle in the dependency graph, the dependency is not added but error'd to the logger.&lt;/p&gt;

&lt;p&gt;After all files are parsed, the dependency graph is reduced by removing
edges u -&gt; w, where u -&gt; ... -&gt; w exists and written to a '.dot' file
that can be converted into various formats using http://www.graphviz.org/.&lt;/p&gt;

&lt;p&gt; Additionally, for each project a more readable dependency graph is generated by masking unrelated dependencies.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Limitations of parsing Boost&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The parser is not
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;a complete preprocessor parser&lt;/b&gt;: it does not care about conditional
  include statements or commented ones. Parsing is based on simple pattern matching
  to keep to code small nice. You might however add a complete parser if you like to&lt;/li&gt;
&lt;li&gt;&lt;b&gt;recording cyclic dependencies&lt;/b&gt;: some include statements cause cyclic dependencies that simply result from the choice of mapping to vertex names. I.e. when file boost/a/detail/detail.hpp includes boost/b/win32/abc.hpp which in turn includes boost/a/other/other.hpp and the mapping resolves vertex names from the first nested directory inside boost, we generate a dependency from a -&gt; b and finally another one from b -&gt; a which will not be added.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-5392556689578656333?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/5392556689578656333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/12/generic-dependency-parser-boost.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/5392556689578656333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/5392556689578656333'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/12/generic-dependency-parser-boost.html' title='Generic Dependency Parser : Boost'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_ZrtfMj01lnc/SUunV29XvnI/AAAAAAAABGs/DESlrW1KsX8/s72-c/archive.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-7316865282202638269</id><published>2008-12-19T15:48:00.003+01:00</published><updated>2008-12-19T16:09:20.711+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='Image'/><title type='text'>Picasa: Add Filename Captions Automatically</title><content type='html'>&lt;p&gt;Ever wanted Picasa captions based on image filenames? Well, here comes a solution.&lt;/p&gt;
&lt;p&gt;When adding pictures to Picasa, Picasa scans the pictures for meta information based on &lt;a href="http://en.wikipedia.org/wiki/IPTC_Information_Interchange_Model"&gt;IPTC-NAA&lt;/a&gt;, a standard for attributing text, images and other media with meta information such as captions, copyright, etc. When Picasa encounters a file with attribute IPTC.Caption set it uses the value of the attribute as default caption.&lt;/p&gt;
&lt;p&gt;I've come up with a litte ruby script &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/picasa/captionize.rb"&gt;captionize.rb&lt;/a&gt; to set the caption attribute to a bunch of images found inside a directory. The caption set is the filename (including the file extension) of the image.&lt;/p&gt;
&lt;p&gt; The script requires a valid ruby installation and &lt;a href="http://www.exiv2.org/"&gt;Exiv2&lt;/a&gt; in your path to modify the meta content of an image.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-7316865282202638269?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/7316865282202638269/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/12/picasa-add-filename-captions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/7316865282202638269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/7316865282202638269'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/12/picasa-add-filename-captions.html' title='Picasa: Add Filename Captions Automatically'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-6879523311625475701</id><published>2008-12-10T20:01:00.005+01:00</published><updated>2008-12-10T20:31:47.535+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Music'/><title type='text'>The Prodigy -  Invaders Must Die</title><content type='html'>&lt;p&gt;The Prodigy is back with their new album "Invaders Must Die". The new long player will be released on 2009/02/27. &lt;a href="http://www.recordstore.co.uk/theprodigy/home.jsp"&gt;Pre-orders&lt;/a&gt; are already accepted.&lt;/p&gt;

&lt;p&gt;The cover track "Invaders Must Die" was free for download during the week of November 30th, 2008. If you missed it, The Prodigy was so kind to bring you an high quality &lt;a href="http://www.youtube.com/watch?v=EiqFcc_l_Kk"&gt;video&lt;/a&gt; for free.&lt;/p&gt;

&lt;p&gt;Enjoy the new/old The Prodigy style.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-6879523311625475701?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/6879523311625475701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/12/prodigy-invaders-must-die.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/6879523311625475701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/6879523311625475701'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/12/prodigy-invaders-must-die.html' title='The Prodigy -  Invaders Must Die'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-3841764591636972544</id><published>2008-12-06T12:24:00.003+01:00</published><updated>2008-12-06T12:42:54.629+01:00</updated><title type='text'>UUIDs For The Masses! (in Ruby)</title><content type='html'>&lt;p&gt;An &lt;a href="http://en.wikipedia.org/wiki/Universally_Unique_Identifier"&gt;UUID&lt;/a&gt; (Universally Unique Identification), also known as GUID, is often used in software to uniquely identify information. Originally designed for distributed systems, UUIDs have quickly found their way to wherever non-conflicting identifiers identifiers are needed.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;UUIDs in Ruby&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Lately, some Ruby projects have evolved that generate UUIDs in pure Ruby language. This hasn't been always that way: in 2005 Brad Wilson posted a &lt;a href="http://www.agileprogrammer.com/dotnetguy/archive/2005/10/27/8991.aspx"&gt;code snippet&lt;/a&gt; that allowed generation of UUIDs on windows platforms. Round about the same time the &lt;a href="http://raa.ruby-lang.org/project/uuid/"&gt;uuid&lt;/a&gt; project was kicked-off with the goal to create a pure ruby implementation and thus cross platform support for generating UUIDs. This project achieved gold status in 2006.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;UUIDs from Web Services&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;There are some web services out there that allow online generation of UUIDs. So i thought it should be easy to fetch UUIDs from those web services in ruby. Turns out it is!&lt;/p&gt;

&lt;p&gt;For those interested in generating UUIDs from such web-services I've added a &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/util/uuid.rb"&gt;uuid.rb&lt;/a&gt;  along with some &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/tests/util/test_uuid.rb"&gt;unit-tests&lt;/a&gt; and an adopted win32 implementation to my &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/"&gt;ruby-snippets&lt;/a&gt; repository.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-3841764591636972544?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/3841764591636972544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/12/uuids-for-masses-in-ruby.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/3841764591636972544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/3841764591636972544'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/12/uuids-for-masses-in-ruby.html' title='UUIDs For The Masses! (in Ruby)'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-7851291034588880780</id><published>2008-12-06T11:32:00.003+01:00</published><updated>2008-12-06T11:43:09.433+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Git'/><category scheme='http://www.blogger.com/atom/ns#' term='Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Code'/><title type='text'>Git repository for blog code</title><content type='html'>&lt;p&gt;To maintain the code I post on my &lt;a href="http://cheind.blogspot.com"&gt;blog&lt;/a&gt; I've joined &lt;a href="http://github.com"&gt;github&lt;/a&gt; a service that provides, amoung other services, free social code hosting. From their homepage&lt;/p&gt;

&lt;blockquote&gt;Not only is Git the new hotness, it's a fast, efficient, distributed version control system ideal for the collaborative development of software. GitHub is the easiest (and prettiest) way to participate in that collaboration [...]&lt;/blockquote&gt;

&lt;p&gt;You can find my repositories at &lt;a href="https://github.com/cheind"&gt;https://github.com/cheind&lt;/a&gt;. I've already added some code along with tests to the &lt;a href="http://github.com/cheind/ruby-snippets/tree/master"&gt;ruby-snippets&lt;/a&gt; repository. You can also find the repository links under the 'Links' section located in the blogs sidebar.&lt;/p&gt;

&lt;p&gt;I will directly link from blogs to relevant source if appropriate in repositories if adequate.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-7851291034588880780?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/7851291034588880780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/12/git-repository-for-blog-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/7851291034588880780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/7851291034588880780'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/12/git-repository-for-blog-code.html' title='Git repository for blog code'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-5249305959497786366</id><published>2008-12-04T16:33:00.022+01:00</published><updated>2008-12-28T19:57:58.898+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Setup'/><category scheme='http://www.blogger.com/atom/ns#' term='Infrastructure'/><title type='text'>Finally A Code Layout That Works!</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Background&lt;/span&gt;

&lt;p&gt;Our team develops software that evolves around robotics/vision. Speaking in software terms we do not have a single product but rather a set of continuosly growing libraries which are integrated in a set of applications. From the very first moment on we headed for modularized software components, to factor out common code.&lt;/p&gt;

&lt;p&gt;Our team consists of ~ten members of which five contribute to the code on a daily basis. We use a versioning control system and build server for continous integration and track our bugs.&lt;/p&gt;

&lt;p&gt;For a developer working on a specific project the code layout on his/her computer harddisk resides in a single project directory containing a flat hierarchy where each library/application resides in a single directory&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sample_project
  +---app_a
  +---lib_a
  +---lib_b
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Within the version control system, however, the code is organized differently. Each library/application is a project on its own. It links to required libraries by including them as externals. Quite often projects consist of tens of externals which makes the projects extremely hard to maintain.&lt;/p&gt;

&lt;span style="font-weight:bold;"&gt;What's the new layout?&lt;/span&gt;

&lt;p&gt;In the version control system there is just a single robotics/vision project containing libraries and applications as single sub-directories.&lt;/p&gt;

&lt;p&gt;The trunk contains stable libraries of general interest only. Applications and customer specific code will reside in branches of the trunk. Additionally we set up the following three simple rules.
&lt;ul&gt;
 &lt;li&gt;No one, except the maintainer, commits to the trunk.&lt;/li&gt;
 &lt;li&gt;The trunk contains libraries that are of general interest.&lt;/li&gt;
 &lt;li&gt;Any code in the trunk exhibits tests and documentation.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Ideally the maintainer is a person that not actively contributes code. That way, all requests to integrate code in the trunk have include unit tests and at least a description of the feature/bugfix to be integrated. Otherwise, how should the maintainer verify rule #2 and #3?&lt;/p&gt;

&lt;p&gt;From the rules it is obvious that the trunk will show only single commits for a single feature/bugfix. That eases the merging of features back into branches and allows for a readable changelog.&lt;/p&gt;

&lt;p&gt;As anyone is actually working on branches of the trunk, project leaders are free to decide if and when to integrate features/bugfixes from the trunk.&lt;/p&gt;

&lt;p&gt;Our build server will continously build the trunk and run all of its tests. Registering a branch for continous integration is only a matter of cloning the build project of the trunk.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Code =&gt; Trunk&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;One of the questions worth asking is: How to motivate people to contribute code to the trunk? There probably is no simple answer to this question. It depends, beside other reasons on
&lt;ul&gt;
 &lt;li&gt;communication: high level of communication between team members will keep everyone updated on what others currently work on. So sentences like "oh nice, could we have this in trunk? I need it too" should occur often.
 &lt;li&gt;the background/education of team members: influences their inner drive to modularize code and actively contribute to the trunk.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Please feel free to criticize.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-5249305959497786366?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/5249305959497786366/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/12/finally-code-layout-that-works.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/5249305959497786366'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/5249305959497786366'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/12/finally-code-layout-that-works.html' title='Finally A Code Layout That Works!'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-3758956041566564776</id><published>2008-12-02T15:46:00.025+01:00</published><updated>2008-12-28T20:09:56.955+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Metaprogramming'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>Method-Hooks In Ruby</title><content type='html'>&lt;p&gt;Method-Hooks provide a way to intercept method calls on objects and can be implemented conveniently using Ruby's meta programming techniques.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Acceptance Test&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Starting with an acceptance test helps focusing on the indended usage of the functionality we will implement. Suppose our simple system has the ability to draw content to windows. Windows can be configured via properties in their appearance. Everytime properties are changed the window needs to be redrawn to reflect the changes. In order to ease the handling of windows, we'd like to call redraw automatically when certain properties are changed.&lt;/p&gt;

&lt;p&gt;Here's a short implementation of class &lt;tt&gt;Window&lt;/tt&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Window capable of rendering items
class Window
  # Access background color of window.
  attr_accessor :background
  # Access text messages overlayed to window content
  attr_accessor :text
  # Simple counter that increments when redraw is invoked
  attr_reader :redraw_counter
  
  # Initialize window by drawing content
  def initialize
    @redraw_counter = 0
  end
  
  # Redraw window in case of changes and increment counter
  def redraw
    #...
    @redraw_counter += 1
  end

  # After setters are invoked update window content
  include FollowingHook
  following :background=, :text= do |wnd, args|
    wnd.redraw
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The most important part about this acceptance test is the syntax we'd like to implement to automatically call redraw when background color or text messages are changed. Here it is again&lt;/p&gt;

&lt;pre&gt;&lt;code class="prettyprint"&gt;include FollowingHook
following :background=, :text= do |wnd, args|
  wnd.redraw
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The keyword &lt;tt&gt;following&lt;/tt&gt; is a method name that takes any number of method symbols and a block. Its semantic is to execute the code block immediaetely after the given methods are called. Block arguments are provided: &lt;tt&gt;wnd&lt;/tt&gt;: the window for which one of the provided methods was called, &lt;tt&gt;args&lt;/tt&gt;: given invokation arguments and return value of method.&lt;/p&gt;

&lt;p&gt;Advantages are
&lt;ul&gt;
&lt;li&gt;Simple generic method hook interface&lt;/li&gt;
&lt;li&gt;Concentrate common code in one place&lt;/li&gt;
&lt;li&gt;Can use provided attribute accessors&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Implementation&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Here's an implementation of &lt;tt&gt;following&lt;/tt&gt; inside a module &lt;tt&gt;FollowingHook&lt;/tt&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Contains methods to hook method calls
module FollowingHook
  
  module ClassMethods
    
    private
    
    # Hook the provided instance methods so that the block 
    # is executed directly after the specified methods have 
    # been invoked.
    #
    def following(*syms, &amp;block)
      syms.each do |sym| # For each symbol
        str_id = "__#{sym}__hooked__"
        unless private_instance_methods.include?(str_id)
          alias_method str_id, sym        # Backup original 
                                          # method
          private str_id                  # Make backup private
          define_method sym do |*args|    # Replace method
            ret = __send__ str_id, *args  # Invoke backup
            block.call(self,              # Invoke hook
              :method =&gt; sym, 
              :args =&gt; args,
              :return =&gt; ret
            )
            ret # Forward return value of method
          end
        end
      end
    end
  end
  
  # On inclusion, we extend the receiver by 
  # the defined class-methods. This is an ruby 
  # idiom for defining class methods within a module.
  def FollowingHook.included(base)
    base.extend(ClassMethods)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Basically what happens is that for a given method to hook its original content is backup'ed using an unique alias name. The original method content is 'replaced' by an invokation of the original method and an invokation of the given block with current parameters. Additionally we prohibit hooking a method twice and make the original method private to prevent direct execution.&lt;/p&gt;

&lt;p&gt;&lt;tt&gt;following&lt;/tt&gt; is used as a class method. To define class methods from within modules we use an inner module that keeps our class methods. When the module is included by the receiver &lt;tt&gt;Module#included&lt;/tt&gt; is invoked with the class as in-parameter. The receiver is then extended by the class methods using &lt;tt&gt;Object#extend&lt;/tt&gt; which causes the receiver class to add &lt;tt&gt;following&lt;/tt&gt; as a class method.&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Usage&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;With our class &lt;tt&gt;Window&lt;/tt&gt; in place we can write a simple unit test to check the behaviour of &lt;tt&gt;following&lt;/tt&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'test/unit'

# Tests for methods in PrivateMethodHooks
class TestFollowingHook &lt; Test::Unit::TestCase
  
  # Test following method hook
  def test_following_hook
    wnd = Window.new
    assert_equal(wnd.redraw_counter, 0)
    wnd.text = "Show me!"
    assert_equal(wnd.redraw_counter, 1)
    wnd.background = [1.0, 1.0, 1.0]
    assert_equal(wnd.redraw_counter, 2)
  end
  
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Further Examples&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Here's a simple demonstration on how to log all invokations to &lt;tt&gt;Kernel#system&lt;/tt&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Object
  include FollowingHook
  following :system do |receiver, args|
    p "#{args[:method]} called with arguments #{args[:args].join(",")}"
    p "return value was #{args[:return]}"
  end
end

system('ruby --version')
# =&gt; ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-mswin32]
# =&gt; "system called with arguments ruby --version"
# =&gt; "return value was true"
# =&gt; true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Grab the code!&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/util/following.rb"&gt;following.rb&lt;/a&gt; Implementation of &lt;tt&gt;FollowingHook&lt;/tt&gt; module described above&lt;/li&gt;
&lt;li&gt; &lt;a href="http://github.com/cheind/ruby-snippets/tree/master/tests/util/test_following.rb"&gt;test_following.rb&lt;/a&gt; Unit tests for &lt;tt&gt;FollowingHook&lt;/tt&gt;.
&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-3758956041566564776?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/3758956041566564776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/12/method-hooks-in-ruby.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/3758956041566564776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/3758956041566564776'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/12/method-hooks-in-ruby.html' title='Method-Hooks In Ruby'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3844230745666800015.post-4372198889597636559</id><published>2008-11-28T18:20:00.028+01:00</published><updated>2008-12-28T20:01:27.737+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='Development'/><title type='text'>On-The-Fly Syntax Highlighting</title><content type='html'>&lt;p&gt;Having opened my blog recently I knew that I needed syntax highlighting for my code snippets in my blog. I found various offline tools to convert code to html, but as I'm changing code frequently I was looking for something more dynamic.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://code.google.com/p/google-code-prettify/"&gt;Google-code-pretty&lt;/a&gt; is what I came up with. A single javascript file contains the code for highlighting dozens of languages and a stylesheet defines the layout. Enabling syntax highlighting for your pages is simple matter of including the javascript and stylesheet file as external resources to your html tree. For simplicity these external resources can simply point to Google's online repository browser.&lt;/p&gt;

&lt;p&gt;Enable syntax highlighting by adding the external resources to your html's head section.&lt;/p&gt;

&lt;code class="prettyprint"&gt;&amp;lt;link href='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css' rel='stylesheet' type='text/css'/&gt; 
&amp;lt;script src='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js' type='text/javascript'&gt;&amp;lt;/script&gt; 
&lt;/code&gt;

&lt;p&gt;The links point to the head revision of the repository. So you stay up-to-date automatically. Finally trigger parsing of the document when it loads by refining the body tag:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;body onload="prettyPrint();"&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Having syntax highlighting enabled simply wrap your code by using pre/code tags and assign the class "prettyprint":&lt;/p&gt;

&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Update: &lt;/span&gt; Turns out &lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;SyntaxHighlighter&lt;/a&gt; provides a similar functionality.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3844230745666800015-4372198889597636559?l=cheind.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cheind.blogspot.com/feeds/4372198889597636559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cheind.blogspot.com/2008/11/here-comes-some-sample-test-returns.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/4372198889597636559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3844230745666800015/posts/default/4372198889597636559'/><link rel='alternate' type='text/html' href='http://cheind.blogspot.com/2008/11/here-comes-some-sample-test-returns.html' title='On-The-Fly Syntax Highlighting'/><author><name>Christoph Heindl</name><uri>http://www.blogger.com/profile/03460671359097206776</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='28' height='32' src='http://2.bp.blogspot.com/_ZrtfMj01lnc/S0xIb1Ws5yI/AAAAAAAABY8/-gL-apivVXc/S220/me_small.png'/></author><thr:total>2</thr:total></entry></feed>
