<?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'><id>tag:blogger.com,1999:blog-3836263610079655093</id><updated>2009-11-14T16:24:03.308-02:00</updated><title type='text'>Daniel Cadenas</title><subtitle type='html'>My technical blog.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default?start-index=26&amp;max-results=25'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>33</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-5278648497182552742</id><published>2009-05-22T19:23:00.007-03:00</published><updated>2009-05-22T19:50:36.052-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>Gem to send mails through Gmail</title><content type='html'>I made this little gem, gmail_sender that I find useful to log things when I'm running some script in my VPS, I use it for things like this:&lt;pre class="c-sharp" name="code" style="font-size:18px"&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'gmail_sender'&lt;br /&gt;&lt;br /&gt;g = GmailSender.new("gmail_account", "password")&lt;br /&gt;begin&lt;br /&gt;  #Some interesting script that returns some interesting results in script_results&lt;br /&gt;  g.send("dcadenas@gmail.com", "It worked!", script_results.to_yaml)&lt;br /&gt;rescue =&gt; e&lt;br /&gt;  g.send("dcadenas@gmail.com", "It didn't work :(", "#{e.message}\n#{e.backtrace}")&lt;br /&gt;  raise e&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;So all you need to pass to the &lt;code&gt;send&lt;/code&gt; method is an email address, the subject and the email content. &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;Source code is &lt;a href="http://github.com/dcadenas/gmail_sender/tree/master"&gt;here&lt;/a&gt; and you can install it with &lt;code&gt;sudo gem install dcadenas-gmail_sender&lt;/code&gt; (remember to add github to your rubygems sources).&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-5278648497182552742?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/5278648497182552742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=5278648497182552742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/5278648497182552742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/5278648497182552742'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2009/05/gem-to-send-mails-through-gmail.html' title='Gem to send mails through Gmail'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-8542078960449912407</id><published>2009-05-22T19:12:00.004-03:00</published><updated>2009-05-22T19:22:29.979-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><title type='text'>Some useful bash functions</title><content type='html'>I use this bash functions to open vim with splits for each file in which grep or rak find the pattern I'm searching for:&lt;pre style="font-size:18px"&gt;&lt;br /&gt;grepvim(){&lt;br /&gt;  grep -rl "$1" $2| xargs mvim -o +/"$1"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;rakvim(){&lt;br /&gt;  rak -l "$1" $2| xargs mvim -o +/"$1"&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-8542078960449912407?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/8542078960449912407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=8542078960449912407' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8542078960449912407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8542078960449912407'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2009/05/some-useful-bash-functions.html' title='Some useful bash functions'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-1499219275513020023</id><published>2009-04-20T17:45:00.002-03:00</published><updated>2009-04-20T17:49:36.902-03:00</updated><title type='text'>Tunneling through SSH</title><content type='html'>To tunnel through ssh do:&lt;br /&gt;&lt;code style="font-size:18px"&gt;ssh -v -nNT4 -R :4007:localhost:3000 linode&lt;/code&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;which maps the remote port 4007 to the local port 3000.&lt;code&gt;linode&lt;/code&gt; is the host you want to connect to which should be properly configured in your ssh config.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-1499219275513020023?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/1499219275513020023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1499219275513020023' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1499219275513020023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1499219275513020023'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2009/04/tunneling-through-ssh.html' title='Tunneling through SSH'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-3190586210161631018</id><published>2009-03-10T01:39:00.004-03:00</published><updated>2009-03-10T01:48:32.135-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ENTP'/><title type='text'>Joined ENTP!</title><content type='html'>Oh I just realized that I never wrote a post about me joining &lt;a href="http://entp.com"&gt;ENTP&lt;/a&gt; 4 months ago!!&lt;br /&gt;I'm thrilled about this great chance I'm having working with some of the most talented Ruby developers the world has to offer and I wish that osmosis does its magic and maybe I get at least some tiny part of their mojo. I'm really happy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-3190586210161631018?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/3190586210161631018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=3190586210161631018' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3190586210161631018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3190586210161631018'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2009/03/joined-entp.html' title='Joined ENTP!'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-131627749005612092</id><published>2009-03-10T01:31:00.003-03:00</published><updated>2009-03-10T01:51:39.617-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RSpec'/><category scheme='http://www.blogger.com/atom/ns#' term='Humor'/><title type='text'>RSpec fun</title><content type='html'>So I was there, running a huge and slow rspec test suite on my Mac and thought "god, I wish I could make it run faster just by tilting my laptop right and make those damn dots fall" so... the most awesome rspec optimization was &lt;a href="http://github.com/dcadenas/rspec_prank"&gt; born&lt;/a&gt; :p&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-131627749005612092?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/131627749005612092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=131627749005612092' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/131627749005612092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/131627749005612092'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2009/03/rspec-fun.html' title='RSpec fun'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-912020076432281214</id><published>2008-12-12T15:51:00.006-02:00</published><updated>2008-12-12T16:22:13.042-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ssh git capistrano'/><title type='text'>Capistrano and Git problems</title><content type='html'>If you get this error when deploying with Capistrano:&lt;/br&gt;&lt;br /&gt;&lt;code style="font-size:12px"&gt;ERROR:gitosis.serve.main:Repository read access denied&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You may solve it with: &lt;/br&gt;&lt;br /&gt;&lt;code style="font-size:18px"&gt;ssh-add ~/.ssh/id_rsa&lt;/code&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;This is needed because my ssh config is configured to forward my identity to the deployment machine (with &lt;code&gt;ForwardAgent yes&lt;/code&gt;) so the ssh agent must know my private key.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-912020076432281214?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/912020076432281214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=912020076432281214' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/912020076432281214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/912020076432281214'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/12/capistrano-and-git-problems.html' title='Capistrano and Git problems'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-6679827179549848537</id><published>2008-10-19T23:58:00.006-02:00</published><updated>2008-10-20T00:07:55.505-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Recursively replace all files with tabs in Linux</title><content type='html'>Ok I don't want to forget this ever again so I will write it here.&lt;br /&gt;&lt;br /&gt;&lt;code style="font-size:20px"&gt;sed 's/  //g' `find -type f`&lt;/code&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li/&gt;Note I'm using both the grave accent "`" and the apostrophe "'". "`" is used to create a special kind of shell escape that substitutes each line of the output in the place you inserted it. So sed will be called once for each file found by find. &lt;br /&gt;It's the same as doing manually:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;sed 's/  //g' ./file1.txt&lt;br /&gt;sed 's/  //g' ./file2.txt&lt;br /&gt;sed 's/  //g' ./dir/file2.txt&lt;br /&gt; .&lt;br /&gt; .&lt;br /&gt; .&lt;/code&gt;&lt;br /&gt;&lt;li/&gt;To type the tab inside the sed regexp hit ctrl-v and then the tab key.&lt;br /&gt;&lt;li/&gt;find -type f will show in each line the path of each file recursively.&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/3836263610079655093-6679827179549848537?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/6679827179549848537/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=6679827179549848537' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6679827179549848537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6679827179549848537'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/10/recursively-replace-all-files-with-tabs.html' title='Recursively replace all files with tabs in Linux'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-8922513960555897722</id><published>2008-10-10T23:13:00.006-02:00</published><updated>2008-10-14T04:34:30.217-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plugin'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><title type='text'>Auto Focusable Forms</title><content type='html'>Just commited a Rails plugin (my first one, yay!!) to give automatic focus to the first input of the first form in each of your views.&lt;br /&gt;&lt;br /&gt;Of course you can disable this behaviour but I think that having focus in the first input by default can be very useful.&lt;br /&gt;&lt;br /&gt;You can read more in the project page &lt;a href="http://github.com/dcadenas/auto_focusable_forms/tree"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-8922513960555897722?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/8922513960555897722/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=8922513960555897722' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8922513960555897722'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8922513960555897722'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/10/auto-focusable-forms.html' title='Auto Focusable Forms'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-1731426878141803956</id><published>2008-10-07T20:00:00.003-02:00</published><updated>2008-10-07T20:03:20.410-02:00</updated><title type='text'>Internet Explorer 7 accept header and Rails respond_to</title><content type='html'>If you browse through IE to an url that should spit html but instead you see the feed reader page (the one with the text "You are viewing a feed that contains frequently updated content") then just be sure that the page's respond_to has the html mime type as its first item instead of atom or rss.&lt;br /&gt;&lt;br /&gt;This solution addresses the way IE constructs its http request accept header which accepts any MIME type you may be serving so the first one available will be accepted which is not necessarily the html you are expecting.&lt;br /&gt;Firefox doesn't have this problem because it explicitly states what it wants.&lt;br /&gt;&lt;br /&gt;So in summary, change this:&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;respond_to do |format|&lt;br /&gt;  format.atom&lt;br /&gt;  format.html&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;to this:&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;respond_to do |format|&lt;br /&gt;  format.html&lt;br /&gt;  format.atom&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-1731426878141803956?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/1731426878141803956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1731426878141803956' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1731426878141803956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1731426878141803956'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/10/internet-explorer-7-accept-header-and.html' title='Internet Explorer 7 accept header and Rails respond_to'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-5615504025510289287</id><published>2008-10-06T02:07:00.009-02:00</published><updated>2008-10-07T20:03:50.802-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>One thing to keep in mind when extending Ruby classes</title><content type='html'>I was reading this &lt;a href="http://eigenclass.org/hiki/class+hierarchy+introspection+evil.rb"&gt;post&lt;/a&gt; in Mauricio Fernandez's blog (rcov) and I got surprised about the behavior I'm summarizing in the following code:&lt;pre class="Ruby" name="code"&gt;class A&lt;br /&gt;  def foo&lt;br /&gt;    "A#foo"&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def singleton_class&lt;br /&gt;    class &lt;&lt; self&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;a = A.new&lt;br /&gt;puts a.foo #=&gt; A#foo&lt;br /&gt;puts a.singleton_class.ancestors.inspect #=&gt; [A, Object, Kernel]&lt;br /&gt;&lt;br /&gt;module X&lt;br /&gt;  def foo&lt;br /&gt;    "X#foo"&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class A&lt;br /&gt;  include X&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;a.extend X&lt;br /&gt;puts a.foo #=&gt; A#foo (and not X#foo!!!!!!!)&lt;br /&gt;puts a.singleton_class.ancestors.inspect #=&gt; [A, X, Object, Kernel] (and not [X, A, X, Object, Kernel])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So remember, you cannot extend or include a class with a module that is already present in the ancestors chain, it will be ignored!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-5615504025510289287?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/5615504025510289287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=5615504025510289287' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/5615504025510289287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/5615504025510289287'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/10/quirk-to-keep-in-mind-when-extending.html' title='One thing to keep in mind when extending Ruby classes'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-1731946161079249906</id><published>2008-09-26T12:29:00.002-03:00</published><updated>2008-09-26T12:32:19.628-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mocha'/><title type='text'>mocha_mock_path error in your tests</title><content type='html'>If you use stubs in your Mocha tests but they break because of this error:&lt;p&gt;&lt;code&gt;ActionView::TemplateError: undefined method `mocha_mock_path'&lt;/code&gt;&lt;p/&gt;&lt;br /&gt;Check that you also stub the &lt;code&gt;class&lt;/code&gt; method.&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;stub(:title =&gt; 'a title', :class =&gt; News)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-1731946161079249906?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/1731946161079249906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1731946161079249906' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1731946161079249906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1731946161079249906'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/09/mochamockpath-error-in-your-tests.html' title='mocha_mock_path error in your tests'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-6182344547901211311</id><published>2008-09-19T18:46:00.013-03:00</published><updated>2008-09-26T12:38:13.110-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>Tip about encapsulation</title><content type='html'>I like this simple and practical Kent Beck's tip about encapsulation (from Fowler's &lt;a href="http://www.martinfowler.com/bliki/GetterEradicator.html"&gt;Getter Erradicator&lt;/a&gt; article):&lt;br/&gt;&lt;br /&gt;&lt;p&gt;&lt;quote&gt;...always beware of cases where some code invokes more than one method on the same object...&lt;/quote&gt;&lt;/p&gt;&lt;br /&gt;It's not a rule but I think it can be considered one more smell to have in mind.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-6182344547901211311?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/6182344547901211311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=6182344547901211311' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6182344547901211311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6182344547901211311'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/09/good-tip-about-encapsulation.html' title='Tip about encapsulation'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-3159629254213296420</id><published>2008-09-10T12:34:00.004-03:00</published><updated>2008-09-10T14:21:38.783-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mocha'/><title type='text'>Stubbing/Mocking constants with Mocha</title><content type='html'>As far as google told me, it seems you can't use Mocha against constants.&lt;br /&gt;One alternative &lt;a href="http://rubyforge.org/pipermail/mocha-developer/2007-July/000394.html"&gt;proposed&lt;/a&gt; is changing the constant in the test where you need it like these:&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;class Module&lt;br /&gt;   def redefine_const(name, value)&lt;br /&gt;     __send__(:remove_const, name) if const_defined?(name)&lt;br /&gt;     const_set(name, value)&lt;br /&gt;   end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;And inside the test case you change the constant to what you need and restore it after the test ends:&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;Object.redefine_const(:RAILS_ENV, 'production')&lt;br /&gt;&lt;/pre&gt;But if you can control the code that uses the constant there's a more clean way. Just wrap it in a method and get the constant through the method. During testing you just mock the method:&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;class A&lt;br /&gt;  def rails_env&lt;br /&gt;    RAILS_ENV&lt;br /&gt;  end&lt;br /&gt;  #...some more code that gets the constant RAILS_ENV using the method rails_env...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;And then in your test you just do a normal expectation:&lt;br /&gt;&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;A.any_instance.expects(:rails_env).returns 'production'&lt;br /&gt;&lt;/pre&gt;You could even have some module that wraps all useful constants in your app, so constant dependent code gets easily testable and clean.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-3159629254213296420?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/3159629254213296420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=3159629254213296420' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3159629254213296420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3159629254213296420'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/09/stubbingmocking-constants-with-mocha.html' title='Stubbing/Mocking constants with Mocha'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-1010806278895371679</id><published>2008-09-03T11:25:00.007-03:00</published><updated>2008-09-05T17:27:27.436-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metaprogramming'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>Singleton class magic without using singleton classes</title><content type='html'>&lt;p&gt;Excellent posts &lt;a href="http://blog.jayfields.com/2008/04/extend-modules-instead-of-defining.html"&gt;here&lt;/a&gt; and &lt;a href="http://blog.jayfields.com/2008/07/ruby-underuse-of-modules.html"&gt;here&lt;/a&gt; by Jay Fields where he gives an alternative to use singleton classes.&lt;/p&gt;Instead of doing this:&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;class &lt;&lt; self&lt;br /&gt;  def hi&lt;br /&gt;    puts 'Hi'&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;You do this:&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;mod = Module.new do&lt;br /&gt;  def hi&lt;br /&gt;    puts 'Hi'&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;extend mod&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-1010806278895371679?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/1010806278895371679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1010806278895371679' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1010806278895371679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1010806278895371679'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/09/singleton-class-magic-without-using.html' title='Singleton class magic without using singleton classes'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-3794494473769856617</id><published>2008-08-25T23:38:00.017-03:00</published><updated>2008-09-03T17:38:52.576-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='reverse ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='comet'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Reverse Ajax and Prototype with Mongrel handlers</title><content type='html'>The most common use of &lt;a href="http://en.wikipedia.org/wiki/Reverse_Ajax"&gt;reverse Ajax or Comet&lt;/a&gt; calls is having some piece of information in your webpage that you want to update as soon as some remote event triggers, always without polling. Let's make a little experiment to see how this works:&lt;h2&gt;Client side&lt;/h2&gt;So you start a request, the web server waits for the event to happen, and finally, a while later, the response is sent back to the client. When this process ends, it's very common that you want to start it again. You want to have a continuous ajax call when the event is also continuous, when it can happen many times.&lt;br /&gt;To deal with this, as I'm using &lt;a href="http://www.prototypejs.org/"&gt;prototype&lt;/a&gt;, I made this javascript routine to start listening for the event:&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;function continuousAjaxCall(url, method, params,&lt;br /&gt;                            successCallback){&lt;br /&gt;  waitingForResponse = false;&lt;br /&gt;  function iteration(){&lt;br /&gt;    if(!waitingForResponse){&lt;br /&gt;      waitingForResponse = true;&lt;br /&gt;      new Ajax.Request(url, &lt;br /&gt;       {&lt;br /&gt;         method: method,&lt;br /&gt;         parameters: params,&lt;br /&gt;         onSuccess: theSuccessCallback&lt;br /&gt;       });&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  function theSuccessCallback(transport){&lt;br /&gt;   successCallback(transport.responseText);&lt;br /&gt;   waitingForResponse = false;&lt;br /&gt;  };&lt;br /&gt;  &lt;br /&gt;  return new PeriodicalExecuter(iteration, 1);&lt;br /&gt;} &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So now if you do:&lt;br /&gt;&lt;pre class="JScript" name="code"&gt;&lt;br /&gt;continuousAjaxCall("/WebFacade/SomeWebService", "post",&lt;br /&gt;                   "taskid=" + taskNumber, callback);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;you will start listening to the web service in /WebFacade/SomeWebService using method post and with taskid=taskNumber as the http body parameter.&lt;br /&gt;The callback is a routine that will be executed each time the event is triggered. For example we could do this to show the result from the web service:&lt;br /&gt;&lt;pre class="JScript" name="code"&gt;&lt;br /&gt;function callback(responseText)&lt;br /&gt;{&lt;br /&gt;  alert(responseText);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The routine returns a prototype &lt;a href="http://www.prototypejs.org/api/periodicalExecuter"&gt;PeriodicalExecuter&lt;/a&gt; that you can use to stop listening:&lt;br /&gt;&lt;pre class="JScript" name="code"&gt;&lt;br /&gt;pe = continuousAjaxCall(...&lt;br /&gt;pe.stop()&lt;br /&gt;&lt;/pre&gt;&lt;h2&gt;Server side&lt;/h2&gt;In the server side I have some &lt;a href="http://dcadenas.blogspot.com/2008/08/standalone-mongrel-handler-hello-world.html"&gt;Mongrel handlers&lt;/a&gt; serving the client pages. The key here is the line &lt;code&gt;sleep 1 until @@submitted_value&lt;/code&gt; that waits until some new value is submitted and &lt;b&gt;only&lt;/b&gt; then responds to the ajax request:&lt;br /&gt;&lt;pre class="Ruby" name="code"&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'mongrel'&lt;br /&gt;include Mongrel&lt;br /&gt;&lt;br /&gt;@@submitted_value = nil&lt;br /&gt;&lt;br /&gt;class ReverseAjaxHandler &lt; HttpHandler&lt;br /&gt;  def process(request, response)&lt;br /&gt;    response.start(200) do |head,out|&lt;br /&gt;      head['content-type'] = 'text/html'&lt;br /&gt;      sleep 1 until @@submitted_value&lt;br /&gt;      out &lt;&lt; @@submitted_value&lt;br /&gt;      @@submitted_value = nil&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class SendHandler &lt; HttpHandler&lt;br /&gt;  def process(request, response)&lt;br /&gt;    response.start(200) do |head,out|&lt;br /&gt;      #Code that renders the "/send" page and sets the @@submitted_value&lt;br /&gt;      #...  &lt;br /&gt;      #...  &lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;h = HttpServer.new("0.0.0.0", "5000")&lt;br /&gt;h.register("/wait", ReverseAjaxHandler.new)&lt;br /&gt;h.register("/send", SendHandler.new)&lt;br /&gt;h.register("/files", DirHandler.new('.'))&lt;br /&gt;h.run.join&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's nice to have this as a Mongrel handler because it's simple, but keep in mind that this implementation was made just for the purpose of this example and it is not thread safe. I don't control the access to the global variable &lt;code&gt;@@submitted_value&lt;/code&gt;&lt;h2&gt;Demo&lt;/h2&gt;I made a little demo about this using some mongrel handlers like the one in the &lt;a href="http://dcadenas.blogspot.com/2008/08/standalone-mongrel-handler-hello-world.html"&gt;previous post&lt;/a&gt;:&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;a href="http://dcadenas.googlepages.com/reverse_ajax.html"&gt;&lt;br /&gt;&lt;img src="http://dcadenas.googlepages.com/reverse_ajax.jpg"/&gt;&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;The code for this article and it's demo can be found &lt;a href="http://dcadenas.googlepages.com/ReverseAjaxExperiment.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-3794494473769856617?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/3794494473769856617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=3794494473769856617' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3794494473769856617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3794494473769856617'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/08/reverse-ajax-and-prototype.html' title='Reverse Ajax and Prototype with Mongrel handlers'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-4768253524696480572</id><published>2008-08-24T14:00:00.007-03:00</published><updated>2008-08-24T14:32:22.229-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mongrel'/><title type='text'>Standalone Mongrel handler Hello World</title><content type='html'>I think this is the simplest Mongrel handler you can create. It doesn't depend on Rails so this is not what you need to write if you want to install a Rails friendly Mongrel handler.&lt;br /&gt;&lt;pre class="ruby" name="code"&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'mongrel'&lt;br /&gt;&lt;br /&gt;class HelloHandler &lt; Mongrel::HttpHandler&lt;br /&gt;  def process(request, response)&lt;br /&gt;    response.start(200) do |head,out|&lt;br /&gt;      out &lt;&lt; 'Hello World!'&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;h = Mongrel::HttpServer.new("0.0.0.0", "5000")&lt;br /&gt;h.register("/hello", HelloHandler.new)&lt;br /&gt;h.run.join&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notes:&lt;ul&gt;&lt;li&gt;If you want to output html remember to include &lt;code&gt;head['content-type'] = 'text/html'&lt;/code&gt; inside the inner block&lt;/li&gt;&lt;br /&gt;&lt;li&gt;To include static pages you can add &lt;code&gt;h.register("/files", DirHandler.new('.'))&lt;/code&gt;. After this you can access all files in the current directory through this url &lt;code&gt;http://localhost:5000/files&lt;/code&gt;&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/3836263610079655093-4768253524696480572?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/4768253524696480572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=4768253524696480572' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4768253524696480572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4768253524696480572'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/08/standalone-mongrel-handler-hello-world.html' title='Standalone Mongrel handler Hello World'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-7434562931021889028</id><published>2008-03-13T01:06:00.006-03:00</published><updated>2008-03-13T03:38:27.204-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>A dependency graphs gem</title><content type='html'>Some days ago I had to do a big refactoring involving almost 100 C# projects, a little monster. I thought it would be very helpful to have a dependency graph that could quickly show me the projects dependencies I was interested in at any given moment and that would let me apply some filters to the result.&lt;br /&gt;I didn't like the few things I found in the net so I did a Ruby program to traverse csproj files and generate a &lt;a href="http://www.graphviz.org/"&gt;Graphviz&lt;/a&gt; graph with the desired results, something like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://depgraph.rubyforge.org/image1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px;" src="http://depgraph.rubyforge.org/image1.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;So as I thought this could be useful to a lot of people I decided to make it open source so I created &lt;a href="http://depgraph.rubyforge.org/"&gt;this Rubyforge project&lt;/a&gt; to host it.&lt;br /&gt;&lt;br /&gt;I made some changes to make it generic so that it could be used for any kind of text files that have parseable dependencies hidden inside. &lt;br /&gt;There are 2 examples stored in a yaml configuration file that deal with C# projects and Ruby &lt;i&gt;requires&lt;/i&gt; statements.&lt;br /&gt;&lt;br /&gt;So for example, if you want to graph your C# projects you do this from the root directory:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;depgraph -type csproj&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And if you want to graph the &lt;i&gt;requires&lt;/i&gt; dependencies of your Ruby files you do this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;depgraph -type ruby_requires&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then if you add a new entry in the yaml configuration file you'll be able to do this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;depgraph -type [new entry name]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You can also filter by directories and node name regular expressions against source and dependency targets. I'll add more functionality in next releases but it's already usable.&lt;br /&gt;&lt;br /&gt;To install it:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;gem install DepGraph&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;As always, check the &lt;a href="http://depgraph.rubyforge.org/"&gt;project's webpage&lt;/a&gt; and the &lt;a href="http://depgraph.rubyforge.org/specs.html"&gt;specs&lt;/a&gt; for more info.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-7434562931021889028?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/7434562931021889028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=7434562931021889028' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/7434562931021889028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/7434562931021889028'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/03/dependency-graphs-gem.html' title='A dependency graphs gem'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-187349153854188811</id><published>2008-03-04T20:50:00.013-02:00</published><updated>2008-03-08T17:31:57.307-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>A file system integration tests helper</title><content type='html'>A common problem that arises when creating integration tests against the file system is not having a one on one mapping between test cases and file structures.&lt;br /&gt;&lt;br /&gt;The most popular solution is having one generic file structure defined outside the test case (manually or in the test fixture set up) in which a generic set of files are shared among many different test cases.&lt;br /&gt;This works, but the problem is that we lose a lot of the documenting benefit of tests.&lt;br /&gt;&lt;br /&gt;The expressive power we could gain by the explicit creation of customized test files for each test case is very important.&lt;br /&gt;&lt;br /&gt;Of course we should test all we can inside our unit tests but it's always good to have something in our toolbox in case we have to go to the real thing at some moment.&lt;br /&gt;&lt;br /&gt;One example is when you have to deal with highly coupled legacy code in which your only practical solution may be testing directly against it's file inputs and outputs.&lt;br /&gt;&lt;br /&gt;So considering all this I made a very simple ruby gem, &lt;a href="http://filetesthelper.rubyforge.org/"&gt;filetesthelper&lt;/a&gt;, that let's you do something like this:&lt;br /&gt;&lt;pre class="ruby" name="code"&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'filetesthelper'&lt;br /&gt;include FileTestHelper&lt;br /&gt;  &lt;br /&gt;...&lt;br /&gt;  &lt;br /&gt;def test_some_code_that_uses_the_file_system&lt;br /&gt;  #Let's say that the current directory here is X&lt;br /&gt;    &lt;br /&gt;  with_files('dir1/file1' =&gt; 'file1 content',&lt;br /&gt;             'file2' =&gt; 'file2 content') do&lt;br /&gt;    #Now the current directory changed to Y which&lt;br /&gt;    #is a new directory created under Dir.tmpdir&lt;br /&gt;    #containing only 'dir1/file1' and 'file2'.&lt;br /&gt;&lt;br /&gt;    #Put some test code here.&lt;br /&gt;    File.open('file2') do |f|&lt;br /&gt;    ...&lt;br /&gt;  end&lt;br /&gt;    &lt;br /&gt;  #When we finish we are back at directory X and&lt;br /&gt;  #the Y directory is deleted with all its contents&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Check the &lt;a href="http://filetesthelper.rubyforge.org/specs.html"&gt;specs&lt;/a&gt; for some more details.&lt;br /&gt;&lt;br /&gt;To install it just type &lt;code&gt;gem install filetesthelper&lt;/code&gt; from the command line.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-187349153854188811?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/187349153854188811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=187349153854188811' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/187349153854188811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/187349153854188811'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/03/file-system-integration-tests-helper.html' title='A file system integration tests helper'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-4192505127370434524</id><published>2008-02-24T22:23:00.007-02:00</published><updated>2008-02-24T23:38:19.173-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>A Visual Studio 2005 solution generator in Ruby</title><content type='html'>Sometimes when you develop a big product you don't have a big solution that involves all your projects, but instead you have many smaller solutions that are used as views for some particular aspect of the product. This has the benefits that brings &lt;a href="http://c2.com/cgi/wiki?SeparationOfConcerns"&gt;separation of concerns&lt;/a&gt; at a product structure level and you also get less resource costs from Visual Studio.&lt;br /&gt;&lt;br /&gt;But sometimes you need a monster solution (100 projects for example) as this would be useful when you need to make global refactorings, build scripts, metric analysis, etc.&lt;br /&gt;&lt;br /&gt;Mantaining this &lt;code&gt;sln&lt;/code&gt; file manually is not the best thing to do because for example you can easily miss new projects added or removed from a smaller &lt;code&gt;sln&lt;/code&gt; in which some developer is working at a smaller scope. You can ask the developer to keep it up to date with each change but that's an RMBT (Risky Manual Boring Thing) which is a smell.&lt;br /&gt;&lt;br /&gt;So I made &lt;a href="http://dcadenas.googlepages.com/createsolution.zip"&gt;this simple ruby script&lt;/a&gt; that creates an &lt;code&gt;sln&lt;/code&gt; file containing all the projects found in the current directory tree.&lt;br /&gt;It will recursively search for &lt;code&gt;csproj&lt;/code&gt; files in your directory tree and create the corresponding project entries with the correct &lt;code&gt;guids&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Note that:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You can optionally specify the &lt;code&gt;sln&lt;/code&gt; file name.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The source control bindings must be mapped manually.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The native &lt;code&gt;guid&lt;/code&gt; generator used is taken from &lt;a href="http://www.agileprogrammer.com/dotnetguy/archive/2005/10/27/8991.aspx"&gt;here&lt;/a&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/3836263610079655093-4192505127370434524?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/4192505127370434524/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=4192505127370434524' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4192505127370434524'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4192505127370434524'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2008/02/visual-studio-2005-solution-generator.html' title='A Visual Studio 2005 solution generator in Ruby'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-6253129277074868701</id><published>2007-12-27T16:32:00.000-02:00</published><updated>2008-12-09T01:47:02.467-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Humor'/><title type='text'>Names</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/_Jy_WaM9LS-I/R3PwKk2jQjI/AAAAAAAAAWE/J95eavUe-zI/s1600-h/exploits_of_a_mom.png"&gt;&lt;img id="BLOGGER_PHOTO_ID_5148722863496446514" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_Jy_WaM9LS-I/R3PwKk2jQjI/AAAAAAAAAWE/J95eavUe-zI/s400/exploits_of_a_mom.png" border="0" /&gt;&lt;/a&gt; From &lt;a href="http://www.xkcd.com/"&gt;http://www.xkcd.com/&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://4.bp.blogspot.com/_Jy_WaM9LS-I/R3Pv_02jQiI/AAAAAAAAAV8/EdAFhzcxhpg/s1600-h/exploits_of_a_mom.png"&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-6253129277074868701?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/6253129277074868701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=6253129277074868701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6253129277074868701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6253129277074868701'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/12/names.html' title='Names'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Jy_WaM9LS-I/R3PwKk2jQjI/AAAAAAAAAWE/J95eavUe-zI/s72-c/exploits_of_a_mom.png' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-2139700509407112805</id><published>2007-12-08T19:09:00.000-02:00</published><updated>2007-12-12T10:58:12.547-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>More about refactoring</title><content type='html'>&lt;p&gt;Some points of disagreement with the critique against the &lt;a href="http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672"&gt;Refactoring book&lt;/a&gt; found in &lt;a href="http://www.codinghorror.com/blog/archives/000589.html"&gt;this article about smells&lt;/a&gt;:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Assuming that he is right in that most of us know how to refactor, I think that's because experience gave us the needed knowledge. Time and pain was needed. This time and pain, as in any knowledge area, can be reduced by reading books, like Fowler's.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Apart from this, I think even a developer with experience and knowledge of most refactorings could miss some smells. We are humans and we acquire bad habits.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;A big part of the knowledge gained with experience is too intuitive and that brings some problems. When things are intuitive and you suddenly find someone that doesn't share your intuition you have to fight back with solid explicit arguments. Fowler's book helps you ease the work needed to find those arguments letting you show clearly why your intuition is the way it is. You can do it yourself of course, but it's easier to reuse the effort someone else did, if you share it of course.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;The book defines a vocabulary to deal with our intuition or implicit concepts, that is very very important. Now we can share it and we can communicate more efficiently. The same advantage we discovered after design patterns appeared.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;He agrees with Fowler that smells are important. The book is a reference to smells. IMO refactoring is more about identifying design problems (smells) than about the relatively simple things needed to make them disappear, but it's both of course.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Things that are important, like smells, must be made explicit so the problem can be easily studied and possibly build some more knowledge on higher level concepts. One of those higher level concepts that could be further developed was the development philosophy of &lt;a href="http://martinfowler.com/ieeeSoftware/continuousDesign.pdf"&gt;Continuous Design&lt;/a&gt; in which a core concept is refactoring.&lt;br /&gt;&lt;br /&gt;&lt;li&gt;I think there's a confusion between simplicity and importance. Not only complicated books are important.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I think that a big reason for this disagreement comes from not seeing smells as thightly tied to refactoring &lt;a href="http://dcadenas.blogspot.com/2007/12/meaning-of-refactoring.html"&gt;as I see them&lt;/a&gt;. &lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-2139700509407112805?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/2139700509407112805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=2139700509407112805' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/2139700509407112805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/2139700509407112805'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/12/more-about-refactoring.html' title='More about refactoring'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-4556612434193084107</id><published>2007-12-04T23:31:00.000-02:00</published><updated>2007-12-05T11:43:24.619-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>More about architects</title><content type='html'>I found &lt;a href="http://martinfowler.com/ieeeSoftware/whoNeedsArchitect.pdf"&gt;this&lt;/a&gt; great Fowler's article which I think compliments well with my &lt;a href="http://dcadenas.blogspot.com/2007/10/architects-and-programmers.html"&gt;previous post&lt;/a&gt; saying that architects are programmers.&lt;br /&gt;&lt;br /&gt;Then I found &lt;a href="http://martinfowler.com/articles/designDead.html"&gt;this&lt;/a&gt; other great webpage he wrote about software design.&lt;br /&gt;In the paragraph &lt;i&gt;Do you wanna be an Architect when you grow up?&lt;/i&gt; he talks about this issue. I think that the &lt;i&gt;I'm not just a mere programmer - I'm an architect&lt;/i&gt; feeling he points out is one of the reasons people feel so much against this natural unification of responsibilities.&lt;br /&gt;&lt;br /&gt;Just imagine a football team in which only forwards are supposed to score goals and discouraged to assist and midfielders can only assist but are discouraged to score goals, it would be just stupid the way resources would be wasted with this separation.&lt;br /&gt;&lt;br /&gt;Now imagine a software system in which only the architect takes architecture decisions and doesn't touch code, and programmers only code and don't take architecture decisions. It is unnatural.&lt;br /&gt;&lt;br /&gt;Wouldn't be better if all players, err, programmers could make that decision as a team? You would just listen more carefully to the experienced programmer that is the architect and you'd have to be more trained on design. But that has always been your responsibility, you develop software! A programmer without design knowledge (or will to improve it) is like a football player that can't make a decent pass.&lt;br /&gt;&lt;br /&gt;Finally it seems Jeremy D. Miller reads my mind with &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2007/10/03/strength-at-the-point-of-attack.aspx"&gt;this&lt;/a&gt; post.&lt;br /&gt;&lt;br /&gt;So I got quite surprised to discover so many similarities. I guess this &lt;i&gt;architects are developers&lt;/i&gt; belief is more popular than I thought and that can only be good!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-4556612434193084107?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/4556612434193084107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=4556612434193084107' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4556612434193084107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4556612434193084107'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/12/more-about-architects.html' title='More about architects'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-4426881911412431189</id><published>2007-12-03T02:21:00.000-02:00</published><updated>2007-12-08T19:55:45.131-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>The meaning of refactoring</title><content type='html'>For a lot of people refactoring is not important. For some people this is because they don't know the correct meaning. They think it's just the name someone made up to describe simple code changes.&lt;br /&gt;That's not refactoring.&lt;br /&gt;You refactor when the changes you make &lt;b&gt;improve&lt;/b&gt; the design while keeping its behaviour unchanged. Knowing &lt;a href="http://martinfowler.com/bliki/CodeSmell.html"&gt;when&lt;/a&gt; and how this improvement should be made is what is taught in the literature.&lt;br /&gt;&lt;br /&gt;But there's another group of people that know the correct definition but still don't believe in it. This is because they measure their design quality just by its behaviour, so if the code already does what it's intended to do, they think it's good code. They believe in the &lt;i&gt;"Don't touch the design if it's not broken"&lt;/i&gt; mantra (a slightly more valid reason to believe in this comes from lacking a good set of tests that act as a safety net for your design improvements).&lt;br /&gt;For this reason they slowly start accumulating &lt;a href="http://c2.com/cgi/wiki?DesignDebt"&gt;design debt&lt;/a&gt;. At some point, not too far in time, this unattended design improvement brings the impossibility to change code as it can't be controlled. This is because it's too difficult to understand and see all consequences of any change they may do, so either they don't change code anymore, or if they do, they start seeing bugs appear everywhere because they broke something they couldn't see it could get broken. So they say &lt;i&gt;"You see? I was right, code shouldn't be changed if it's not broken"&lt;/i&gt;, a self-fulfilling prophecy.&lt;br /&gt;&lt;br /&gt;So in summary, refactoring requires the ability to systematically &lt;a href="http://martinfowler.com/bliki/CodeSmell.html"&gt;identify&lt;/a&gt; and improve problematic designs and it's important because it pays your design debt. This is necessary to understand and change your code easily.&lt;br /&gt;&lt;br /&gt;This are great books you can read to improve your refactoring skills:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672"&gt;Refactoring: Improving the Design of Existing Code&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/Refactoring-Patterns-Addison-Wesley-Signature-Kerievsky/dp/0321213351"&gt;Refactoring to Patterns&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/xUnit-Test-Patterns-Refactoring-Addison-Wesley/dp/0131495054"&gt;xUnit Test Patterns: Refactoring Test Code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-4426881911412431189?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/4426881911412431189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=4426881911412431189' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4426881911412431189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4426881911412431189'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/12/meaning-of-refactoring.html' title='The meaning of refactoring'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-6625251626494486235</id><published>2007-11-13T23:13:00.000-02:00</published><updated>2007-12-06T21:09:34.085-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CDATA'/><category scheme='http://www.blogger.com/atom/ns#' term='Xml'/><title type='text'>Escaping CDATA</title><content type='html'>This is a handy code that wraps a string with CDATA escaping any CDATA inside it:&lt;br /&gt;&lt;br /&gt;&lt;pre class="c-sharp" name="code"&gt;&lt;br /&gt;public static string CData(string data)&lt;br /&gt;{&lt;br /&gt;    if (string.IsNullOrEmpty(data))&lt;br /&gt;    {&lt;br /&gt;        return string.Empty;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    string result = Regex.Replace(data, "]]&amp;gt;", "]]]]&amp;gt;&amp;lt;![CDATA[&amp;gt;");&lt;br /&gt;    return "&amp;lt;![CDATA[" + result + "]]&amp;gt;";&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update:&lt;/b&gt; Clarity&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-6625251626494486235?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/6625251626494486235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=6625251626494486235' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6625251626494486235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6625251626494486235'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/11/escaping.html' title='Escaping CDATA'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-1697573532062128457</id><published>2007-11-09T19:48:00.000-02:00</published><updated>2008-12-09T01:47:02.850-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>Architects and programmers</title><content type='html'>I'm currently working in a project for a client bank. Some days ago we had a meeting where they showed us their IT's organizational chart which had been recently updated. They had two departments, one of them was made by business analysts and software architects, and the other department is the software factory where programmers are supposed to be. I interpreted that they were even physically two different sections. I'm not sure if I'm alone in this but I think this separation is wrong. I think that architects belong to the same space as developers because they are essentially the same thing.&lt;br /&gt;&lt;h4&gt;Architects are programmers&lt;/h4&gt;We must notice that architecture is a subset of design which implies that an architect is a designer. Now, if we agree that &lt;a href="http://www.developerdotstar.com/mag/articles/reeves_design_main.html"&gt;programmers are the designers of software&lt;/a&gt; then a good programmer that is capable of designing this subset, is an architect. Also notice that there are no good architects that are bad programmers. Being a good programmer is a prerequisite for being called architect. &lt;p&gt;&lt;a href="http://4.bp.blogspot.com/_Jy_WaM9LS-I/RzUMGJToLGI/AAAAAAAAAVc/-S0hQH2EjuE/s1600-h/arch.gif"&gt;&lt;img id="BLOGGER_PHOTO_ID_5131020650175540322" style="margin: 0px auto 10px; display: block; text-align: center;" alt="" src="http://4.bp.blogspot.com/_Jy_WaM9LS-I/RzUMGJToLGI/AAAAAAAAAVc/-S0hQH2EjuE/s200/arch.gif" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;With a similar reasoning we can conclude that a programming language is not more nor less than a software design language.&lt;br /&gt;Of course there are other tools to design software (diagrams, etc), but a programming language is better as its product is the only design representation that is directly interpreted by the computer and the only one that has a 1 on 1 relation between what you expect and what you get.&lt;br /&gt;Used correctly, they are better at communicating a design than the alternatives.&lt;br /&gt;&lt;br /&gt;So architects should program but they also should do it in the same physical space as the rest of programmers. Not only because they have to communicate their ideas but also for two extra benefits:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Feedback.&lt;br /&gt;Probably the most essential characteristic of software design is that it evolves by iterating. Even when doing waterfall, you evolve and iterate (the problem is that you stop too early doing so and you do it on paper). This evolution can only be done with realtime feedback and you won't get feedback if you are not inspecting and touching the guts of your child.&lt;br /&gt;You can only assess the correctness of your initial "paper level architecture" with the detailed "code level architecture", you should have a deep understanding of the most real representation of your architecture so you can be able to gather immediate feedback both against interpretation errors when some developer didn't understand you and against your own errors. Even if you are errorless (I don't believe that), feedback lets you learn about the domain and improve continuously your design.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Maximizing transfer of expertise and knowledge.&lt;br /&gt;If an architect is just a good and respected developer, then why not transfer its merits to the teammates. This would end up being a win-win situation because they'll keep improving and they know they will be responsible for the general improvement. The waste and problems that represent using programmers as if they were code generation tools is as silly as having not programming architects. Their design abilities must be improved. If they can't improve, well, then they are too stupid and risky even as a code generation tool, so don't hire them if you don't intend to use them as architects.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;&lt;br /&gt;The separation problem&lt;/h4&gt;Divide and conquer it's a great heuristic. But if you fail to spot the point in which you should stop dividing, then it's counterproductive. Some things belong together. And when those things are split you'll find problems.&lt;br /&gt;So if we have an architect that is not involved in programming we'll get two situations:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;We have an excellent programmer, probably the best in our company, that is being used only for writing word documents and making presentations and diagrams. This is any design representation you imagine except the one in which he is good at and at the same time the one that is the more important and cannot be skipped. Yes, this is the one that is least understood by other departments of the company, but the architect role should be constructing software, his main target of communication are the programmers from the trenches. Communication with the rest is secondary.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;We have a bad programmer that was choosen for some reason to make the architecture of the system. Let's not extend on this for obvious reasons.&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/3836263610079655093-1697573532062128457?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/1697573532062128457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1697573532062128457' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1697573532062128457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1697573532062128457'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/10/architects-and-programmers.html' title='Architects and programmers'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='01768789512016709947'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_Jy_WaM9LS-I/RzUMGJToLGI/AAAAAAAAAVc/-S0hQH2EjuE/s72-c/arch.gif' height='72' width='72'/><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry></feed>