Automatic Annotation with Rails 3 With the Annotate Gem

Posted by Rick DeNatale Sat, 29 Jan 2011 16:17:00 GMT

One of the oldest Rails plugins is annotate_models which was originally written by Prag Dave Thomas. For those few of you who might not have run across it, it adds a comment block at the top of a rails active record model class with the relevant section from db/schema.rb.

I'd fallen out of using it, just falling into the habit of opening up schema.rb and searching for the model name. But I got the yen the other day to start using it again.

Google alerted me to what seems to be a popular, modernized fork, maintained by Cuong Tran, who gemified it and expanded the function. It now annotates things like model specs, fixtures (if you are still using them) and various fixture replacement files like machinist blueprints. It also provides a separate command/rake task for annotating your config/routes.rb with the output from rake routes.

Installing the gem provides an annotate gem binary with which you can manually produce these annotations.

The readme claims that installing it as a plugin also modifies the the db:migrate rake tasks to automatically run schema annotation to keep in sync with what your migrations are doing to the schema. But plugins are a bit passé in Rails 3. I wanted to install the gem with bundler, after which it took me a bit of experimentation to get automatic annotation.

After playing around with it for awhile I came up with this. First I added the annotate gem to the development group in my Gemfile. Then I added the following file which I called 'annotations.rake' to lib/tasks in my Rails 3 project:

 namespace :db do
  task :migrate do
    unless Rails.env.production?
      require "annotate/annotate_models"
      AnnotateModels.do_annotations(:position_in_class => 'before', :position_in_fixture => 'before')
    end
  end

  namespace :migrate do
    [:up, :down, :reset, :redo].each do |t|
      task t do
        unless Rails.env.production?
          require "annotate/annotate_models"
          AnnotateModels.do_annotations(:position_in_class => 'before', :position_in_fixture => 'before')
        end
      end
    end
  end
end

It's pragmatic, it works, although I guess I'm a bit exposed to changes in the annotate gem. I think the right way to do this would be to fork annotate on github, and change it to register the rake task using a railtie, and then issue a pull request, but I guess I'm being lazy.

Update

Some have reported problems with this. I just checked and my current lib/tasks/annotations.rake looks like the above.

Note that the strings in the require statements should both be "annotate_models", for some reason the code formatter in typo is changing the underscores to spaces, no time right now to debug that.

This version only requires the annotate_models gem code for the migration tasks, and also only in a non-production environment.


Time Flies, The Anniversary Gem is Already 2.0.0

Posted by Rick DeNatale Sat, 22 Jan 2011 16:35:00 GMT

I just pushed two versions of the anniversary gem

Version 1.0.2 fixed a bug which I found when trying to answer one of the comments on my original blog post. It was an edge case which returned one two many months and a negative day count.

But I also realized in responding to the comment which exposed the bug, that I had a minor misconception about where extra monthiversaries should fall in March. I've now fixed that so that they always fall on March 1.

Since this is a breaking change, I've bumped the major version number to 2

Date calculations are just inherently complex!


They Say It's Your Birthday?!

Posted by Rick DeNatale Sun, 16 Jan 2011 20:51:00 GMT

As I mentioned earlier today, I've started a new job which involves, among other things working on medical support applications.

One recent task was generating pdf reports for lab result documents. The header at the top of the report page needs to report, among other things, the age of the patient at the time of the lab. The age should be the patients age in years, or in the case of an infant less than one year old, in months. Other documents might require reporting the age in years and months since the last birthday, and perhaps the number of days remaining.

Since my birthday is the eighth of December, I'm Plenty-Nine years, one month, and eight days old today, which happens to be the sixteenth of January.

Computing that correctly is trickier than it appears at first.

First I thought this was just a matter of taking the integer part of dividing the difference between the dates by 365.25, and so on. Think again.

Then I thought there must be some open source solution to this, but every google search I tried for something like "age in years months and days" came back with the same results as if I'd searched for age in years months OR days, which is a horse of a different color.

Leapin' Lizards!

