29 Oct 2007
At my previous job, I worked as a C++ programmer. The job, which started out primarily working with Objective-C and Objective-C++ to port a Windows application to Mac OS X, became progressively more a C++ job as time went on. I came to learn C++ fairly well, just enough to realize how much I dislike it. I haven’t properly been able to summarize just why I dislike C++ so much, but fortunately someone else has: The
Defective C++ page, part of the larger
“C++ FQA”, lays bare a number of problems inherent to the design of the C++ language.
In less than two years of C++ usage, I encountered nearly every one of these problems. Some of them can be dealt with by deciding upon and following “best practices” within the team, which we did (to give credit where credit is due, the more experienced C++ developers I was working with pretty much laid out the best practices for the rest of us to follow), but some of them are just things that you pretty much have to live with, and end up turning the development process into a real straightjacket.
(via Another Day in the Code Mines, which had picked it up from a discussion at Joel On Software; check out the comments in that discussion if you want to see some extremely blindered C++ apologists in action…)
Read on →
16 Oct 2007
Yesterday I installed Apple’s new Logic Studio. The system I was installing it on was already crowded, with the first four Jam Packs among other things, and I only had about 11 gigs free. To top it off, the Jam Packs weren’t properly installed; I had manually copied them from an older machine, but in a slightly non-standard location, without the .pkg files etc, so the Logic Studio installer couldn’t see them and wanted to do a fresh install of the new versions included with Logic Studio.
So I figured I’d wipe out the old Jam Packs manually, and let Logic reinstall them, and hope I’d have room for all the parts of Logic I wanted. I stumbled across this thread on Apple’s support site, which mentioned that the new versions of the Jam Packs in Logic Studio now have audio saved in compressed (but lossless) format, so they’re a bit smaller. Cool!
So, I wiped out my old Jam Packs, and installed most of Logic Studio, including all software and all 5 Jam Packs, but not the new extra audio content (which as far as I can tell seems to be intended mostly for soundtrack use). After installing all that, I ended up with 17 gigs of free space! Installing the new version, with more stuff, saved me 6 gigs! Woot!
Read on →
11 Oct 2007
There seems to be a sort of mystique around the concept of metaprogramming, but in Ruby it’s really not mystical at all. It’s all about leveraging a few existing methods in smart ways to let you eliminate boring code. This sort of thing is possible in my “native language” of Objective-C, but in Ruby it turns out to be even easier.
Today I’m going to talk about how to use the method_missing
method to eliminate repetitive boilerplate code. method_missing
is Ruby’s fallback strategy for unresolvable method names encountered at runtime. If you try to call a non-existent method on any object, the Ruby runtime will instead call method_missing
on that object; The default behavior of this method is to raise an exception, but we can override it to do smart things based on the method name.
The Badness
The Rails application I’m working on has a role-based permissions scheme, with a ManagementRole model that sits between User and Program models, allowing us to define roles that a User can have relative to a given Program, which in turn constrain what they’re allowed to do with the application. These roles are expressed concretely as simple subclasses of ManagementRole, with names like RoleSearchMembers and RoleCreateMemberships. Up until recently, checking whether a User were allowed to perform certain tasks within a Program consisted of calling one of several nearly-identical class methods defined on ManagementRole, such as these:
def self.allow_search_members?(user, program)
return User.is_administrator?(user) ||
self.program_role_exists?(user, program, "RoleSearchMembers")
end
def self.allow_create_memberships?(user, program)
return User.is_administrator?(user) ||
self.program_role_exists?(user, program, "RoleCreateMemberships")
end
# etc
# the program_role_exists? method, not listed here, simply checks
# for the existence of a matching ManagementRole object
As you can see, this is extremely repetitive, and adding new types of permissions means adding new, nearly-identical methods. As of now there are 18 methods like this, and that number will just go up in the future, leading to even more boilerplate code. Where will this madness end?
The Goodness
Providing a simple definition for method_missing
in the same class lets me eliminate all those 18 methods, and will automatically deal with any new ManagementRole subclasses. It looks like this:
# instead of a bunch of allow_xxx_xxx_xxx? methods that differ only slightly,
# we just catch all such calls here.
def self.method_missing(method_id, *args)
if match = /allow_([_a-zA-Z]\w*)\?/.match(method_id.to_s) and args.size==2
return User.is_administrator?(args[0]) ||
self.program_role_exists?(args[0], args[1], "Role#{match[1].camelize}")
else
super
end
end
The first thing this does is examine the method and the argument list, to determine if it looks like the kind of method we are trying to replace. If the method name looks anything like “allow_xxx_yyy_zzz?
”, and if there are exactly 2 arguments, then we’ll do something interesting. Otherwise, we’ll just call super
and let the parent class deal with it.
In the “interesting” case, we simply perform the same checks we did before to determine whether we return true or false, but now we’re using indexed values from the args array, and constructing the string naming the MangementRole subclass by using the result of the earlier regexp match. Voila!
The Even-Betterness
Of course, software development being what it is, any time you revisit an old design to try to improve it, the act of improving it can lead you to discover new, further improvements. While writing this, I realized something that would have avoided all this boilerplate code, and the metaprogramming it led to, in the first place: Instead of asking the ManagementRole class explicitly about each of these permissions, I’ll be better off writing a single method called allow?
in the ManagementRole class, which will do a lookup based on the recipient’s class-name, e.g. RoleSearchMembers, RoleCreateMemberships. And then instead of calling ManagementRole.allow_search_members?(u,p)
, I will call RoleSearchMembers.allow?(u,p)
. That will lead to even less code than the metaprogramming version, and less code is always better code. I haven’t written it yet, but it’ll probably look something like this:
def self.allow?(user, program)
return User.is_administrator?(user) ||
self.find_by_user_and_program(user, program)
end
So this problem, with its metaprogramming-to-the-rescue solution, turned out to be easily solvable with more traditional object-orientation, but that’s OK! Hopefully this post will be helpful anyway, for someone else whose problem doesn’t have the same easy solution that mine turned out to have…
Read on →
26 Sep 2007
I attended RailsConf Europe 2007 in Berlin last week. This has already been blogged to death I suppose, but here’s my take on a few items:
- Bratwurst on Rails
- The local Berlin ruby brigade organized this event; The night before the conference began, over 400 rails nerds gathered to eat freshly-made sausage and drink beer. This was a great way to start things off, I had lots of interesting conversations with a variety of people.
- Networking, in general
- All throughought the 3+ days I was there, I had the opportunity to meet and speak to so many people doing so many interesting things. Anywhere you turned, you could find people ready and willing to strike up a conversation about rails, programming in general, business strategies, or anything else you might think of. This was just a great, friendly crowd to hang out with. Extra props to Thoughtworks for picking up the tab for everyone to get a beer or two at the hotel bar one evening!
- JRuby
- There was lots of emphasis on JRuby. Thoughtworks and others are pushing JRuby on Rails as an easy way to get Rails into the enterprise, since you can deploy on any standard Java app server; Sun includes support for JRuby (as well as normal Ruby) in NetBeans; and Ola Bini’s new book on the topic has just hit the shelves, and there’s a lot of interest and potential in JRuby overall. But at the same time, I’m somewhat skeptical of Sun’s involvement. It seems great that they’re embracing JRuby, but hopefully they won’t somehow turn it into the sort of bloated mess that Java has become. And frankly, I’m not convinced that the Java app server stack isn’t a part of the IT bloatware beast that Thoughtworks’ own Cyndi Mitchell was railing against during her short talk.
- RejectConf
- This was a hoot. The idea is that any presentation that had been rejected by the conference organizers, plus anything else that anyone wanted to present, could be shown in a severely time-limited format (20 seconds per slide). Lots of interesting presentations, in a short time and fun, friendly atmosphere. Unfortunately the venue was a little too small, it was tough to get close enough to be able to see and hear much, but hey.
- Australians
- Throughout the conference (including RejectConf), the presentations that were the most consistently funny and entertaining, while remaining informative and valuable, were almost always led by Australians. Make of that what you will.
- The occasional bad presentation
- I’m not going to name names here. But one presentation, held on the final day, was really a waste. The guy started off by telling us that some time after his topic was approved for the conference, further development of the topic revealed to him that what he was going to present really wasn’t working out that well in practice. Now this could have been interesting in and of itself, to see in detail how a seemingly-sound concept breaks down when pushed a bit farther. Instead of that, however, the audience were treated to a lot of hand-waving and disclaimers of “I’m not going to show you the code because it won’t work for you anyway”. When there was still half an hour of time left, he was completely out of material and stopped; At that point, at least a third of the audience had already given up and left.
- DHH Keynote
- I don’t think I’m going out on a limb if I say that DHH’s public speaking is known for balancing a fine line between self-confidence and arrogance. I think this one was edging towards the latter. Sure, it’s great to hear that Rails 2.0 is approaching quickly, etc, but the part of the speech where he was proclaiming that the Rails revolution is already over, we’ve won, and now we can rest on our laurels and “enjoy Rails”? I think he’s putting the cart before the horse. Maybe DHH is at this point surrounded by so many yes-men that to him, Rails seems like it’s truly the only game in town, but in reality it can still go so much farther, be applied to so many areas, that it strikes me as premature to proclaim victory. In some places and markets, Rails is still a very difficult sell; In Stockholm where I live, for example, as far as I can tell most of the big consulting firms that have dipped a toe into Rails development have had a really hard time making inroads into the Swedish enterprise market.
Read on →
31 Aug 2007
I haven’t done anything with
RMagick at all, partly because it relies on
ImageMagick which many consider difficult to install on Mac OS X. Lately I’ve had a reason to use the excellent
Gruff charting library, so it was time to buckle down and install this stuff.
As it turns out, it’s really not as complicated as some people say. Assuming you’ve already got
macports and
rubygems installed, it basically comes down to this:
sudo port install freetype
sudo port install ghostscript
sudo port install imagemagick
#sudo port install graphicsmagick
sudo gem install rmagick
Somewhere I got the idea to also install
graphicsmagick after imagemagick, but for some reason port doesn’t seem to be able to find the package; It tries a few dozen sites and fails, so I commented it out above, but feel free to try it yourself. But no matter, it seems to not be necessary; I can now create images, render text, write them to disk, etc.
On Ubuntu (6.10), the incantation is different, but even simpler (via
EXPRESSICA:
sudo apt-get install imagemagick
sudo apt-get install libmagick9-dev ruby1.8-dev
sudo gem install rmagick
The only caveat is that if you’re running without swap space and have less than a couple hundred megs of free
RAM (e.g. on a
VPS server) you may need to stop some of your processes before attempting that last line, or you’ll get this not-so-helpful set of errors after it runs for a while, consumes all available
RAM, and subsequently crashes:
Building native extensions. This could take a while…
ERROR: While executing gem … (Gem::Installer::ExtensionBuildError)
ERROR: Failed to build gem native extension.
Hopefully this will save someone a few hours.
Read on →