This Week In Refactoring: Active Record

  • Posted By Stuart Halloway on July 26, 2007

Ever wonder why this doesn't work with ActiveRecord models?

class Topic < ActiveRecord::Base
  alias_method :body, :content
end

In the latest installment of This Week In Refactoring, we explore this issue, and refactor ActiveRecord to improve the situation.

Relevance welcomes Venkat Subramaniam as Chief Learning Officer

  • Posted By Stuart Halloway on July 25, 2007

We are pleased to announce that Dr. Venkat Subramaniam is joining Relevance as the Chief Learning Officer. Venkat is known for his passion, enthusiasm, and ability to impart complex concepts using clear analogies and examples. Venkat co-authored the 2007 Jolt Productivity Award-winning book Practices of an Agile Developer and authored .NET Gotchas. Venkat is frequently invited to speak at international conferences and user groups. He has over 15 years experience developing software applications and teaching software development practices to thousands of software developers in US, Canada, and Europe.

At Relevance, Venkat will work closely with industry experts to expand our top-of-the-line training curriculum. Our curriculum will focus on three key areas: Software Development Practices, Languages, and Frameworks and Platforms.

The Software Development Practices area covers the principles, practices, tools, and techniques you need to succeed on your software projects. We emphasize proven agile practices from our decades of experience on successful software projects.

The Languages area focuses on languages that we use on projects today, plus languages that are driving innovation for the future. We are offering courses on Ruby, JavaScript, Java, Groovy, C#, Erlang, Scala, Smalltalk, and Lisp.

The Frameworks and Platforms area covers environments that have proven themselves for enterprise development, plus promising technologies on the horizon. This area currently includes the Java Virtual Machine, Rails, Grails, Spring, Lift, and Streamlined.

Welcome Venkat!

This Week In Refactoring Slides Posted

  • Posted By Stuart Halloway on July 24, 2007

I am reposting the slides from the 7/16 This Week in Refactoring. It was a pleasure interacting with the Typo team, and they have applied a patch with the changes.

Today in "Poor Decisions in Software"

  • Posted By Justin Gehtland on July 24, 2007

Within the last 12 hours, I’ve been confronted with three instances of unbelievably bad, perhaps unspeakably bad, decisions in the great world of software that I simply have to share. I think there are lessons here for all of us.

Part the First

My wife had to apply to have her passport renewed, and we have an international trip coming up in two months. So she sent in her form for expedited service. Its been about two weeks, and we decided to go online and check on the status of the application, since the government went to all the trouble to expose that service and all. So, when we get to the final page where you type in your name, SSN and date of birth, it is, of course, protected by SSL. Exccept, however, for the dialog box that popped up to say that the certificate used to verify the connection is invalid and, more specifically, expired.

This is the Start Department website, mind you, where they are claiming to have slowed down the process of getting or renewing passports in the name of “security”, and yet, their IT department can’t even be bothered to update the certificate after it expires. Granted, it expired at 8pm Saturday night, but still.

Part the Second

So, after deciding that I wasn’t going to fret about possible hijacking of the State Department website, my wife filled in the form and submitted it. We got back, instantly I might add, a results page showing her passport number and name, when the application was received, and that it was “in process”. And, also, that she had requested “Routine Service”, which means we’d have to wait 10-12 weeks. Now, we know that we sent in the appropriate amount for expedited service, and that we filled the form in correctly. So, now, we’re mad, because if they get it wrong, the only remedy is to go to DC and talk to somebody there.

So, we called the hotline. After waiting on hold for five minutes, the automated voice told us that the hotline was only servicing “customers” who have tickets for international travel within the next 14 days. Which just meant, probably, that we’d have to call back in 6 weeks, once we were inside that obnoxious window. Argh.

Finally, we hung up. Frustrated, and a little loopy (it was almost midnight), I did the only thing I could think of: I hit “back” on the browser, and re-submitted the form.

On the results page, nothing had changed, except for the word “Routine” now read “Expedite”. Ignoring the lack of a “d” at the end of the word, we were still miraculously faced with the opposite result, less than 10 minutes after have requested the same information (at 11:45pm on a Monday). So, we printed the page, then submitted the form five more times just to see if it randomly flipped back and forth a few times. No dice; we were firmly in expedited territory.

Now, if you think about this for a second, probably less than 1% of all users of the State Department’s website would ever bother to resubmit the form. 99% would see that their request was placed in the wrong bucket and immediately plan to travel to DC or call their congressman or something. The software, though, clearly has a bug: it misrepresented our status, in the worst possible way, on the first request only. If our case is not unique, then imagine the wasted time and energy on those hotlines by people calling in only to discover that everything was fine. Unfortunately, having now figured out the problem, blogging this is my only means of communicating the discovery to the fine people at travel.state.gov, since their online email form doesn’t actually let you type in a message.

