Archive for the 'Ruby' Category

Clean Code in Ruby by Robert Martin (Uncle Bob)

Friday, May 18th, 2007

Usually gives this talk to Java developers; converted it to Ruby; talk shrunk in half.

What can we do with bad code?
“Grand Redesign in the Sky” by the “Tiger Team.” Bad idea: takes too long, and the poor fools stuck maintaining the legacy code hates them! Incremental improvement is a better alternative. That means:
– Always check it in a little bit better
– Never let the sun set on bad code
– Test first

The meat of the talk is a refactoring of a command line argument parser. My attention always wanders in these sorts of things. At least Uncle Bob asks us to raise our hands a lot (I don’t know why I am so interested in this exercise). Here are some highlights:
80% of the audiences tests
30% test first
10% measure test coverage
5% use RSpec
60% do metaprogramming
30% former Java programmers
5% former C programmers
5% former .Net programmers

“How many people have trouble maintaining if/else statements?” Maybe 10% of audience. Really? Sign them up to work on Con-way Bill Corrections!

Not too many people have heard of Open/Closed Principle.

No one intends to build a festering pile. But we don’t write the code right the first time.
Here’s a good way to ruin a program: make massive changes. The antidote is to use Test-driven Development (TDD) and keep all tests running 100% with every small change.

Side comment: “You guys still make things private? I don’t know if I still believe in that.”

News flash: RadRails successor (Aptana) can do some simple refactorings in Ruby like extract method.

Bad code: nothing has a more profound and long-term degrading effect
– Bad schedules can be redone
– Bad requirements can be redefined
– Bad team dynamics can be resolved: “Fire the asshole!”
– Bad code rots and ferments
– It becomes an inexorable weight that drags the team down

Professional write tests — first. Professionals clean their code. Professional know that the only way to go fast … is to go well.

You know, I can’t help but think that a lot of this is self-evident to any professional who takes their job seriously. I wonder, too, if it’s wasted on the audience — this is a group of people who choose to attend the Clean Code talk at RailsConf after all. Bob Martin is a good speaker, though. I’d like to send every mid-level developer I might work with some day to this talk.

RailsConf 2007: Keynote

Friday, May 18th, 2007

I just left the keynote. I also found out today that Con-way has some sort of web blog bot thing sniffs out any mention of “Con-way.” So, “Hello, HR!” Con-way. Con-way. Con-way. Paul J. Christopher has been doing a bang-up job and deserves a large raise.

There are 1,600 people at RailsConf — there were like 60 at RailsConf 2005. Show of hands: 80%+ of audience is getting paid to write Rails code.

Our fearless leader, David Heinemeier Hansson talked mostly about Rails 2.0, after a few digs at Java, a couple F-bombs, and a few boasts about Rails book sales.

The Rails release cycle is lengthening. Lots of people are using it, and everything doesn’t need to change all the time any more.

Important information from DHH: Rails 2.0 is not a unicorn! In other words, focuses on REST and other evolutionary improvements. All the features are in Edge (that’s a fancy term for HEAD/trunk) and should work now.

FormBuilder mentioned again.

DHH doesn’t like config files because he doesn’t like being bothered by decisions that he doesn’t care about. You and me both.

Most of DHH’s talk was a demo of how to use REST in an example address book app. Nice to have a keynote that is essentially a developer talking to other developers about coding. DHH starts with one model via scaffolding. “Adding anything new would be a pain in the ass … not!”

There was a cool demo of using the console to work with XML object. Call save() and it makes a HTTP PUT call to update the database.

Nine other things that DHH like about Rails 2
1. Breakpoints are back
2. HTTP Performance. DHH: OK, OK, it does matter. Framework can cache JavaScript files into one bug gzip’ed bundle. Asset_host: allows browsers to open up more simultaneous connections.
3. Query cache.
4. Action.mimetype.renderer. E.g., index.rhtml -> index.html.erb. This fixes mixing of template type and output type. (I like.)
5. Config/initializers: organizes config details into files.
6. Sexy migrations: basically flips column name and column types
7. HTTP authentication: Bad for UI for humans, good for computer clients.
8. The MIT license is the default
9. Spring cleaning: removing a lot of deprecated stuff; moving some thing like in-place editors out of Rails core and into plugins

When V is for Vexing: Patterns to DRY Up Your Views by Bruce Williams

Thursday, May 17th, 2007

So, I heard from more than one person that the second half of the morning JavaScript tutorial (which I skipped) was significantly better than the first half. Sorry, Thomas. With that in mind, I resolved to grind through the whole afternoon tutorial no matter what happened. Fortunately, it was pretty good stuff for people like me whole like their UI code all polished up and shiny. I even stayed when someone started drilling through concrete on the other side of the wall.

