Alex Chaffee's blog
That's it. I'm done with TextMate. It hasn't been updated in over 2 years, either for essential functionality (replace in path) or performance fixes (searching through log files) or UI issues (how many boxes must you click to enable autosave?) or bug fixes. Every few months the author pokes his head up and says "I'm working on TextMate 3.0!" and then disappears again, happily accepting new license fees into his PayPal account.
I've just been bitten twice in two weeks by a bug that caused not just data loss, but data mangling in a way that was very difficult to fix. Here's the rough steps to reproduce:
- Edit some files in TextMate
- Leave TextMate running in the foreground
- Switch to console and "git pull" in the latest code from your workmates
- Run a search-and-replace that edits a file that was changed (by someone else) during the merge
- Save that edit
You'll see (with "git diff") that your version of the file has your new post-merge changes... but it also has reverted your buddy's changes from the merge. It's like you decided that those changes were no good and reverted them yourself and then added your own.
If you fail to notice this before checking in, you will totally hose your version control... but just for that one file. You can't just revert a whole commit... you'll have to step through change by change to figure out which change was theirs, which change was yours, and which change looks like it was yours but was actually an inadvertent revert of theirs!
I can't work under these conditions. I'm switching to RubyMine today. See http://bjclark.me/2010/03/10/rubymine-a-love-story/ for why.
P.S. I just found a bug report for this dated 2008-04-02. That's just about 2 years ago for a critical data-losing bug. Woot.
Now that I'm starting to use DelayedJob to perform jobs in the future in my Heroku Sinatra app, its important that they happen at the scheduled time. But unless you pay attention, you'll find that times get mysteriously changed -- in my case, since I'm in San Francisco in the wintertime, by +/-8 hours -- which means that some conversion to or from UTC is being attempted, but it's only working halfway.
Trying to keep a handle on which libraries are attempting, and which are failing, to convert times is a losing battle, so I'm trying to do the right thing and save all my times in the database in UTC, and convert them to and from the user's local time as close to the UI as possible. Unfortunately, a variety of gotchas in Ruby and ActiveRecord and PostgreSQL makes this trickier than it should be. Here's a little catalog of my workarounds.
Remember the annotate_models rake task? Dave Thomas wrote it many years ago and it corrects one of the flaws in ActiveRecord: it describes the schema for a table as a comment inside the Ruby model file that it maps to. Unfortunately Dave hasn't had time to maintain it, so a couple of years ago I cleaned up some bugs and re-published it as a pastie. Then Cuong Tran made it a gem and put it on Github, and since then, there's been a whole lotta forkin' goin' on!
I recently pulled in a bunch of the forks into ctran's master branch, and just pushed it to Gemcutter as version 2.4.0. Just run gem sources and make sure http://gemcutter.org is in your list -- otherwise do gem source -a http://gemcutter.org -- and sudo gem install annotate and it'll install a binary called annotate in /usr/bin. See the README on github for more info and have fun!
One caveat: ImageMagick installs a tool called annotate too (if you're using MacPorts it's in /opt/local/bin/annotate). So if you see
Usage: annotate imagein.jpg imageout.jpg
then put /usr/bin ahead on the path and you'll get ours instead.
No, seriously. Why wouldn't you use Erector? Cause I think it's a pretty awesome view framework, but for some reason it hasn't caught fire yet. So if you think writing actual Ruby to emit HTML, with a clean, nestable syntax with full support for Ruby features like inheritance, delegation, and yield is neat, but there's something holding you back, then please let us know what it is. At best we can fix it, and at worst, at least we'll know why.
Here are some reasons I think you might not use Erector:
You love angle brackets. If this is the case then I can't help you. I don't think anybody can.
You like typing every tag name twice. Since Erector elements are Ruby statements, every open tag gets automatically closed.
You like invalid HTML. Since Erector elements are Ruby statements, every open tag gets automatically closed. (See how that works?)
You always remember to call 'h'. Rails 3.0 is going to HTML-escape all output by default. Erector's been doing this the whole time. Cause, you know, why wouldn't you?
You like having to rewrite your code when you extract a partial, and then again when you extract a helper method. In ERB, templates, partials, and helpers all have slightly (and annoyingly) different syntax for things like referring to variables and calling other code. Erector is all Ruby, so you can use your favorite refactoring browser, or just cut and paste, to move your code around. Check out this excerpt from Jeff Dean's RailsConf talk to see this in action, or read the slides from the whole talk on SlideShare.
You hate encapsulation. You think that your views should have direct access to all the instance variables of your controller. Unless they're partials, in which case you shouldn't, even though you can, although the names might be different. Confused yet? So am I.
You like putting code for one component in three separate files. Erector's new "externals" feature allows you to put all the code -- HTML, CSS, and JavaScript -- inside a single Ruby class. The CSS and JavaScript then get output inside the HEAD, once per HTML page, while the HTML gets rendered in the BODY as usual, as many times as necessary. This follows the OO paradigm of cohesion, otherwise known as "put similar stuff together," which is the complement of loose coupling, which means, "keep different stuff apart."
Okay, so those were sarcastic reasons. Here are some more possible reasons why you wouldn't use Erector. I suspect that these next ones hit closer to the mark. But I believe that they're all specious, if not downright false.
Your site contains a whole lot of complex HTML and a few inserted Ruby variables. OK, this makes sense. Erector's not great for static sites. But I've never personally worked on a web application where the code inside the views didn't quickly get complex enough to require codey things like loops and functions. And if you're writing code, then why not do it in a programming language?
Your designers don't know Ruby. I've heard this complaint a lot, but I have yet to meet this mythical designer who's smart enough to understand modern HTML, CSS, JavaScript, ERB, and partials, but is not smart enough to learn that "div 'foo', :class=>'bar'" outputs "<div class='bar'>foo</div>". On the contrary, I've worked with several designers who, after a few tutorial pairing sessions, were comfortable checking code in and out and editing Erector view code at will. Like any junior coder, they need to stay away from the tough stuff, but they're pretty good at knowing what they don't know and asking for help when they need it. (Which they would also do if working inside ERB.)
View code needs to look as similar to HTML as possible. Well, I hear this, but have you looked at HAML? That language is hella popular, and it doesn't look anything like HTML. Its structure is similar, in the abstract, but so's Erector's, and at least in Erector the method for emitting a div is called, you know, "div". And it's a method. And I don't want to turn this into a war between HAML and Erector -- I think HAML is gorgeous -- but HAML suffers from the same design flaw as every templating technology: views are not objects, and markup isn't code. After a certain point of complexity, HAML's elegance breaks down and you'd be better off doing loops and functions in code.
You've already got a bunch of stuff in ERB and it'd take too long to convert it. Yes, legacy code is a pain, but we have a command-line tool that converts ERB (or HTML) to Erector to make it a bit smoother. And you don't have to convert your whole app to Erector at once. Erector views can interoperate with ERB or HAML in Rails and Sinatra.
You're stuck on an old version of Erector. Yes, legacy code is a pain, but we have an upgrade guide for getting to 0.6.0, and people on the mailing list ready to help.
Erector's too slow. Lies! Erector is faster than a greased rattlesnake going downhill. Check out these benchmarking results. Erector is about 2x as fast as ERB and 4x as fast as HAML about the same speed as ERB and HAML(*) under typical conditions. We make sure to use the same output stream to minimize string copy or realloc, and using Ruby objects means much lower parsing overhead.
(*) Update: the "2x/4x" figure was based on a benchmark program that didn't use template caching, which speeds things up for both ERB and Haml. With template caching, Erector and Haml are about the same speed; Haml is about 20% faster when rendering a page with no partials. See this ongoing thread on the Erector list.
There's no documentation. More lies! We have a whole bunch of documentation at http://erector.rubyforge.org, including a FAQ and a user guide.
You got burned by Markaby. Underneath the elegant facade of Markaby lay a confusing and often counter-intuitive engine. Its use of instance_eval and other tricks made simple things break in weird ways and made debugging a real chore. Erector was born out of those frustrations, and one of its main design goals is "no magic." Also, there was a long time where Markaby wasn't being maintained (although that's changed recently); we have a core group of developers committed to responding on the mailing list and github, and we run integration tests against the latest stable Rails release (and soon, against Edge) to catch incompatibilities early on.
Rails has all these great helpers and I want to keep using them. Okay, go right ahead! Erector's Rails integration allows you to call any helper, either directly through a proxy method, or indirectly through the helpers object. If you find a helper that doesn't work, let us know and we'll add it to the list of supported helpers. (We haven't done all of them yet because it's a pain in the neck to look at each function and figure out what its input and output semantics are. Does it return a string or emit directly onto the output stream? Does it take a block? An options hash? An html_options hash? Etc.) We're also slowly putting some Rails functionality into Erector, either in the base class or in custom widgets. If there's something you need, ask on the mailing list, or better yet, send us a patch.
Its name is a dirty word. I've heard this more from people who didn't grow up in the United States, where the Erector Set was a popular toy among the 6-to-12-year-old DIY set in the 70s and 80s. (Apparently it was called Meccano in the UK.) Erector is a normal word, used all the time in the news and in business names. And as the name of a view library it's evocative in a way that's relevant and interesting, in that it's a builder, and you build a view up out of parts.
But we have heard this complaint, and in sympathy, changed the name of the command-line tool (oh, sorry, guess I can't say "tool" either)-- uh, executable-- from "erect" to "erector" even though the former is a venerable English verb that's grammatically appropriate ("I asked him to erect the scaffolding."). If you introduce the library and your coworkers get all giggly then I think if you just say the name with a straight face and then roll your eyes and mock your bawdy buddies when they snicker then all will be well. After a few repetitions it won't sound odd at all.
You've never heard of it. Help spread the word! Post a review on your blog! Ask your favorite app framework whether they support it! Post code samples in Erector and when people say "What's that?" then point them at http://erector.rubyforge.org! Give a talk at a meetup! Write your congressman and ask if she supports the Erector Mandate Bill of 2009! Buy ad space on the moon!
So, in conclusion, and despite my somewhat snarky tone throughout, I am honestly and desperately curious to know why the world has not yet beat a path to Erector's door. Anybody got any more ideas?
The following monkey patch gives a bit more information in the ActiveRecord SQL logs. Instead of just saying "User Load" it also says the file and line number in your code that asked AR to perform the operation. That way you can have a hope of tracking it down and optimizing it away if at all possible.
User Load (0.2ms) views/main_page.rb:107:in `filters_box' SELECT * FROM `users`
Code after the jump. I guess you put it in environment.rb with all the other monkeys.
def capturing_output
output = StringIO.new
$stdout = output
yield
output.string
ensure
$stdout = STDOUT
end
then...
it "exits immediately from --version" do
output = capturing_output do
lambda {
Erector.new(["--version"])
}.should raise_error(SystemExit)
end
output.should == Erector::VERSION + "\n"
end
I was just sitting around my living room listening to NPR, and heard the following Car Talk puzzler:
I want you to get a pencil and write down the numbers, 1 - 9, inclusive, and leave enough space between them. At your disposal you have one plus sign and two minus signs. You can insert those plus and minus signs wherever you want, to make the total come out to 100.
Naturally, I thought, "Gee, that would be tedious to solve it by hand. But it would be fun to write a Ruby program to solve it!" 9 minutes later I was sending the result (and the source code) to Car Talk Plaza.
So here's your challenge: can you write a program to solve this puzzle? And can you beat my time?
My solution is below the fold... don't click "more" until you've taken a stab at it yourself.







