TextMate Haml Indentation
November 27th, 2007
TextMate has this clever feature where when you paste something, it is adjusted to the same indentation as the preceding text. This works wonderfully in languages like Ruby that have block ending delimiters. However, in certain languages (Haml, Sass, Yaml, Python), you indicate the end of a logical block by indentation alone. The result is that when you copy and paste code, the indentation feature is more often wrong than it is right.
You can turn off the indentation feature from the preferences pane, but then you lose a useful feature when using languages like Ruby. TextMate also provides a Paste Without Re-Indent feature, but it’s rather a pain to type Ctrl+Cmd+V every time I want to paste. My solution was to make a macro which calls Paste Without Re-Indent, and map it to Cmd+V in Haml and Yaml files. The macro is available for download.
SWORD in Ruby
July 31st, 2007
On a number of occasions, I’ve played around with manipulating the text of the Bible in Ruby. Tonight, for the first time, I did it the correct and obvious way, and was successful.
require 'rubygems'
require 'inline'
class Bible
inline(:C) do |builder|
builder.include ''
builder.add_compile_flags '-x c++', '-lstdc++', '-I/opt/local/include/sword', '-L/opt/local/lib', '-lsword'
builder.prefix %{
using namespace sword;
}
builder.c %{
char * text(char * ref) {
SWMgr mgr = SWMgr();
SWModule *kjv = mgr.getModule("KJV");
kjv->setKey(ref);
return kjv->RenderText();
}
}
end
end
puts Bible.new.text("jn 3:16").gsub(/<[^>]*>/, '')
You need to sudo port install sword sword-bible-kjv first. This is going to be useful.
Navigation
June 16th, 2007
dotjerky$ related app/controllers/products_controller.rb
app/controllers/products_controller.rb (31)
test/functional/products_controller_test.rb (28)
app/models/product.rb (18)
app/views/products/index.rhtml (18)
test/unit/product_test.rb (16)
app/views/products/_form.rhtml (15)
db/schema.rb (15)
app/models/image.rb (14)
app/models/user.rb (14)
app/views/products/show.rhtml (14)
app/views/layouts/site_layout.rhtml (14)
test/fixtures/products.yml (14)
app/helpers/products_helper.rb (14)
test/fixtures/taggings.yml (14)
... ... ...
This was accomplished with about 10 minutes of Ruby hacking and a few calls to subversion. It basically looks up which files tend to get committed at the same time as the given file, which means that it has no dependency on the language or framework used. Performance is a problem, accessing all that history, so I need to figure out some sort of cache. Otherwise, it’s a simple matter of slapping a GUI on this thing and hooking it into TextMate.
It seems like you could do some really cool stuff with line numbers too, like showing snippets of code from other files that were modified at the same time. That would be quite a bit more work though, and probably more annoying when it showed the wrong thing.
The Naming of the Beasts
May 31st, 2007
It’s said that naming things is one of the more difficult aspects of programming. Lately, it hasn’t been as much of a problem for me as it used to be. Ruby doesn’t make you have as many classes to name as Java does. And when you really get to know Ruby, you figure out that local variables are basically useless too, which makes for even less things to name. But there’s still the matter of has_many :through models in Rails, which almost always feel contrived.
Suppose we were to invent a language for naming things. You could start with more or less English vocabulary, I suppose, but you would change things.
First of all, you would want it to be highly inflected in useful sorts of ways. Temporal inflection (past tense, etc) would have to be replaced with something more useful to computer time, with an eye toward the conceptual complexity of thread management. Plurality would stay mostly the same, but you would add another inflection for collections of things (singular words for pluralities, which are ad hoc in English).
Also, I tend to care a lot about how things lay out spatially. I like my code to line up in two dimensions. Sometimes I even make pictures and diagrams in my code (in a very abstract sense). The English language has a bit of serendipity here:dx = [other.right - left, other.left - right].map(&:abs).min
dy = [other.bottom - top, other.top - bottom].map(&:abs).min
On the other hand, you have the absurd imbalance of the ultimate dichotomy, true and false. Our hypothetical language, then, would insist that all words in a semantic set have the same number of letters, so that things will line up.
But I think the greatest significance of a new language would be to remove the inhibition to making up words. There are thousands of concepts that don’t have good names even outside of programming (see The Meaning of Liff). The original intent, as I understand it, of Christopher Alexander’s design patterns was not to put together a toolbox of design techniques, but to put together a pattern language for discussing and understanding design, as it is done by designers.
I tried to use design patterns in Java, and they never really felt right. I think most of the Java patterns, converted to Ruby, end up being a block. Just a block. Consequently, my attitude toward design patterns has been that they were standard hacks around limitations of the language. You don’t hear a lot about patterns in Ruby land.
I think we lost something when patterns became prescriptive rather than descriptive. I would expect that there are some very abstract practices in Ruby that would be worth naming, if we thought about it. It’s much easier to codify things in Ruby than in Java, so it may be that certain libraries are actually living design patterns. But there’s plenty of difference between really good Ruby code and really bad Ruby code, and I think we could describe the difference, and it’s usually more than just a missing library.
A better language helps you to think better thoughts. At some point the programming language itself is no longer the bottleneck, and innovation has to happen elsewhere. I’m not really sure what the right approach is here—I’d hate to decrease the accessibility of programming. But I’m proud to be in the only field I know of that has this problem. We are the only sons of Adam who carry on the tradition of the naming of the beasts.
Sanity
May 18th, 2007
Thread.new {puts 1, 2, 3}; puts 1, 2, 3
Now, if you happen to have some other native thread out there, and it happens to be calling CoreAudio APIs, and the main Ruby thread happens to print more things than the other Ruby thread, the other native thread inexplicably gets into an infinite loop and pegs the CPU.
I kid you not.
It dies peacefully if the main Ruby thread finishes. Otherwise it requires a force quit. If the secondary Ruby thread prints more than the main thread, it has no effect. And it is clearly the number of objects printed, not the amount of data.
In some cases, the behavior will wait until after a gets, if there is one. This seems to depend on how many Ruby threads there are, and sometimes on whether there is a second gets (which is, of course, impossible).
It seems like there’s a race condition on access to standard out, but I can’t imagine what CoreAudio has to do with it. CoreMidi APIs have no effect. And it doesn’t have any business touching standard out. It may have something to do with thread scheduling, and the fact that the CoreAudio calls access the high speed timer. But CoreAudio could be a red herring.
Any help?
Homeschool Fixtures
April 15th, 2007
homeschool_fixtures :posts, :comments, :users
def test_comment
post :create, :post_id => the_post(:user => some_user).id, :comment => {:text => 'hi'}
assert_equal last_comment, the_post.comments.last
assert_equal some_user, last_comment.moderator
end
There are no supporting fixture definitions anywhere. It’s wicked-awesome in practice. Everything is right there.
It was a late innovation on a recent project, so I don’t know much about the performance impact of doing things this way. On the one hand, it’s more dynamic, which is usually means slower, but on the other hand, you don’t have to load anything you don’t need, so it might be faster. To have any chance at good performance, you would want to use this method consistently, turn off the yaml features, and do appropriate DELETE FROMs in between tests (we were using blank yml files to get that effect, but that’s bad).
Jester
April 2nd, 2007
It was bound to happen. Jester allows you to consume REST APIs, a la ActiveResource, in Javascript. I think the Rails community is slowly moving toward more data-oriented AJAX, and I heartily approve.
fast_dnd
March 1st, 2007
The Script.aculo.us drag and drop code is really very nice. Unfortunately, it starts to slow down when you have more than 30 droppables on the page. I thought it would be fun to do something about that, so I did.
This is one of the few times I’ve ever been able to directly apply any of the knowledge I’ve accumulated about how game engines work. Drag and drop is just a collision detection problem, and spatial indexing is a very simple way to optimize it.
In general I consider my experiment to be a success. The test file is noticeably more responsive when using the optimization. I do wish that it didn’t have to rebuild the entire index so often, or at least that the rebuild could be run in a thread, but we’re up against the limitations of the browser.
#dom_id
February 26th, 2007
class ActiveRecord::Base
def dom_ids
@dom_ids ||= Hash.new
end
def dom_id(*suffixes)
name = suffixes.empty? ? 'element' : suffixes.collect(&:to_s).join('_')
dom_ids[name] = [self.class.name.underscore, self.id, *suffixes].compact.join('_')
end
end
I like it. It takes the idea of DOM id generation from Sails and makes it more universal. Example usage:
1 2 3 4 5 |
<fieldset id="<%= @object.dom_id %>"> <legend id="<%= @object.dom_id :title %>"><%=h @object.title %></legend> ... </fieldset> <script>new ClientSideController(<%= @object.dom_ids.to_json %>)</script> |
lsub
February 20th, 2007
#!/usr/bin/env ruby
pattern, replace = *ARGV
regexp = Regexp.new(pattern)
until $stdin.eof?
line = $stdin.readline
$stdout.print(line.sub(regexp, replace)) if line.match(regexp)
end
Which you might use this way:
#!/bin/bash
svn st | lsub "^\?\s+(.*)$" "\1"
Fore!
February 15th, 2007
script/plugin install http://dotjerky.com/svn/shared/trunk/plugins/javascript_routes
It even has tests.
routes.js
February 14th, 2007
Once upon a time there was a web framework in Java called Sails. It was pretty much a knock-off of Rails, and it was pretty much the best Java code ever written (whatever that means). All of us who worked hard on Sails (all two of us) are now programming Rails full-time and loving it. Well, mostly loving it.
We still miss parts of Sails. Parts of it were better (better!) than Rails. I’m not kidding. We still talk about Sails Components. Sails Components were amazing. We wrote like three of them. They were a late innovation on the last Sails project ever. But they were awesome.
Basically you bundled together related Java, Javascript, CSS, and template code and it all got wired up automatically. The files were included in the right spots. The ajax callbacks were defined in javascript for you. The DOM elements were already found for you, and they always had unique and meaningful ids. Data got converted to JSON and passed to the client for free, and could be sent back as context with your ajax request for a nickel. It was beautiful. But the structure it provided for heavy scripting was even better than the automation.
In Rails, now, we have this RJS thing. It’s alright, I suppose. It does encourage you to be light on scripting, which might be considered a good thing. It doesn’t embrace the web the way REST does, which is a bit concerning. And you couldn’t do anything with it if you didn’t depend on the javascript libraries, which are heavy scripting themselves—heavier for being stand-alone the way they are.
I think that if we implemented something similar to Sails Components in Rails, we would find the Sails approach to be superior for highly dynamic pages and widgets, and that RJS still has its place on simpler screens.
Anyway, I was hacking around with routes and came up with something comparable to the ajax magic in Sails. This much works:
Routes.someObjectUrl(24);
// => '/someObject/24'
Routes.someObjectUrl(24, {_method: 'delete'});
// => '/someObject/24?_method=delete'
It would probably be better to return a Url object so you can do things like this:
Routes.someObjectUrl(24).send({onSuccess: this.callback.bind(this)});
iPhone
January 15th, 2007
I really thought it was going to be a pocket Mac. It ran OS X, after all. But then Mr. Steve Jobs (who is a lying liar) told us that it wouldn’t be running any third-party software. It really is just an iPod, a phone, and an internet communicator, like he said. But we all know that it’s actually an iPod nano, a Cingular-exclusive phone, and a 2.5G internet communicator. I will be buying the OpenMoko phone instead.
Desktop hack
January 2nd, 2007
mv ~/Desktop ~/OldDesktop && ln -s ~ ~/Desktop
Log out and back in.
Javascript macros
December 22nd, 2006
function get(symbol) {
return function(target) {
return target[symbol];
}
}
function set(symbol, value) {
return function(target) {
return target[symbol] = value;
}
}
Thus:
disableUnchecked: function() {
this.checkboxes.reject(get('checked')).each(set('disabled', true));
}