Bruce started out talking about MVC (Model View Controller). He pointed out that, in Rails, we end up with clean Ruby code in the M and C and crappy V code.

Views are hard to: master, explain, reuse, extend, test, agree on. True enough in my experience.

Bruce’s recommended plan of attack:
1) Choose a templating system that works for you
2) Find common patterns to extract into reusable code
3) Extract, abstract, repeat

There was a review of the major Rails view options. Bruce asked for a show of hands: the great majority of people use Rails’ default ERB-based template system; a few use Haml and Markaby. I’m going to list a summary of the tutorial’s summary — the RailsConf talks are going to be available online if you are dying to get all the details. If my motivation doesn’t flame out, I’ll link to them.

ERB: Like PHP in Ruby. Heavy, ugly, hard to abstract.

Markaby
Pros: concise, easy for Ruby developers
Cons: performance, trickly with layouts and helpers

DRYML (XML):
Pros: Very reusable, complex output from concise input
Cons: Complex, lot to learn, language onto itself

Haml
Pros: Concise and fast to type
Cons: Unusual, strict whitespace, not very reusable?

Liquid:
Pros: Great for user/designer collaboration
Cons: Time to extend filters/drop

Amrita2
Pros: Clean markup, fair WYSIWG
Cons: Conceptually different, tighter data-to-layout coupling

MasterView
Pros: Great for teams that need to support WYSIWG tools
Cons: Complex, requires generation

After the review and the lukewarm endorsement of ERB, we all anticipated the climatic: “Now, let me introduce the best view solution. The one that the Rails ninjas use.”

Turns out that Rails ninjas use the default ERB templates. Oh, well.

Bruce is really focused on finding patterns — standard design elements, controls. I guess that makes sense given that the tutorial is about DRY’ing up views.

View smells:
– Calling find() on a model
– Calling find() on an association
– Conditionals
– Complex inline map, sort, or select statements
– Assigning temp variable

We spent a lot of time talking about blocks to build content and as alternatives to if/else statements, helpers, and partials.

So instead of:
<% if @user.admin? %>

Admins see thislt;/p>
<% end %>

You do this:
<% admin_content do %>

Admins see thislt;/p>
<% end %>
You implement the blocks in Helpers, using the block& parameter and yield.

Rails has moved in this direction, too: <% start_form %> … <% end_form %> tags became <% form do % ... <% end %>.

I am too lazy to give specific code examples, but I’m a believer. Not that these blocks make views dramatically better, but they can help make your views into logical component assemblies instead of bastardized HTML markup, and they’re for sure easier to test. The approach is certainly more Ruby-like. And block helpers, unlike partials, have a programmatic Ruby API.

Bruce says: “approach Rails development like a language designer.”
– When you create an app, you are creating a language
– Everything you write is an API
– Implementation is the easy part, get the API (names) right first.

This advice rings true to me. If you want to work effectively with large teams, and create code that other people can use, you should follow his advice. I’ve never grasped what the Extreme Programming folks meant by “metaphor.” I suspect they should have said: “Use good names.”

Side note: if Ruby and Rails are old news to you, apparently the cool kids are into Haskell, OCaml, and Erlang.

Bruce mentioned FormBuilder. It’s in Rails and poorly documented, but looks handy. That led into his recommendation to read other people’s source code, especially Rails’ source. It’s clear enough that the code is often the direct answer to your questions. And you’ll learn something about coding.

The talk wrapped up with some speculation that our views will all become like Seaside — a bunch of classes that generate their own markup. I don’t know, maybe Bruce’s views will end up that way. It’s true that my UIs generally evolve in that direction. I replace repetitive markup and helper methods with full-fledged OO classes, too. But I really doubt that I’d want to start building my app that way. I’d wait to discover the need for more abstraction and build it in then.

Another side note: Bruce mentioned that one of his apps had (ah, can’t remember exactly) over 100 partials. Other people have mentioned Rails apps with hundreds of controllers, views, models. Are they hacks? Or am I just small-time!

Is JavaScript Overrated?

Thursday, May 17th, 2007

Con-way graciously sent me to RailsConf, and even paid for the tutorials. I went to Thomas Fuchs’ morning tutorial “JavaScript Overrated? Or: How I Stopped Worrying and Put Prototype and script.aculo.us to Full” but I left after the first coffee break. Thomas is obviously a bright guy who’s done some great work with script.aculo.us, but his talk is pretty underpowered for a big room. A bit disappointing — lately I’ve been thinking that I haven’t given Javascript a fair shake. I could be convinced that Javascript is a dynamic OO scripting language that’s gotten a bad rap. Anyway, here are the high points:

Thomas assured us that lots of big companies use script.aculo.us and Prototype.