Part the Third

Get to the office this morning, and our office manager is having trouble connecting to the networked printer from the Windows side of her Mac/Windows hybrid machine. The printer is brand new, and set up to be available at a static internal IP address. On the Mac side, setup was three clicks and type in the IP address. No sweat. She couldn’t get it hooked up in XP, though.

So, I verified that the network was passing through to the Windows image, and then verified that I could ping the IP address of the printer. Next, I tried adding it as a network printer. No luck. I tried sharing it from the Mac partition and connecting to that. No luck.

Frustrated and angry, I finally went to the great source of all knowledge in the universe and searched for “ip printer windows xp”. The top hit was from Wellesly college’s IT department, giving a detailed walkthrough of setting up an IP printer on their network (with screenshots and everything). The key to setting up an IP printer over the network in Windows XP, it turns out, is to set it up as a LOCAL PRINTER. (See, you choose, not the LPT1 port, but the TCP/IP port, and then, eight clicks later, you have a printer set up.)

So, what are the three lessons we learned today?

  1. If you are going to claim that your awesome security features are what is causing your poor customer service, please don’t forget to do obvious things like keep your certificates from blasting all your users with giant security warnings. Makes you look either disingenuous, incompetent, or severely understaffed.
  2. You never get a second chance at a first impression. Make sure your results are right the first time.
  3. Name things for humans, not for computers. My computer might very well think of that IP printer as a Local printer, but that is the absolute last place a human being would go to set up a printer over a network, when the other option they have is “Network Printer”.

Rich Web Experience September 6-8th

  • Posted By Stuart Halloway on July 17, 2007

I will be speaking at the Rich Web Experience, September 6-8 in San Jose. I will be giving five talks on JavaScript, Ajax, Prototype, Scriptaculous, and Rails.

Posted Slides: How (Not To?) Test Validations

  • Posted By Stuart Halloway on July 17, 2007

I just finished giving my erubycon talk "Keeping Tests DRY." The slides are posted here. Pay special attention to slide 20. The code is probably not a good idea, but it accomplished my secondary goal of provoking a reaction from Jim Weirich.

This Week in Refactoring

  • Posted By Stuart Halloway on July 16, 2007

Tomorrow at erubycon I will be presenting the first installment of "This Week in Refactoring." For each installment, I will take an existing open source project, analyze part of the code base, and contribute or commit a set of suggested refactorings.

This week's refactoring project is the Typo blog engine. I have submitted the refactorings under Trac 1131, along with a slideshow showing what I did.

How (to?) Test Validations, Part 4

  • Posted By Stuart Halloway on July 16, 2007

This is Part 4 of the preview for the "Keeping Tests Dry" I will be giving at erubycon tomorrow.

One issue with the previous three code examples is that they are testing ActiveRecord. This is unnecessary: ActiveRecord has its own tests, and I already believe that validations work as advertised.

We can use mocks to simply verify that validations are called with the correct arguments. The only tricky thing is that we are mocking class level behavior that happens when the file is read. To avoid dependencies on other tests, we will read the file again in its own module:

# Gross Trick: reload a model class in a different namespace so we can 
# set expectations without interfering with load of the "real" class
class ValidationInteractionTest < Test::Unit::TestCase
  class ValidationInteractionTest::Contact < ActiveRecord::Base; end

  def test_validates_presence_of_gets_called
    model_source = File.join(RAILS_ROOT, "app/models/contact.rb")
    Contact.expects(:validates_presence_of).with(:name)
    Contact.expects(:validates_presence_of).with(:email)
    self.class.class_eval(File.read(model_source))
  end
end

What do you think? I would be interested to see the Groovy equivalent.

How Not to Test Validations, Part 3

  • Posted By Stuart Halloway on July 15, 2007

This is Part 3 of the preview for "Keeping Tests Dry" at next week's erubycon.

Several people have commented with improvements to the code shown in Part 1 and Part 2. I will respond to some of these in the final installment, but first this: What if you could declare validation tests, just like you declare the validations themselves?

class ContactTest < Test::Unit::TestCase
  test_validates_presence_of :name, :email
end

This is nice and DRY: all future arguments about exactly what to assert, or why, can be settled in one place--the body of the validates_presence_of method. Here is a possible implementation:

  # TODO: eliminate the dependency @tested
  def self.test_validates_presence_of(*field_symbols)
    field_symbols.each do |field_symbol|
      field = field_symbol.to_s
      define_method("test_#{field}_required") do
        @tested.send("#{field}=", nil)
        assert !@tested.valid?, "Validation failed due to missing required field #{field}"
        field_errors = @tested.errors.on(field_symbol)
        assert_not_nil field_errors, "Validation did not run on field #{field}"
        error_message = ActiveRecord::Errors.default_error_messages[:blank]
        assert field_errors.include?(error_message), "Incorrect validation message for field #{field}"
      end
    end
  end

