For this post to be of any use to you, see if you are sitting in the following boat:
- you’re an experienced software developer
- you are well versed and frequently use C, C++, C++, Java, C# and/or Objective-C
- you’ve can code in Awk, Perl, and/or Phython
- regular expressions don’t scare you
- you can write HTML, XML, XSLT, CSS, and feel that JavaScript is a toy language, though you’re impressed with what people are currently doing with it
- you easily mastered PHP, JSP, and/or ASP
- you run Linux/FreeBSD, you installed a webserver, and it might even be running FastCGI
- you know SQL, maybe even stored procedures, and you use MySQL and/or Postgress without incident
- you are no stranger to programming languages, and secretly have Assembler, Pascal, Fortran, BASIC, under your belt
- you have a solid working grasp on object oriented programming and design
- …you’ve heard all this great news about Ruby on Rails, and you want to give it a try.
You’ve seen the Ruby videos where they make an application in 15 minutes. You got the two must-have books: Programming Ruby and Agile Development with Rails. You even downloaded Ruby and got Rails installed.
Yet, despite all that, you hit the weirdest stumbling blocks: you can’t find methods being called in either your code on the framework, there’s a lot of scripts writing more terse config files than code, there are object oriented things going on you’ve never seen before, and even with the documentation things are going slow. Feel like you’re just not getting it, even when you put it down and come back to it later?
That’s basically how my initial experience with Ruby on Rails went.
But then I had a breakthrough, some things suddenly became apparent and that provided the clarity I needed to pick up and start coding. And if you’re sitting in the same boat, I’m about to share them with you so that you may also start becoming productive.
My first mistake was not having a good development environment. This is almost imperative in order to pick up Ruby on Rails. I tried it on Windows, I tried it on a Linux install, but what go me through was this configuration:
- Get a Macintosh running the latest version of OS X – there is nothing like it out there for developing code!
- Download a copy of Locomotive – this is everything you need for Ruby and Rail, all preconfigured
- Install MySQL on OS X, I suggest a pre-made installation package from the MySQL download area.
- Download CocoaMySQL to visually manipulate MySQL.
- Buy a copy of TextMate – you will not be disappointed.
What all of this gives you is an instant Ruby on Rails configuration that’s totally graphically and insulated from everything else on your machine. Want to upgrade? Drop a new copy of Locomotive on your machine. Want to deploy? Move your project directory to your production system. Learning Ruby by picking up Rails is possible, but can be frustrating.
As for book resources, you’re missing one: Ruby on Rails: Up and Running. This has got to be one of the best resources for non-Ruby developers. I’ll be honest… if you already know Rails, this isn’t for you. If you don’t know a good deal of programming, this also isn’t for you. What makes this book nice is that it is not the blind type-what-I-type “tutorials” where you just follow along. No, it explains what’s going on, as well as what’s happening under the hood. The problem, though, is that some of the stuff happening is dang clever, and there are things that may take a little bit of thinking to wrap your mind around.
Over on Ruby-Doc.org, there is a presentation called “10 Things Every Java Programmer Should Know About Ruby” by Jim Weirich from his presentation at OSCON 2005. While worth the read, he makes one very interesting point up front. He teaches C programming to old school Fortran programmers, and what he concludes, from looking at a lot of bad C code, is that Fortran-like code can be written in any language. The point being his students aren’t thinking in C when they are writing in C. And that was my problem with Ruby.
I had glanced over the Ruby book enough to recognize most of the language constructs such that I could read and understand what I was looking at, and I browsed the API to see it was extensive and useful. Some languages fall short of libraries, and Ruby doesn’t make that mistake. But what wasn’t apparent was that I was still thinking like a C++ / C# / Java programmer, and falling back to procedural languages didn’t help either. To use Ruby means learning some neat, new stuff.
For example, we’ve all used the Model-View-Controller (MVC) pattern, but did you know Rails uses a variant called Model2? Did you know that when you generate a Rails project, it includes a web server – and, while WEBrick is common, and lighttpd is known for speed, Rails is now defaulting to one called Mongrel. And, I suggest checking Mongrel’s FAQ; I learned more computer science in an evening, just following links than I would have in a semester or two of college. Turns out, beyond BNF, there’s ABNF, which can be used with Ragel (a Lex / Flex / Yacc / Bison like lexer/tokenizer) to generate note just code (including for the D programming language) but also state machine graphics using dot files from GraphVis. To which, someone made an ANBF file with the HTTP 1.1, thereby generating a very fast web server as a huge state machine. There are also links to fast data structures like Ternary Search Tree (with code). It seems that some kind of lambda calculus is used, making code blocks and closures acting like fast include templates for web pages. …don’t get scared off, like I said, it certainly appears that those that use Ruby think differently.
Ruby’s philosophy is convention over configuration and coding; meaning, if you do thing’s Ruby’s way, you won’t have to write a lot of complicated configuration files, nor will you have to write a lot of code. That poses the first major problem, you can’t just search for for something in the project, because it more likely than not, default behavior is causing it to happen. You need documentation, not grep, to survive.
The good news is that if Ruby is anything, it’s consistent. Its Rails script file generates all projects with the same layouts, and once you learn this, you’re set. Luckily, most of the real action happens in just a few directories. Your code happens in a directory called app, and your database happens in a directory called db; that knowledge alone will get you along 90% of the way.
Here are some other interesting Ruby tid-bits that tripped me up.
I’ll assume you know the subtle differences between scalars, constants, variables, pointers, and references. Ruby introduces something unexpected: identifiers that start with a colon, called symbols — what’s going is that you’re making symbol table entries directly. The idea is that you can save a lot of space if you’re reusing arbitrary keys but don’t care about the value. The newbie will get these confused with strings or variables, just as a novice doesn’t understand the difference between a pointer and a reference. Learn it, it’s everywhere.
Ruby also has method names that end with ?, !, and =. If you’re looking for something special to happen, don’t. These are simply visual sugar to let you know you’re asking, doing something dangerous, or assigning.
Rails is clever in that it uses scripts. You tell a script to generate a model, and it generates you a class (based on ActiveRecord::Base), and a database “migration” (based on ActiveRecord::Migration). The database table and the class are magically tied together by the framework and naming conventions. Migrations, it turns out, are incremental changes to the database, allowing you to move forward and backward in time to different versions of the scheme. You can even execute code to populate and initialize data. Quite clever. ActiveRecord manages the object-relational-mappings (ORM), providing quite a bit of API in the process.
Rails allows for three different databases: development where you do all your dangerous stuff, an optional test database which gets reloaded with virgin test data after each automated test to keep tests independent, and an optional production database for doing the real work – it never gets zapped.
Ruby has a lot of things that take object oriented programming to the next level. Those who have programmed in Objective-C or SmallTalk might recognize some of the constructs. Messages are really, and honestly, separated from methods. You send an object a message, and that in turn invokes a method. This is not like C++, Java, or C# where the notation object.method() simply performs a lookup. No. A message with parameters is sent to the object, and some twiddling might happen with that message before it decides which method to invoke. This means you might, and quite deliberately, send messages to objects for which they have no method to honor it! These can be forwarded, mutated, or dropped on the floor. Or, even more magical, you can capture messages and replay them, and not just to itself.
In fact, this is how Rails accomplishes much of its magic with ActiveRecord. When you send a message like .find_by_name, there is no method on the object. This triggers a missing method routine, it then does string parsing on the message finding out it started with “find_by_” and then it decides to look up in its ORM associated table to see if it has a column called “name”, and if so, it then passes that information to some routine that manages object persistence. As such, you’ll find you use a lot of human readable “methods” (which are really messages) that simply don’t exist, and that’s why grep can’t find them.
To make matters even worse, classes can be extended at any time. You can add new methods (messages!) dynamically. Real ones. Even to ones you don’t own. Including language primitives, which are also true objects. There’s no need for interfaces, just objects that respond to the same message. Weird, eh? Things get stranger when you can extend individual instances, not just the class as a whole.
Naturally your language snobs will get all up in arms about the total lack of type safety and so forth, but it turns out all of the automated tests tend to prevent you from getting into that kind of trouble in the first place. The end result is physically less code that happens to be very readable.
Ruby does use notations, like an at-sign to prefix class instance variables. And, just like C# has get/set accessors, Ruby has a shortcut to turn members into accessors. Hmm, what else, well null happens to be a perfectly legal thing that you can send messages to, and that gets rid of the famous ‘null pointer exception.’
Ruby doesn’t have statement terminator, it’s more like Python in that the end of a line is the end of the statement — unless of course you’re in the middle of one by defining a block of code. Ruby does something else neat, which is kind of like Java’s anonymous functions, it lets you create blocks of code with named parameters. You can store these blocks, pass them around, and invoke them later from other routines. It feels a lot like C++’s Standard Template Library (STL). More than function pointers, more than method pointers, just isolated blocks of reusable functionality. Virtually every message has optional parameters and the ability to take an optional block of code; these get passed to some method eventually.
One of the more confusing aspects visually, at least for me, is the fact that because these things are messages, parenthesis aren’t needed. Thus you’ll see many examples where an object is allocated with .new, and immediately following it is a do-end block that makes no logical sense, especially if it were executed immediately after the allocation. Turns out that’s not what’s going on. The new() method is overloaded, and one version of it takes a block of code. (Remember parameters and code blocks are different and both can be passed.) The new method can hand this block of code to the created object, which is free to shove it away in some member variable for some use later.
Lots of what-would-be-complicated-in-other-languages-stuff is accomplished by clever anonymous code blocks being passed around. These constructs just don’t exist in many languages, and that’s why they feel foreign, and that’s why Ruby can be confusing to pick up.
Ruby comes with an interpreter (irb) and a shell (script/console); both are used from the context of the project. In development mode, each Rails web request reloads the Ruby class files. And while slow (by production standards), this gives instant feedback to code changes. But, fret not, the production version does all the correct caching you’d expect.
Surprisingly, knowing just this little bit of information above is enough to give you the jump forward if you’re stuck. And it turns out learning, using, adopting, and accepting the Ruby way happens very quickly.