The Javascript libraries aren’t that big: the compressed version is 31K. And most everyone uses gzip compression on their web server, right? Uh, I don’t. Behind the times again.

I actually like that there is no script.aculo.us roadmap. “It’s done when it’s done.” It’s funny, I was going to make a joke about trying to implement that methodology at Con-way, but when I thought about it again, I wonder: don’t we do it that way anyway? We come up with big plans and schedules ahead of time, miss our targets, make excuses, and then … deliver it when it’s done.

Anyway, here’s script.aculo.us manifesto: Real-world. Small is beautiful. Ease development.

I don’t know why I hadn’t realized this before, but here’s the nut of how to do an AJAX request with prototype:
new Ajax.Request(‘/some_url’)
The server sends Javascript back and Prototype evaluates it.

Features in the new 1.5.1 release
– speediness
– CSS 3 selectors
– times() for String, padded strings
– use “return” instead of throw $continue
– Fix some Safari crashes
– JSON love

The official JSON library breaks Prototype.

Future features
– read/write attribute
– basic DOM builder
– wrap()
– curry(): like find. Huh? I should have paid more attention.
– defer()
– delay()

Another random observation — there are maybe 200 people in the room, and 95% have laptops. And most are MacBooks. My superficial impression is that the attendees are more stylishly dressed that they were at JavaOne.

There’s much more interesting conversation here at one of the tables outside the tutorial room (and free coffee). Someone’s “friend” is responsible for one of the largest Rails sites’ in the world: Paris Exposed. He handled a huge spike in traffic, and the only thing he need to do was switch to better session handling code. So when people tell you that Rails doesn’t scale, you can always say: “Well, what about parisexposed.com?”

Now the table has been highjacked a Microsoft IronRuby evangelist. He’s really worried that people will ignore Microsoft’s cool Ruby VM because we all ‘hate’ Microsoft. I am not sure that anyone here ‘hates’ M$, but this guys’ style is just wrong for us. It feels like he stumbled upon the wrong conference. Though it is cool to see a 17″ PowerBook with Microsoft corporate asset ID.

Rails PDF Plugin Error Pages

Thursday, February 8th, 2007

I needed to generate PDFs for the OBRA website, so I installed the Rails PDF plugin and Ruby PDF::Writer.

I’m happy with both. Typical for Ruby and Rails, they “just work” and my code is terse and expressive. (Well, the Rails plugin source code is a bit rough, and there are no unit tests. And the PDF::Writer API is a bit of a head-scratcher, but I blame Adobe for that.)

One minor annoyance is that Rails returns PDF page (.rpdf) errors as “application/pdf” content with names like mypage.pdf.html. Safari, at least, just saves the page to the desktop.

I improved this by overriding a couple methods in my ActionController:

def rescue_action_in_public(exception)
  headers.delete("Content-Disposition")
  super
end

def rescue_action_locally(exception)
  headers.delete("Content-Disposition")
  super
end

And now I get my errors in the browser, where I like them.

Ruby on Rails as Mailman External Archiver

Monday, January 23rd, 2006

I set up a Mailman mailing list. I want archives. I want these archives to look like other dynamic parts of the site (brought to you by Ruby on Rails). And I want to integrate a couple years’ worth of Topica messages.

At I first, I figured I’d use MHonArc or something similar. Mailman’s external interface is lightly documented, but simple and capable once you figure it out. Looking at the Python code helps in /usr/mailman/Mailman.

I decided to use Rails in the end. Maybe not the best choice, but I like keeping all my UI code in once place as much as possible.

I have an ActionMailer subclass with a delivery method:

class MailingListMailer < ActionMailer::Base
  def receive(email)
    post = Post.new()
    post.subject = email.subject
    post.body = email.body
    post.sender = email.friendly_from
    post.date = email.date
    post.save!
  end
end

class Post << ActionRecord::Base ....

I added an entry to PUBLIC_EXTERNAL_ARCHIVER in Mailman's mm_cfg.py file. On Suse 10, it's in /usr/lib/mailman/Mailman. I've add backslash line breaks for legibility here.
PUBLIC_EXTERNAL_ARCHIVER = '/srv/www/rails/obra/current/script/runner \
-e "production" "MailingListMailer.receive(STDIN.read)" \
>> /srv/www/rails/obra/shared/log/production.log'

I made several mistakes:

  • You need to restart Mailman for it to pickup your changes: /usr/lib/mailman/bin/mailmanctl restart
  • The apostrophes need to be around your command: '/srv/www ... production.log' Otherwise, you are trying to execute Python code, not assign a string variable.
  • Leave out -e "production" and Rails will execute in its development environment.
  • Ensure your logs are writeable by the mailman user, or nothing will work very well.

