Rails 0.13
It looks like Rails 0.13 has been released. There’s a lot of nifty-looking stuff in there, but the one that really grabs my eye is the new migration facility. Rails now has the ability to make schema changes when you upgrade your app. So, when version 1.1 of your app comes along, and it needs to add an eye_color field to the user table, you can now handle it entirely within Rails:
class AddEyeColor < ActiveRecord::Migration
def self.up
add_column :users, :eye_color, :string
end
def self.down
remove_column :users, :eye_color
end
endThe nice thing is that this is completely database-independent. It’ll work on both MySQL and PostgreSQL now, and other databases as soon as their Rails drivers are updated.
One possible shortcoming with migrations: I don’t see any obvious way to enforce the correct order of migrations–if version 1.2 adds a pets table, and version 1.3 adds a nickname field to pets, then I don’t see how the upgrade process from 1.1 to 1.3 will know which order to apply the two migration scripts.
Also, the migration code seems to be a nice first start on database-independent schemas. I suspect that a near-future Rails revision will allow developers to specify the schema as a set of migrations, and then the migration mechanism will create all of the tables needed automatically. That’ll be another huge step for Rails usability.
More Typo patches
I work up early today, so I made a few more changes to Typo. In addition to the earlier threaded comments patch, I’ve added:
- Sort category lists by name. By default, Typo displays category lists in a more or less random order. This patch causes them to be sorted by name.
- Enhanced text filtering. This adds support for SmartyPants-style filtering with RubyPants, fixes a cross-site-scripting vulnerability, adds the ability to stack filters, and splits comment formating away from article formatting, so you can use different defaults for each.
All three patches are against revision 262 from the Typo SVN repository. For convenience, I’ve generated a combined patch with all three sets of changes, plus a couple other minor fixes.
Typo, part 1
My test installation of Typo is up and running. First impressions:
It’s easy to install, once you have Rails working. I’d never actually installed Rails on my home web server, so I had to do the whole install gems, install rails, hunt down dependancies bit. I had an early version of Active Record installed, and it was being loaded instead of the current version that comes with rails, so I had to hunt it down and kill it.
The MT-to-typo conversion script worked okay, once I fixed the MySQL-isms in it. Specifically, it assumed that it could access the MT database by using
databasename.tablename; that syntax gets you into different namespaces in Postgres, not different databases. I worked around it by copying the MT tables into the Typo database and then removing thedatabasename.part of the queries. It probably would have been easier to just passpublicas the database name, though.The script also assumes that all articles are formatted as HTML. Mine are (almost) all Markdown. To fix this, I had to install BlueCloth (
gem install bluecloth) and then rebuild all of the cached versions of each of my articles. There’s no obvious way to do this in the Typo UI, but Typo’s internals makes it easy. All I had to do was run the Railsscript/consolecommand and then runArticle.find_all.each { |article| article.text_filter='markdown'; article.save }. For the Ruby-phobic, this iterates through each Article in Typo’s DB, changing the formatting type and then saving the results back to the DB. Typo’s Article model hooks thesavemethod to verify that text format conversions are handled correctly, so that’s all I really had to do. Nice and simple.It’s kind of slow. I’m running Typo in debugging mode right now, and Rails is always slow in Debugging mode. I suspect that Typo will still be a bit pokey, even in production mode, though. I need to look into how it handles caching.
For some reason, not everything is sorted correctly. The ‘latest comments’ sidebar seems to be sorted randomly. This is probably an artifact of the conversion script–it looks like entity IDs aren’t in the same order as they were in MT, and that screws stuff up. That’s just a one-line fix to the conversion script.
Comments are listed newest-first. I’d rather have them appear in the order that they were written. This should be a one-line change.
The admin interface is sort of sparse. Rails makes it really easy to make calls directly into Typo’s API from the command-line, so I’m not too concerned with the lack of things to click on.
Somehow, some of the Article-Category associations were lost during the conversion. Most of them seem okay, but not all of them. This might take a bit of work to fix.
The categories list on the sidebar seems to be in ID order; it should be sorted by category name. This is easy to fix.
Nothing here looks fatally broken. I need to spend 15 minutes updating the conversion script and another few minutes fixing little issues with Typo, but the system seems to work well enough, at least on initial inspection. I’ll re-import the MT data again later today using the fixed conversion script, and then see what it takes to get it working with Apache in production mode. If that works out okay, then I’ll spend some time tweaking the CSS to make it look a bit more like my current site and then make it live.
Oh–one more thing–I need to have the conversion script save the old URLs from each entry to a file so I can write a bit redirect list that will point each MT article’s URL to the new Typo URL. I get too much traffic from Google to lose all of the links.
My little rails app is complete
The Rails app that I was working on a couple weeks ago for work is finally complete and in the hands of users. A couple higher-priority tasks kept me from getting back to it until yesterday, and the final push towards release was fantastically ugly.
For legacy reasons, I need two of my app’s controllers to use HTTP basic authentication, not form-based authentication. HTTP authentication with Rails isn’t well documented, but the ”teenage mutant ninja hero coders” have an example that works just fine with webrick. Unfortunately, it completely fails with Apache for reasons that weren’t particularly clear. It’s fairly well-known that Apache blocks CGIs from seeing HTTP authentication headers, but none of the workarounds that should have worked with Apache 1.3 (mod_rewrite, fastcgi, etc) actually worked.
In the end, I ended up upgrading to Apache 2 with FastCGI, and it worked flawlessly. Er, except the CGI variable that I was looking for went from being called HTTP_AUTHORIZATION to Authorization, so I had to tweak application.rb a bit.
Rails success
My little Rails project at work is starting to wind down finally. I’ve been heads-down on this thing for almost a week, but it seems to be worth it–I have a nice UI around my database, along with a clean schema to replace the hacked-up one from the previous design. I have 8 models, 9 controllers, and 40-some views. Including tests and documentation, I currently have *90* files open in xemacs, a personal record.
Of the 4 days I’ve spent on this, at least one full day was lost to bugs in Rails that I had to find workarounds for. Probably another day was spent searching for examples and documentation on specific Rails features and generally learning how the system fits together. The remaining two days were divided between database/code design (there are some weird controller issues for this application) and HTML design. It’s been years since I last threw together anything more complex then a Movable Type theme, so my web-design skills were years out of date.
All things considered, 4 days doesn’t strike me as amazingly fast for this project, but I doubt that I could have been much faster using any other framework. The big thing that’s impressed me with Rails is the amount of polish that I’ve been able to put into this in a very small amount of time–things like pagination and live searching were nearly trivial to implement. With a bit of practice, I suspect that I could churn out similarly-complex applications in a little over a day, and that would be just astounding.
Another run at Rails
I’ve started to pick up Rails a few times, always to be interrupted by something–usually either a missing feature that I really needed, or a change in project priorities at work. I finally sat down with it today in an attempt to finish a little project that I’ve been avoiding for months at work.
It was all going well until suddenly one of my classes stopped working right. My Host class belongs_to my Group class. At one point this afternoon, host.group stopped working from inside of my web app. It worked perfectly with the unit tests, but I got a method undefined exception whenever I tried to access the group method on a Host object. Here’s a snippet of the code involved:
class Host < ActiveRecord::Base
belongs_to :group
belongs_to :customer
has_and_belongs_to_many :messages, :order=>"id"
end
class Group < ActiveRecord::Base
has_and_belongs_to_many :packages, :order=>"pkgorder"
has_many :hosts
validates_length_of :name, :maximum=>40
validates_format_of :name, :with => /^[-0-9a-zA-Z.]+$/,
message=>"may only contain letters, numbers, ., and -"
endThese two classes were in their own files, as generated by Rail’s generator script. Can you see what’s wrong? The line right above end in the Group definition should start with :message, not message. That missing colon in group.rb broke the Host class, but only when it was used after Group was defined. So the unit tests worked, because they tested each class separately, but it failed in a bizarre way when the two were used together.
Things like this make me wonder if maybe Rails isn’t getting just a wee bit too clever for its own good.
Other then that, though, it’s been working great. I’m spending too much time searching the Rails docs for help, but I’m moving right along.
Seattle on Rails
The Seattle Ruby users group is meeting tonight. David Heinemeier Hansson and several of the other Ruby on Rails folks will be present. If you aren’t familiar with Rails, it’s an up-and-coming web framework that has been getting a lot of attention lately, even from outside of the traditional Perl/Python/Ruby communities. Oreilly’s ONLamp has a nice introduction.
If you’re interested in Ruby, this will almost certainly be worth attending.
We’re meeting at The Omni Group’s offices, near University Village. The Seattle.rb page has directions.
Programming Ruby 2nd Edition Pre-order
The second edition of Programming Ruby is now available for pre-order. The first edition was easily my favorite Ruby book, but it’s a bit dated now. The second edition covers Ruby 1.8.2, and includes an extra couple hundred pages of reference material.
In addition to the printed version (available via Amazon, etc), they’re also selling PDF copies of the book. If you order them together, then you pay $10 extra for the PDF, which seems really reasonable to me. I’d much rather search through the PDF then deal with the index of a printed book. I’d like to see other programming books sold with the same model.
ActiveRecord
So, I’ve started playing with Rails, a Ruby MVC web framework. So far, I’ve just barely dipped my toes into it, but I’m really impressed with what I’ve seen so far.
My first stop was ActiveRecord, the database layer for Rails. It’s clearly young, but it’s almost a perfect match for Ruby’s dynamic nature. It uses reflection and database metadata to build table classes on the fly. Here’s a brief example:
#!/usr/bin/ruby
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "postgresql",
:host => "localhost",
:username => "user",
:password => "password",
:database => "asterisk")
class Cdr < ActiveRecord::Base
def self.table_name() "cdr"; end
end
Cdr.find_all().each do |cdr|
puts "#{cdr.uniqueid}: #{cdr.channel} -> #{cdr.dstchannel}"
endThis dinky block of code is enough to connect to my Asterisk database and extract call details from the cdr table. I didn’t need to tell ActiveRecord anything about the structure of the table; it does the right thing on its own.
It’s actually quite a bit more capable then this example shows; it understands relationships and keys, and it has a full set of searching and updating methods. I suspect that I’ll be getting a lot of use out of it.
Borges
I’ve been playing with Borges, a Ruby web application server for the past week or so at work. It’s an interesting beast; it’s continuation-based, which means the flow of control in Borges apps is much closer to traditional programming then it is to normal web applications. I don’t have a good example handy, but it’s kind of mind-twisting, because you can make non-event-driven web applications with it. I’ll probably move some of my Asterisk stuff over to Borges, partly because Borges could use a good example, and partly because the big pile of Asterisk stuff that I’ve been accumulating could use a better web framework.