Wednesday, December 22, 2010

What to do when code breaks your migrations (Ruby/Git edition)

Sometimes, unless you're working for a retarded startup, you'll go away for a week or so and come back to find a massive change merged in. It's likely that you have a long list of pending database migrations.

It's also likely that the second migration got broken by a code commit that removed something it needs to work, so you can't run it. You need to figure out a quick way to roll back the code to a revision that works, run the migration, then revert back to the head commit. There's a few ways that git will help you here.

1. (easiest) Use git bisect. Find a commit that lets you get past the migration and run it (usually the one that contains the migration works...) then cancel the bisect. Done, total time 2 mins.
2. (still easy) Use git reset to reset your repo to the state it was at the failing commit. After the migration runs pull again and you're dandy.
3. If you don't have good version control you're a bit screwed. Anonymize a prod database dump and reload your dev database. Or find exactly what code breaks you migrations and temporarily monkeypatch it to get past the problem. Doing this multiple times sucks so after doing this once refuse to work without decent version control. If your boss won't budge then quit and go work for someone that's not a moron.

6 git lessons for git beginners from a git beginner

1) Bookmark this: Just do it.

2) Branching is awesome and mentally cheap right now, but can be confusing and cognitively expensive in 5 weeks time. Try to keep your commit history clean (eg. with rebase), your git log will love you for the decrease in merge commits.

3) For god's sake do most of your work in a branch. Getting merge issues when you pull just before beer o'clock will ruin your weekend. Keep your master branch looking as much like origin/master as possible.

4) Merge master into your branches often if you're planning to merge back later.

5) Merging is shit no matter how you do it. Use a tool whenever possible (try lots) but learn to do it manually too, that way you'll never have a problem when your tool of choice can't handle gnarly 3 way merge issues.

6) For the similar reasons to point 2, Learn to 'git pull --rebase'

Thursday, September 2, 2010

ActiveRecord for Oracle won't update records

I needed a script to fix up some date data in an Oracle database that didn't lend itself to using a pl/sql script. Naturally I turned to Ruby and ActiveRecord to sort out the issue. I drafted the script against a local MySQL DB and it worked flawlessly, but when run against the Oracle instance it wouldn't save any data changes.

Here's what happened, and how to fix it...

So turns out this has happened before to other - see this post on for details. The OP didn't get any replies so I'm assuming that he figured it out on his own, unluckily for me he didn't share the fix so I had to do some leg work.

For starters I'm running Ruby v1.8.7, activerecord v2.3.8 and activerecord-oracle_enhanced-adapter v1.3.0

My schema is legacy with a primary key that isn't "id". Easy to sort out, I coded up something like this:

class Receipt < ActiveRecord::Base
set_primary_key "rt_receipt_id"

So onto the issue. This worked fine with MySQL but not Oracle so my first stop is to check out the SQL that's being created:

SET "RT_RECEIVED" = TO_DATE('2008-01-03 23:58:19','YYYY-MM-DD HH24:MI:SS')


On any update, AR tries to sql encode the value of the AR object's "id" value as the primary key. Calling the id accessor on my Receipt objects was returning nil, and AR was translating that to a sql NULL. There's a simple fix, alias id to our new primary key accessor:

class Receipt < ActiveRecord::Base
set_primary_key "rt_receipt_id"
alias_attribute :id, :rt_receipt_id

Thursday, July 22, 2010

A couple of useful Ruby scripts

Ruby is my goto language for a lot of small scripting tasks, here's a couple of useful examples. Note that these scripts are good enough implementations and could be made easier to use by adding commandline arguments, I've just never had reason to use them regularly enough for that.

Gutenberg Crawler
Sometimes you need some real files with decent content for testing or whatever. Project Gutenberg is a great source of Public Domain books. Their robot page shows a number of ways to automate download of their files, however a quick and dirty (or effective, depends on your point of view) is to build the path to the text document itself. Exercise restraint - hammering their servers is rude, there are guidelines on the page linked earlier.

SVN Stripper
Git is friendly, it only adds one folder to your project. Subversion is invasive adding one to every single folder. Sometimes clients want source, for whatever reason this script strips out the .svn or _svn folders so you can unversion some subversioned source without folder hunting.