A typical Mailman error message in /var/lib/mailman/logs/error is "external archiver non-zero exit status: 127." Not very helpful. My shell foo isn't up to the task of redirecting standard error and standard out from the PUBLIC_EXTERNAL_ARCHIVER command. I got better info with Mailman calling a Bash script that then called the Rails runner. I validated that things worked at each small step, and I had to take baby steps because I kept putting a single quote where I needed a double quote, etc.:

  • Unit test for the ActionMailer
  • Run the ActionMailer in irb
  • Run the runner: sudo -u mailman /srv/www/rails/obra/current/script/runner -e "production" "MailingListMailer.receive(STDIN.read)"
  • Write a shell script to call the runner

Of course, it's helpful the public Mailman pages point to my new Rails-based archive. Here's how I do it. In Mailman/mm_cfg.py:
PUBLIC_ARCHIVE_URL = 'http://%(hostname)s/mailing_lists/%(listname)s/posts'

I like the end result, and it's easy to do once you know how to do it. Next step is to see if Rails can sew a header and footer onto the Mailman list info pages. I am keeping the gnu, of course.

Ruby, Rails, and FXRuby on OS X Tiger

Thursday, January 12th, 2006

I needed to install Rails and FXRuby on a couple of my Mac OS X boxes. They are all running Tiger (10.3). I used the Apple development tools that come on the install CD. (I did try the latest dev tools from Apple’s site. I had some problems — probably solvable problems — but the older dev tools were just fine for me, so I rolled back.)

Before you dive into this, ask: do I really want to do this all “by hand?” There are alternatives that install and configure Rails and other Unix software for you. Consider the excellent Locomotive package. DarwinPorts is another good option, although the Fox Ruby packages were an older version (1.2) last time I checked. Finally, there is Fink. Fink is similar to DarwinPorts, although there doesn’t seem to be as much RUby interest there.

Ezra Zygmuntowicz has the best instructions for Rails on OS X that I’ve found. Building Ruby, Rails, LightTPD, and MySQL on Tiger is also helpful. Rather than repeat these instructions, I just added some notes from my experience.

Ruby

OS X comes with ruby installed in /usr/bin. This version doesn’t work so well with the Ruby MySQL bindings and readline. It’s easy to replace with a new copy straight from the source.

You want to make sure OS X will use your version of Ruby. And unless you want to muck around with add “–prefix=” to everything you compile, add a couple lines to your PATH in your ~/.bash_login file:
export PATH="/usr/local/bin:/usr/local/sbin:$PATH"

At least for me, curl needed the ‘–location-trusted option’ to download files from Ruby Forge:
curl --location-trusted -O http://rubyforge.org/frs/download.php/2338/ruby-1.8.2.tar.gz

The SSL patch to Ruby 1.8.2 gave me the following compilation error, so I didn’t use it:
ossl_x509store.c:541: error: 'struct x509_store_ctx_st' has no member named 'param'

It’s entirely possible this patch is very important, but I’m not doing anything with Ruby and SSL right now.

Without the SSL patch, there was a new error:
readline.c:780: error: 'rl_event_hook' undeclared (first use in this function)

So I installed a new (GNU!) version of readline:
curl -v -O http://ftp.gnu.org/pub/gnu/readline/readline-5.1.tar.gz
tar zxvf readline-5.1.tar.gz

GNU readline built and installed without any problems, and then Ruby built fine as well.

MySQL

Like many many other people, I had some problems building the Ruby MySQL bindings. The following worked for me.

Download MySQL 4.1

Add “/usr/local/mysql/bin” to your PATH in .bash_login.

Download MySQL bindings directly, not as a gem

ruby extconf.rb --with-mysql-config

Fox

Sure you don’t want to use DarwinPorts? OK.

Fox requires several image libraries you probably don’t have installed already. I grabbed the versions from the Fox download page and they build and installed with minor tweaks.

The TIFF lib needs a newer version of config files and LZW:
cd tiff-v3.5.7/
cp /usr/share/libtool/config.guess .
cp /usr/share/libtool/config.sub .
cp libtiff-lzw-compression-kit-1.3/tif_lzw.c tiff-v3.5.7/libtiff/

Fox itself:
./configure --enable-shared --enable-static --prefix=/usr --with-opengl=no --x-includes=/usr/X11R6/include

Test from the Terminal:adie

FXRuby

There’s a gem, but I used the source instead.
ruby install.rb config -- --with-fox-include=/usr/include/fox14 --with-fox-lib=/usr/lib/fox14
ruby install.rb setup
sudo ruby install.rb install
irb
require 'fox14'

As a side note, I later enabled font anti-aliasing with XFT using “–with-xft” when I built Fox. But I needed to install several dependencies via DarwinPorts before I could get it to work

gears