<?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-3836263610079655093</id><updated>2012-02-05T06:13:30.408-02:00</updated><category term='linux'/><category term='ENTP'/><category term='sysadmin'/><category term='CDATA'/><category term='javascript'/><category term='ajax'/><category term='mocha'/><category term='Design'/><category term='Functional programming'/><category term='reverse ajax'/><category term='Metaprogramming'/><category term='Refactoring'/><category term='Testing'/><category term='C#'/><category term='Duck typing'/><category term='ssh git capistrano'/><category term='comet'/><category term='Ruby Cubox'/><category term='RSpec'/><category term='Xml'/><category term='Ruby'/><category term='plugin'/><category term='BDD'/><category term='DSL'/><category term='Linq'/><category term='rails'/><category term='mongrel'/><category term='Humor'/><category term='vim'/><category term='Autotest'/><category term='prototype'/><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?max-results=100'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>39</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-8474148220405939479</id><published>2011-10-11T19:04:00.001-02:00</published><updated>2011-10-11T19:04:56.833-02:00</updated><title type='text'>CORS with Ext.js</title><content type='html'>&lt;code&gt;&lt;br /&gt;Ext.Ajax.useDefaultXhrHeader = false&lt;br /&gt;Ext.Ajax.request({url:'http://ipv4.0-9.fi', success: function(res,req){alert(res.responseText)}})&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-8474148220405939479?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/8474148220405939479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=8474148220405939479' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8474148220405939479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8474148220405939479'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2011/10/cors-with-extjs.html' title='CORS with Ext.js'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-4174907813369232528</id><published>2011-09-10T16:42:00.003-03:00</published><updated>2011-10-29T17:49:56.977-02:00</updated><title type='text'>Download a file from upload or file sonic with curl</title><content type='html'>Wupload:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;curl -d "email=your@email.com&amp;password=yourpassword&amp;redirect=/file/159344782" -b cookies.txt -c cookies.txt  www.wupload.com/account/login -L -o 20110910StokevsLiverpool1ertPachorriarte.mkv&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Filesonic:&lt;br /&gt;&lt;br /&gt;curl -d "email=your@email.com&amp;password=yourpassword&amp;redirect=/file/2820172905" -b cookies.txt -c cookies.txt www.filesonic.com/user/login -L -o somevideo.wmv&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-4174907813369232528?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/4174907813369232528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=4174907813369232528' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4174907813369232528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4174907813369232528'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2011/09/download-file-from-megaupload-with-curl.html' title='Download a file from upload or file sonic with curl'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-7118612781020906703</id><published>2011-03-09T21:47:00.001-02:00</published><updated>2011-03-09T21:48:47.234-02:00</updated><title type='text'>Valgrind on your ruby extension test</title><content type='html'>&lt;code&gt;valgrind --num-callers=50 --error-limit=no --partial-loads-ok=yes --undef-value-errors=no rspec spec/extension_spec.rb&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-7118612781020906703?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/7118612781020906703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=7118612781020906703' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/7118612781020906703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/7118612781020906703'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2011/03/valgrind-on-your-ruby-extension-test.html' title='Valgrind on your ruby extension test'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-1959407511178868017</id><published>2010-12-21T13:48:00.001-02:00</published><updated>2010-12-21T13:49:51.718-02:00</updated><title type='text'>Ssh tunnel for socks proxy</title><content type='html'>&lt;code&gt;ssh -fCqND 8080 linode&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-1959407511178868017?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/1959407511178868017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1959407511178868017' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1959407511178868017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1959407511178868017'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2010/12/ssh-tunnel-for-socks-proxy.html' title='Ssh tunnel for socks proxy'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-4298477527725130856</id><published>2010-04-05T02:40:00.002-03:00</published><updated>2010-04-05T02:45:20.686-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><title type='text'>apr_poll timeout while running ab</title><content type='html'>&lt;span class="Apple-style-span"   style="  line-height: 14px; font-family:sans-serif;font-size:12px;"&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="  line-height: 14px; "&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="color:#333333;"&gt;If you get this error while running ab (ApacheBench) on your mac:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="  line-height: 14px; "&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="color:#333333;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="color:#333333;"&gt;&lt;code&gt;apr_poll: The timeout specified has expired (70007)&lt;/code&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="  line-height: 14px; "&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="color:#333333;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="  line-height: 14px; "&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span"  style="color:#333333;"&gt;It may get fixed with:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial; font-size: medium; color: rgb(51, 51, 51); line-height: 19px; white-space: pre; "&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial; font-size: medium; color: rgb(51, 51, 51); line-height: 19px; white-space: pre; "&gt;&lt;code&gt;sudo sysctl -w net.inet.ip.portrange.first=32768&lt;/code&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial; font-size: medium; color: rgb(51, 51, 51); line-height: 19px; white-space: pre; "&gt;&lt;code&gt;sudo sysctl -w net.inet.tcp.msl=100&lt;/code&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-4298477527725130856?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/4298477527725130856/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=4298477527725130856' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4298477527725130856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/4298477527725130856'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2010/04/aprpoll-timeout-while-running-ab.html' title='apr_poll timeout while running ab'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-1060375237642877894</id><published>2010-04-02T16:45:00.003-03:00</published><updated>2010-04-02T17:22:29.479-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby Cubox'/><title type='text'>New Ruby gems</title><content type='html'>Oops! It's been a while since the last time I updated my blog.&lt;div&gt;I promise I'll try to get back to business and write more often whenever I find some time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;First of all, many months ago I joined &lt;a href="http://cuboxsa.com/"&gt;Cubox&lt;/a&gt;, a new uruguayan startup that works for many US clients including partnership and offshore work with renowned Rails shops. We are continuously growing and we are always happy to add talented developers to our team so if you are interested and are passionate about Ruby, Rails and agile development just email us.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We believe in open source, we use it in all our projects and we also contribute to the community. Here is an update of my latest new gems which were not covered in this blog. I hope you find them useful, click on the links for more info:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://github.com/dcadenas/preforker"&gt;Preforker&lt;/a&gt;: Easily create preforking servers (like Unicorn architecture)&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/cubox/rankable_graph"&gt;Rankable Graph&lt;/a&gt;: Calculates the pagerank of medium sized graphs (some millions of nodes).&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/dcadenas/state_pattern"&gt;State Pattern&lt;/a&gt;: A classical implementation of the state pattern.&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/dcadenas/active_record_state_pattern"&gt;Active Record State Pattern&lt;/a&gt;: State pattern applied to Rails models.&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/dcadenas/stubborn"&gt;Stubborn&lt;/a&gt;: Helps finding missing external API stubs in your tests.&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/cubox/google_set"&gt;Google Set&lt;/a&gt;: API for Google Sets.&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/dcadenas/change_watcher"&gt;Change Watcher&lt;/a&gt;: Helps keeping a log of changes. Useful to store scraped info from cron scripts. &lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;So check them out and contribute!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-1060375237642877894?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/1060375237642877894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1060375237642877894' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1060375237642877894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/1060375237642877894'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2010/04/new-ruby-gems.html' title='New Ruby gems'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=6679827179549848537' title='1 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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>1</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='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=8922513960555897722' title='5 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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>5</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='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=1731426878141803956' title='1 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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>1</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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=3159629254213296420' title='5 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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>5</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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=7434562931021889028' title='1 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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>1</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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></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>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>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='http://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:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-6549508027309854154</id><published>2007-11-06T23:31:00.000-02:00</published><updated>2007-11-07T22:45:23.391-02: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'>A bit of metaprogramming</title><content type='html'>I added some functionality to the technique shown in &lt;a href="http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html"&gt;this Jay Fields post&lt;/a&gt; so that you can retrieve your original method(s). You can use it for aspect programming but remember that if you want a real framework for doing so then just use &lt;a href="http://aspectr.sourceforge.net/"&gt;AspectR&lt;/a&gt; or some similar library.&lt;br /&gt;&lt;br /&gt;First you add your behaviour to an existent method:&lt;br /&gt;&lt;pre name="code" class="Ruby"&gt;&lt;br /&gt;class TestClass&lt;br /&gt;def testMethod(param1, param2)&lt;br /&gt;"Original output with parameters #{param1} and #{param2}"&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class TestClass&lt;br /&gt;after_method :testMethod do |returnValue, param1, param2|&lt;br /&gt;"The original output is: " + returnValue&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If now you call &lt;code&gt;testMethod('a', 'b')&lt;/code&gt; you'll get this string:&lt;br/&gt;&lt;code&gt;The original output is: Original output with parameters a and b&lt;/code&gt;, so you successfully decorated the initial method.&lt;br/&gt;&lt;br /&gt;Now if you want to get the original behaviour and remove your method decoration, just do:&lt;br /&gt;&lt;pre name="code" class="Ruby"&gt;&lt;br /&gt;TestClass.undo_after_method(:testMethod);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now after sending &lt;code&gt;testMethod('a', 'b')&lt;/code&gt; you'll get:&lt;br/&gt;&lt;code&gt;Original output with parameters a and b&lt;/code&gt;.&lt;br/&gt;&lt;br /&gt;Here is the code:&lt;br /&gt;&lt;pre name="code" class="Ruby"&gt;&lt;br /&gt;class Class&lt;br /&gt;def after_method(method_symbol)&lt;br /&gt;previous_method = instance_method(method_symbol)&lt;br /&gt;&lt;br /&gt;define_method(method_symbol) do |*args|&lt;br /&gt;returnValue = previous_method.bind(self).call(*args)&lt;br /&gt;yield returnValue, *args &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;singleton_class.push_previous_method(previous_method)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def undo_after_method(method_symbol)&lt;br /&gt;method_key = instance_method(method_symbol).to_s.to_sym&lt;br /&gt;previous_method = singleton_class.pop_previous_method(method_key)&lt;br /&gt;if previous_method&lt;br /&gt;define_method(method_symbol) do |*method_args|&lt;br /&gt;previous_method.bind(self).call(*method_args)&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def singleton_class&lt;br /&gt;class&amp;lt;&amp;lt;Class;self;end;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class&amp;lt;&amp;lt;Class&lt;br /&gt;#A hash that holds all the stacks.&lt;br /&gt;#Each stack holds the current set of behaviours that decorate the initial method&lt;br /&gt;@previousMethodsStacks = {} &lt;br /&gt;&lt;br /&gt;def pop_previous_method(method_key)&lt;br /&gt;previous_methods_stack = previous_methods_stacks[method_key]&lt;br /&gt;&lt;br /&gt;if(previous_methods_stack and previous_methods_stack.size &gt; 0)&lt;br /&gt;previous_method = previous_methods_stack.pop&lt;br /&gt;&lt;br /&gt;if(previous_methods_stacks[method_key].size == 0)&lt;br /&gt;previous_methods_stacks.delete(method_key)&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;previous_method&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def push_previous_method(previous_method)&lt;br /&gt;previous_methods_stack = previous_methods_stacks.fetch(previous_method.to_s.to_sym) do&lt;br /&gt;previous_methods_stacks[previous_method.to_s.to_sym] = []&lt;br /&gt;end&lt;br /&gt;previous_methods_stack.push(previous_method)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def previous_methods_stacks&lt;br /&gt;@previousMethodsStacks&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The RSpec tests are &lt;a href="http://dcadenas.googlepages.com/after_method.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-6549508027309854154?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/6549508027309854154/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=6549508027309854154' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6549508027309854154'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6549508027309854154'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/11/bit-of-metaprogramming.html' title='A bit of metaprogramming'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-8159895211630953971</id><published>2007-11-04T08:02:00.000-02:00</published><updated>2007-11-05T20:17:19.953-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Functional programming'/><title type='text'>Functional programming</title><content type='html'>I remember making a Mastermind solving program in Erlang some years ago back in university. I enjoyed it a lot and I thought it was a shame that the functional programming paradigm was not mainstream. &lt;br /&gt;&lt;br /&gt;Luckily it seems times are changing and you can find that Erlang is getting more popular, DSLs are getting attention (so Lisp will gain it too) and Microsoft seems to be slowly trying to put his own functional language, F# (ML), at the same level of C#. &lt;br /&gt;&lt;br /&gt;So while surfing the web about this interesting times I found &lt;a href="http://www.defmacro.org/ramblings/articles.html"&gt;this&lt;/a&gt; very pleasant to read web page that can make you recover from your imperative programming comma. Good reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-8159895211630953971?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/8159895211630953971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=8159895211630953971' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8159895211630953971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/8159895211630953971'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/11/functional-programming.html' title='Functional programming'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-3442366309101462917</id><published>2007-11-03T00:52:00.000-02:00</published><updated>2007-11-03T01:29:16.208-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Design'/><title type='text'>About the object-relational impedance mismatch</title><content type='html'>I agree 100% with &lt;a href="http://blog.objectmentor.com/articles/2007/11/02/active-record-vs-objects"&gt;this Bob Martin's post&lt;/a&gt; about the problems that arise when using the Active Record data layer pattern.&lt;br /&gt;&lt;br /&gt;I think Active Record's problems come from implementing it from the beginning of your project which has the risk of biasing your design towards a weak domain model. You will tend to make design decisions that fit well with Active Record and it will be more difficult to realize you should be refactoring towards having a domain model.&lt;br /&gt;&lt;br /&gt;You can't be sure whether your app will be really data-centric until it's finished and the most flexible design to cope with this doubt is &lt;strong&gt;starting&lt;/strong&gt; with the &lt;strong&gt;simplest&lt;/strong&gt; domain model. If at the end it gets anemic and you don't find it's behaviour outside, possibly in a service layer, then you can substitute it with the data layer, which could be Active Record.&lt;br /&gt;&lt;br /&gt;So I like Martin's idea of considering the datalayer as an implementation detail. It may be an important detail, but details don't belong to abstractions, and if you mix them you fix'em (sorry couldn't avoid that).&lt;br /&gt;&lt;br /&gt;It's easier to start flexible and then tighten than start tight and then get flexible.&lt;br /&gt;&lt;br /&gt;So I think it's easier to add Active Records to your domain models than adding a domain model to your Active Records. And you'll have the advantage that you are not tied with a particular Active Record implementation.&lt;br /&gt;&lt;br /&gt;I'd use a pure AR solution only if I'm very very sure that I'm dealing with a simple CRUD that won't change in the future, and even then I'd feel uncomfortable. The application may be data centric now but you can't be sure if it will keep being that way in the future. You don't know that even if you are the only client of the app.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-3442366309101462917?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/3442366309101462917/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=3442366309101462917' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3442366309101462917'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/3442366309101462917'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/11/about-object-relational-impedance.html' title='About the object-relational impedance mismatch'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-158895405334015609</id><published>2007-10-30T22:23:00.000-02:00</published><updated>2008-12-09T01:47:03.005-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='RSpec'/><category scheme='http://www.blogger.com/atom/ns#' term='Autotest'/><title type='text'>Running RSpec with Autotest on Windows</title><content type='html'>If you are on Windows and you see a &lt;i&gt;LoadError&lt;/i&gt; similar to this when running autotest:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_Jy_WaM9LS-I/RyRqC3hHWDI/AAAAAAAAAU0/r866ZNRcxhI/s1600-h/autotestError.GIF"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_Jy_WaM9LS-I/RyRqC3hHWDI/AAAAAAAAAU0/r866ZNRcxhI/s200/autotestError.GIF" border="0" alt="custom_require.rb:27:in `gem_original_require': no such file to load -- c:\ruby\bin\spec (LoadError) from..." id="BLOGGER_PHOTO_ID_5126338873349593138" /&gt;&lt;/a&gt;&lt;br /&gt;Open &lt;code&gt;ruby\gems\1.8\gems\rspec-1.0.8\lib\autotest\rspec.rb&lt;/code&gt; and change this line &lt;br /&gt;&lt;pre name="code" class="Ruby"&gt;&lt;br /&gt;    return "#{ruby} -S #{@spec_command} #{add_options_if_present} #{files_to_test.keys.flatten.join(' ')}"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;with this one&lt;br /&gt;&lt;pre name="code" class="Ruby"&gt;&lt;br /&gt;    return "#{ruby} -S spec #{add_options_if_present} #{files_to_test.keys.flatten.join(' ')}"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This fixed my problem but of course it's just a hack.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-158895405334015609?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/158895405334015609/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=158895405334015609' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/158895405334015609'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/158895405334015609'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/10/prueba_28.html' title='Running RSpec with Autotest on Windows'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Jy_WaM9LS-I/RyRqC3hHWDI/AAAAAAAAAU0/r866ZNRcxhI/s72-c/autotestError.GIF' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-6784831486604932617</id><published>2007-10-29T00:38:00.000-02:00</published><updated>2007-10-29T02:14:55.706-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Linq'/><title type='text'>Ruby and DSLs</title><content type='html'>&lt;a href="http://www.xaop.com/articles/2007/10/07/metaprogramming"&gt;Here&lt;/a&gt; you can see an excelent screencast by Peter Vanbroekhoven about creating DSLs in Ruby.&lt;br /&gt;This is another appealing aspect of the language.&lt;br /&gt;&lt;br /&gt;In my work I program mainly in C# and I couldn't avoid comparing his example with Linq.&lt;br /&gt;I still didn't put my hands deeply on Linq, and I guess it must be much bigger than Peter's example but I can't avoid being amazed at the simplicity in which he shows how to have your own hand made Linq in Ruby with very little work.&lt;br /&gt;&lt;br /&gt;So Ruby seems to be not just a programming language, it's a DSL programming language which it's something extremely powerful due to the productivity and readability that brings the higher level of abstraction. That's always good.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-6784831486604932617?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/6784831486604932617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=6784831486604932617' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6784831486604932617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/6784831486604932617'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/10/ruby-and-dsls.html' title='Ruby and DSLs'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-2925532621514808243</id><published>2007-10-28T20:24:00.000-02:00</published><updated>2007-10-28T20:32:32.238-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Humor'/><title type='text'>Funny BDD logo</title><content type='html'>This is a nice one from &lt;a href="http://www.robbyonrails.com/articles/2007/02/08/is-bdd-kinkier-than-tdd"&gt;Robby on Rails&lt;/a&gt;&lt;/br&gt;&lt;br /&gt;&lt;img src="http://www.robbyonrails.com/files/kinkier_than_tdd.jpg" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-2925532621514808243?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/2925532621514808243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=2925532621514808243' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/2925532621514808243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/2925532621514808243'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/10/funny-bdd-logo.html' title='Funny BDD logo'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-5526479418711428245</id><published>2007-10-28T03:59:00.000-02:00</published><updated>2008-12-09T01:47:03.187-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Duck typing'/><title type='text'>Ruby</title><content type='html'>My new toy is the Ruby language (I know, toys are a serious thing). I'm experimenting a bit, trying to see why all the fuss. Until now I like what I see. It's my first experience with a dynamic language and I feel quite comfortable with it. What I like most until now is &lt;i&gt;duck typing&lt;/i&gt;, and no, it's not a typo, I really mean duck, the animal.&lt;br /&gt;&lt;br /&gt;This comes from the more pure concept of interface in which you consider that two different objects have the same interface if they respond to the same messages, to the same way of interacting, of talking to them.&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_Jy_WaM9LS-I/RyVUf3hHWEI/AAAAAAAAAU8/GMqjCQbJgBs/s1600-h/duck.gif"&gt;&lt;img id="BLOGGER_PHOTO_ID_5126596657286699074" style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://2.bp.blogspot.com/_Jy_WaM9LS-I/RyVUf3hHWEI/AAAAAAAAAU8/GMqjCQbJgBs/s200/duck.gif" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The duck analogy comes from saying that if something looks like a duck, then it's a duck. So in Ruby you don't need to define interfaces the way C#/Java does. Interfaces exist but you don't define them, they are already there, conceptually.&lt;br /&gt;&lt;br /&gt;The biggest advantage I see about this is that the effort needed to assure that your design is low coupled is greatly reduced. Any object can be substituted by any other that implements the required methods without extra work. This dramatically improves your efficiency when making changes and evolving your design and testing gets easier.&lt;br /&gt;&lt;br /&gt;Let's compare a bit C# and Ruby interfaces:&lt;br /&gt;&lt;br /&gt;&lt;pre class="c-sharp" name="code"&gt;&lt;br /&gt;interface Cuackable&lt;br /&gt;{&lt;br /&gt;  void Cuack();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class ADuck : Cuackable&lt;br /&gt;{&lt;br /&gt;  public void Cuack()&lt;br /&gt;  {&lt;br /&gt;    //cuack the ADuck way&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Swim()&lt;br /&gt;  {&lt;br /&gt;    //swim the ADuck way&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class BDuck : Cuackable&lt;br /&gt;{&lt;br /&gt;  public void Cuack()&lt;br /&gt;  {&lt;br /&gt;    //cuack the BDuck way&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Swim()&lt;br /&gt;  {&lt;br /&gt;    //swim the BDuck way&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;br /&gt;So If you need the &lt;code&gt;Cuackable&lt;/code&gt; interface from your client code, you declare a variable or parameter that will eventually hold an &lt;code&gt;ADuck&lt;/code&gt; or &lt;code&gt;BDuck&lt;/code&gt; implementation.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The compiler&lt;/b&gt; knows you can use &lt;code&gt;ADuck&lt;/code&gt; or &lt;code&gt;BDuck&lt;/code&gt; because they declare they implement the needed interface.&lt;br /&gt;&lt;b&gt;You&lt;/b&gt; know you can use &lt;code&gt;ADuck&lt;/code&gt; or &lt;code&gt;BDuck&lt;/code&gt; because they implement the method signatures that you need. So there's a mismatch between the way you think of the interface and the way the compiler does (Ruby doesn't have this mismatch, we'll see this later).&lt;br /&gt;&lt;br /&gt;What happens if now you realize that &lt;code&gt;Swim&lt;/code&gt; should be part of another interface? Well then you'll have to define a new &lt;code&gt;Swimable&lt;/code&gt; interface and define &lt;code&gt;Swim&lt;/code&gt; as it's only member. You'll also need to implement that interface in each of the classes that implement the method and you'll also need to make the clients use that interface.&lt;br /&gt;&lt;br /&gt;&lt;pre class="c-sharp" name="code"&gt;&lt;br /&gt;interface Cuackable&lt;br /&gt;{&lt;br /&gt;  void Cuack();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;interface Swimable&lt;br /&gt;{&lt;br /&gt;  void Swim();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class ADuck : Cuackable, Swimable&lt;br /&gt;{&lt;br /&gt;  public void Cuack()&lt;br /&gt;  {&lt;br /&gt;    //cuack the ADuck way&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Swim()&lt;br /&gt;  {&lt;br /&gt;    //swim the ADuck way&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class BDuck : Cuackable, Swimable&lt;br /&gt;{&lt;br /&gt;  public void Cuack()&lt;br /&gt;  {&lt;br /&gt;    //cuack the BDuck way&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void Swim()&lt;br /&gt;  {&lt;br /&gt;    //swim the BDuck way&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now imagine that you have a third client method that requires to use both &lt;code&gt;Cuack&lt;/code&gt; and &lt;code&gt;Swim&lt;/code&gt;. In that case you'd have to pass the same object in two parameters to the client method:&lt;br /&gt;&lt;br /&gt;&lt;pre class="c-sharp" name="code"&gt;&lt;br /&gt;void ClientMethod(Cuackable cuackable, Swimable swimable)&lt;br /&gt;{&lt;br /&gt;  //code&lt;br /&gt;  cuackable.Cuack();&lt;br /&gt;  //...&lt;br /&gt;  swimable.Swim();&lt;br /&gt;  //more code&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//later you could call ClientMethod with the same object if you think that's correct...&lt;br /&gt;aDuck = new ADuck();&lt;br /&gt;ClientMethod(aDuck, aDuck)&lt;br /&gt;&lt;br /&gt;//or with different objects if that's what's intended...&lt;br /&gt;ClientMethod(new ADuck(), new BDuck());&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Another alternative for the first example, although not the same, is creating another new interface that joins &lt;code&gt;Cuackable&lt;/code&gt; and &lt;code&gt;Swimable&lt;/code&gt;, a good name would be, ermmm... &lt;code&gt;Duck&lt;/code&gt;. Then now you'd be implementing three different interfaces, all describing the different ways your client code can see your objects.&lt;br /&gt;&lt;br /&gt;This grows exponentially being the number of possible interfaces 2^n - 1, where n is the number of public methods. Of course your clients see them only in the subset of 2^n - 1 that is useful to them, but this will be still a big number in non trivial systems, always assuming that you really believe in object polymorphism and the &lt;a href="http://www.objectmentor.com/resources/articles/isp.pdf"&gt;interface segregation principle&lt;/a&gt; which encourages having small interfaces.&lt;br /&gt;&lt;br /&gt;As you can see, such a big number of interfaces is impractical to handle in strongly typed languages so we usually change the way we design to cope with this programming infrastructure problem and we start violating principles of good design. The most common solution to this is having huge interfaces with clients that, of course, just use a subset of them. This is, among other problems, a manteinance nightmare. But a smaller nightmare than the more correct alternative of having one interface for each method.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Ruby's duck typing&lt;/h5&gt;Let's see the previous code in it's Ruby version:&lt;br /&gt;&lt;br /&gt;&lt;pre class="ruby" name="code"&gt;&lt;br /&gt;class ADuck&lt;br /&gt;  def cuack&lt;br /&gt;    #cuack the ADuck way&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def swim&lt;br /&gt;    #swim the ADuck way&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class BDuck&lt;br /&gt;  def cuack&lt;br /&gt;    #cuack the BDuck way&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def swim&lt;br /&gt;    #swim the BDuck way&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def client_method(can_cuack_and_swim)&lt;br /&gt;  #code&lt;br /&gt;  can_cuack_and_swim.cuack&lt;br /&gt;  #...&lt;br /&gt;  can_cuack_and_swim.swim&lt;br /&gt;  #more code&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;client_method(ADuck.new)&lt;br /&gt;&lt;br /&gt;#or you can split it in two like the previous C# example&lt;br /&gt;&lt;br /&gt;def client_method(can_cuack, can_swim)&lt;br /&gt;  #code&lt;br /&gt;  can_cuack.cuack&lt;br /&gt;  #...&lt;br /&gt;  can_swim.swim&lt;br /&gt;  #more code&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;client_method(ADuck.new, BDuck.new)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, we don't define interfaces, they are implicitly there because as we said earlier, interfaces are defined by the clients. &lt;code&gt;client_method&lt;/code&gt; just asks that the object that is passed as a parameter implements the required method. If you create a new class that implements those required methods, then you implement &lt;code&gt;client_method&lt;/code&gt; required interface, your client sees a duck because your object behaves the way it expects, like a duck, no more extra work for creating and mantaining your classes interfaces. That's simplicity and programmer friendliness, from there you get easier abstraction, free low coupling and the resultant increase in quality and productivity in the right way.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-5526479418711428245?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/5526479418711428245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=5526479418711428245' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/5526479418711428245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/5526479418711428245'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/10/ruby.html' title='Ruby'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_Jy_WaM9LS-I/RyVUf3hHWEI/AAAAAAAAAU8/GMqjCQbJgBs/s72-c/duck.gif' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3836263610079655093.post-2023836471417967213</id><published>2007-10-24T01:06:00.000-02:00</published><updated>2007-10-27T03:48:55.667-02:00</updated><title type='text'>Welcome!</title><content type='html'>Welcome to my technical blog!&lt;br /&gt;&lt;br /&gt;Here I intend to have a place to write about all the things I find interesting in my profession. Next, I hope my posts are also interesting. My second objective is having a place to keep all those code snippets and small tips we all learn while working, which in my case are usually spread between books, computer files, notes, torn papers, and the most volatile of them all, my head. I just hope I don't forget my &lt;em&gt;url.&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3836263610079655093-2023836471417967213?l=www.danielcadenas.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.danielcadenas.com/feeds/2023836471417967213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3836263610079655093&amp;postID=2023836471417967213' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/2023836471417967213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3836263610079655093/posts/default/2023836471417967213'/><link rel='alternate' type='text/html' href='http://www.danielcadenas.com/2007/10/welcome.html' title='Welcome!'/><author><name>Daniel Cadenas</name><uri>http://www.blogger.com/profile/07935636187234627682</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://lh3.google.es/dcadenas/Ra-_PqMllUI/AAAAAAAAAAw/-221g8_4dkc/594183122.jpg'/></author><thr:total>0</thr:total></entry></feed>