The power of Rails isn't Rails

My team is primarily a Java team (we write servers and services) so when we have to do some web work it always ends up being with a Java web framework, some reasonable, some not so good.

There are a lot of reasons to choose Java, it's stable, it's very well understood (by the people who care to dig into it's internals) and it's brilliant for long running processes like servers. What it's not good at, it seems, is web. Of all of the mainstream languages, Java seems to have the worst web frameworks, the worst view engines and the most over-thought data access conventions. If there's one saving grace in the enterprise Java landscape it's Spring, but even their best (Spring @MVC), while better or even the best in the Java landscape, it never quite feels like the right tool for the job.

Given this, the Spring team created ROO, a project to add code generation to Spring MVC. While it may not be the case here, many other web frameworks looked at the Power of Rails project and asset generation and thought "YES! That's the secret sauce!", and it turns out they're wrong. Spring ROO generates a whole load of code, it manages your DAO layer, but even with all of that power, it's real world use case has by many been deemed prototyping, not enterprise project ready. I felt the same way when I tried to use it, easy to get running, hard to keep progressing. Turns out all of the code generation in the world isn't the secret sauce.

There are two factors that I believe make Rails a killer web framework.

The first is The Community (yes, proper noun). The Rails lighthouse shows over 800 open bug tickets. I've seen people mock this fact (ROO in contrast appears to have 240, and I couldn't find the one...) but the nature of The Community is that bugs, when found, are reported, usually with a failing test and possibly a patch for good measure. The number of found bugs (along with the rate of commits on the Rails Github account) is actually a positive sign, it means that The Community as a whole is invested in the success of Rails, and is working together to improve it rather than letting it atrophy or ignoring the issues with it and working around them. This leads to a vibrant piece of software that is a joy to use, that while not perfect, won't settle for average.

The second reason is that Rails, for multiple reasons, naturally lends itself towards a style of application that allows you to maintain the original "Wow I got that project running fast!" feeling (PM types: read "Project Velocity") for the entire duration of the Project. This seems to be why people who love agile seem to gravitate towards Rails, your burndown chart isn't logarithmic, it's flatter, more predictable, and more accurate (until the client changes their mind) than that old Gannt chart.

For the same reason that excites customers and project managers is why developers love it, because we love adding features, getting a product past just enough to the wow stage. Working a lot with legacy code teaches you the value of testability, simplicity, readability, and other agile-ilities, so anything that promotes these things to the extent that Rails does or that lets us add value to a product at the same rate regardless of the age or size of a codebase is a good thing, for everyone.

I believe that any framework garners all of it's power from the language that it sits on. A Java based web framework will be constrained by Java's inherit strengths and limitations. Likewise, a Ruby based web framework will live and die by Ruby's strengths and limitations.

It seems for the web, that Ruby's strengths are paramount to it's success, and it's limitations can be lived with.

Thursday, January 14, 2010

Scaffolds from existing tables

I find it hard to believe how often I see experienced, skilled and intelligent programmers who when faced with a tiny feature they would like to see in an OSS product resort to whining on forums or to colleagues instead of checking if there's already an existing solution

Granted this doesn't seem to come up in the Ruby world as much as it does a few other language circles I'm involved in (I suspect it's because it's usually faster to write the Ruby code to do what you want than to log into a forum...), but there are a few of these "requests" that you see over and over again, even when a good solution already exists and is freely and openly available. In rails it tends to be the "I have an existing schema but I want scaffolding!" one.

I had that thought trigger while dealing with a CRUD app I've been working on that deals with a large, legacy schema. It's not because I like scaffolding all that much (I don't) but because I wanted to get a working prototype up and running really fast and Ruby is much better at writing boilerplate code than I am.

In an attempt to become more like the programmers I look up to I didn't whine, I didn't log into a tech forum or post to a google group (where they are undoubtedly already 500 emails requesting the same thing already) I just started coding. Here's the "simplest thing that might work", it uses eval, it's not altogether elegant, but it fits all of the requirements.

We don't need to Google everything, there's nothing wrong with programmers that actually write code to solve problems...