How would you improve this method? (More to follow...)

How Not to Test Validations, Part 2

  • Posted By Stuart Halloway on July 14, 2007

This is Part 2 of the preview for "Keeping Tests Dry" at next week's erubycon.

Several people offered improvements to the bad code from Part 1. Here is my own second cut, much better than the first:

  # Still tests ActiveRecord as well as your code, but at least
  # no exception handling
  # tests only one attribute
  # not (as) fragile
  def test_name_validation
    c = Contact.new
    assert(!c.valid?)
    assert_equal("can't be blank", c.errors['name'])
  end

This could still be a lot better. How would you DRY this up for use across a large codebase? (More to follow...)

Sorry for breaking the feed

  • Posted By Stuart Halloway on July 13, 2007

Earlier this week I moved the blog into its own section within Mephisto, so the home page could be a page instead of a blog. Unfortunately I did not update Feedburner with the new settings, so some subscribers have been seeing the home page instead of the blog entries.

I believe this is fixed now--let me know if you have any problems.

How Not to Test Validations, Part 1

  • Posted By Stuart Halloway on July 13, 2007

One of the things I will look at in "Keeping Tests Dry" at next week's erubycon is various ways people test ActiveRecord validations.

Here's one bad idea:

def test_contact_requires_name
  c = Contact.new
  err = assert_raise(ActiveRecord::RecordInvalid) {
    c.save!
  }
  assert_equal "Validation failed: Name can't be blank", 
                err.message
end

What's wrong with this example? (More to follow...)

Who is adopting Rails?

  • Posted By Stuart Halloway on July 13, 2007

Michel Barbosa has a written a bachelor thesis on Rails adoption Results that jumped out at me:

  • Q22: 95.9% would still adopt Rails, knowing what they know now.
  • Q26: On average, companies have completed seven projects since adoption, with only (Q30) an average of two full-time Rails employees.

I didn't see the summary for Q7 ("When did the company start using Rails?"). That's too bad. If the average company has completed seven projects already, then they either adopted Rails 2+ years ago, or they are completing a new project per quarter.

IDE hints vs. Continuous Integration

  • Posted By Stuart Halloway on July 12, 2007

Passing unit tests are good. 100% code coverage is good. Continuous integration builds are good. But what happens when your IDE has a cool feature that lives outside of your automated tool suite?

Earlier this week, I was raising test coverage on some legacy Java Spring code. I usually commit changes from the command line, but I decided to use IDEA's svn integration instead. By default, this integration does some code review, reporting various hints. This is pretty cool, and it helped me find problem areas in the code. But the code review also poses a problem:

  • How do I invoke these hints programmatically, so that I can include them in continuous integration?
  • How do I overrule these hints on a case-by-case basis? This is incredibly important. If I could disable a hint in a particular location, I would open a whole universe of heuristic hints that are useful, but report too many false positives to simply run across the entire codebase.
  • How do I track history? Overriding a hint should have a textual representation that goes into source control.

All IDE code-analysis functions should be exposed as services first, then skinned into the IDE interface. Wouldn't it be cool if the interfaces to these services were (even partially) standardized? Then we could share features across different IDEs. A good starting place for such standardization would be TextMate's elegant command model.

For Ruby, Tor Norbye is adding Ruby hints to NetBeans. Hopefully there will be some way to selectively enable such hints, scoped to specific regions of code.

Airplane Reading

  • Posted By Stuart Halloway on July 11, 2007

I use air travel as a chance to catch up on technologies that look interesting, but aren't at the center of my day job. Ideal airplane reading is

  • engaging and well-written
  • organized in digestible chunks
  • very small in my carry-on

I am finding that the Pragmatic Fridays fit the bill nicely. Today I read Google Maps API, V2 by Scott Davis. GMAV2 provides just enough information to help me decide to explore more deeply, in a light format that I could enjoy even after a long day. Now if I could only persuade the Prags to offer a universal subscription...

Config file design

  • Posted By Stuart Halloway on July 09, 2007
XML lends itself to big, ugly configuration files. Nevertheless, there is room for better and worse in configuration file design. Which of these do you like better, and why?
<beans>
  <bean id="java-bean" class="di.FixedSource"/>
  <lang:jruby id="ruby-bean"
    script-source="file:ruby/message_source.rb"
    script-interfaces="di.MessageSource" />
</beans>
Or option 2:
<beans>
  <bean id="java-bean" class="di.FixedSource"/>
  <bean id="ruby-bean"
    lang:source="file:ruby/message_source.rb"
    lang:interfaces="di.MessageSource" />
</beans>