Your age in years, is actually the number of birthdays you have had, or to use a more generic term for the yearly recurrence of an event anniversaries. This seems straightforward until you consider the case of someone born on February 29, on a leap year.

People joke that someone born on say February 29, 1980, is 'only' 7 years old, since February 29 has only occurred seven times after 1980 up to this day.

Obviously leap babies age like the rest of us, and enjoy (or is it suffer?) a birthday each year. But when?

I've heard it said that leap babies can choose to celebrate their birthdays either February 28, or March 1 in a non-leap year. While that's true in the sense that anyone can choose to celebrate anyone's birthday on any day they wish; most of us choose to celebrate Christmas on December 25, although there's more evidence that that's because the early Christians wanted to celebrate Jesus' birth under the cover of pagan celebrations around the Winter Solstice, such as the Roman Saturnalia, than because Jesus was actually born on December 25.

The actual 'legal' birthday for a leap baby is muddy. I think that it makes sense to use March 1 in non-leap years in cases where the actual birth-hour is unknown, and that's what I decided to do.

It's not just Anniversaries.

Now let's say you were born on say January 31, 1970. how old were you on say, July 4, 2010.

Since you had had 40 birthdays, or anniversaries of your birth, the year part is easy, you were Forty years old.

But how many months and days are left over. Well it should be the number of monthly recurrences of your birthday's month day since your last birthday, and the number of days since the last montly recurrence. Let's just call these monthly recurrences 'monthiversaries'.

Now if your birthday falls in the range of the 29th through the 31st, there will be months which don't have that day. It's the same problem as a leap baby. So in the case of your hypothetical January 31, 1970 birthday, the last 'monthiversary' before July 4, 2010 can't June 31 so it must, by the logic above have been July 1, so you would have been 40 years, 6 months, and 3 days old on Independence Day 2010.

Announcing the Anniversary Gem

Last weekend I decided to work on this problem, 'off-the-books', the result is a new gem 'anniversary' which adds a couple of methods to Ruby's Date class. the primary one is Date.years_months_days_since which returns a 3 element array comprising the number of years, months and days which have occurred between the argument initial_date and the receiver.

The source is available on my github account. You can just "gem install anniversary" as well

Enjoy! And as soon as it IS your birthday, Happy Birthday!

Clarification

The comments which this article received made it evident that I didn't explain what I mean by monthiversaries enough

Let's say someone was born on the thirty-first of any month. Then I claim that the 'monthiversaries' in any given year would fall on:

  1. January 31
  2. March 2 in a leap year, or March 3 in a non-leap year
  3. March 31
  4. May 1
  5. May 31
  6. July 1
  7. July 31
  8. August 31
  9. October 1
  10. October 31
  11. December 1
  12. December 31

So rather than having an 'early' monthiversary in a month too short to have the anniversary day, I move it 1 or two days into the next month, depending on whether 1 or 2 days are needed, and the next month (which will always have 31 days) has two monthiversaries. And I consider an anniversary date to be contrained to fall on a monthiversary, which only moves it for leap babies, since any other month but February will always have enough days.

Now having written that table out, I'm not sure I shouldn't treat the extra monthiversary in March as always occurring on the 1st.

Consider someone born on February 29, 1984, wanting to order an alcoholic drink in a US bar, on February 28, 2009. The bartender asks for his driver's license, looks at the sign with the changeable date which says "If you were born after February 28, in 1980, we can't sell intoxicating beverages to you", and says "Sorry kid."

If he walks in the next day, the sign now says "If you were born after March 1, in 1980, ..", and the bartender says, "what will it be?"

So I think I should make the change to always put the extra monthiverary when needed on the first of the next month.


Abstract vs Concrete Approaches to Learning

Posted by Rick DeNatale Sun, 16 Jan 2011 18:52:00 GMT

Once again discussion on the internets have prompted me to write an article.

Last weekend, someone posed a question to the ruby language forum about whether Rails was suitable for a Ruby beginner.

Shortly thereafter Xavier Noria opined:

