tag:blogger.com,1999:blog-64455766184929399382019-09-11T02:01:13.450-07:00sometimes nothin' can be a real cool handMarty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.comBlogger111125tag:blogger.com,1999:blog-6445576618492939938.post-85768254643181212872013-01-24T21:52:00.001-08:002013-01-24T21:52:18.280-08:00Roodi 2.2.0 released<p>Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured. </p><p>Changes in 2.2.0: <ul><li>More changes around 1.9 compatibility.</li>
<li>Updated to use bundler.</li>
</ul></p><p><a href="https://rubygems.org/gems/roodi">https://rubygems.org/gems/roodi</a> </p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com4tag:blogger.com,1999:blog-6445576618492939938.post-70300153226615257482012-10-21T03:08:00.004-07:002012-10-21T03:08:34.581-07:00Best videos of SF MusicTech Summit 2012One of the side effects of running a business like <a href="http://www.eventer.com/">Eventer</a> (more about that in another post) is that I get to see videos of great conference content a <b><i>lot</i></b>. A little over a week ago, <a href="http://www.sfmusictech.com/">SF MuiscTech Summit 2012</a> was recorded and published on an <a href="http://sfmusictech.eventer.com/">SF MusicTech Eventer channel</a>. Here's my summary of the most popular talks, and my personal picks of the bunch.<br />
<br />
<h2>Most Popular</h2><br />
There's two clear winners in the most popular category at the conference.<br />
<br />
Firstly, there's a panel named named Artist Tools, where <a href="http://twitter.com/HishamDahud">Hisham Dahud</a> moderates a panel with <a href="http://twitter.com/Renman1">Steve Rennie</a>, <a href="http://twitter.com/DavidDufresne">David Dufresne</a>, <a href="http://twitter.com/taynahreis">Taynah Reis</a>, <a href="http://twitter.com/mattmason">Matt Mason</a> and <a href="http://twitter.com/haynes_dave">David Haynes</a>. They talk about tools that artists can use to operate the digital side of their business.<br />
<br />
<iframe frameborder="0" height="370" scrolling="no" src="http://sfmusictech.eventer.com/SF-MusicTech-Summit-11-1007/Artist-Tools-by-Steve-Rennie-and-David-Dufresne-and-Taynah-Reis-and-Matt-Mason-and-Hisham-Dahud-and-David-Haynes-1080?embed=true&size=small" width="640"></iframe><br />
<br />
Secondly, Celia Hirschman interview the very cool <a href="http://twitter.com/michaelfranti">Michael Franti</a> of <a href="http://michaelfranti.com/">Spearhead</a>. He talks about his music, fans, social media, Barack Obama and more. Jump to 21:10 if you just wanna see him start rockin on the guitar.<br />
<br />
<iframe frameborder="0" height="370" scrolling="no" src="http://sfmusictech.eventer.com/SF-MusicTech-Summit-11-1007/Sci-Fi-to-Hi-Fi:-A-Conversation-on-the-Future-of-Music-&-Technology-by-Michael-Franti-and-Celia-Hirschman-1112?embed=true&size=small" width="640"></iframe><br />
<br />
<h2>My Favourites</h2><br />
I'm a geek at heart, so my favourite talks at the conference were a couple of demos of some new iPad apps. In both cases, I watched and said "wow". They blew me away.<br />
<br />
The first is commercial app about to be released. The presenter, with a straight face, says "<i>Our technology analyses and codifies the meaningful elements in a song, capturing the compositional genome and then deploys that through the architecture, to render it interactively.</i>" I ate it up, maybe because the software was so cool and I could see myself (with no musical ability) playing some cool music.<br />
<br />
Here's <a href="http://twitter.com/recombinantinc">Dave Park</a> talking about <a href="http://www.jambandit.com/">JamBandit</a>.<br />
<br />
<iframe frameborder="0" height="370" scrolling="no" src="http://sfmusictech.eventer.com/SF-MusicTech-Summit-11-1007/Demo:-JamBandit-by-Dave-Park-1122?embed=true&size=small" width="640"></iframe><br />
<br />
Finally, as part of the SF MusicTech hack day, <a href="http://twitter.com/yosunchang">Yosun Chang</a> built a virtual reality app named <a href="http://aralbum.com/">AR Album</a> in just 6 hours! It lets you imagine you're holding an original vinyl album in your hands, including the ability to slide the record out from the cover, Incredible.<br />
<br />
<iframe frameborder="0" height="370" scrolling="no" src="http://sfmusictech.eventer.com/SF-MusicTech-Summit-11-1007/Hack-Day-Winner:-AR-Album-by-Yosun-Chang-1126?embed=true&size=small" width="640"></iframe><br />
Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com3tag:blogger.com,1999:blog-6445576618492939938.post-24477094728629470172012-01-14T20:51:00.000-08:002012-01-14T20:51:06.926-08:00House hunting letter drop<br />
The real estate market naturally slows down over the christmas period, and my house hunting for the previous few months hadn't been very fruitful, so I decided to do a letter drop in the are we wanted to buy in. &nbsp;Here's what I wrote:<br />
<br />
<blockquote class="tr_bq">
Dear homeowner,<br />
<br />
Hi. My name is Marty Andrews. My wife and I moved to the area about 6 months ago to be closer to family, and been looking for a house in xxxxxxxxx to buy for our young family. We are wondering if you might be interested in having a conversation about selling your home. If not, thanks for reading this far, and please disregard this letter.<br />
<br />
We’re open to all sorts of options, from major renovations to homes that are ready to move in. We’re ready to make a decision quickly if needed, but we can also be flexible on long settlement periods if you need time to find another place of your own.<br />
<br />
I’d be happy to share what I know about recent sales in the area to help figure out a fair price for your home, or to get some independent advice too if you want to. A direct sale could mean hefty savings on commission and advertising for you.<br />
<br />
If you think you might be interested, please call me on xxxx xxx xxx, or email me at xxxxx@xxxxxxxxxxx.xxx with as much or as little detail as you like. I’m happy to explore any option further to see if it is right for both of us.<br />
<br />
Thanks for your time.<br />
<br />
Marty Andrews</blockquote>
<br />
For a cost of about $25, I created about 400 copies of that letter and dropped it in houses in an area I was interested in. &nbsp;I didn't bother with envelopes. &nbsp;The letters were dropped off on January 1st and 2nd. &nbsp;At the time I did it, I hoped I might get about 0.5% as a response rate.<br />
<br />
Now, a couple of weeks later, my response rate has been exactly that. &nbsp;I got one phone call and one email. &nbsp;One of those responses was for a house that I was actually interested in, and from a seller who wasn't otherwise exploring selling. &nbsp;That was great, because I got to have a conversation that I would never have had if I had just waited for real estate listings to appear.<br />
<br />
As it turned out, the seller was hoping for too high a price, biased by a valuation he had done about 6 months ago (the market has steadily dropped since then). &nbsp;We both walked away totally relaxed and happy though, as our investment in the conversation was virtually nil.<br />
<br />
I'll absolutely try this technique again one day if I'm buying again. &nbsp;It's so simple and cheap, and much more comfortable to have a conversation without an agent trying to influence both parties.Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com61tag:blogger.com,1999:blog-6445576618492939938.post-72454377160242443222011-10-25T17:51:00.000-07:002011-10-25T17:52:24.532-07:00A ruby spec profiling storyI've always used ruby-prof in the past to profile my Ruby code, but a colleague yesterday put me on to the <a href="https://github.com/tmm1/perftools.rb">Google perftools for Ruby</a> code. A bit of research turned up some interesting things.<br />
<br />
According to <a href="http://blog.pluron.com/2008/01/ruby-on-rails-i.html">http://blog.pluron.com/2008/01/ruby-on-rails-i.html</a>,<br />
<blockquote>
<i>ruby-prof attributes full cost of garbage collection to the method where GC gets triggered, not the methods that allocate the memory</i></blockquote>
<br />
That was news to me. I tried running the profiler over an app I'm working on, and got this:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Total: 23530 samples</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">13244 56.3% 56.3% 13244 56.3% garbage_collector</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">4223 17.9% 74.2% 4331 18.4% ActiveRecord::ConnectionAdapters::Mysql2Adapter#execute</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">678 2.9% 77.1% 678 2.9% ActiveRecord::ConnectionAdapters::Mysql2Adapter#active?</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">402 1.7% 78.8% 403 1.7% MonitorMixin::ConditionVariable#signal</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">258 1.1% 79.9% 294 1.2% Method#call</span><br />
<br />
Wow! That's a massive amount of time in GC! What can I do about that?<br />
<br />
That lead me to an article by Jamis Buck at <a href="http://37signals.com/svn/posts/2742-the-road-to-faster-tests">http://37signals.com/svn/posts/2742-the-road-to-faster-tests</a>. It describes a way to address GC issues for Test::Unit, but there's a nice rspec version at <a href="https://gist.github.com/790118">https://gist.github.com/790118</a>. I applied that to my code and ran it again. The new profile was:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Total: 11331 samples</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">3730 32.9% 32.9% 3804 33.6% ActiveRecord::ConnectionAdapters::Mysql2Adapter#execute</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">2021 17.8% 50.8% 2021 17.8% garbage_collector</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">720 6.4% 57.1% 720 6.4% ActiveRecord::ConnectionAdapters::Mysql2Adapter#active?</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">376 3.3% 60.4% 376 3.3% MonitorMixin::ConditionVariable#signal</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">352 3.1% 63.5% 353 3.1% Object#reconsider_gc_deferment</span><br />
<br />
My spec build time also cam down from 375 seconds to 238 seconds. That's a massive improvement.<br />
<br />
The post form Jamis also mentions cleaning up some instance variables, which I'll try amongst some further tweaking, but that improvement was significant enough that I figured it might point someone else in the right direction too.Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com3tag:blogger.com,1999:blog-6445576618492939938.post-18954976307715796072011-01-06T02:28:00.000-08:002011-01-06T02:34:01.131-08:00Collaboration Day<p>
Many IT companies have embraced the idea of giving their staff some autonomy in working on something of their own without any constraints. Perhaps most famous are <a href="http://blogs.atlassian.com/news/2010/11/fedex_day_in_the_wild.html">Atlassians FedEx day</a> and <a href="http://googleblog.blogspot.com/2006/05/googles-20-percent-time-in-action.html">Googles 20% time</a>. Those companies, and others who are trying the idea, are beginning to reap the rewards in terms of innovations that have evolved into more concrete solutions.
</p>
<p>
At Cogent, we're trying to embrace that idea too, with a spin on it that matches the style of work we do and the values we have internally. Firstly, we're a consulting company. Most of the work we do is spent on a customer site, and they pay us for the hours we're there. We need to balance the customers needs with any internal activities we have. Secondly, our staff all want to work with each other. For most of us, that's a big factor for why we joined Cogent in the first place. Finally, we're a family friendly company that tries very hard to support a balance between home life and time at work.
</p>
<p>
The implementation we have settled on is an idea we call Collaboration Day. One day a month, everyone in the company takes the day off from consulting and comes to spend it in the office. It's booked well in advance, so our customers are aware of what is going on. The day is a normal working day, so nobody is expected to take any time off or work after hours to be there.
</p>
<p>
On the morning of the day, everyone brainstorms ideas for things to work on and we put them together on a planning wall. They might be expected to take anywhere from an hour up to the whole day. People then sign up to work on things and get started. You can switch between ideas as many times as you like during the day.
</p>
<p>
There's only one rule: you cannot work alone.
</p>
<p>
At the end of the day, we all get together and have a short presentation about what people worked on and do a demo of what was built.
</p>
<p>
For Cogent, Collaboration Day is working really well so far. Many demos spawn new ideas for the next Collaboration Day, and some inspire people to do some more in their own time. A few are coming up for discussion about Cogent sponsoring more time to evolve them further. Those outcomes are exactly what we're hoping for.
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com3tag:blogger.com,1999:blog-6445576618492939938.post-5915226168663308202010-11-01T16:12:00.000-07:002010-11-01T16:13:16.221-07:00PragProWriMoNovember is the <a href="http://forums.pragprog.com/forums/190">Pragmatic Programmers Writing Month</a>, or PragProWriMo for short. It's an idea they stole from the <a href="http://www.nanowrimo.org/">National Novel Writing Month</a> (NaNoWriMo). They're encouraging people to spend the month writing, with the goal of putting together a book. No doubt their publishing business will get something out of it. The declared target is 60 pages worth, which means averaging 2 pages per day. I'm not sure if I can commit to that much, but I am hoping to get back into the habit of writing a bit each day. Some of that writing may come out in this blog, but I'm not expecting it all will. Perhaps some of it will never see the light of day. For now, I'll just start writing, and see where it takes me.Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com2tag:blogger.com,1999:blog-6445576618492939938.post-47608840033797305282010-02-08T23:16:00.000-08:002010-02-08T23:21:37.874-08:00Cogent without Steve Hayes<p>
In mid 2006, Steve Hayes and I were both working as independent consultants. I had known Steve since 2001, when the Melbourne XP Enthusiasts Group (MXPEG) had started up and we both became active members. When I became independent, he was kind enough to let me use the company name he had registered, Cogent Consulting, for invoicing. I paid him a nominal fee for processing my payroll.
</p>
<p>
Within six months, Steve and I decided to run some public training courses together to help market ourselves, and improve the Cogent brand for us both. Easy Access Training (EAT) was born. We ran a few reasonably successful public courses in early 2007.
</p>
<p>
The market at the time was quite buoyant, and pretty soon Steve and I both found ourselves turning away work because we were already engaged on projects. That triggered conversations between us about taking the opportunity to build Cogent as a business, and we decided to hire some staff and pay ourselves a salary rather than remaining effectively independent.
</p>
<p>
And so, on July 1 2007, Cogent Consulting was re-born as a structured business. Two and a half years later, we're a profitable consulting company with a dozen staff. Our values and policies are unlike any other local company that I know of, and we're proud of those unique attributes.
</p>
<p>
Sadly, <a href="http://iridescenturchin.blogspot.com/2010/01/cogent-departure.html">Steve is now planning to leave</a> the country soon, and therefore Cogent as well.
</p>
<p>
To say he'll be missed is an understatement. For me, Steve has been a friend, mentor and business partner for years. The shape of my career has undeniably been shaped by his influence, and the opportunity to help build a great company came with our partnership. For the rest of the staff at Cogent, Steve has been a thought leader and guiding influence.
</p>
<p>
I'm charged with being the steward of the company in Steve's absence. I'm quietly confident of my ability to do the job. Indeed, I've been sharing the role with Steve since the beginning, and we've got a good team around us to help. Cogent Consulting will continue on comfortably without Steve, which is perhaps one of his great achievements. It's culture will undeniably change though. Perhaps sometimes better, more often a little bit worse, but mostly just different. That difference is what I'll miss most of all.
</p>
<p>
Good luck Steve. I hope your new adventures are prosperous and enjoyable. With any luck, our paths will again cross someday sooner rather than later.
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com2tag:blogger.com,1999:blog-6445576618492939938.post-25391946952116296612009-12-27T23:07:00.000-08:002009-12-27T23:10:35.113-08:00Roodi 2.1.0 released<p>
Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured.
</p>
<p>
Changes in 2.1.0:
<ul>
<li>Ruby 1.9 compatible.</li>
<li>New framework support to allow start and end file events.</li>
<li>Added MissingForeignKeyIndexCheck. Still experimental for now.</li>
</ul>
</p>
<p>
<a href="http://gemcutter.org/gems/roodi">http://gemcutter.org/gems/roodi</a>
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-87162284616239117202009-10-25T17:19:00.000-07:002009-10-25T17:23:02.631-07:00Roodi 2.0.1 released<p>
Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured.
<p>
<p>
Changes in 2.0.1:
<p>
<ul>
<li>Fixed a bug where roodi.yml was not being loaded. Patch supplied by Rob Mitchell.</li>
</ul>
</p>
<p>
<a href="http://roodi.rubyforge.org/">http://roodi.rubyforge.org/</a>
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-61781812600944798302009-08-23T04:23:00.000-07:002009-08-23T04:27:21.225-07:00Roodi 2.0.0 released<p>
Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured.
</p>
<p>
Changes in 2.0.0:
<ul>
<li>Changed internal structure to use a design much more like a visitor pattern.</li>
<li>Got *much* faster as a result of the change.</li>
<li>Design change fixed 'feature' where nested blocks would all get listed if the inner one exceeded complexity.</li>
<li>Outline for NPath complexity check is now possible. Not working yet though.</li>
<li>Removed dependency on facets library.</li>
</ul>
</p>
<p>
<a href="http://roodi.rubyforge.org/">http://roodi.rubyforge.org/</a>
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com2tag:blogger.com,1999:blog-6445576618492939938.post-72307227979901029242009-08-15T23:43:00.001-07:002009-08-16T00:06:21.743-07:00GreenScreen - a build monitor BVC<p>
I've just released <a href="http://martinjandrews.github.com/greenscreen/">GreenScreen</a>. It's a build monitor that aggregates feeds from your build server and publishes them as large as it can on a web page. The idea is that you can set up a monitor somewhere in your workspace running GreenScreen so that everyone can see the current status of the build all the time.
</p>
<p>
Other tools like <a href="http://ccmenu.sourceforge.net/">CCMenu</a> and <a href="http://confluence.public.thoughtworks.org/display/CCNET/CCTray">CCTray</a> exist so that people can see the current status on their local computers, and they are absolutely useful. Having something in your workspace that is visible to *anyone* that happens to walk past has some powerful implications though. Many teams actually use a build light or lava lamps that change colour when builds pass or break. GreenScreen is a software based equivalent that you can use on any spare computer.
</p>
<p>
A widely visible build status means the team is completely transparent all of the time to anyone who can see it. For teams I've coached in recent times, managers, project stakeholders and regular visitors would often ask what the monitor meant, and quickly learnt to ask what was wrong if builds were red. It gave the team a strong sense of commitment to keep the build passing.
</p>
<p>
I hope you find it useful. Have a look at the pics below for some examples of what GreenScreen looks like for projects with various numbers of builds:
</p>
<img src="http://martinjandrews.github.com/greenscreen/one.jpg" alt="A single project"/>
<img src="http://martinjandrews.github.com/greenscreen/few.jpg" alt="A few projects"/>
<img src="http://martinjandrews.github.com/greenscreen/some.jpg" alt="Many projects"/>
<img src="http://martinjandrews.github.com/greenscreen/lots.jpg" alt="Lots of projects"/>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com5tag:blogger.com,1999:blog-6445576618492939938.post-47288732332942102402009-05-29T22:35:00.000-07:002009-05-30T00:52:11.889-07:00Enforcing Ruby code quality<p>
Over the last year or so, there's been lots of great tools coming out in the Ruby community that help with static analysis of code. We've now got <a href="http://ruby.sadi.st/Flay.html">flay</a>, <a href="http://ruby.sadi.st/Flog.html">flog</a>, <a href="http://wiki.github.com/kevinrutherford/reek">reek</a> and <a href="http://roodi.rubyforge.org">roodi</a>. <a href="http://metric-fu.rubyforge.org">metric-fu</a> runs all of these tools (plus more) to generate nice reports.
</p>
<p>
Generating nice reports is all well and good, but frankly it's not enough. We need to start <em>enforcing</em> code quality, not just reporting on it. The Java community is way ahead on this front, which I find rather embarrassing as a Ruby developer. Fear not though, I'm going to show you exactly how to set it up here.
</p>
<h2>Getting Started</h2>
<p>
First of all, you need a continuous integration build. Don't have one? Fail. Go have a good hard look at yourself in the mirror and consider if you really want to be a professional software developer or not.
</p>
<p>
If you're still here, you need the tools installed. That's pretty easy:
<blockquote>
<pre>
sudo gem install flay
sudo gem install flog
sudo gem install reek
sudo gem install roodi
sudo gem sources -a http://gems.github.com
sudo gem install jscruggs-metric_fu
</pre>
</blockquote>
</p>
<p>
What you've just installed is a set of tools that check for duplicate code, complex code, code smells and code problems. The last one (metric-fu) will give you a report to look at if you need to browse for more details.
</p>
<p>
Next, you need to add some tasks to your rakefile. For my rails applications, I have this in <code>lib/tasks/quality.rake</code>, but you can put it anywhere that gets loaded by your build.
<pre class="textmate-source sunburst"><span class='linenum'> 1</span> <span class="source source_ruby"><span class="meta meta_require meta_require_ruby"><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">require</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>flog<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span></span>
<span class='linenum'> 2</span> <span class="meta meta_require meta_require_ruby"><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">require</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>flay<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span></span>
<span class='linenum'> 3</span> <span class="meta meta_require meta_require_ruby"><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">require</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>roodi<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span></span>
<span class='linenum'> 4</span> <span class="meta meta_require meta_require_ruby"><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">require</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>roodi_task<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span></span>
<span class='linenum'> 5</span> <span class="meta meta_require meta_require_ruby"><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">require</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>metric_fu<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span></span>
<span class='linenum'> 6</span>
<span class='linenum'> 7</span> desc <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>Analyze for code complexity<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span>
<span class='linenum'> 8</span> task <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>flog</span> <span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby">do
</span><span class='linenum'> 9</span> flog <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="support support_class support_class_ruby">Flog</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">new</span>
<span class='linenum'> 10</span> flog<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>flog_files <span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span><span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>app<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span>
<span class='linenum'> 11</span> threshold <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="constant constant_numeric constant_numeric_ruby">40</span>
<span class='linenum'> 12</span>
<span class='linenum'> 13</span> bad_methods <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> flog<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>totals<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>select <span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby">do </span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span><span class="variable variable_other variable_other_block variable_other_block_ruby">name</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">,</span> <span class="variable variable_other variable_other_block variable_other_block_ruby">score</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span>
<span class='linenum'> 14</span> score <span class="keyword keyword_operator keyword_operator_comparison keyword_operator_comparison_ruby">&gt;</span> threshold
<span class='linenum'> 15</span> <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class='linenum'> 16</span> bad_methods<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>sort <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">{</span><span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"> </span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span><span class="variable variable_other variable_other_block variable_other_block_ruby">a</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">,</span><span class="variable variable_other variable_other_block variable_other_block_ruby">b</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span> a<span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span><span class="constant constant_numeric constant_numeric_ruby">1</span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span> <span class="keyword keyword_operator keyword_operator_comparison keyword_operator_comparison_ruby">&lt;=&gt;</span> b<span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span><span class="constant constant_numeric constant_numeric_ruby">1</span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span> <span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">}</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>each <span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby">do </span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span><span class="variable variable_other variable_other_block variable_other_block_ruby">name</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">,</span> <span class="variable variable_other variable_other_block variable_other_block_ruby">score</span><span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby">|</span>
<span class='linenum'> 17</span> puts <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>%8.1f: %s<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span> <span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby">%</span> <span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span>score<span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> name<span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span>
<span class='linenum'> 18</span> <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class='linenum'> 19</span>
<span class='linenum'> 20</span> <span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">raise</span> <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span><span class="source source_ruby source_ruby_embedded source_ruby_embedded_source"><span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">#{</span>bad_methods<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>size<span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">}</span></span> methods have a flog complexity &gt; <span class="source source_ruby source_ruby_embedded source_ruby_embedded_source"><span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">#{</span>threshold<span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">}</span></span><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span> <span class="keyword keyword_control keyword_control_ruby">unless</span> bad_methods<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>empty?
<span class='linenum'> 21</span> <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class='linenum'> 22</span>
<span class='linenum'> 23</span> desc <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span>Analyze for code duplication<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span>
<span class='linenum'> 24</span> task <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>flay</span> <span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby">do
</span><span class='linenum'> 25</span> threshold <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="constant constant_numeric constant_numeric_ruby">25</span>
<span class='linenum'> 26</span> flay <span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby">=</span> <span class="support support_class support_class_ruby">Flay</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">new</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">{</span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>fuzzy</span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> <span class="constant constant_language constant_language_ruby">false</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>verbose</span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> <span class="constant constant_language constant_language_ruby">false</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>mass</span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> threshold<span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby">}</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">)</span>
<span class='linenum'> 27</span> flay<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>process<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby">*</span><span class="support support_class support_class_ruby">Flay</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>expand_dirs_to_files<span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">(</span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span><span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>app<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span><span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby">))</span>
<span class='linenum'> 28</span>
<span class='linenum'> 29</span> flay<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>report
<span class='linenum'> 30</span>
<span class='linenum'> 31</span> <span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">raise</span> <span class="string string_quoted string_quoted_double string_quoted_double_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">"</span><span class="source source_ruby source_ruby_embedded source_ruby_embedded_source"><span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">#{</span>flay<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>masses<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>size<span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">}</span></span> chunks of code have a duplicate mass &gt; <span class="source source_ruby source_ruby_embedded source_ruby_embedded_source"><span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">#{</span>threshold<span class="punctuation punctuation_section punctuation_section_embedded punctuation_section_embedded_ruby">}</span></span><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">"</span></span> <span class="keyword keyword_control keyword_control_ruby">unless</span> flay<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>masses<span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span>empty?
<span class='linenum'> 32</span> <span class="keyword keyword_control keyword_control_ruby">end</span>
<span class='linenum'> 33</span>
<span class='linenum'> 34</span> <span class="support support_class support_class_ruby">RoodiTask</span><span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby">.</span><span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby">new</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>roodi<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span><span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>app/**/*.rb<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>roodi.yml<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span>
<span class='linenum'> 35</span>
<span class='linenum'> 36</span> task <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>quality</span> <span class="punctuation punctuation_separator punctuation_separator_key-value">=&gt;</span> <span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">[</span><span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>flog</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>flay</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"><span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby">:</span>roodi</span><span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby">,</span> <span class="string string_quoted string_quoted_single string_quoted_single_ruby"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_ruby">'</span>metrics:all<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_ruby">'</span></span><span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby">]</span></span></pre>
</p>
<p>
Now, add the <code>quality</code> task to the list of things done in your continuous integration build. That will make sure these things run all the time. Try running <code>rake quality</code> manually. It will almost certainly fail on either flog, flay or roodi. I'll show you how to tweak them one at a time.
</p>
<h2>Flog</h2>
<p>
When I ran <code>rake flog</code> for the first time, I got something like this:
<blockquote>
<pre>
~/Data/runway $ rake flog
(in /Users/marty/Data/runway)
42.3: Token#parse_incubation_days
60.4: Token#parse_incubation_date
81.7: ActionParser#parse
89.7: ActionFormat#format
rake aborted!
4 methods have a flog complexity > 40
</pre>
</blockquote>
</p>
<p>
This tells me that I have four methods in my code base that have a flog complexity greater than 40. If you look back at line 11 of <code>quality.rake</code> shown above, you'll see that this threshold is configurable. Set that number to something that is just higher than the most complex method in your application (in my case, I set it to 90) and run <code>rake flog</code> again. It should pass.
</p>
<p>
What you've just done is create a stop loss for complexity in your application. If anyone checks in a method that is more complex than the configured threshold, your build will fail. In other words, methods are not allowed to get any more complex than the most complex method in your application now.
</p>
<p>
Unless you're working on a green fields application, I'll bet that the threshold you set was higher than you want it be. That method that was the most complex in your application needs some refactoring. You've got somewhere to focus your refactoring efforts now though. Pick that method, refactor it, and ratchet down the threshold to the next most complex method. Now you're incrementally improving the quality in your app in terms of complexity.
</p>
<h2>Flay</h2>
<p>
When I ran <code>rake flay</code> for the first time, I got something like this:
<blockquote>
<pre>
~/Data/runway $ rake flay
(in /Users/marty/Data/runway)
Total score (lower is better) = 164
1) Similar code found in :defn (mass = 60)
app/controllers/signups_controller.rb:5
app/controllers/user_sessions_controller.rb:6
2) Similar code found in :if (mass = 54)
app/controllers/forgot_passwords_controller.rb:15
app/controllers/signups_controller.rb:16
3) Similar code found in :defn (mass = 50)
app/helpers/actions_helper.rb:43
app/helpers/actions_helper.rb:49
rake aborted!
3 chunks of code have a duplicate mass > 25
</pre>
</blockquote>
</p>
<p>
This tells me that I have two pieces of code with a mass greater than 30 that have been duplicated. If you look back at line 25 of <code>quality.rake</code> shown above, you'll see that this threshold is configurable. Set that number to something that is just higher than the largest mass of duplicated code in your application. This can be a little confusing, because the tool reports a number equal to that mass multiplied by the number of times it appears. In my case, the reported mass of 60 is actually 30 (a mass of 30 found in 2 places). So I set the threshold to 30. Run <code>rake flay</code> again. It should pass.
</p>
<p>
What you've just done is create a stop loss for duplication in your application. If anyone checks in code that duplicates an existing mass of code larger than the configured threshold, your build will fail. In other words, people can't copy and paste code in a fashion any worse than they already have.
</p>
<p>
Again, that threshold you set is almost certainly higher than you want it be. That largest mass of code that was duplicated needs some refactoring to make your code more DRY. Find that code, refactor it, and ratchet down the threshold to the next largest mass of code. Now you're incrementally improving the quality in your app in terms of duplication.
</p>
<h2>Roodi</h2>
<p>
When I ran <code>rake roodi</code> for the first time, I got something like this:
<blockquote>
<pre>
~/Data/runway $ rake roodi
(in /Users/marty/Data/runway)
app/models/action_format.rb:10 - Method name "format" cyclomatic complexity is 12. It should be 10 or less.
app/models/action_parser.rb:11 - Method name "attributes" cyclomatic complexity is 12. It should be 10 or less.
app/models/action_parser.rb:30 - Method name "parse" cyclomatic complexity is 14. It should be 10 or less.
app/models/token.rb:173 - Method name "parse_incubation_date" cyclomatic complexity is 11. It should be 10 or less.
app/models/token.rb:226 - Method name "parse_incubation_days" cyclomatic complexity is 12. It should be 10 or less.
app/models/action_parser.rb:50 - Block cyclomatic complexity is 11. It should be 8 or less.
app/models/token.rb:11 - Block cyclomatic complexity is 10. It should be 8 or less.
app/models/action.rb:103 - Method "apply_defaults_from_name" has 23 lines. It should have 20 or less.
app/models/action_parser.rb:30 - Method "parse" has 23 lines. It should have 20 or less.
rake aborted!
Found 9 errors.
</pre>
</blockquote>
</p>
<p>
This tells me that I have several pieces of code failing Roodi checks. If you look back at line 34 of <code>quality.rake</code> shown above, you'll see a reference to <code>roodi.yml</code>. This is a config file that allows you to configure Roodi. Create that file in the root directory of your application and paste the following contents into it. It's what I have in my config file.
<blockquote>
<pre>
# AssignmentInConditionalCheck: { }
# CaseMissingElseCheck: { }
ClassLineCountCheck: { line_count: 300 }
ClassNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
# ClassVariableCheck: { }
# TODO Get block cyclomatic complexity back down to 6 or less
CyclomaticComplexityBlockCheck: { complexity: 12 }
# TODO Get method cyclomatic complexity back down to 8 or less
CyclomaticComplexityMethodCheck: { complexity: 14 }
EmptyRescueBodyCheck: { }
ForLoopCheck: { }
# TODO Get method line count back down to 20 or less
MethodLineCountCheck: { line_count: 27 }
MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }
ModuleLineCountCheck: { line_count: 300 }
ModuleNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
ParameterNumberCheck: { parameter_count: 5 }
</pre>
</blockquote>
</p>
<p>
This config file describes a list of checks that get run by Roodi, and some parameters that are used to initialise those checks. Have a look at <a href="http://roodi.rubyforge.org/">http://roodi.rubyforge.org/</a> for details on each of those checks. Make a call on which checks are appropriate for your project, and then change the configuration on each item so that your build just passes. What you've just done is create a stop loss for these coding problems in your application. If anyone checks in code that doesn't pass these configured checks, your build will fail.
</p>
<p>
It's possible that you've had to comment out some checks that you really want on, to get the build to pass. For the ones that are still turned on, you've probably had to increase some of the thresholds. You can find that code, refactor it, and turn checks back on or ratchet down the thresholds over time. Now you're incrementally improving the quality in your app in terms of coding checks.
</p>
<h2>Summary</h2>
<p>
The installation of metric-fu will give you some nice reports to browse and find the next worst problems in your application. Run <code>rake metrics:all</code> to get that report generated. Put some time aside regularly to clean up your code and ratchet down the thresholds as described above until you get them to a point you're happy with.
</p>
<p>
As far as I'm concerned, these tools are not an optional extra for your project. You <b>must</b> have them tools set up. It's unprofessional of you not to do everything you reasonably can to keep the quality of your code high. I hope this helps.
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com21tag:blogger.com,1999:blog-6445576618492939938.post-20412165206129315812009-05-29T21:19:00.000-07:002009-05-29T21:59:39.295-07:00GTD Tweekly review<p>
<a href="http://www.davidco.com/blogs/kelly/">Kelly Forrister</a>, who goes by the name <a href="http://twitter.com/GTDCoachKelly">GTDCoachKelly on twitter</a> recently ran a GTD Tweekly review on twitter. I found it enourmously valuable, and we're in the middle of building support for weekly reviews into <a href="http://www.runwayapp.com/">Runway</a>, so I wanted to record the transcript of the session somewhere that I could reference it for myself again.
</p>
<p>
Here's how it went.
</p>
<p>
<blockquote>
#Tweekly #GTD Hello everyone! Ready? We'll do this in 3 parts/11 steps
<br/>#Tweekly #GTD PART ONE: GET CLEAR. Collect loose paper and materials. Gather everything that's loose into an Inbox, Tray or folder.
<br/>#Tweekly #GTD You have 5 minutes for this step. Go...
<br/>#Tweekly #GTD You all have one more minute on step one: Collect loose papers and materials.
<br/>#Tweekly #GTD PART ONE-STEP TWO-GET CLEAR: Get In to Zero. Choose the inbox that can good progress on in 5 min--email? paper? VM? Go!
<br/>#Tweekly #GTD - a good way to process in is 4D's: Delete it, Do it (under 2 mins), Delegate it, Defer it (onto a list)
<br/>#Tweekly #GTD PART ONE-STEP THREE-GET CLEAR: Empty your head. Open a Word doc, or grab and pad and clear your head for 5 minutes. Go.
<br/>#Tweekly #GTD - STEP THREE - SOME MINDSWEEP TRIGGERS: Family, health, meetings you've had, meetings you're going to have...
<br/>#Tweekly #GTD SOME MORE MINDSWEEP TRIGGERS: Your direct reports, finances, 401k, the dog, your car, health appts you've been putting off...
<br/>#Tweekly #GTD PART TWO, STEP FOUR-GET CURRENT: Review your Action lists (or maybe you call them Tasks or To Do's.)5 minutes start now. Go!
<br/>#Tweekly #GTD (10:25am) 2 more minutes to review action lists--are they current? anything to mark done? anything trigger you to add?
<br/>#Tweekly #GTD (10:28am) PART TWO-STEP 5-Review previous calendar info. Any triggers?
<br/>#Tweekly #GTD Many times reviewing your old calendar (go back about 3 wks) catches things you meant to do. 3 more mins left (10:30am)
<br/>#Tweekly #GTD PART TWO-STEP 6-REVIEW UPCOMING CALENDAR DATA - anything you should start getting ready for? (10:35am) Go!
<br/>#Tweekly #GTD REVIEW UPCOMING CALENDAR TIP: if you find something you need to process, you can add to your mindsweep for now.
<br/>#Tweekly #GTD if you don't get anything on reviewing your calendar, try going further out. Recurring Tasks are great for calendar. 10:40am
<br/>#Tweekly #GTD PART TWO-STEP 7-REVIEW WAITING FOR - if you've got a list review it. If you don't have one, what are you waiting on? 10:41am
<br/>#Tweekly #GTD WAITING FOR TIP: Review your email Sent folder. Usually some waiting for's hiding in there. 10:43am
<br/>#Tweekly #GTD - PART TWO-STEP 8-REVIEW PROJECT LISTS. Projects are your outcomes that require more than one action step. 10:47am Go!
<br/>#Tweekly #GTD PROJECT TIP: Projects are typically completed within 18 mos. If you can NEVER mark it done, it's likely an Area of Focus.
<br/>#Tweekly #GTD PROJECT TIP: Most people we coach have 30-100 current personal & professional projects. Don't be surprised! 10:51am.
<br/>#Tweekly #GTD PROJECT TIP: If you are not willing to take any next action on a current project, are you sure it's not Someday/Maybe?
<br/>#Tweekly #GTD PART 2-STEP 9 - REVIEW CHECKLISTS - birthday checklists? travel checklists? home mntce? 10:55am Go!
<br/>#Tweekly #GTD CHECKLIST TIP: Maybe you want to CREATE a checklist? Anything recurring that would be good? What to always pack for vacation?
<br/>#Tweekly #GTD PART 3-GET CREATIVE!-STEP 10-REVIEW SOMEDAY/MAYBE: If you have one, update it. If you don't have one, create it! 11:00am
<br/>#Tweekly #GTD SOMEDAY /MAYBE TIP: S/M is not just a "fantasy wish" list. It can be a fantastic place to stage "not yet" projects. 11:02am
<br/>#Tweekly #GTD SOMEDAY TIP: You'll trust S/M list(s) more if you know you're actually going to review them again. Otherwise they'll die.
<br/>#Tweekly #GTD PART 3-STEP 11-BE CREATIVE & COURAGEOUS! Any new thought-provoking, creative, risk taking ideas to add to your system? 11:07a
<br/>#Tweekly #GTD CREATIVE & COURAGEOUS TIP: What's REALLY got your attention in your job, family, environment? This is the last step! 11:09am
<br/>#Tweekly #GTD Thanks everyone. Hope you got value! Please post comments here: <a href="http://bit.ly/VZbP5">http://bit.ly/VZbP5</a> Let me know if you want me to do again.
</blockquote>
</p>
<p>
Thanks for a great review Kelly, I really enjoyed it.
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com4tag:blogger.com,1999:blog-6445576618492939938.post-16851688591633768572009-05-23T17:11:00.000-07:002009-05-23T17:50:21.268-07:00Geeking things done (GTD) with Runway<p>
At <a href="http://www.cogentconsulting.com.au/">Cogent Consulting</a>, we've had a goal of building our own products in house for some time. What better product to build than something you know about and will use every day. We're geeks, and we use the process of Getting Things Done&trade; (GTD) on a daily basis to help plan our own work. None of the products on the market seem to understand the way we want to work though.
</p>
<p>
To define an action in GTD, you need to give it a name, context, time & energy. It might also be associated with a person and have some tags. The average bit of software out there thinks this is 4 - 6 different fields. You click in one, type, click in the next, type, and so on.
</p>
<p>
Enter <a href="http://www.runwayapp.com/">Runway. Getting Things Done&trade; (GTD) for geeks</a>
</p>
<p>
In Runway, there's only one input box that understands a simple structured language to parse all of the information needed. So I can type all of that in without clicking around.
</p>
<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_IrJ0ID4PgQM/ShiZhjwc87I/AAAAAAAAAAs/hlUUOYKiA_A/s1600-h/runway-input-box.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 62px;" src="http://1.bp.blogspot.com/_IrJ0ID4PgQM/ShiZhjwc87I/AAAAAAAAAAs/hlUUOYKiA_A/s400/runway-input-box.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5339186160060920754" /></a>
<p>
When I'm done putting that information in, I end up with this. You can see that action listed, with the context, time, energy and tags all listed separately underneath it.
</p>
<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_IrJ0ID4PgQM/ShiZtC9NUhI/AAAAAAAAAA0/H54VYXp62-M/s1600-h/runway-actions.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 200px;" src="http://4.bp.blogspot.com/_IrJ0ID4PgQM/ShiZtC9NUhI/AAAAAAAAAA0/H54VYXp62-M/s400/runway-actions.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5339186357414482450" /></a>
<p>
Runway gets out of my way and lets me worry about managing my actions, rather than managing it. I can navigate around the whole application just using the keyboard, and it gives me clues along the way about how to do it.
</p>
<p>
It's still early days for Runway, and we're in the process of adding lots more. We'd love you feedback though, so please have a play and let us know what you think.
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com2tag:blogger.com,1999:blog-6445576618492939938.post-68277428479902094802009-05-23T04:19:00.000-07:002009-05-23T04:51:35.468-07:00Finding CRAP Ruby code<p>
A couple of days ago, <a href="http://blog.objectmentor.com/articles/2009/05/20/clean-code-and-battle-scarred-architecture">Uncle Bob blogged about the CRAP in FitNesse</a>. That prompted a few people in the Twitterverse to ponder about whether it would be possible to build such a tool for Ruby. In particular, <a href="http://onestepback.org/">Jim Weirich</a> and <a href="http://silkandspinach.net/">Kevin Rutherford</a> considered it.
</p>
<p>
Kevin and I have both built code quality tools for Ruby already (<a href="http://github.com/kevinrutherford/reek/tree/master">Reek</a> and <a href="http://roodi.rubyforge.org/">Roodi</a> respectively), so I pinged him to suggest we could have a go. The <a href="http://www.crap4j.org/">CRAP (Change Risk Analysis and Predictions)</a> metric is a function of cyclomatic complexity and coverage. I already knew how to calculate cyclomatic complexity from Roodi, and <a href="http://eigenclass.org/hiki/rcov">Rcov</a> was an obvious choice to get coverage information from.
</p>
<p>
After a couple of hours, we came up with <a href="http://github.com/kevinrutherford/crap4r/tree/master">crap4r</a>. It's pretty rough, but it basically works. Here's what it looks like:
<blockquote>
<pre>
~/Data/roodi $ ruby -Ilib ../crap4r/bin/crap4r
1.0 ./lib/roodi/core/visitable_sexp.rb#accept
1.0 ./lib/roodi/core/visitable_sexp.rb#node_type
1.0 ./lib/roodi/core/visitable_sexp.rb#children
[snip]
2.0625 /Users/marty/Data/roodi/lib/roodi/checks/name_check.rb#evaluate
2.0625 /Users/marty/Data/roodi/lib/roodi/checks/line_count_check.rb#evaluate
3.0 /Users/marty/Data/roodi/lib/roodi/checks/abc_metric_method_check.rb#branch?
3.04166666666667 /Users/marty/Data/roodi/lib/roodi/checks/check.rb#evaluate_node
3.04166666666667 /Users/marty/Data/roodi/lib/roodi/checks/assignment_in_conditional_check.rb#has_assignment?
3.04166666666667 /Users/marty/Data/roodi/lib/roodi/checks/parameter_number_check.rb#evaluate
3.140625 /Users/marty/Data/roodi/lib/roodi/core/runner.rb#parse
3.33333333333333 /Users/marty/Data/roodi/lib/roodi/core/iterator_visitor.rb#visit
4.128 /Users/marty/Data/roodi/lib/roodi/checks/empty_rescue_body_check.rb#has_statement?
6.0 /Users/marty/Data/roodi/lib/roodi/core/runner.rb#load_checks
</pre>
</blockquote>
</p>
<p>
Kevin and I will clean it up over the next few weeks and see if we can publish it somewhere in a more usable fashion.
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-82791986295434795922009-05-15T21:54:00.000-07:002009-05-15T22:11:03.488-07:00Roodi 1.4.0 released<p>
I released version 1.4.0 of Roodi while I was a RailsConf last week. It changes the underlying engine from ParseTree to ruby_parser because of <a href="http://blog.zenspider.com/2009/04/parsetree-eol.html">ParseTree reaching end of life</a>. That means Roodi should now work on Ruby 1.9.
</p>
<p>
<a href="http://roodi.rubyforge.org/">http://roodi.rubyforge.org/</a>
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-23062827266644306452009-05-08T03:46:00.000-07:002009-05-15T21:50:13.016-07:00RailsConf talks by rating<p>
I did my <a href="http://en.oreilly.com/rails2009/public/schedule/detail/6752">talk at RailsConf</a> today, which I thought went pretty well. I'm always keen to get some feedback though, so I slurped up the rating information from the RailsConf web site to see how I compared to the other speakers. I ended up 6th out of 61 talks, which I'm happy with. The Rails Envy guys were speaking at the same time as me and came second. I suspect that means I got less people attending and voting as a result. That might not have affected my position in the list though.
</p>
<p>
Here's the full list for those who are interested. This might still be changing over time as people review the sessions, but it should be reasonably indicative.
</p>
<p>
<b>UPDATE:</b> It's about seven hours since the conference finished now and some more reviews have come in. They must have been positive for me, because now I've moved up to second. I'll check again in a couple of days to see if anything else dribbles in.
</p>
<p>
<b>UPDATE:</b> It's a week later now, so I'm assuming the votes have stabilised. There's actually been quite a bit of movement. Here's the list from now. It may stil be changing, but I won't bother updating again. I've moved down to 6th, but I'm still amazed that I'm even that high.
</p>
<p>
<table>
<tr><th width="5%">Posn.</th><th width=5%>Rating</th><th width="%5">Reviews<th width="30%">Speaker</th><th width="55%">Topic</th></tr>
<tr><td>1</td><td>4.65</td><td>105</td><td>Ryan Singer</td><td>UI Fundamentals for Programmers</td></tr>
<tr><td>2</td><td>4.64</td><td>25</td><td>David Bock</td><td>Modeling Workflow in Ruby and Rails </td></tr>
<tr><td>3</td><td>4.56</td><td>39</td><td>Matthew Deiters</td><td>When to Tell Your Kids About Client Caching</td></tr>
<tr><td>4</td><td>4.51</td><td>43</td><td>Obie Fernandez</td><td>Blood, Sweat and Rails</td></tr>
<tr><td>5</td><td>4.50</td><td>14</td><td>Fernand Galiana</td><td>R-House - Rails for Home Automation</td></tr>
<tr><td>6</td><td>4.48</td><td>25</td><td>Marty Andrews</td><td>Automated Code Quality Checking In Ruby And Rails</td></tr>
<tr><td>7</td><td>4.45</td><td>55</td><td>Charles Nutter, Evan Phoenix</td><td>What Makes Ruby Go: An Implementation Primer</td></tr>
<tr><td>8</td><td>4.44</td><td>64</td><td>Bryan Helmkamp</td><td>Webrat: Rails Acceptance Testing Evolved</td></tr>
<tr><td>9</td><td>4.42</td><td>43</td><td>Ben Scofield</td><td>And the Greatest of These Is ... Rack Support</td></tr>
<tr><td>10</td><td>4.36</td><td>39</td><td>Gregg Pollack, Jason Seifer</td><td>Rails: A Year of Innovation</td></tr>
<tr><td>11</td><td>4.31</td><td>35</td><td>Jake Scruggs</td><td>Using metric_fu to Make Your Rails Code Better</td></tr>
<tr><td>12</td><td>4.31</td><td>16</td><td>Michael Bleigh</td><td>Twitter on Rails</td></tr>
<tr><td>13</td><td>4.30</td><td>30</td><td>Charles Nutter, Thomas Enebo</td><td>JRuby: State of the Art</td></tr>
<tr><td>14</td><td>4.27</td><td>90</td><td>Scott Chacon</td><td>Smacking Git Around - Advanced Git Tricks</td></tr>
<tr><td>15</td><td>4.24</td><td>58</td><td>Ezra Zygmuntowicz</td><td>Rube Goldberg Contraptions, Building Scalable Decoupled Web Apps and Infrastructure with Ruby</td></tr>
<tr><td>16</td><td>4.19</td><td>59</td><td>Jim Weirich</td><td>Writing Modular Applications</td></tr>
<tr><td>17</td><td>4.14</td><td>69</td><td>Larry Karnowski, Jason Rudolph</td><td>JavaScript Testing in Rails: Fast, Headless, In-Browser. Pick Any Three.</td></tr>
<tr><td>18</td><td>4.08</td><td>24</td><td>Ilya Grigorik</td><td>Art of the Ruby Proxy for Scale, Performance, and Monitoring</td></tr>
<tr><td>19</td><td>4.00</td><td>14</td><td>Jeff Dean</td><td>Advanced Views with Erector</td></tr>
<tr><td>20</td><td>4.00</td><td>10</td><td>Scott Raymond</td><td>Confessions of a PackRat</td></tr>
<tr><td>21</td><td>4.00</td><td>10</td><td>David Czarnecki, Ola Mork, Eric Torrey</td><td>Guitar HeroÂ®: Behind the Music</td></tr>
<tr><td>22</td><td>4.00</td><td>10</td><td>Matt Wood</td><td>Orchestrating the Cloud</td></tr>
<tr><td>23</td><td>3.98</td><td>45</td><td>Ilya Grigorik</td><td>Building a Mini-Google: High-Performance Computing in Ruby</td></tr>
<tr><td>24</td><td>3.93</td><td>44</td><td>Scott Penberthy, Michael Bryzek, Geir Magnusson Jr, Yonatan Feldman</td><td>The Gilt Effect: Handling 1000 Shopping Cart Updates per second in Rails</td></tr>
<tr><td>25</td><td>3.91</td><td>87</td><td>Aslak Hellesoy</td><td>Quality Code with Cucumber</td></tr>
<tr><td>26</td><td>3.91</td><td>11</td><td>Daniel Lathrop, Eric Mill, Wynn Netherland</td><td>Gov 2.0: Transparency, Collaboration, and Participation in Practice</td></tr>
<tr><td>27</td><td>3.89</td><td>18</td><td>Pat Maddox, BJ Clark</td><td>Working Effectively with Legacy Rails Code</td></tr>
<tr><td>28</td><td>3.85</td><td>40</td><td>David A. Black</td><td>Getting to Know Ruby 1.9</td></tr>
<tr><td>29</td><td>3.81</td><td>26</td><td>Blythe Dunham</td><td>Integrating SMS Messaging with your Rails Application</td></tr>
<tr><td>30</td><td>3.77</td><td>40</td><td>Davis W. Frank</td><td>I Rock, I Suck, I am - Jumpstart Your Journey to Agile</td></tr>
<tr><td>31</td><td>3.74</td><td>27</td><td>Neal Ford, Paul Gross</td><td>Rails in the Large:How We're Developing the Largest Rails Project in the World</td></tr>
<tr><td>32</td><td>3.71</td><td>21</td><td>Jonathan Dahl</td><td>Five Musical Patterns for Programmers</td></tr>
<tr><td>33</td><td>3.71</td><td>17</td><td>Jay Phillips</td><td>Call into your Ruby code! Writing voice-enabled apps in Ruby with Adhearsion</td></tr>
<tr><td>34</td><td>3.66</td><td>70</td><td>Alexander Dymo</td><td>Advanced Performance Optimization of Rails Applications</td></tr>
<tr><td>35</td><td>3.65</td><td>31</td><td>Michael Koziarski</td><td>Are You Taking Things Too Far?</td></tr>
<tr><td>36</td><td>3.65</td><td>43</td><td>Ryan Tomayko</td><td>HTTP's Best-Kept Secret: Caching</td></tr>
<tr><td>37</td><td>3.64</td><td>14</td><td>Brian Hogan</td><td>Rails and Legacy Databases</td></tr>
<tr><td>38</td><td>3.62</td><td>34</td><td>Chris Wanstrath, Tom Preston-Werner, PJ Hyett, Scott Chacon, Jon Maddox</td><td>The GitHub Panel</td></tr>
<tr><td>39</td><td>3.61</td><td>18</td><td>Mike Subelsky</td><td>It's Not Always Sunny In the Clouds: Lessons Learned</td></tr>
<tr><td>40</td><td>3.60</td><td>10</td><td>Greg Borenstein</td><td>Giving Rails the Big 'F': Surviving Facebook Integration Unscarred </td></tr>
<tr><td>41</td><td>3.59</td><td>37</td><td>Noel Rappin</td><td>Below and Beneath TDD: Test Last Development and Other Real-World Test Patterns</td></tr>
<tr><td>42</td><td>3.57</td><td>77</td><td>Adam Wiggins</td><td>Rails Metal, Rack, and Sinatra</td></tr>
<tr><td>43</td><td>3.56</td><td>57</td><td>Paolo Negri</td><td>%w(map reduce).first - A Tale About Rabbits, Latency, and Slim Crontabs</td></tr>
<tr><td>44</td><td>3.56</td><td>59</td><td>Matt Aimonetti</td><td>Rails3: Step Off of the Golden Path </td></tr>
<tr><td>45</td><td>3.56</td><td>9</td><td>Jeremy Hinegardner</td><td>Crate: Packaging Standalone Ruby Applications </td></tr>
<tr><td>46</td><td>3.54</td><td>80</td><td>David Chelimsky</td><td>Don't Mock Yourself Out</td></tr>
<tr><td>47</td><td>3.52</td><td>63</td><td>Yehuda Katz, Carl Lerche</td><td>The Russian Doll Pattern: Mountable apps in Rails 3</td></tr>
<tr><td>48</td><td>3.50</td><td>18</td><td>Desi McAdam, Sarah Mei, Lori Olson</td><td>Discussion Panel: Women In Rails</td></tr>
<tr><td>49</td><td>3.38</td><td>69</td><td>James Adam</td><td>The Even-Darker Art of Rails Engines</td></tr>
<tr><td>50</td><td>3.29</td><td>59</td><td>Ninh Bui, Hongli Lai</td><td>Scaling Rails</td></tr>
<tr><td>51</td><td>3.27</td><td>11</td><td>Wynn Netherland, Jim Mulholl, Bradley Joyce</td><td>Build an App, Start a Movement</td></tr>
<tr><td>52</td><td>3.14</td><td>7</td><td>Jimmy Schementi</td><td>IronRuby on Rails</td></tr>
<tr><td>53</td><td>3.09</td><td>11</td><td>John Woodell, Ryan Brown</td><td>JRuby on Google App Engine</td></tr>
<tr><td>54</td><td>2.92</td><td>25</td><td>Erik Kastner</td><td>Interfaces are Dumb (and that's a Very Good Thing)</td></tr>
<tr><td>55</td><td>2.91</td><td>34</td><td>Jason LaPorte</td><td>PWN Your Infrastructure: Behind Call of Duty: World at War</td></tr>
<tr><td>56</td><td>2.87</td><td>23</td><td>Rein Henrichs</td><td>Rails Is from Mars, Ruby Is from Venus</td></tr>
<tr><td>57</td><td>2.83</td><td>18</td><td>Ed Laczynski, Nathaniel Bibler</td><td>Building a Video Portal in Rails - Or How the Teenage Mutant Ninja Turtles Started Streaming</td></tr>
<tr><td>58</td><td>2.73</td><td>15</td><td>Tony Hillerson</td><td>Integrating Flex and Rails with RubyAMF</td></tr>
<tr><td>59</td><td>2.69</td><td>29</td><td>Nick Plante, Joe Fiorini, Ben Scofield, Chris Saylor, James Golick</td><td>Starting Up Fast: Lessons from the Rails Rumble</td></tr>
<tr><td>60</td><td>2.54</td><td>28</td><td>Marc-Andre Cournoyer, Christian Neukirchen, Blake Mizerany, Ryan Tomayko, Adam Wiggins, James Lindenbaum</td><td>The Future of Deployment: A Killer Panel</td></tr>
<tr><td>61</td><td>1.87</td><td>46</td><td>Kevin Barnes</td><td>In Praise of Non-Fixtured Data</td></tr>
</table>
</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com1tag:blogger.com,1999:blog-6445576618492939938.post-68513617231239078212009-03-07T04:36:00.000-08:002009-05-13T04:31:31.305-07:00I'm speaking at RailsConf 2009 on automated code quality<p>I've had a proposal accepted to speak at RailsConf 2009. The title of my talk is &quot;Automated Code Quality Checking In Ruby And Rails&quot". It's a natural follow on form my work on <a href="http://roodi.rubyforge.org/">Roodi</a>, but I will also discuss the other code quality tools appearing that are useful to help maintain a high level of quality in your code.</p><p>You can find out more about the <a href="http://en.oreilly.com/rails2009/public/schedule/detail/6752">session</a> on the <a href="http://www.railsconf.com">RailsConf 2009</a> site. If you're attending, come along to my session and say hi.</p><p><center><a href="http://www.railsconf.com/"><img src="http://assets.en.oreilly.com/1/event/24/rails2009_banner_speaking_210x60.jpg" width="210" height="60" border="0" alt="RailsConf 2009" title="RailsConf 2009" /></a></center></p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-67926698729635918122009-03-07T04:30:00.000-08:002009-05-13T04:31:31.305-07:00Release of Roodi 1.3.2<p>I've just released version 1.3.2 of <a href="http://roodi.rubyforge.org/">Roodi</a>. It contains a couple of patches submitted by Xavier Shay and Chris Leishman. Both of them were around the EmptyRescueBodyCheck, so it should be more stable now.</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com1tag:blogger.com,1999:blog-6445576618492939938.post-40459525117838930262008-09-19T17:22:00.000-07:002009-05-13T04:31:31.305-07:00Release of Roodi 1.3.0<p>I've just released version 1.3.0 of <a href="http://roodi.rubyforge.org/">Roodi</a>. It contains two new checks - a CaseMissingElse checks that ensures you have a default path through your case statements, and an AssignmentInConditional check that looks out for things like <code>if foo = 1</code> which are likely to be mis-typed equality checks. I've also DRY'ed up the code a bit, but the major new feature is the ability to provide your own configuration file.</p><p>If a config file is not provided, <a href="http://roodi.rubyforge.org/">Roodi</a> now configures itself with via a YAML one that ships inside the gem which looks like this:</p><blockquote class="code"><pre>AssignmentInConditionalCheck: { }CaseMissingElseCheck: { }ClassLineCountCheck: { line_count: 300 }ClassNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }CyclomaticComplexityBlockCheck: { complexity: 4 }CyclomaticComplexityMethodCheck: { complexity: 8 }EmptyRescueBodyCheck: { }ForLoopCheck: { }MethodLineCountCheck: { line_count: 20 }MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }ModuleLineCountCheck: { line_count: 300 }ModuleNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }ParameterNumberCheck: { parameter_count: 5 }</pre></blockquote><p>It's basically a list of checks, each with a hash of options for that check. You can take this file as a starting point and remove existing check, add your own custom new checks, or change the default values on some of them. I've intentionally been strict on the values on some of the checks with thresholds, setting high standards that I'd expect to see on my own projects. If you're working through a long list of warnings and want to eliminate some of the noise, or if you decide the thresholds should be different, you can save this in your own config file and use it to configure roodi.</p><p>To use your own config file with roodi, pass it in as a value to the <code>-config</code> parameter on the command line like this:</p><blockquote class="code"><pre>roodi -config=my_roodi_config.yml "rails_app/**/*.rb"</pre></blockquote>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com1tag:blogger.com,1999:blog-6445576618492939938.post-83506404154829958072008-09-12T03:27:00.000-07:002009-05-13T04:31:31.306-07:00Roodi version numbering changes.<p>After releasing <a href="http://roodi.rubyforge.org/">Roodi</a> a couple of days ago, and getting some feedback from people, I realised that they had the wrong version of it. When I first started getting the project set up as a gem with Hoe, I pushed a 1.x version up to Rubyforge. Since that time, I tried to change the version numbering back to 0.x, and deleted the old files on Rubyforge. Too late though, Rubyforge has cached the old gem file with a higher version number. That means people who've installed Roodi in the last couple of days actually got an older version.</p><p>Since then, I've changed the version numbering (again) back to a 1.x version, and published a new gem with a higher version number than the old one. It actually contains a couple of enhancements and some new checks as well. Version 1.2.0 is now published, and is the latest stable version.</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-90797916268445511792008-09-10T13:47:00.000-07:002009-05-13T04:31:31.306-07:00First official release of Roodi<p><a href="http://roodi.rubyforge.org/">Roodi</a> is now a few weeks old, and ready for it's first official release. It statically parses your Ruby code and warns you about design issues that you might have. to install Roodi, run this command:</p><blockquote class="code"><pre>gem install roodi</pre></blockquote><p>You can then run the <code>roodi</code> command and give it a list of patterns to find files to check. For example, you could:</p><p>Check all ruby files in a rails app:</p><blockquote class="code"><pre>roodi "rails_app/**/*.rb"</pre></blockquote><p>...or check one controller and all model files in a rails app:</p><blockquote class="code"><pre>roodi app/controller/sample_controller.rb "app/models/*.rb"</pre></blockquote><p>Here's an example of what <a href="http://roodi.rubyforge.org/">Roodi</a> output looks like when it is run against the <a href="http://code.whytheluckystiff.net/hpricot/">Hpricot</a> source code:</p><blockquote class="code"><pre>pudbookpro:Data marty$ roodi "hpricot/**/*.rb"hpricot/extras/mingw-rbconfig.rb:155 - Block cyclomatic complexity is 5. It should be 4 or less.hpricot/lib/hpricot/builder.rb:75 - Block cyclomatic complexity is 5. It should be 4 or less.hpricot/lib/hpricot/parse.rb:52 - Block cyclomatic complexity is 40. It should be 4 or less.hpricot/lib/hpricot/traverse.rb:50 - Block cyclomatic complexity is 5. It should be 4 or less.hpricot/lib/hpricot/traverse.rb:315 - Block cyclomatic complexity is 5. It should be 4 or less.hpricot/lib/hpricot/traverse.rb:555 - Block cyclomatic complexity is 11. It should be 4 or less.hpricot/setup.rb:215 - Block cyclomatic complexity is 6. It should be 4 or less.hpricot/setup.rb:578 - Block cyclomatic complexity is 5. It should be 4 or less.hpricot/lib/hpricot/builder.rb:63 - Method name "tag!" has a cyclomatic complexity is 15. It should be 8 or less.hpricot/lib/hpricot/traverse.rb:247 - Method name "search" has a cyclomatic complexity is 27. It should be 8 or less.hpricot/lib/hpricot/traverse.rb:553 - Method name "each_hyperlink_attribute" has a cyclomatic complexity is 11. It should be 8 or less.hpricot/lib/hpricot/traverse.rb:774 - Method name "author" has a cyclomatic complexity is 12. It should be 8 or less.hpricot/setup.rb:147 - Method name "standard_entries" has a cyclomatic complexity is 13. It should be 8 or less.hpricot/setup.rb:858 - Method name "parsearg_global" has a cyclomatic complexity is 10. It should be 8 or less.hpricot/setup.rb:1265 - Method name "update_shebang_line" has a cyclomatic complexity is 9. It should be 8 or less.hpricot/lib/hpricot/traverse.rb:781 - Rescue block should not be empty.hpricot/lib/hpricot/traverse.rb:791 - Rescue block should not be empty.hpricot/lib/hpricot/traverse.rb:800 - Rescue block should not be empty.hpricot/lib/hpricot/traverse.rb:807 - Rescue block should not be empty.hpricot/lib/hpricot.rb:17 - Rescue block should not be empty.hpricot/setup.rb:592 - Rescue block should not be empty.hpricot/setup.rb:601 - Rescue block should not be empty.hpricot/setup.rb:613 - Rescue block should not be empty.hpricot/lib/hpricot/builder.rb:63 - Method name "tag!" has 31 lines. It should have 20 or less.hpricot/lib/hpricot/traverse.rb:247 - Method name "search" has 80 lines. It should have 20 or less.hpricot/setup.rb:147 - Method name "standard_entries" has 51 lines. It should have 20 or less.hpricot/setup.rb:948 - Method name "print_usage" has 31 lines. It should have 20 or less.hpricot/test/test_parser.rb:69 - Method name "scan_basic" has 50 lines. It should have 20 or less.hpricot/lib/hpricot/parse.rb:3 - Method name "Hpricot" should match pattern /^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/</pre></blockquote><p>The default parameters for checks can be changed, and checks can be turned on and off. Lots of additional checks are planned, as is support for rake tasks and rails plugins.</p><p>Please drop me a line if you use Roodi and find it useful. I'd love to get some feedback on how people are using it. Happy checking!</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-52143457699682875032008-09-09T17:22:00.000-07:002009-05-13T04:31:31.306-07:00Line numbers with ParseTree<p>I reluctantly moved <a href="http://github.com/martinjandrews/roodi/tree">Roodi</a> over to ParseTree recently because I figured that would make it more accessible to people. My reluctance came from the fact that I was going to lose the line number support the I had in JRuby. Happy days - it turns out I was wrong!</p><p>The following bit of code sets up ParseTree so you can parse some code:</p><blockquote class="code"><pre>parse_tree = ParseTree.newtree = parse_tree.parse_tree_for_string(ruby_code_as_text, filename)</pre></blockquote><p>It turns out that one very small tweak will give me line number support:</p><blockquote class="code"><pre>parse_tree = ParseTree.new(true)tree = parse_tree.parse_tree_for_string(ruby_code_as_text, filename)</pre></blockquote><p>The addition of that boolean means that ParseTree will now include newline nodes in the tree structure that it gives me. Every newline node includes the line number and the name of the file that it came from. That made the output from Roodi <b>much</b> more useful.</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-33611834123507258552008-09-07T14:35:00.000-07:002009-05-13T04:31:31.306-07:00Roodi on ParseTree instead of JRuby<p>I started out building <a href="http://github.com/martinjandrews/roodi/tree">Roodi</a> using the JRuby apis to parse Ruby code and build an AST. My main alternative to that was to use ParseTree. The advantage of using JRuby was twofold. Firstly - I could get access to newline characters as part of the AST, which I wanted to use in checks. Secondly - I could get access to line numbers, which I wanted to use as part of the error message output.</p><p>I've just sacrificed both of those advantages and switched the code base to use ParseTree instead. Why? Because it makes the tool more widely usable. I had my own non-JRuby projects that I was trying to run Roodi in, and it became a pain in the ass. Now I get much better portability, which is worth the sacrifices I had to make. I suspect I can patch line numbers in later, and I'm not that fussed about losing the checks that needed to check the newline characters.</p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0tag:blogger.com,1999:blog-6445576618492939938.post-10160927510839562482008-09-04T15:14:00.000-07:002009-05-13T04:31:31.306-07:00Roodi - Checkstyle for Ruby<p>I've been working on a tool for the last couple of weeks that I'm calling Roodi (Ruby Object Oriented Design Inferometer). It's very much a tool for Ruby like <a href="http://checkstyle.sourceforge.net/">checkstyle</a> is for Java. The framework was surprisingly quick to get up and going, and I've written seven checks (class name, method name, method cyclomatic complexity, block cyclomatic complexity, empty rescue body, for loop, method line count) for it so far. It's working pretty well for all of them.</p><p>Here's a sample of what it looks like (I've trimmed some of the output for brevity):<blockquote class="code"><pre>pudbookpro:Data marty$ ./roodi/bin/roodi "rspec/**/*.rb"rspec/lib/spec/rake/spectask.rb:152 - Block cyclomatic complexity is 11. It should be 4 or less.rspec/lib/spec/rake/verify_rcov.rb:37 - Block cyclomatic complexity is 6. It should be 4 or less.rspec/lib/spec/matchers/be.rb:57 - Method name "match_or_compare" has a cyclomatic complexity is 12. It should be 8 or less.rspec/lib/spec/matchers/change.rb:12 - Method name "matches?" has a cyclomatic complexity is 9. It should be 8 or less.rspec/lib/spec/matchers/have.rb:28 - Method name "matches?" has a cyclomatic complexity is 11. It should be 8 or less.rspec/lib/spec/expectations/errors.rb:6 - Rescue block should not be empty.rspec/lib/spec/rake/spectask.rb:186 - Rescue block should not be empty.rspec/lib/spec/expectations/differs/default.rb:20 - Method name "diff_as_string" has 21 lines. It should have 20 or less.rspec/lib/spec/matchers/change.rb:35 - Method name "failure_message" has 24 lines. It should have 20 or less.rspec/lib/spec/matchers/include.rb:31 - Method name "_message" should match pattern (?-mix:^[a-z]+[a-z0-9_]*[!\?]?$).rspec/lib/spec/matchers/include.rb:35 - Method name "_pretty_print" should match pattern (?-mix:^[a-z]+[a-z0-9_]*[!\?]?$).</pre></blockquote></p><p>You can <a href="http://github.com/martinjandrews/roodi/tree">get Roodi from github</a> for now. I'm still in the process of getting it packaged and ready as a gem.</p><p>One of the interesting features (and issues) with Roodi is that it is dependent on JRuby. That's because it uses the JRuby AST libraries to parse Ruby source code. There's no good way (IMHO) yet to build an AST of Ruby source code in Ruby. <a href="http://parsetree.rubyforge.org/">ParseTree</a> is close, but gives you an AST of the executable code, not the source. What does that mean? Well, for example, I can't get any representation of whitespace characters from ParseTree. Nor can I get line numbers from it. JRuby isn't perfect either, but it gives me line numbers and newlines at least.</p><p>The use of JRuby libraries makes the Roodi design interesting. Roodi is completely written in Ruby, but the calls out to libraries written in Java means it needs to run under JRuby. So I end up running JRuby to execute a Ruby app, which then calls out to JRuby to parse Ruby code.<p><p>I'd love some feedback if anyone has a play. Writing checks is really easy too. I'll happily roll them in if someone wants to write some.<p>Marty Andrewshttp://www.blogger.com/profile/02704401325754822667noreply@blogger.com0