tag:blogger.com,1999:blog-10428019644884881852008-07-15T08:39:18.844-05:00Jake ScruggsJake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comBlogger73125tag:blogger.com,1999:blog-1042801964488488185.post-78051698322982786862008-06-30T21:35:00.003-05:002008-06-30T22:00:42.594-05:00Presenting at Lone Star Ruby ConfWord on the street is that <a href="http://lonestarrubyconf.com">Lone Star Ruby Conf</a> was one of the best regional conferences last year, so I'm pretty excited to be presenting at the 2008 edition. It's happening Sept 4-6th in Austin, TX and speakers include: Matz, Evan Phoenix, Jim Weirich, The Rails Envy Guys, and too many other luminaries to name.<br /> <br />I'll be giving a talk entitled "<a href="http://lonestarrubyconf.com/speakers.html#umthlyc">Using Metrics to Take a Hard Look at Your Code</a>" which is a distilation of my thinking over the past year about how to use numbers not as a hammer with which to beat people, but as a tool to improve your code. <br /><br />Registration just opened up today, but space is limited so go sign up soon.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-5388205342377156252008-06-17T23:41:00.002-05:002008-06-17T23:58:10.598-05:00Disconnecting RSpec from the DatabaseRecently I spent some time away from <a href="http://rspec.info/">RSpec</a> and I did miss it, but one thing especially liked about the testing framework I lived in for 10 months was that it had two ways to test anything you liked: One that hit the database and one that did not. We used <a href="http://unit-test-ar.rubyforge.org/">UnitRecord</a> to disconnect unit tests from the database (I should mention that we defined unit tests as tests that didn't hit the db and functional tests as those that did). So the vast majority of our tests didn't really need to hit the db and ran very fast. Sometimes we wanted or needed to hit the db and we did (only occasionally in model tests, more often in controller tests, and we kept logic out of the view and only tested views at the Selenium level). It was pretty cool and I'd like to thank the team who set it up before I got there.<br /><br />There's been a lot of talk about whether model, view, or controller tests should hit the db but I've found that there's enough exceptions that it's worth just saying that you should try not to hit the db when possible for speed's sake and then when the test becomes 20 lines long because of all the mocking/stubbing required, go ahead and hit it. That's all well and good, but how do you disconnect the db from an RSpec example?<br /><br />I, personally, have had success with <a href="http://agilewebdevelopment.com/plugins/nulldb">NullDB</a>. NullDB is the <a href="http://en.wikipedia.org/wiki/Null_Object_pattern">null object pattern</a> applied to databases. Which is a fancy way of saying that it swallows up any call that would go to the db and returns a nil. So you mock/stub all you like, but if you forget or just don't care about a particular call then don't worry because nothing goes to the db and nil is returned (which, surprisingly, is more than enough in most situations). On the NullDB page I've linked to, the author provides a way to universally disconnect RSpec from the db, but, as I've already said, I'm a bit pragmatic when it comes to disconnection so In my spec_helper.rb I define something like this:<br /><div class="CodeRay"><br /><div class="code"><pre><span class="no"> 1</span> share_as <span class="sy">:Disconnected</span> <span class="r">do</span><br /><span class="no"> 2</span><br /><span class="no"> 3</span> before <span class="sy">:all</span> <span class="r">do</span><br /><span class="no"> 4</span> <span class="co">ActiveRecord</span>::<span class="co">Base</span>.establish_connection(<span class="sy">:adapter</span> =&gt; <span class="sy">:nulldb</span>)<br /><span class="no"> 5</span> <span class="r">end</span><br /><span class="no"> 6</span><br /><span class="no"> 7</span> after <span class="sy">:all</span> <span class="r">do</span><br /><span class="no"> 8</span> <span class="co">ActiveRecord</span>::<span class="co">Base</span>.establish_connection(<span class="sy">:test</span>)<br /><span class="no"> 9</span> <span class="r">end</span><br /><span class="no"><strong>10</strong></span> <span class="r">end</span></pre></div><br /></div>and then in any "Describe" block where I'd like to disconnect the db I do this:<br /><div class="CodeRay"><br /><div class="code"><pre><span class="no">1</span> describe <span class="co">User</span> <span class="r">do</span><br /><span class="no">2</span> include <span class="co">Disconnected</span><br /><span class="no">3</span> <span class="c"># 'it' blocks or</span><br /><span class="no">4</span> <span class="c"># more describes</span><br /><span class="no">5</span> <span class="r">end</span></pre></div><br /></div>If I want to hit the db I just don't include Disconnected.<br /><br />Now since NullDB fails silently I've heard tell of problems with it being hard to debug (although I have yet to experience these problems). Any spec/test/example that just isn't behaving properly can be exiled to the 'hit the database' area. Also I should mention that when I tried to use NullDB with Rails 1.2.6 I got a bunch of errors but it worked fine with 2.0.2. If you are looking for a more vigorous enforcement of 'don't touch the db in unit tests' then UnitRecord may be for you. I thought RSpec and UnitRecord didn't play nice, but my fellow Obtivian, <a href="http://daddy.platte.name/">Ryan Platte</a> informs me that he's got it working. So I'm calling him out to post about disconnecting the RSpec with UnitRecord.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-31269963915261513982008-06-12T20:36:00.002-05:002008-06-12T21:05:52.470-05:00Breadcrumbs are EvilI've been on a few projects where I've had to mess with breadcrumbs and they tend to be much more trouble than they're worth. It all starts simply enough: put the users path through the app on the page. Which isn't too hard. <br /><br />Then the bugs come.<br /><br />Like: "This page is linked to from page A but it really is more of a child of page B so the breadcrumbs should show page B and not A even if they come from A"<br /><br />Or: "When I come to page X from the search on the front page it should have breadcrumbs with Y and Z because that's really what it's under."<br /><br />And: "Page L is listed under K but should be under M (followed a few days later by) Page L is listed under M but should be under K"<br /><br />Oh, you want a hierarchy. Which is hard. <br /><br />First: There's now a new job to be filled: Taxonomist. Somebody is going to have to figure out where all the pages in the app fit in some sort of tree. And this list must be maintained.<br /><br />Second: There must be code that manages all this. And since there are always exceptions to the rules of hierarchy it can get quite complicated.<br /><br />Third: Although your customers don't really care where the page fits in the hierarchy, everyone else in the business does. A lot. So much so that there will be many (perhaps endless) arguments about exactly where things "belong."<br /><br />All this will take plenty of time that could be used doing something that, you know, your customers may actually need. I guess I really don't have too much problem with "true" breadcrumbs (aside from the fact that I never use them and they're kind of a relic from the days of "Wow the internet is so complicated people will get lost!" and they can probably be replaced with a good search box) but what I really hate is when they act as a gateway drug to hierarchy.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-21485026964805436772008-05-29T17:00:00.000-05:002008-05-29T17:00:44.339-05:00Syntax Highlighting for Ruby Made Very EasySo I've been making do with lame-o 'pre' tags for my code snippets and my blog looks, well, lame. But the various alternatives for highlighting ruby either didn't work for Blogger or smacked of effort (and you know how I feel about effort).<br /><br />Well, recently I joined <a href="http://obtiva.com/">Obtiva</a> and I was being all nosy looking at my new co-worker's blogs, when I found <a href="http://squaremasher.blogspot.com/">Tyler Jennings</a> <a href="http://squaremasher.blogspot.com/2008/03/pretty-code-snippets-for-masses.html">post</a> about creating <a href="http://spotlight.heroku.com/">Spotlight</a>. Take some ruby code, paste it into the text box, press a button, and you get some html which you copy and past into your blog. You'll have to include the default css in your css file the first time, but that's not too hard. Output looks like this:<br /><br /><div class="CodeRay"><br /> <div class="code"><pre><span class="no"> 1</span> <span class="r">class</span> <span class="cl">Echo</span> &lt; <span class="co">ActionMailer</span>::<span class="co">Base</span><br /><span class="no"> 2</span> <span class="r">def</span> <span class="fu">copy</span> formatted_message, incident_id<br /><span class="no"> 3</span> users_with_echo_emails = <span class="co">User</span>.find(<span class="sy">:all</span>, <br /><span class="no"> 4</span> <span class="sy">:conditions</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">echo_email is not NULL</span><span class="dl">&quot;</span></span>)<br /><span class="no"> 5</span> users_with_echo_emails.reject! {|user| !user.receive_echo?}<br /><span class="no"> 6</span> recipients_array = users_with_echo_emails.map { |user| user.echo_email }<br /><span class="no"> 7</span> recipients_array &lt;&lt; <span class="co">ENV</span>[<span class="s"><span class="dl">'</span><span class="k">default_echo_email</span><span class="dl">'</span></span>] <span class="r">if</span> <span class="co">ENV</span>[<span class="s"><span class="dl">'</span><span class="k">default_echo_email</span><span class="dl">'</span></span>]<br /><span class="no"> 8</span> <br /><span class="no"> 9</span> recipients recipients_array<br /><span class="no"><strong>10</strong></span> subject <span class="s"><span class="dl">&quot;</span><span class="k">Globex pager incident </span><span class="il"><span class="idl">#{</span>incident_id<span class="idl">}</span></span><span class="k"> updated</span><span class="dl">&quot;</span></span><br /><span class="no">11</span> from <span class="s"><span class="dl">'</span><span class="k">noreply@globex.com</span><span class="dl">'</span></span><br /><span class="no">12</span> body <span class="sy">:message</span> =&gt; formatted_message, <span class="sy">:incident_id</span> =&gt; incident_id<br /><span class="no">13</span> <span class="r">end</span><br /><span class="no">14</span> <span class="r">end</span></pre></div><br /></div><br /><br />Cool huh?Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-70444591593727888792008-05-27T19:22:00.004-05:002008-05-27T20:13:21.225-05:00Saving in TextMateSo today, for something like the fifth time, I sat down in front of a TextMate installation that didn't have saving set up the way I like it: Auto save on loss of focus and when running a test (or spec). And, for the fifth time, I had to dig around the internet for the answer. So, as a public service to my future self:<br /><br />Save files when focus is lost:<br /><a href="http://macromates.com/textmate/manual/saving_files">http://macromates.com/textmate/manual/saving_files</a><br />TextMate --> Preferences --> Advanced --> Saving<br /><br />Save all files when running a test (or spec):<br /><a href="http://macromates.com/textmate/manual/commands">http://macromates.com/textmate/manual/commands</a><br />for specs:<br />Bundles --> Bundle Editor --> Edit Commands --> Rspec --> Run Single Example<br />Bundles --> Bundle Editor --> Edit Commands --> Rspec --> Run Examples<br />for tests:<br />Bundles --> Bundle Editor --> Edit Commands --> Ruby --> Run<br />Bundles --> Bundle Editor --> Edit Commands --> Rspec --> Run Focused Unit Test<br /><br />Change the save drop down to "All Files in Project"<br /><br />Hope this helps, future Jake.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-47705330276567723692008-05-11T21:39:00.004-05:002008-05-11T23:03:08.240-05:00Metric_fu Now Measures ChurnSometimes a class does too much. It's used everywhere, does everything, knows all about the nasty internals of all the other classes, and every time you change anything in the application it has to change. Or maybe you have a class that everyone loves to refactor because it's so bad. One developer changes it to be better, then another changes it to be a different kind of better, and this keeps happening without it ever getting any easier to use. There's a lot of bad reasons why a class might change all the time so I added a churn report capability to <a href="http://metric-fu.rubyforge.org/">metric_fu</a> version 0.6.0.<br /><br />Now you can use metric_fu to create a report like this:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://metric-fu.rubyforge.org/churn.gif"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px;" src="http://metric-fu.rubyforge.org/churn.gif" alt="" border="0" /></a>By issuing this command:<br /><pre> rake metrics:churn<br /></pre> And use the results to find classes that might have problems. It uses 'svn log' to create this report so currently you need to be using subversion. (If anyone out there want to contribute some code to get it working with git, lemme know.) The default is to start counting changes from the start of the repository's creation. That may be too much for your project so you can change the start by adding these lines to your Rakefile:<br /><pre> namespace :metrics do<br /> CHURN_OPTIONS = { :start_date => lambda{3.months.ago} }<br /> end<br /></pre> The Proc is there because '3.months.ago' only works after the Rails Environment is loaded (and Rails extends Fixnum) which I didn't want to do every time you run a rake task.<br /><br />You can also change the minimum churn count (files with changes less than this number won't be included in the report) like so:<br /><pre> namespace :metrics do<br /> CHURN_OPTIONS = { :minimum_churn_count => 3 }<br /> end <br /></pre>Also in this release I added the 'metrics:all_with_migrate' task because of some feedback I got that running 'metrics:all' in a cc.rb build fails if there have been migrations. So the new task migrates the test db before running all the metrics tasks.<br /><br />Check out the metric_fu project <a href="http://metric-fu.rubyforge.org/">home page</a> for more information and installation instructions. Or you can jump right in by doing this in the root of your project:<br /><pre>script/plugin install \<br /> http://metric-fu.rubyforge.org/svn/tags/CURRENT/metric_fu/<br />sudo gem install rcov<br />sudo gem install flog<br /></pre>Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-7906435723877057162008-04-27T17:45:00.004-05:002008-04-27T17:46:23.322-05:00Dead Simple Rails Metrics with metric_fuEvery time I create a new rails project I usually put off writing tasks to analyze the code's quality 'cause it takes time and time is, you know, finite. So I've decided to extract some code into a rails plugin which I call metric_fu.<br /><br />It's a bunch of rake tasks that produce reports on code coverage (using <a href="http://eigenclass.org/hiki.rb?rcov">Rcov</a>), cyclomatic complexity (using <a href="http://saikuro.rubyforge.org/">Saikuro</a>), flog scores (using <a href="http://ruby.sadi.st/Flog.html">Flog</a>), and rails stats (using 'rake stats'). It knows if it's being run inside a <a href="http://cruisecontrolrb.thoughtworks.com/">CruiseControl.rb</a> build and puts the output in the Custom Build Artifacts folder so when you view a build you see this:<br /><br /><a href="http://www.flickr.com/photos/7589554@N02/2439999164/" title="Cruise control build page by jake_scruggs, on Flickr"><img src="http://farm3.static.flickr.com/2186/2439999164_bf56434580.jpg" alt="Cruise control build page" height="282" width="500" /></a><br /><br />The coverage report is your standard rcov report:<br /><a href="http://www.flickr.com/photos/7589554@N02/2439174439/" title="Rcov output by jake_scruggs, on Flickr"><img src="http://farm4.static.flickr.com/3112/2439174439_214ba97cde.jpg" alt="Rcov output" height="155" width="500" /></a><br /><br />Flog output is thrown into an html file:<br /><a href="http://www.flickr.com/photos/7589554@N02/2439174463/" title="Flog output by jake_scruggs, on Flickr"><img src="http://farm4.static.flickr.com/3035/2439174463_19cf8395d9.jpg" alt="Flog output" height="363" width="311" /></a><br /><br />At the end metric_fu calculates the average flog score per method:<br /><a href="http://www.flickr.com/photos/7589554@N02/2439999274/" title="Flog average score by jake_scruggs, on Flickr"><img src="http://farm4.static.flickr.com/3071/2439999274_fdb8890133.jpg" alt="Flog average score" height="70" width="352" /></a><br />You might want to check out my previous posts on what to do with a Flog report: <a href="http://jakescruggs.blogspot.com/2008/01/method-hit-list.html">The Method Hit List</a> and <a href="http://jakescruggs.blogspot.com/2008/01/when-you-should-ignore-metrics.html">When You Should Ignore Metrics</a><br /><br />Saikuro's output is the same as always:<br /><a href="http://www.flickr.com/photos/7589554@N02/2439174543/" title="Cyclomatic complexity output by jake_scruggs, on Flickr"><img src="http://farm4.static.flickr.com/3173/2439174543_fa95ca3a77.jpg" alt="Cyclomatic complexity output" height="227" width="500" /></a><br />(I changed the warning and error levels for this pic -- more on how I did that later)<br /><br />And 'rake stats' is always useful:<br /><a href="http://www.flickr.com/photos/7589554@N02/2439999240/" title="Stats output by jake_scruggs, on Flickr"><img src="http://farm3.static.flickr.com/2331/2439999240_61a0c61035.jpg" alt="Stats output" height="183" width="500" /></a><br /><br />So how do you get all these reports?<br />1. install Flog<br />sudo gem install flog<br /><br />2. install rcov<br />sudo gem install rcov<br /><br />3. install metric_fu<br />ruby script/plugin install \<br /> http://metric-fu.rubyforge.org/svn/tags/REL_0_5_1/metric_fu/<br />(in the base of your rails app)<br /><br />4. rake metrics:all<br /><br />Which should work fine if you have standard Rails testing and you like my defaults. But what if you use a combination of RSpec and stock Rails testing? Then you can insert this into your Rakefile:<br /><pre><br />namespace :metrics do<br /> TEST_PATHS_FOR_RCOV = ['spec/**/*_spec.rb', 'test/**/*_test.rb']<br />end<br /></pre>The namespace isn't strictly necessary, but I like it for intentional purposes. Multiple paths are useful if, like on my last project, you need to be specific about which tests to run as some tests go after external services (and the people who manage them get cranky if you hammer 'em a lot).<br /><br />If you also want Rcov to sort by lines of code (loc) and have more aggressive cyclomatic complexity settings then do this:<br /><pre><br />namespace :metrics do<br /> TEST_PATHS_FOR_RCOV = ['spec/**/*_spec.rb', 'test/**/*_test.rb']<br /> RCOV_OPTIONS = { "--sort" =&gt; "loc" } <br /> SAIKURO_OPTIONS = { "--warn_cyclo" =&gt; "3", "--error_cyclo" =&gt; "4" }<br />end<br /></pre><br />That's it -- hope you find it useful, lemme know if you find a bug, and check out the project home page at:<br /><a href="http://metric-fu.rubyforge.org/">http://metric-fu.rubyforge.org</a><br /><br />Oh, and thanks to all my co-workers who helped write the original code, in its various forms, that became this plugin.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-91310662558406918552008-04-15T22:25:00.001-05:002008-04-15T22:27:10.848-05:00The Bug that Took Down a Release and How I Wrote ItWe were disposing of some last minute bugs today and I was reminded of the time, a few projects ago, when I introduced a bug that screwed up a whole release. <br /><br />Let's say that I was writing a pizza ordering web site and, after many successful releases, the Big Wigs at PizzaCo wanted to introduce a new feature that would let customers customize their pizzas even more. In production the user could select style of pizza (thin, pan, stuffed), size, and toppings. There was even some cool logic in place to make sure the toppings were available before being offered. Now, in this brave new world of pizza, the customer would be given the chance to customize each topping. If you chose peppers, you would be given the choice of green, yellow, or red (through the magic of javascript). A choice of onions would prompt the question of red or yellow. Pepperoni? -- regular or spicy?<br /><br />So we wrote the feature, which was difficult, as we had to totally change how we interacted with the Topping Provider Service (TPS) in order to reserve these new toppings and check for availability. But then the Big Bosses had a thought: "What if this new extra bit of choice scares away the customer?" And their solution was to have "switch" to turn the extra customization on and off. As developers we pointed out that this would be costly as making the new system look like the old, but still work with the new under the covers (by selecting defaults for each topping), was not easy. And it would introduce extra complexity by having two different pages that do almost the same thing. But they wanted it, so we did it.<br /><br />A few weeks later we found a bug with the topping selection page: If you take the last bit of pepperoni, but later come back to the toppings page to change your selections, the TPS service thinks all the pepperoni is gone (because you just reserved it) so you don't see that option on the page. We fixed it by looking at the customer's saved pizza and putting any toppings missing from the page back on the page. Except that we forgot to make the changes in the page that mimics the old behavior. Even worse, the way we fixed the new toppings page caused the pseudo-old page to blow up if you ever try to go back to it. And we found out that this happens right before a release.<br /><br />And this was all my fault.<br /><br />I worked on the feature to add the new customization, I also worked on the feature to hide the new feature, and (believe it or not) I worked on the bug fix. There was no one else on the team who was in a better position to remember that there were two pages and each need a change. Aside from my obvious point that I'm a terrible developer and you should never hire me, I think there's an interesting lesson here about unused functionality.<br /><br />We wrote the new page and then we used it. It did all sorts of sexy Ajax and was cool. And after a few weeks we forgot about the legacy page and things that might break it. There are many times when it will be tempting to bifurcate your code, but every time you do so you create a place for a bug. This is especially true if one path is rarely used. The new code will continue to evolve and the old will sit around and collect bugs. We did a full pass through regression testing, in addition to our normal tests and QA, but we didn't find this bug until hours before the release. Which is better that finding it after, but still, if the release had gone out it would have had the new feature turned off (for "safety") and the monster bug on. We were mere hours away from disaster.<br /><br />I know it's hard not to want options, but every option has its price.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-28434175342313383892008-03-31T22:11:00.004-05:002008-03-31T22:31:48.998-05:00Ruby Substrings and Testing Legacy CodeRecently <a href="http://www.cuberick.com/">Josh Cronemeyer</a> and I were working on writing a game in Ruby. <a href="http://code.google.com/p/gosu/">Gosu</a>, a 2D game library for Ruby and C++, and <a href="http://wiki.slembcke.net/main/published/Chipmunk">Chipmunk</a>, a 2D Physics engine, do lots of the heavy lifting so we thought it would be a fun Saturday afternoon thing to do. However, the examples had no tests so when we tried to change some stuff, and of course it didn't work, we were all sorts of clueless as to why.<br /><br />Well, wrapping tests around legacy code is not a lot of fun but it is an effective way of debugging. Here's how I like to do it:<br />Step one:<br />Identify potentially troublesome code (As in "What the hell is that doing?").<br />Step two:<br />Write some tests that verify the functionality of the code.<br />Step three:<br />Pull out the offending code into a method or methods.<br /><br />There's a fair bit of interplay between steps two and three, testing one piece may require extracting it.<br /><br />Step four:<br />Either you've found your problem or you've accomplished four things:<br />1. You really understand the troublesome code.<br />2. You've refactored it into a more readable version.<br />3. You've put some code under test.<br />4. You at least know where the problem isn't.<br /><br />I adopted this strategy after reading Michael Feathers "<a href="http://www.amazon.com/Working-Effectively-Legacy-Robert-Martin/dp/0131177052">Working Effectively with Legacy Code</a>" -- which is good reading.<br /><br />Eventually we found the problem to be an erroneous substring.<br />"hello"[0] doesn't return "h"<br />Ex:<br /><pre><br />irb(main):001:0> "hello"[0]<br />=> 104<br /></pre>Which is the character code of 'h', so we tried:<br /><pre><br />irb(main):002:0> "hello"[0,1]<br />=> "h"<br />irb(main):003:0> "hello"[4,5]<br />=> "o"<br /></pre><br />Aha! Now we thought that "string"[x,y] gives you the characters between position x and y. But no, we got some really strange behavior until we realized that "string"[x,y] gives you y number of characters starting at x position. The reason "hello"[4,5] gives "o" is because if you ask for 5 things starting at position 4 there's only one character left, which Ruby is only too happy to hand over. "hello"[0,1] is the way to get the first letter, just as "hello"[1,1] is how to get the second letter.<br /><br />Now one of the things that drew me to Ruby in the first place was it's intuitiveness. Often I found that if I didn't know how something worked, I could just write what I thought would work and it did. But substrings are a rare anomaly -- I find I have to look up how they work every time. And I still make mistakes even after I check the ruby doc. Does anyone know the history behind this weird syntax?Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-87880827245044831002008-03-18T21:33:00.007-05:002008-03-18T21:55:27.863-05:00On Inject, Complexity, and George CarlinInject has been stalking me for over 2 years.<br /><br />The year was 2005 and I had decided that I would write a website in this new fangled Rails thing. But while debugging some plugin code I ran into inject. To my java encrusted eyes, it looked like this:<br /><br />weird_thing = dont_know.inject({}) {|m, o| m[o] = some_crazy_method o; m}<br /><br />Holy crap. I had no freakin' clue. I looked up the docs on inject and saw that inject, from what I could understand, was used to add up numbers in an array. Which confused me even more. Of course since then I've come to understand inject, but have remained wary as it can be awful confusing to those new to Ruby. Just recently <a href="http://jakescruggs.blogspot.com/2008/01/when-you-should-ignore-metrics.html">I wrote about</a> how some teammates and I decided against using it to shorten a method, because the particular use sacrificed readability. Then <a href="http://buddingrubyist.wordpress.com/2008/02/05/why-i-like-to-inject/">Brian Dainton</a> wrote about how he loves inject. And finally, my team got in a rather spirited discussion over its use. One side of the argument was that it should be used with care as it's hard to read, especially for Ruby newcomers (whom we will be handing this project over to sooner or later). And the other side pointed out, rightfully so, that the reason we use Ruby in the first place is precisely because of powerful methods like inject. If we have to stay away from inject, or method_missing, or metaprograming, or anything else that is deemed 'too hard' then why not just write in Java or C# or Visual Basic?<br /><br />All this reminded me of one my favorite quotes form George Carlin:<br /><br /><blockquote>Have you ever noticed that anybody driving slower than you is an idiot, and anyone going faster than you is a maniac? </blockquote><br />The funny thing is that I can see both sides of this argument. Back in 2005 I would have thought you crazy to use such a method. Now it would take only a moment to figure out that in:<br /><br />weird_thing = dont_know.inject({}) {|m, o| m[o] = some_crazy_method o; m}<br /><br />dont_know is probably an array full of objects and the author of the code wants to pass each one of them into some_crazy_method, get the result, and then put that result in a hash with the original value as the key. When I was a slow driver in Ruby I thought all the other speed daemons were crazy to write such complex code. Now that I'm a budding speed racer myself, it's temping to see people who can't understand my code as stupid.<br /><br />At this point it would be pretty easy to wrap up this post by saying something useless like: “Use your best judgment when adding complexity, making sure to balance blah, blah, blah...” You, like everyone else on the planet, think you know the difference between too slow (code for dummies) and too fast (code for computers only). Even if you're only wrong 10% of the time (which I doubt) you're going to write a lot of horrible code.<br /><br />So how do you know when you're wrong (or right)? You don't. Let me try to explain though two examples of complexity: one good and one bad.<br /><br />In our application, we have a number of different ways to go through a sign up process. So we created an object called Flow. And each flow contains a number of FlowPages that specify the order of the flow. With a little bit of Ruby magic, we made it so you can define a flow like this:<br /><br />NormalFlow << FirstPage << SecondPage << ThirdPage<br /><br />Which is damn cool. But there was a bunch of code needed to support this. Over time we came to realize that we weren't seeing a lot of benefit from this notation. We didn't define flows that often and even if we did, the much simpler:<br /><br />NormalFlow.new(“FirstPage, SecondPage, ThirdPage”)<br /><br />expresses the intent just as well. When the time came to play a story dealing with putting flows into the database so the business can try different paths, we saw that the complexity was actually making it harder to use and so we threw out the old code and went with the totally unsexy “pass in a string” constructor. Lame, but way easier to maintain.<br /><br />In the other example, we had a requirement that the business should be able to change any of the words on the page quickly (without a release). This, of course, meant putting the page content into the database. We created a PageContent object that, through the magic of Ruby, gets created before any view is rendered, looks up all the content that it might need, and dynamically defines methods on itself that correspond to its contents. A given page may have a title or an address_line_one_label and to access these methods in the view you can call: content.title or content. address_line_one_label and you don't have to do a thing to set this up (other that put content in the db) -- it's available in every view. And over the months since we implemented this feature it's been easy to use, extend, and maintain.<br /><br />And that's really my point here. I can't just say “use your best judgment” because you already are. Furthermore, when you've just learned about, say, metaprograming you're probably going to use it more than you should. You won't have any idea if that super cool bit of Ruby is good or bad complexity until you get back in there and try to make it do something new. If the complexity fights you at every turn, it's bad and you should get rid of it. Good code you won't even notice because the tasks that touch it go so smoothly.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-61476084701389835592008-03-11T21:45:00.013-05:002008-03-11T22:47:20.320-05:00A Small Rails Site and Why You Should Build One<a style="font-size: 24px;" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.lesliescruggs.com"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; font-size: 24px; width: 550px; height: 240px;" src="http://bp1.blogger.com/_aBgnJ2sew-c/R9dE2Xof3rI/AAAAAAAAADk/IUO11YHlDZU/s400/Road_Runner_thumb.jpg" alt="" id="BLOGGER_PHOTO_ID_5176681997533175474" border="0" /></a> The other day I was showing Rails off to a friend of mine who wanted to know what all the fuss was about and I realized a shocking thing: I was horrible at starting a Rails site. Why? Because for the last year or so I've been working on large, established Rails codebases. Which is kinda cool as it shows how far Rails has come, but it sucks because one of the very cool things about Rails is how much you can do in a short amount of time.<br /> <a style="font-size: 24px;" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.lesliescruggs.com/public/view_gallery/9"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; font-size: 24px; width: 388px; height: 361px;" src="http://bp0.blogger.com/_aBgnJ2sew-c/R9dFGHof3sI/AAAAAAAAADs/zwK7NHzNYyU/s400/TroubleInMind_thumb.jpg" alt="" id="BLOGGER_PHOTO_ID_5176682268116115138" border="0" /></a> So I resolved to create and deploy a small site as a way to exercise some atrophied muscles and have some good old fun with Ruby and Rails. My father, Leslie Scruggs, is a sculptor and I had written him a pure html site back in 1999. And he maintained it editing the raw html files, creating his own thumbnails, and uploading via ftp. Not the easiest of tasks for a man in his 60's. So I created <a href="http://www.lesliescruggs.com">lesliescruggs.com</a> which has an admin section so he can upload pics and get them resized and thumbnailed with 10% of the effort.<br /> <a style="font-size: 24px;" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.lesliescruggs.com/public/view_gallery/2"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; font-size: 24px; width: 498px; height: 348px;" src="http://bp0.blogger.com/_aBgnJ2sew-c/R9dFVHof3tI/AAAAAAAAAD0/EGAfTpKotK0/s400/OutOfDepression_thumb.JPG" alt="" id="BLOGGER_PHOTO_ID_5176682525814152914" border="0" /></a>Of course I used attachment_fu for image handling (resizing, saving, deleting, etc.) but I was impressed how much better it's gotten in the last year. You can now use ImageScience or MiniMagick as the back end image processor and you can save files to Amazon's S3. And, maybe it's just me, but it seems like it's easier to use than ever. Restful_authentication was also impressive in its gains. I've had mixed results with Rails plugins for authentication in the past, but everything went very smoothly. One of the things I really liked was the ability to omit the action mailer part of the plugin. Sometimes you just don't need to be able to email lost username/passwords and it's nice to be able to leave that code out. Another nice surprise was after upgrading to the latest IntelliJ I found that creating a project with RSpec was an option. Worked well, too.<br /> <a style="font-size: 24px;" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.lesliescruggs.com/public/view_gallery/17"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; font-size: 24px; width: 550px; height: 281px;" src="http://bp3.blogger.com/_aBgnJ2sew-c/R9dFe3of3uI/AAAAAAAAAD8/eL_3Fq3-i6Y/s400/Iron_Wood_Trail_thumb.jpg" alt="" id="BLOGGER_PHOTO_ID_5176682693317877474" border="0" /></a>And page caching, something I've been meaning to try, was pretty easy to implement and perfect for a site like this. All in all I highly recommend banging together a quick (this one took about 2 weeks of spare time) Rails website as you'll have fun and learn a bunch of things your day job won't teach you. Especially about deployment -- but that's another post.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-30632977615931288252008-02-21T06:26:00.003-06:002008-02-21T06:41:45.102-06:00ActiveSupport::CoreExtensions::String::Inflections underscore<pre><br />>> "ActiveSupport::CoreExtensions::String::Inflections".underscore<br />=> "active_support/core_extensions/string/inflections"<br /><br />>> "ActiveSupport::CoreExtensions::String::Inflections".underscore.camelize<br />=> "ActiveSupport::CoreExtensions::String::Inflections"<br /></pre><br />Looks like Rails extension to the string class does more than underscore a string. Which is useful, but shouldn't the name be "relative_path" or something similar? When I see the method name "underscore" I know exactly what it means, except that's not what it does. The opposite of "underscore" is "camelize " which, again, may not have the best name.<br /><br />And if you want to find the declared constant specified in a string:<br /><pre>>> "ActiveSupport::CoreExtensions::String::Inflections".constantize<br />=> ActiveSupport::CoreExtensions::String::Inflections<br /></pre>Useful and an excellent name.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-34301135486328148752008-02-19T23:09:00.003-06:002008-02-19T23:21:58.303-06:00Return to Generating a Unique NumberIn a previous post I discussed <a href="http://jakescruggs.blogspot.com/2008/01/generating-unique-number.html">generating a unique id number</a> for an order in our database. Read the original post for the full story, but to sum up, I decided that playing around with rand(100_000_000) was good enough. <a href="http://martin.ankerl.com/">Martin Ankerl</a> posted a comment pointing out that I was vulnerable to the <a href="http://en.wikipedia.org/wiki/Birthday_problem">Birthday Problem</a>. So after a quick trip to wikipedia, I found out that if you have 23 people in a room you might think you have a 23/365 chance of having 2 people with the same birthdays, but no, your chances are closer to 50%! This is because in a group of 23 people there are 253 pairs each of which have a 1/365ish chance to have the same birthday. The formula to calculate how many are needed to give you a 50% chance of having a birthday collision is:<br /><br />.5 + sqrt(.25 + 2 * 365 * ln(2) ) = 22.9999<br /><br />Which means the 50% chance of collision mark for 100,000,000 is:<br />.5 + sqrt(.25 + 2 * 100,000,000 * ln(2) ) = 11774.600235771<br /><br />Holy crap! I'm screwed if I can only have 12,000 orders before I get a 50% chance of collision. But wait, I think I'm ok because every time I insert a key into the database I check to make sure it isn't already in there. This means that when I'm at 12,000 orders the next key has only a 1 / (100,000,000 - 12,000) chance of being a collision. I think I've changed from the "Birthday problem" to the "<a href="http://en.wikipedia.org/wiki/Birthday_problem#Same_birthday_as_you">Same birthday as you</a>" problem, which is way more intuitive. If I'm wrong I rely on you, the blogoshpere, to tell me so.<br /><br />If you haven't already, go read the wikipedia article on the <a href="http://en.wikipedia.org/wiki/Birthday_problem">Birthday problem</a>. It's pretty cool.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-61597154058104811072008-01-23T06:44:00.000-06:002008-01-23T20:30:51.345-06:00When You Should Ignore MetricsOur team has been doing a bang up job of reducing our complexity through our <a href="http://jakescruggs.blogspot.com/2008/01/method-hit-list.html">hit list</a> (All of our methods are ranked by <a href="http://ruby.sadi.st/Flog.html">Flog</a> score and we spend some part of every iteration picking the worst methods and trying to refactor them.). But sometimes we run into a situation where we prefer a high Flog score to a low one. For instance, this bit 'o code:<br /><pre><br />def is_something?(x, y)<br /> x.foo == y.foo &amp;&amp;<br /> x.bar == y.bar &amp;&amp;<br /> x.blah == y.blah &amp;&amp;<br /> # and 10 more lines of the same<br />end<br /></pre><br />which had a Flog score in the 60s. We could have changed it to this:<br /><pre><br />def is_something?(x, y)<br /> ["foo", "bar", "blah", # and so on ].inject(true) do | a, b |<br /> a &amp;&amp; x.send(b) == y.send(b)<br /> end<br />end<br /></pre>Which gave a us a much lower score. However that first method is crazy simple. Merely glance at it and you know what it's doing. The 'less complex' method? Even an experienced ruby dev would need a moment or two. As for a new dev... I can remember a time not so long ago when I found an inject in some source code and was pretty confused even after I looked up the Rdoc. It's one of those methods that's so powerful and flexible that the documentation only seems to make sense after you understand it.<br /><br />After some discussion with the team we decided that while Flog is very good at telling you which methods to inspect for badness, it is not always true that a better score makes a for better method. The same can be said for all metrics, really.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-54375308147271313892008-01-21T23:08:00.000-06:002008-01-22T00:04:35.002-06:00Automated Javascript Rails TestingOn my last project we did some javascript unit testing in browser using the unittest.js library from scriptaculous, but because it's kind of a pain to set up and integrate into your build, I didn't get it working on my current job. Which makes me a bad person. However, <a href="http://drnicwilliams.com/">Dr Nic Williams</a> has made yours and my life easier with his <a href="http://drnicwilliams.com/2008/01/04/autotesting-javascript-in-rails/">javascript_test plugin</a> for rails. Now I can type:<br /><pre><br />ruby script/generate javascript_test fancy_javascript_file<br /></pre><br />And get a fancy_javascript_file_test.html to test my fancy_javascript_file.js. It's all set up to go right at the javascript file. Just open the html file in a browser and it runs the tests. The sweet doctor explains it all better than me so go check out his site.<br /><br />What I want to talk about is trying to integrate this into your Cruise Control build. The first problem we faced is that the tasks that come with the plugin don't close the tabs created in the browser. So after 10-20 builds, you're going to have way too many tabs and some memory problems. My teammate <a href="http://www.workingwithrails.com/person/11263-toby-tripp">Toby Tripp</a> came up with this:<br /><pre><br /> def teardown<br /> applescript &lt;&lt;-EOS if macos?<br /> tell application "System Events"<br /> tell process "Firefox"<br /> set frontmost to true<br /> click menu item "Close Tab" of menu "File" of menu bar 1<br /> end tell<br /> end tell<br /> EOS<br /> end<br /></pre><br />This code snippet goes inside<br /><pre><br />class FirefoxBrowser &lt; Browser<br /></pre><br />inside the plugins/javascript_test/lib/javascript_test.rb file. We could have just killed Firefox but we needed to play nice in case any other builds were using Firefox at that moment (we test with Selenium to use Firefox to click through the app).<br /><br />Now all you have to do is change the position of the browser.teardown call in that same file in the 'define' method. By default, teardown is called just before the end of the block created by<br /><pre><br />@browsers.each do |browser|<br /></pre><br />But I want it to get called after every test so you can put it just before the end of the block created by:<br /><pre><br />@tests.each do |test|<br /></pre><br />So that the end result looks like this:<br /><pre><br /> def define<br /> trap("INT") { @server.shutdown }<br /> t = Thread.new { @server.start }<br /><br /> # run all combinations of browsers and tests<br /> @browsers.each do |browser|<br /> if browser.supported?<br /> browser.setup<br /> @tests.each do |test|<br /> browser.visit(" http://localhost:4711#{test}?resultsURL=http://localhost:4711/results&amp;t=" + ("%.6f" % Time.now.to_f))<br /> result = @queue.pop<br /> puts "#{test} on #{browser}: #{result}"<br /> @result = false unless result == 'SUCCESS'<br /> browser.teardown<br /> end<br /> else<br /> puts "Skipping #{browser}, not supported on this OS"<br /> end<br /> # browser.teardown<br /> end<br /> @server.shutdown<br /> t.join<br /> end<br /></pre><br />You can see by the comment where the teardown used to be. So now when you run your test the tabs will close when finished. In my next post I'll talk about how to fail the build when the javascript tests fail.<br /><br />Oh, and I'd like to thank <a href="http://m2ward.blogspot.com/">Mike Ward</a> for turning me on to javascript unit testing in the first place.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-41122369480263908372008-01-16T22:35:00.001-06:002008-02-19T23:26:14.646-06:00Generating a Unique NumberWeird requirement at work today: We needed a number that was alpha-numeric, exactly 11 digits, unique, and non-sequential. At first we thought of using a hash, but MD5 and Sha1 give you way too many digits. We could truncate to 11, but not knowing much about hashing, that made me pretty nervous that we'd have collisions. After a bunch of discussion, we decided on this:<br /><pre><br />MAXIMUM_FOR_10_DIGITS = 0xffffffffff<br />(MAXIMUM_FOR_10_DIGITS + cart_id + Time.now.to_i + rand(100_000_000)).to_s(16)<br /></pre>I was thinking about using hex, but I was worried about what would happen if the number got too big and went to 12 digits. So I did this calculation:<br /><pre><br />irb(main):001:0> 0xfffffffffff - 0x10000000000<br />=> 16492674416639<br /></pre>Turns out there's all sorts of room between the lowest and highest 11 digit hex number. <a href="http://pgrs.net/">Paul</a> and <a href="http://schubert.cx/">Schubert</a> did some quick calculations assuming lots of carts per day and figured out that we have about 250,000 years before we run out of space. Now since we use a random number, there is a chance that we could get two matching numbers so we do a database lookup to make sure that a generated number hasn't been used before. There is still a small chance that two identical numbers could be generated in the time between generation and saving to the database, but I think we can live with that.<br /><br />Update: After some interesting comments, I wrote a follow up post. <a href="http://jakescruggs.blogspot.com/2008/02/return-to-generating-unique-number.html">Check it out</a> for more math goodness.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-11460029332614209822008-01-09T20:01:00.000-06:002008-01-10T07:41:23.430-06:00The Method Hit ListIn a previous post I talked about how my team has a <a href="http://jakescruggs.blogspot.com/2007/09/metrics-for-rails.html">daily metrics build</a> that reports on our code quality. One of the things we measure is the <a href="http://ruby.sadi.st/Flog.html">Flog</a> score of our methods (Flog is a ruby program that evaluates the complexity of your methods). We have a standing developer task to look at the top five worst methods in our application and reduce their complexity. Every iteration we try to spend some time looking at a few developer tasks, which are things that don't provide any direct business value but do make our code better, automate some manual process, or fix some technical problem not tied to a bug.<br /><br />Yesterday <a href="http://chirdeepshetty.net/">Chirdeep </a>and I refactored the worst method in our app and while it wasn't easy it produced a number of benefits:<br /><br />First, about halfway though the refactoring we came upon some twisted logic that seemed as if it could never evaluate to false. After pulling in <a href="http://www.aliaghareza.com/">Ali</a> for some consultation, we realized that indeed it would always be true and therefore was a bug. We fixed it and moved on.<br /><br />Second, we noticed that the object inherited from another object, but overwrote the constructor with a strange one that took different arguments. This had the effect of making other objects interact weirdly with this class in order to deal with its 'sorta' inheritance. We managed to get rid of the custom constructor and have it rely on it's super class.<br /><br />Finally, we noticed a nice clear division of labor in the complex method so we were able to extract a method that had a single responsibility. So we ended up with two methods that shared the complexity of the original one.<br /><br />I would have been happy if all we accomplished was the last item. However, when you take a look at your most complex methods you often find bad stuff hiding amongst the craziness.<br /><br />It's also important to note what we didn't do:<br />We did not simply extract out every 5 lines into a method. While that would have improved our Flog score per method, it would been the code equivalent of sweeping dirt under the rug. Methods should, as much as possible, have a <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle">single responsibility</a>. If they don't then all you're doing is moving a bunch of complex stuff into many different arbitrary places, instead of just one place. Which is actually worse. So, like all metrics, complexity is something to pay attention to but you don't want to slavishly chase after good numbers.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-24842352198394847172008-01-07T21:21:00.000-06:002008-01-07T21:37:22.369-06:00Svn Merge from Trunk to BranchEvery time I need to do this I try to use svn's --help which is only possible to decipher if you already know what to do. Then I spend too much time on the internet looking for the answer. And finally I break down and ask my tech lead. With much shame. I'm putting this up here so I least I'll know where to find it. Maybe you too.<br /><br />In the directory of the branch:<br /><pre><br />svn merge -r 1001:1002 https://svn.dev.your/repo/trunk/src .<br /></pre><br />If you checked in some files in revision 1002, then what you're saying is that you only want the changes from that checkin with '-r 1001:1002' Which is followed by the url of the trunk (where the changes were checked in) and a '.' to say that you want to merge to the current directory. Use '--dry-run' if you want to see what would happen without actually screwing anything up. After the merge is successful, you now have some modified files in the branch you can check in.<br /><br />Done and done.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-83164976916080848772007-11-14T21:20:00.000-06:002007-11-14T20:24:04.697-06:00Mocking Backticks and Other Kernel MethodsThere's a bunch of places in our build where we need to execute a system command (such as running sqlplus) but often times we've found that the command fails and our build happily churns away. It's pretty easy to check the result of system command with $?.success? but we have to remember to do that everywhere... So that means it's time to extract a method. Here's what <a href="http://kseebaldt.blogspot.com/">Kurtis</a> and I came up with:<br /><pre><br />module DatabaseHelper<br /> def self.command_runner(command)<br /> output = `#{command}`<br /> puts output<br /> fail unless $?.success?<br /> fail if output.include? "ERROR"<br /> output<br /> end<br />end<br /></pre><br /><br />So you pass in a command as a string and the command runner:<br /><ul><li>runs the command</li><li>prints out the output</li><li>fails if the return code is bad</li><li>fails if the output includes "ERROR" (useful when running database imports with sqlplus)</li><li>and returns the output just to be a good citizen</li></ul><br />So that's cool but when I started to write the module I thought "Hey, I can test this." Which is a cool side effect of extracting methods from your rake file. So cool that you probably want to consider it even if the method won't be re-used.<br /><br />Anyway, I started to write the test and realized that I would need to mock the backtick, which is a Kernel method. I'm using mocha so I tried:<br /><pre><br />Kernel.expects(:`)<br /></pre><br />and<br /><pre><br />Kernel.any_instance.expects(:`)<br /></pre><br />But no luck. Turns out you need to mock the class in which the backtick method will be called. So here's the tests I ended up with:<br /><pre><br />unit_tests do<br /><br /> test "command runner executes a command" do<br /> DatabaseHelper.expects(:`).returns("some output")<br /> $?.expects(:success?).returns(true)<br /> <br /> output = DatabaseHelper.command_runner("command")<br /> assert_equal "some output", output<br /> end<br /><br /> test "command runner fails if ERROR in output" do<br /> DatabaseHelper.expects(:`).returns("ERROR: this is broken")<br /> $?.expects(:success?).returns(true)<br /> <br /> assert_raises(RuntimeError) do<br /> DatabaseHelper.command_runner("command")<br /> end<br /> end<br /><br /> test "command runner fails if process fails" do<br /> DatabaseHelper.expects(:`)<br /> $?.expects(:success?).returns(false)<br /> <br /> assert_raises(RuntimeError) do<br /> DatabaseHelper.command_runner("command")<br /> end<br /> end<br />end<br /></pre><br />I was having such a good time mocking that I later went back and mocked out the puts so I didn't have to see the output when running the tests. Gotta keep things clean.<br /><br />Btw, you may notice that I'm not using Rspec a work anymore. Yep, it's true. My new team is using <a href="http://dust.rubyforge.org/">DUST</a> (well, the code that would later be part of the inspiration for DUST) which is pretty cool. I still prefer Rspec, but I can't really make the case that we should switch to Rspec mid-stream (if we were using straight Rails testing, I would make that case.).Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-49489033057218102562007-10-24T20:21:00.001-05:002008-05-04T12:01:57.151-05:00Employee AppreciationAs a consultant, I spend a lot of time at big companies and big companies generally have a morale problem. Bureaucracy, cubicles, and lax management will tend to do that. Typically they try to solve this problem by exchanging money for goodwill. A few projects ago I was consulting at one such company where they spent a ton of money having a day of games, burgers, and events during business hours. There was a big ad campaign, lots of promotional tie-ins and give-aways -- a team of people clearly worked on this for weeks (or maybe months). Now add up productivity, food, and sumo-suit rental costs and we are talking some serious money. All worth it in service of employee good will, right?<br /><br />A few days after this spectacle, my team and I decided to walk to lunch. Now the most direct route to our restaurant of choice happened to be through the front door. However, the security guy stopped us when we tried to leave. Employees aren't allowed to enter or leave through the front door. Why? That's the policy.<br /><br />So we backtracked, went out another door, had lunch, and came back. At which point we discovered that our badges wouldn't let us in the side doors. So we tried another side door. No luck. We went around to the front where we found out that security had turned off our badges intentionally so that we would have to go in the front door and get a lecture from the head security guy. I'm not kidding. 3 grown men got a talking to for trying to exit through the front door.<br /><br />Later we heard, informally, that the company wants to keep the front entrance looking nice so that's why employees may not enter or leave through the front door. The obvious implication here is that visitors would be put off by the sight of employees walking in through the same door as them. Does anyone really believe that one day of corporate sponsored fun is likely to make up for the daily humiliation of having to enter through the back door of your own company?Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-17217468324276881742007-09-13T23:31:00.000-05:002007-09-13T23:54:23.094-05:00Metrics for RailsEveryone thinks they write good code -- it's just part of human nature. You can't do something every day and not secretly suspect that you're good at it. Self-delusion is a powerful thing so you need to use metrics to take a hard look at your code.<br /><br />On my current project, we've just added a daily metrics build (run every day at midnight by <a href="http://cruisecontrolrb.thoughtworks.com/">CruiseControl.rb</a>) that takes a look at our code in three ways:<br /><ul><li>Code coverage with <a href="http://rubyforge.org/projects/rcov/">Rcov</a></li><li>Cyclomatic complexity with <a href="http://saikuro.rubyforge.org/">Saikuro</a></li><li>And um..., Flogging with <a href="http://ruby.sadi.st/Flog.html">Flog</a></li></ul>Rcov is a code coverage tool that can be used with <a href="http://agilewebdevelopment.com/plugins/rails_rcov">Rails Rcov</a> to add a bunch of rake tasks to your build so you can figure out which lines of code are run by your tests... and which are not.<br /><br />Saikuro computes <a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity">cyclomatic complexity</a> which "measures the number of linearly independent paths through a program's source code." Methods with more paths are harder to understand/debug/modify.<br /><br />And Flog is cyclomatic complexity with an attitude. It scores ruby methods with an "ABC metric: Assignments, Branches, Calls, with particular attention placed on calls."<br /><br />Why do we use both Saikuro and Flog? Well Flog catches Ruby specific complexities that cyclomatic complexity doesn't (for instance, calls to eval are given particular weight) and it picks up methods that Saikuro misses (we use metaprogramming to define a fair amount of methods and Saikuro seems to miss anything not defined with a 'def'). But Flog outputs a flog score which isn't very familiar to most developers, while cyclomatic complexity is a relatively well understood computer science term.<br /><br />Also flog has the scariest looking software website ever. I can't believe those guys have the chutzpa to put such pictures on a site that advertises them as consultants for hire.<br /><br />So how did our code look? Pretty good -- 96% code coverage overall but some methods need testing love. And some of our code had pretty high cyclomatic complexity or Flog numbers so we'll need to write some developer tasks to fix the problems uncovered. (Every iteration we work on a few developer tasks in addition to the development, bug, and production tasks.)<br /><br />But the metrics build isn't done, in the future I'd like to:<br /><ul><li>Find duplicate code with <a href="http://pmd.sourceforge.net/cpd.html">CPD.</a></li><li>Use <a href="http://ruby.sadi.st/Heckle.html">Heckle</a> to see if our tests are any good. (Heckle changes your code (not permanently) and then re-runs your tests -- if they don't fail then you've got bad tests)</li><li>Fail the build on bad metrics numbers.</li><li>Figure out how to integrate the results <a href="http://www.panopticode.org/">Panopticode</a>-style with all the cool visualizations it offers.</li></ul>Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-14013120933263964642007-08-30T22:02:00.000-05:002007-08-30T21:07:27.887-05:00Why 50% Test Coverage Seems More Painfull Than No Test CoverageRecently I was on a project where a bunch of code had been written before we arrived. It was quite a struggle to get the application under test. After a number of months the team hit 50% and then we just stayed there. We had a hard time getting client developer buy-in on the push upward from 50%. I didn't really understand this attitude at first, but after talking with the devs, I realized that the tests were mostly a nuisance for them. They saw it like this:<br /><blockquote> "If I have to gut a few pages, as part of a changing requirement, now I also have to spend a day fixing the stupid tests. And the tests never really catch any bugs, so what was the point? All the tests are doing is slowing me down."</blockquote>Since the coverage was low and many of the test writers were new to unit tests we didn't really have a lot of protection from bugs. But we also had a sizable suite to maintain. They were feeling all the pain of keeping a test suite running but seeing none of the benefits. Now, as a guy who'd been on projects with good test coverage I could see how the suite was making things better: First, I had a roughly 50% chance of being able to use the tests as documentation for any class I opened up. And second, the tests <span style="font-style: italic;">were</span> catching <span style="font-style: italic;">some</span> problems before they made it into the repository -- but not enough for the client devs to notice.<br /><br />We never did convince them that writing tests allowed them to go faster in the long run and it caused me to wonder how many teams get to 30-60% coverage and give up because the benefits are too subtle and the pain is too much?Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-33510993986644006822007-08-29T20:59:00.000-05:002007-08-29T21:23:56.087-05:00Renting American CarsAs a traveling consultant I've developed a pretty poor opinion of American cars. Sliding doors that won't close, unfathomable glove boxes, ugly/boring interiors, boring/ugly exteriors, crazy placements for the radio/ac/heating/trunk release buttons, plastic doors with tails of extra material hanging off poorly drilled holes... The other day I was sitting in a Chrysler 300 talking with one of our tech leads about how he really wishes the rental company hadn't upgraded him and would've much preferred a Hyundai Elantra -- a car that costs half as much as the 300. It's a bit depressing.<br /><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_aBgnJ2sew-c/RtYnoTH6QfI/AAAAAAAAADM/CqXZtgFF_uM/s1600-h/chrysler-300c.jpg"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 272px; height: 234px;" src="http://bp3.blogger.com/_aBgnJ2sew-c/RtYnoTH6QfI/AAAAAAAAADM/CqXZtgFF_uM/s320/chrysler-300c.jpg" alt="" id="BLOGGER_PHOTO_ID_5104310800952738290" border="0" /></a><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_aBgnJ2sew-c/RtYoezH6QgI/AAAAAAAAADU/U01kV834yoE/s1600-h/07_Hyundai_Elantra_21.jpg"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 302px; height: 202px;" src="http://bp1.blogger.com/_aBgnJ2sew-c/RtYoezH6QgI/AAAAAAAAADU/U01kV834yoE/s320/07_Hyundai_Elantra_21.jpg" alt="" id="BLOGGER_PHOTO_ID_5104311737255608834" border="0" /></a>Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-57062845877820875322007-08-28T23:01:00.000-05:002007-08-28T22:08:05.857-05:00Writing Your Own Custom Log ParserI spent the last week writing a log parser of all things. The web site I'm currently working on is the sign-up page for a large company so they are very concerned about how far the average joe gets into the process before they give up. If everybody bails on the address page, then perhaps it's too complicated or unresponsive or... something. Up till now we've been redesigning pages based on some data and a lot of guesswork so we thought it might be about time to get some serious data on every session every day. Now the first thing you learn about parsing rails logs is that they are designed to be human readable but not so much computer parsable. An Apache request is one line. A Rails request log has lots more info, but is multi-line. And since we run a lot of Mongrel instances at once, all these requests are interleaved, so sorting them out is an intense computation (we actually ran out of memory a few times before we did some optimization).<br /><br />Our first pass was mostly a ruby script that called off to a few classes which produced a csv file that our analysts could play with. But they wanted more data -- data only Apache could provide. So we added an Apache log parser and merged the results. And there were bugs (stuff wasn't logged, sessions seemed to end at the starting page or somehow start in the middle (both of which are impossible)). And the code was complicated and hard to understand. At this point we realized that we had been treating this task like a second class citizen -- a task to be automated but not really cared about. So we bit the bullet and refactored the log parser to be more Object Oriented. Now there's a log collector that has lots of requests. Each request mostly delegates to its Apache Request or its Rails request. Along the way things became more clear and the bugs started to become obvious, including one I have to share with you:<br /><br />Remember how I said many sessions seemed to start in the middle while others ended at the beginning? Well, it turns out that a typical telemarketer will sign up one user and then head back to the first page to sign up another. At which point Rails grabs the session id of their previous session and logs it before processing the request (which wipes the session, but too late for the logging). So it looks like the previous session ended at the beginning. Of course, the next session has had it's beginning stolen so it looks like it started in the middle. Since Rails does this bit of logging before it even looks at our code we needed to adjust our logger to ignore the false session and look for the real session id in a extra bit of logging we added in.<br /><br />Anyway now that we have our own custom log parser we can get some awesome info on our app for debugging weird stuff that only happens under load and the analysts get a csv file then can import into Excel and generate all sorts of cool graphs and statistics. Yes it did mean a week of writing regular expressions and some crazy command line statements (like: cat access.log | grep <something> | awk <something> | sort | uniq -c | sort ) but we now have a very sharp, well tested, tool.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.comtag:blogger.com,1999:blog-1042801964488488185.post-53628179028980327372007-08-09T23:58:00.000-05:002007-08-10T00:17:46.139-05:00RSpec on Rails: ModelsIn my last post I talked about using RSpec without Rails, but since just about all my Ruby programming involves Rails, I should probably get into how to specify Rails code.<br /><br />RSpec on Rails: Models<br /><br />So if you're new to RSpec and you want to get started quickly, head on over to <a href="http://rspec.rubyforge.org/">rspec.rubyforge.org</a>, get RSpec, Spec::Rails and then, after you've created a new Rails project and rspec-ed it (all detailed on the website) you can run:<br /><pre><br />ruby script/generate rspec_scaffold person name:string<br /> phone_number:integer<br /> cash:decimal<br /></pre><br />Which will get you a bunch of stuff to play with. Of you'll need to set up your database.yml and run rake db:migrate if you want thing to work.<br /><br />Fire up your favorite IDE and find the person_spec.rb and you should see something like:<br /><pre><br />require File.dirname(__FILE__) + '/../spec_helper'<br /><br />describe Person do<br /> before(:each) do<br /> @person = Person.new<br /> end<br /><br /> it "should be valid" do<br /> @person.should be_valid<br /> end<br />end<br /></pre><br />Not the world's most rigorous set of specs, but then our model doesn't have much behavior at the moment. You'll notice that the auto-generated spec does use RSpec's built in interrogative feature. 'be_valid' makes RSpec look for 'valid?' Running the spec with spec formatting ('-fs') produces the following output:<br /><pre><br />Person<br />- should be valid<br /><br />Finished in 0.028878 seconds<br /><br />1 example, 0 failures<br /></pre><br />But what's interesting is that if I remove the string between it and do, the output remains the same. If you leave off the name of a spec, RSpec guesses a name for you by looking at what you're asserting.<br /><br />But let's say I want to specify that a Person is invalid without a name. Here's how I would do that:<br /><pre><br /> it "should be invalid without a name" do<br /> @person.should have(1).error_on(:name)<br /> @person.should_not be_valid<br /> end<br /></pre><br />As you can see, there's some built in sugar to make checking errors more readable. Now that I have a failing spec I can add the following line to my Person model to make the it pass:<br /><pre><br /> validates_presence_of :name<br /></pre><br />Now let's do something a little more interesting:<br /><pre><br /> it "should run inside a transaction when making rich" do<br /> Person.should_receive(:transaction).and_yield<br /> @person.make_rich<br /> @person.cash.should == 1000000.00<br /> end<br /></pre><br />What I'm saying here is that a Person should have a method that makes them rich and furthermore that the rich-making should happen inside a transaction. RSpec has mature mocking/stubbing framework built into it. 'and_yield' can even specify what to yield so you could return an object that you created in the spec and then check to make sure some things happened to it. Of course, before I get to much further, I should write some code to make the spec pass:<br /><pre><br /> def make_rich<br /> Person.transaction do<br /> self.cash = 1000000.00<br /> end<br /> end<br /></pre><br />Now what if you already have a codebase that has a lot of Test::Unit tests but you want to start using rspec? Well you could just start writing specs in their own spec folder and rspec's default rails task will run both your tests and your specs. But that's not your only option. You can use RSpec inside your tests like so:<br /><pre><br />require File.dirname(__FILE__) + '/../../test_helper'<br />require 'spec/test_case_adapter'<br /><br />class PersonTest < person =" Person.new(:name"> "Jake")<br /> end<br /><br /> def test_person_can_have_name_set<br /> @person.name.should == "Jake"<br /> end<br />end<br /></pre><br />Now if you do this you won't have access to all the feature of RSpec: The reports, the mocking frameworks, and some custom matchers won't work (for instance '@person.should have(1).error_on(:name)' isn't available) but it's a low impact way of trying out RSpec before you commit.<br /><br />Next time we'll look at view specing with RSpec.Jake Scruggshttp://www.blogger.com/profile/16274380203959781950noreply@blogger.com