"I have the feeling that Rails is not for beginners. It abstracts web programming in a way that I think makes more sense for someone that already knows web programming. In that situation you get a Ferrari to run. It is just an opinion, it could be the case that you are presented first with the framework and work fine... I've seen that with Java people. But I doubt you'll understand what you are doing."

"I think that learning web programming passes through HTTP and bare CGI, where you're naked, you value as gold even being able to have the query string parsed for you. There you understand what's all about, and can build from that. Then the abstractions make sense and act as shortcuts."

"Other than that, I would not say Rails is complicated. Rails is rather big, you need to invest time to master it."

While I understand Xavier's stance, and I've heard similar things from many people over the years about needing to master the "low-level" basics first. I'd like to offer a second opinion.

Levels of Abstraction

To be a good programmer, or as I prefer software craftsman, one needs to be able to work at a range of levels of abstraction. To get to this level requires a formidable amount of learning. It's not a question of whether one needs to learn low-level abstractions, it's a question of when.

Teaching Programming "Bottom up"

As someone who has been learning programming for more than 40 years, I've seen a variety of pedagogical approaches to teaching people how to program.

In my early days, there were lots of books about programming specific computers using one or a small set of languages for that computer. For example, the first computer I ever worked with was an IBM 1620, and one of the oldest books in my library is "Programmng the IBM 1620, 2nd ed." by Clarence B. Germain. That book starts with the following chapters:

  1. Computing Fundamentals - covering things like flow charts, magnetic core memory, punch cards and paper tapes. By the way the contents of the dedication page is in the form of picture of the holes in a paper tape, which when you decode them read "TO THE COMPUTER PROGRAMMERS OF TOMORROW"
  2. 1620 Instructions - which covers the basic machine level instruction set at the machine code level.
  3. Operation of the 1620 - which covers how to read the lights and flip the switches on the machine console, and work the IO devices like the console typewriter, and the card reader punch.
  4. Programming - Back to flowcharts, and then a simple program in machine code.
  5. Introduction to FORTRAN
  6. Additional FORTRAN statements
  7. The Symbolic Programming System - which is what the Assembler language and tools for the 1620 was called.

Note that the approach is to start with the bare metal, then raise the abstraction level up to FORTRAN and then lower it to symbolic assembly language.

Later, the trend in programming education was to start with a language roughly at the level of abstraction of FORTRAN, maybe Algol, or BASIC, or PL/I, or C. suitable for implementing operating sytems, something which had previously required assembly language.

At the assembly language level of abstraction we have to deal with things like register loading and storing, arithmetic operations, perhaps memory to memory operations if the machine support them, conditional and unconditional branch instructions, and the peculiarities of how the particular machine does I/O, or at least the peculiarities of the low-level I/O subroutine library.

At the next level up, machine registers tend to be abstracted away, we think in terms of assignments to variables with the compiler abstracting the location of those variables (including when and whether they are held in a register or 'memory'), and the compiler turns algebraic expressions into sequences of machine instructions. The compiler also abstracts the notion of conditional and unconditional branches into IF and looping statements, and before the structured programming revolution, GOTO statements.

C is actually at an abstraction level which ranges from FORTRAN down to being a 'portable assembly language' its raison d'être was to be a portable language

Programmers who start this way build up a mass of low level knowledge and, if they are to achieve the ability to work at higher level of abstraction. must learn to suppress always thinking of the details when the details aren't really important.

In my case I was fortunate in that, beside my formal Computer Science courses, I had an enormous thirst to learn lots of languages from the very beginning, so I forged out my own to learn or at least dabble in a wide variety of languages, including SNOBOL4, LISP 1.5, APL, and a number of other, smaller languages, while my programming synapses were still forming.

The Car Analogy

Xavier brought up an analogy with cars in the form of a Ferrari, so I'd like to talk a bit about how the driver's view of the interface to a car has evolved.

When I learned to drive, there were still cars which had manual chokes. A manual choke consisted of a valve in the carburetor controlled by a lever or pull knob in the cabin, which when activated made the air-gas mixture richer. You would pull it before starting the engine, depending on the temperature, and gradually adjust it as the engine warmed up.

