WebDAV in Ruby
I missed this when it first came out, but Why has a pointer to a WebDAV server plugin for Webrick, Ruby’s native web server. I wonder how easy this would be to port out of Webrick and into a generic FCGI controller, so we could use it with Rails? That would let Rails act as a file server, which would be useful for some sorts of user interaction. I wonder if we could make use of it in Typo?
Apache tuning for Rails and FastCGI
There’s a surprisingly small amount of documentation out there on tuning Apache for optimum Rails performance. Almost everyone mentions the first step (use FastCGI, not regular CGI), but that’s such a huge performance boost that it’s really obvious–waiting 2-3 seconds per hit for Rails to start up is an indicator that you’re doing something wrong.
Once you get past that, there’s not a lot of documentation. There are examples from place to place, but no one seems to discuss what they mean or why they should be used.
Ever since I switched to Typo, I’ve been seeing occasional HTTP 500 errors from Apache, suggesting that Apache was unable to talk to Typo. Looking in the logs shows that Apache was usually in the middle of restarting a FastCGI instance whenever the errors occurred. Digging through the mod_fastcgi shows that FastCGI can work in three different modes with Apache:
- Static. FastCGI servers are started when Apache is reloaded and remain running.
- Dynamic. FastCGI server processes are started whenever a FastCGI URL is hit. Excess processes are killed off when there’s no traffic.
- External. Apache and your FastCGI app talk via TCP sockets.
Dynamic mode is the default, but that’s not a good fit for Rails, because of its slow startup time. Switching to static mode really helps. To do that, I added this line to /etc/apache2/apache.conf on my Debian server:
FastCgiServer /var/web/typo/public/dispatch.fcgi -idle-timeout 120 \
-initial-env RAILS_ENV=production -processes 2Notice that I had to list the full path to Rails’s dispatch.fcgi file; on some systems you may be able to get away with only listing public/dispatch.fcgi, but that will almost certainly not work if you’re using virtual hosting.
By default, FastCGI assumes that your server will respond to queries within 30 seconds. I added the -idle-timeout 120 parameter just so I can deal with really slow responses better. Typo’s article admin page currently tries to list all 466 articles on one page, and that can take over 30 seconds to process.
The -processes parameter tells Apache how many FastCGI processes should run for this application. For 95% of users, 1 or 2 will be best. If you get a lot of traffic, then raising this to 3-4x the number of CPUs in your system might get you slightly better performance.
Finally, the -initial-env bit makes sure that Rails runs in production mode, talking to my production DB and not returning error backtraces to the user.
Agile Web Development with Rails is complete
Dave Thomas just posted that Agile Web Development with Rails is complete and has been released for printing.
This is the first book on Rails, and it’s been a fascinating process watching them develop it. Dave claims that their beta book process has made this one of the most Agile book development processes ever. I ordered it on the first day that it was available, and I’ve now sorted through three or four different PDFs and reported a couple different errors. I think I posted the very first error on their public errata-tracking system, but it’s hard to get it to sort by time now that there are zillions of entries from different reviewers.
I’m looking forward to receiving my final PDF and (eventually) the final printed copy of the book.
Congratulations on a job well done, guys.
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.
Behavior: CSS-like application of Javascript
Lamda the Ultimate just pointed out a cool new Javascript tool that should make AJAX-ifying web sites much cleaner and more maintainable. By using Behavior, you can strip all of the ugly little <script> and onclick="" tags out of your website and then specify all of the Javascript actions out of line using CSS selectors. Here’s the example from their website:
So, instead of this:
<li>
<a onclick="this.parentNode.removeChild(this)" href="#">
Click me to delete me
</a>
</li>You can use:
<ul id="example">
<li>
<a href="/someurl">Click me to delete me</a>
</li>
</ul>Then you feed something like this into Behavior:
var myrules = {
'#example li' : function(el){
el.onclick = function(){
this.parentNode.removeChild(this);
}
}
};
Behaviour.register(myrules);It’s a little too verbose to use in this example, but the basic mechanism is really cool. I’d love to see this extended one step further, with Behavior being able to parse a configuration more like this:
#example li:onclick {
this.parentNode.removeChild(this);
}You’d drop that into a file on your web server, say mylayout.jcss. Then you’d have a block like this at the top of the HTML file:
<script>Behavior.import("mylayout.jcss");</script>I’m not exactly a Javascript wiz, but this looks vastly cleaner to me. I’d love to see something like this included into a future release of Rails.
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.