Radiant Scoped Multi Update
Blog updates have been pretty infrequent lately. As have my OSS contributions. Sorry! I haven't had an awful lot of spare. Between client work and wrapping up my book (more on that soon), things have been a bit... insane.
However, I did find some time over the weekend to finally update the scoped multi-site Radiant extension fork to work with 0.6.7 and latest edge.
Note that the old URL is no longer accessible, as the Radiant Github presence was recently refactored to remove extensions from the core project repo.
I'm Back
I'm back on the east coast after a 1.5 week west coast mini-tour, culminating with Railsconf information overload and a couple of great parties. I've written up some session highlights over at DDJ (parts one and two) in case you're interested.
Greets to all the fine folks I met at the conference, especially the Offrails crew and Jason LaPier, my local guide to Portland nightlife and microbrews (also a badass Rails developer who was recently published in Pragprog's Advanced Rails Recipes). Thanks dude!
If you've sent an email and I haven't gotten back to you yet, my apologies; I'm still a bit jetlagged and trying to catch up on a ton of things.
Travel Plans
I’ve got my itinerary planned out for Railsconf. I’ll also be hitting up a couple other west coast cities before landing in Portland. First stop is SF to visit my friend Ankit on Friday. Then trekking up to Eugene on the Amtrak Coast Starlite (I love me my trains) late night Sunday to hang and brainstorm with our designer/big-thinker Ty for a few days. Then it’s off to Portland on Thursday.
If you’re in any one of those cities and want to grab a beer (or a smoothie), hit me up on Twitter. Hopefully I’ll see some of you at Railsconf.
Also, I’ll be giving a talk at NHRuby tonight (5/20) on Rack. If you’re in the area, stop by.
Random Passenger Observations
Played around with Passenger (v1.0.4) a bit more last weekend. Deployed a Radiant instance with it for staging. Overall, quite happy. However, thought I’d take the opportunity to jot down a few observations:
First, Radiant, with a smattering of extensions, takes a while to start up on the vhost we’re playing around on (yes, need to pump up those specs before deploying anything real). Since Passenger cleans up idle Rails instances when they fall into disuse, this can mean a harsh initial request delay for infrequently accessed hosts.
Passenger has clearly targeted the shared host market, where hosts have a large memory footprint and a large number of applications. The same strategy doesn’t work quite as well for a small VPS memory footprint and a single application root, where it would make sense to keep an instance in memory at all times (and clean it up and respawn it, perhaps, on occasion if an idle timeout is reached).
The solution in this case is to increase the Rails pool idle timeout to something that matches your traffic profile (of course, the tradeoff there is with the growing memory footprint of long-running processes…). And while you’re at it, adjust the maximum number of spawned instances—it defaults to 20, which is great for a dedicated with 2GB but not so good if you’re running a single application on a 512MB VPS.
More information is available in the architectural overview and the excellent user guide that the Phusion guys have put together. The SpawnManager itself is written in Ruby, and has a set of RDocs that you might want to take a look at as well.
We initially had some process ownership issues, where the Rails production log wasn’t being written to. The Rails server instance is going to be running as whatever user owns config/environment.rb (unless you change this in the config), so make sure to chown/chmod appropriately.
Finally, the restart mechanism, although a bit odd, is pretty useful. If you touch a file called restart.txt in the #{RAILS_ROOT}/tmp directory, it’ll reload the application instance on the next request without having to explicitly restart apache. Here’s a Capistrano deploy:restart task for this:
namespace :deploy do
task :stop, :roles => [:app] do
puts "Use the deploy:restart task to restart the Rails application"
end
task :start, :roles => [:app] do
puts "Use the deploy:restart task to restart the Rails application"
end
task :restart, :roles => [:app] do
run "touch #{current_path}/tmp/restart.txt"
end
end
Note To Self (Good Examples Are Hard)
The single hardest thing about writing a book, I’ve decided, isn’t the writing part. It certainly isn’t the technical part either. I don’t mean to downplay my own skills or those of any other technical author, but the truth is that you don’t have to be a guru to write a fantastic book (it does help, of course).
The key to writing good book material is being able to show people how to do things while keeping them entertained. That means coming up with examples that tread the line between being practical, being appropriately demonstrative, and being correct. Good examples are the hard part.
When developing an example, you’re trying to come up with something that has the following characteristics:
- It’s interesting. That is, it’s worth writing about in the first place. No one wants to read another blog post creation example (unless there’s something unusual about it). BORING.
- It represents best practices. You’re an author. You have to be right. Or at least able to competently defend your implementation choices, anyway.
- It illustrates those concepts that you’re trying to write about. This should be first point, rather than third. See what I mean?
- It minimizes focus on those elements that you are not writing about. That is to say, we shouldn’t spend a lot of time talking about fuzzy bears, even though there are many different types of fuzzy bears, six species of which are listed as vulnerable or endangered by the World Conservation Union (whose headquarters are in the scenic Lake Geneva area of Gland, Switzerland).

The last point is a tricky one, but it’s important. You have to cut corners when creating good examples—but not the important ones or the ones that would prevent it from representing best practices. If you’re trying to illustrate, for example, how to build a tag cloud, you don’t want to spend lots of time on the CSS for making it pretty. Leave that to the CSS books.
If your example involves video transcoding, and the point is to illustrate the use of a message queue, you’re going to be really tempted to spend an extra 2-3 pages on certain format discrepancies or operational edge cases but DON’T. It’s interesting, sure, but if it’s not central to what you’re currently trying to demonstrate (message queues). You must resist.
If your example is a simple web service for sharing geographic location data and you’re using a bunch of RESTful conventions, make sure to explain the basic concepts. But don’t write a dissertation on REST vs SOAP. And don’t worry about responding to formats that aren’t part of the main example.
Just because it’s how you’d do it in a real-life project doesn’t mean it’s how you’d write about it or explain it to others. Keep it simple, lively, and (as Gold Five so succinctly put it before crashing his X-Wing) stay on target.
Mod_Rails Revealed
The hardworking hackers over at Phusion finally unveiled Passenger (mod_rails) earlier today. I just moved one of our staging servers over to it and will be playing around with it a bit more over the weekend. So far I'm happy to report that, as advertised, it's dead easy to use... and the performance seems solid.
I'd write up a tutorial but honestly it's so simple that it's completely unnecessary (imagine that!). Check out the updated screencast for all you need to know to get up and running and make sure to dig into the provided docs if you need more. They're very thorough.
In summary, it's great to see more Rails deployment options emerging, and it'll be interesting to see viable shared hosting options for the low-end market too. Although I'm a strong believer in the app server + frontend web server pairing, there are a whole class of applications for which it just seems like overkill.
A hearty 'nice work' to everyone involved! Now where's my mod_rack? :-)
UPDATED 4/12: this blog is now running on mod_rails and Mephisto 0.8!
2008 Rails Rumble Rumors
Just to clear up any possible confusion on that matter: Yes. There will be a 2008 Rails Rumble. And no. It will not be in May. Probably September or October.
We have a lot of great ideas (thanks to everyone that participated and commented) that will improve on last year’s experience dramatically and the whole team is looking forward to it, in a big way. More information coming soon, I promise, as well as an announcement, new blog, and specific details prior to Railsconf.
Oh, and speaking of Railsconf… Josh Owens, of TastyPlanner fame (last year’s grand prize winner), is giving what looks to be a fun talk about their experience building a kickass app in a 48 hour timeframe. If you’re going to be there, you ought to check it out. Especially if you’re interested in participating this year. If you haven’t yet registered for the conference, you’re running out of time, so get to it.
See you there!
Radiant Super Multi Go!
Here at Ubikorp we've used Radiant as the basis for a number of client projects who needed core CMS-like functionality. As anyone who's played with it knows, Radiant is very barebones; this is intentional and a welcome change from most packages, which attempt to throw the kitchen sink of clutter at each and every problem. Fortunately, it's easy to build on top of the basics by using a well thought-out extensions system -- there are a large number of stock extensions available (and it's also really easy to roll your own for custom app logic).
One of the more interesting extensions is the multi-site extension. To be honest, multi-site capability is something I really feel should be a core feature, but since it's not, this approach works surprisingly well (at least until you start adding other extensions that would ideally be multi-site-aware). Radiant school headmaster Sean Cribbs wrote an initial version of the extension back in November, but it didn't quite work for us on a particular project -- we needed to be able to scope individual user-level access to particular sites. So we extended it, and thus the scoped multi-site extension was born.
Well, actually, it's just a fork of the multi-site plugin. For now.
Features:
- scopes user level access to individual sites (admins and developers still have access to all sites)
- regular users can't see, edit, or access other user sites that they don't own
- optionally scopes layouts to sites
- snippets usable everywhere, but display and editing of snippets limited to admins/developers
Anyway, if you're interested in seeing our changes you can check it out on github in my fork of Sean's Radiant repo. Is this a candidate for integration into the existing extension, or should we spin it out as a separate extension? Let me know what you think.
UPDATED 6/24: The URL is out of date. See this blog post for more details and updates (the latest version requires Radiant 0.6.7 or later).
NHRuby: Django/Rails
If you're in the ME/NH/MA area on Tuesday, make sure to stop by the NH Ruby SIG; Brian Turnbull will be presenting an in-depth talk comparing the philosophies of Rails and Django. I've never done any Python work at all myself, so I'm really looking forward to this dissection.
If you're not in the area, you're still in luck. We're trying something new this month, and will be broadcasting the event live via WebEx. Big thanks to Tim Golden and our sponsor RMC Research for hooking this up!
(NOTE: we usually meet on the third Tuesday of the month, but our host had some scheduling problems this time around; we'll be returning to the normal schedule in April)
Git Ur Radiant Extensions
I published a new Radiant extension yesterday: Database Form. It provides a new page type and tags for constructing contact and request info forms and will save user responses to a database table. Those responses can then be exported for use in another application (CRM, etc). See the README for usage and examples. It was extracted from some client work that we'll be deploying soon.
If this sounds good, you can download or clone it from GitHub. While you're there, spend a few minutes poking around; GitHub is pretty dang cool. They've definitely succeeded in making Git repository hosting stupid simple. Just click a button to create a project, follow a few commands on your local system to import your sources, and you're off and running. You can then view the repo history, browse the source, see diffs, download a tarball (for easy extension installation in your pre-existing Radiant project), and fork it if you want to add your own features. That's where things get sweet, of course: Fork the project, make some changes, and send me a pull request so we can merge them into the master branch. All this is possible without GitHub, but it sure does a swell job of streamlining things and abstracting the suck away.
It also exposes the links between developers and their project contributions in a pretty cool way. See the DataMapper project's "network" page to see what I'm talking about. Ryan Tomayko has pointed out that this sort of interaction starts to smell an awful lot like a MySpace for developers, where lines in the social graph are drawn based on OSS project work. Wow, that's a cool thought, ain't it?
Principle of Least Surprise
One of the things I love about Ruby is that it tends to follow the principle of least surprise; things just work the way you would expect them to, with precious few exceptions (cough cough inject cough). Horray for intuitiveness.
On the other hand, I've become so spoiled by The Principle that willful violations stand out like the sober guy at the all-inclusive beach resort. I know I'm being a nitpicky ass here, but this Rails bug^H^H^H ticket makes me kinda ill. Funny, because in my previous life as a Java/PHP developer, I wouldn't have even batted an eyelash at it.
Plugins Are Unnecessary
Plugins really are unnecessary -- Jay Fields is absolutely right. RubyGems is a great package management system and there's no reason it can't do double duty here, if we just impose a few extra restrictions on Gem/Plugin structure. There are other benefits too using Gems too, such as versioning and dependency management, which is somewhat painful in the world of Rails plugins.
Merb already uses Gems for plugins/extensions. Why doesn't Rails? Historic reasons, most likely. Rails itself predates the existence of Gems iirc. But seriously, how hard would it be to rewrite script/plugin to install a gem and unpack it into vendor? That's the first step.
ActiveRecord: Importing YAML
Backing up ActiveRecord models to YAML is easy to do, and can be convenient for creating 'templates' that can later be imported to restore basic project structure in a new database. However, the import process can be a little tricky, as the instantiated objects don't seem to respond correctly to new_record?
Here's what our YAML might look like. Note that it's a serialized CustomWidget, which is an ActiveRecord model that inherits from ActiveRecord::Base.
---
- !ruby/object:CustomWidget
attributes:
name: DoSomething
updated_at: 2008-01-28 05:02:54
description: Does something.
created_at: 2008-01-28 05:02:54
attributes_cache: {}
We can automatically instantiate the appropriate Ruby objects from this using the following code:
class CustomWidget < ActiveRecord::Base
end
YAML::add_private_type("CustomWidget") do |type, value|
CustomWidget.new(value)
end
obj = YAML::load(File.open("template.yml"))[0]
=> #<CustomWidget name: "DoSomething", created_at: "2008-01-28 05:02:54", updated_at: "2008-01-28 05:02:54", description: "Does something.">
obj.save
=> true
So what we've done is taken YAML from one project and tried to use it to import the same data into another database (note that IDs are stripped). We define a transfer type for the YAML, providing a mapping to the class, and then we call YAML::load to load it up. This returns an array, in this case it contains a single CustomWidget object. Everything looks swell. We call save on the new object and it returns true.
However, if we check the count of CustomWidgets that exist in the database, we'll find that it's unaltered; no new record has been saved. It turns out that this is because the imported CustomWidget doesn't report true when new_record? is called.
obj.new_record?
=> nil
In order to save itself to the database and get a new ID, the CustomWidget instance has to first respond correctly to inquiries as to whether or not it's new. We'd like new_record? to return false. The quick-fix secret is to manually toggle the @new_record instance variable. If we do that, calling save will return true and actually save the object to the database as well this time. Big yay, right?
CustomWidget.count
=> 0
obj.instance_variable_set("@new_record", true)
obj.new_record?
=> true
obj.save
=> true
CustomWidget.count
=> 1
Yes, this is an ugly hack. Did I say ugly? I meant gross.
Constraint-Driven Development++
There was a nice writeup on ReadWriteWeb yesterday about Montreal's upcoming Blitzweekend event. Like the Rails Rumble that we organized last September, Blitzweekend is another attempt to give people a "weekend to build a functioning startup from scratch". Unlike the Rumble, it isn't a competition, and it places added value on physical proximity.
Josh Catone (of ReadWriteWeb and also RailsForum) dubs this sort of thing "organized ultra-rapid development" and notes that, against all odds, this strange weekend-startup phenomenon seems to be trending up. This is really just constraint-based development taken to the next level, and the reason it's exciting is simple: it pushes you to deliver results, immediately.
Results, of course, are what makes application development exciting; seeing a movie get automatically reencoded after upload, a search query produce a number of restaurants in my area, plotted on a map, a transaction get authorized and recorded by the third party API you're using. And later, seeing your end to end workflow in place and your first user signup. When we see results, we get motivated to work harder in order to see even more impressive results. When we don't see results, we get depressed, we step back, we overthink. Our motivation falters.
Events and contests like Blitzweekend help give developer-entrepreneurs that extra motivation kick that they often need to get over the motivation speedbump, and an opportunity to gather a team together and just go for it. If you haven't tried it, you really should. In the process you'll find out if you can work with your team in the long haul, write some (hopefully) great code, and figure out whether or not the idea has legs to stand on. If it does, you'll have visible proof of this and will probably be more jazzed than ever to continue working on it. If it doesn't, you won't have to spend any more time wondering "what if". All in 2-3 days.
(It's important to have realistic expectations of course. Are you going to be able to build the next Flickr in 48 hours? Or the next Facebook? Nope. But you sure can get a nice first iteration out there and working. Every application has to start somewhere, and if the first iteration of your idea isn't ugly and lacking important features, then there's a good chance it took you too long. Short iterations can pack a lot of result-fu ninja punch.)
Anyway, I've rambled on for a bit and I'm sorry. We'll do another Rumble, or something like it, later this year. For sure. But the reason I'm writing is this: You don't have to wait.
That is, you don't need an actual event to prove that this brand of ultra-mega CDD works.
Take the last 3-4 days of this month, of next month, of whenever. If you're a freelancer, just tell clients you're unavailable during those days. Get 3 other people to do the same thing. Agree on an idea, flesh out the basics. And just do it, Nike-style.
I'm seriously thinking about doing just this, starting at the end of February, maybe at the end of each month. Anybody wanna be on my team?