Besides manual chokes, earlier cars had things like manual spark advance levers. The driver would need to manually adjust the small details of the timing between the pistons and spark plug firing to keep the engine running smooth over a range of speeds and loads.

Of course this is all of the past, unless you are an antique car collector. Manual spark advance got replaced by automatic regulation via a combination of mechanical governors and vacuum hoses. Automatic chokes used the same kind of bi-metal strips found in thermostats to adjust the choke. These days, cars don't have carburetors anymore, and a computer implements much finer control over spark timing, and fuel mixture.

So although those matters are important to the engine, they're no longer important, or at least visible, to the driver, and the low-level knowledge needed by a mechanic when things go wrong is much different than it was back in the day.

Is there another way?

Must programming be taught from the bottom up? Clearly not. There are many proficient programmers who learned following the lead set by MIT whose introductory programming course was based on Ableson and Sussman's seminal text book "Structure and Interpretation of Computer Programs", or as it is known SICP.

SICP doesn't start with assignments and GOTOs. Instead it approaches programming from a more mathematical approach, based on Alonzo Church's Lambda Calculus, and using the Scheme programming language, a variant of LISP, which had been developed by Jay Sussman and Guy Steele at the M.I.T. A.I. lab. During the development of Scheme Sussman and Steel wrote a series of A.I. Lab memos collectively known as the lambda papers, which explored the implementation of Scheme and various programming features such as assignment, and control flow branching using lambda calculus. In many ways SICP is a distillation of the Lambda Papers into an introductory programming text.

For old codgers, like me, who started out 'the old way' encountering SICP can be a mind-distorting and altering experience, much like entering Dr Who's TARDIS for the first time and realizing it's bigger on the inside than it is on the outside.

In it's first chapter (70 out of 500 pages in the first edition) SICP covers building abstractions with procedures, and in so doing manages to broadly introduce a got bit of what I learned in a whole semester course on Numerical Analysis. The second chapter (around 100 pages in the first edition) talks about data abstraction, but still manages to wrap data in procedural abstractions. A few pages into chapter two is where we find the interesting idea of defining a LISP cons cell as the closure of a function call, which reminds me of the message dispatching and data encapsulation in languages like Smalltalk and Ruby, something I built my recent RubyConf talk on.

And it's not until the third chapter that SICP introduces the notion of assignment, which apparently gave "Uncle" Bob Martin his TARDIS moment.

In chapter four, SICP starts to introduce the notion of language into programming, talking about how scheme programs are represented and how that representation is evaluated.

It's only at the end of the text, in chapter 5. that SICP mentions hardware, and introduces the concept of a machine with registers and memory. The chapter finishes the book by describing the implementation of a compiler for Scheme, and a garbage collector.

Bottom up or Top down?

So what's better? Should one first learn the low level basics and then turn the collection into abstractions or go from abstraction to detail? It's clear that there are competent programmers who have learned both ways. The best of those who learned 'the old way' managed to gain a large experience base by 'playing' with a wide range of programming languages and approaches to programming, whether that was part of their formal education or not.

If I were to start over again, I think I would prefer to start the SICP way, but that's just me

Bringing it back to the initial question, personally I think learning how to write web applications starting with Rails, and learning Ruby, HTTP, REST etc. along the way is perfectly reasonable given such an attack fits your own mode of learning.

If you go that route, reading the code you are using when you need to drill down is a valuable way to learn the details. There's a lot to be learned about Ruby and the 'plumbing' level of web application implementation by reading the Rails code. This is particularly true with Rails 3, which represents a major refactoring and distillation of the experience gained as Rails has matured in the five years of its existence.


New Year, New Gig

Posted by Rick DeNatale Sun, 16 Jan 2011 18:23:00 GMT
In mid-November of last year, I started a consulting gig with Scimed Solutions in Durham, NC. As of January 3, I am now a full-time employee there. Scimed Solutions develops applications primarily for medical research and clinical organizations, but also for educational institutions and businesses. My position there is both as a senior developer and 'agile mentor.' The team is talented and seems to appreciate my attempts to do things like 'test infect' them.