Some rather opinionated plugins

Update: The belongs_to patch has now made it into core Rails. Thanks to Jon for persevering with it where I threw in the towel, and to Koz and Pratik for giving it their attention. I’m still not entirely convinced that going through the conventional channels (as opposed to “whinging on a blog”) would have yielded the crucial feedback to get the patch in a state where it could be accepted, but all’s well that ends well…


A shark, being jumped, yesterday.Take this as the rantings of a pissed-off curmudgeon who’s had his second core patch rejected if you will, but it’s my sad duty to report that Rails has jumped the shark. It happened when it stopped being a framework and started being a collection of opinions instead.

You see, in my book a framework is something that’s meant to make programmers work more effectively – not to lecture them about what they’re doing wrong. Lecturing can be a good thing, as long as it ultimately results in better code – I’ll happily admit that following the Rails learning curve has taught me an awful lot about good programming practices. More and more though, I’m finding it being used to justify just plain broken features: If it doesn’t work, it’s your fault for not doing it the right way. Take this bit of profoundly broken behaviour:

let’s say we have a Company model, which belongs_to :city

>> torchbox = Company.find_by_name('Torchbox')
=> #<Company id: 1, name: "Torchbox", city_id: 1>
>> torchbox.city
=> #<City id: 1, name: "London">

Aha, we’ve got the city wrong. Let’s reassign a bundle of attributes then, like we would if we were doing this through a web form:

>> torchbox.attributes = {:name => 'Torchbox', :city_id => 2}
=> {:name => 'Torchbox', :city_id => 2}

Right, so now we should retrieve the correct one.

>> torchbox.city
=> #<City id: 1, name: "London">

Er, oops.

But no – according to the response on that ticket, Rails isn’t at fault, because there’s a convoluted way I can rewrite my code to make that not happen. Out in the real world we would refer to that as “a workaround for the bug”. But Rails doesn’t have bugs… it just has opinions. My code sucks, as indicated by the fact that it doesn’t work, and if I just learn to write it in a way that doesn’t suck then I’ll become a better programmer. In that way, the Rails developers have, intentionally or not, painted themselves into a position where they’re immune to criticism: Rails works perfectly logically to them, because they wrote it. There’s no counter-intuitive behaviour to them, because they know the codebase back to front and understand exactly what it’s doing and not doing. Anyone who believes it is counter-intuitive just has a different opinion to them, and should be forced to either accept their opinions or get out of the Rails party.

Well, quite frankly that’s bollocks isn’t it? Even if their method is genuinely better according to some metric I don’t understand (and I don’t believe it is: my original approach works perfectly well the other 98% of the time and is in fact such a well-established way of doing things that even the Lighthouse website where I reported the bug does it that way) – my approach is better according to the rather more important measure of being the one I thought of first. The big promise of agile development was that we’d all spend more time implementing what we want to implement, and less time fighting against the mundane details of our platform. Any time the platform forces me to get out of the flow of building my application and figure out how to bend Rails to do what I want it to do, that’s a FAIL for agile programming and for Rails.

And if the code I write is something that logically should work, that I so passionately believe must work that I’m willing to spend my own time tracking down the reason why it doesn’t work, fixing it, testing the fix, writing it up and handing my results to you on a plate, then if you don’t agree then you’d damn well better have a better reason than “meh, I don’t really like doing it that way”.

But wait, there’s still hope! If I act as cheerleader on the Rails core mailing list, then I might be able to drum up enough support to get my patch pushed through! Well, nuts to that. I’ve put enough effort into fixing their broken software already, and now I have no desire to spend even more effort campaigning for a result that doesn’t even personally affect me any more, because now I’ve got a plugin that fixes it, and nobody but me will ever know about it until they get bitten by that bug themselves, spend several hours tearing their hair out over it, and eventually stumble upon this blog post. Hello there. I suppose you’d like to know how to fix it then? Here, have a plugin.

So, having stuck the knife in, allow me to give it an extra twist by stating that indenting with spaces is shit. Tab characters make sense for the same reason that using <h1> rather than <font size=”36″> makes sense for HTML headings: it’s meaningful markup that indicates the purpose of that whitespace, and allows the reader to tune its appearance and behaviour, such as how much space it takes up and what happens when you move your cursor through it. And no, I don’t care about your vim profile that does automatic tab conversion. Some people actually like using other text editors, and if you use tab characters in your files then your choice isn’t limited to editors with esoteric make-your-text-behave-like-something-it-isn’t features, but is instead limited to editors which can insert the characters you tell it to without arsing everything up. (Unfortunately, it turns out that some editors aren’t capable of that, as we discovered: the Rails mode of Eclipse insists on chucking spaces in, even after we changed every conceivable preference setting.)

This is where my retabulate plugin comes in. It converts the customary two-space indent into tabs, both via an explicit rake task that does a sweep of your codebase, and by intercepting any new files created via script/generate. It can’t deal with YAML files though, because the YAML spec obnoxiously insists that only spaces are valid.

Enjoy these plugins… or ignore them entirely if you prefer. Hurrah for freedom of choice.

19 Responses to “Some rather opinionated plugins”

  1. @#{} says:

    instance_variable_set(“@#{reflection.name}”, nil)

    Haha. @#{} looks like like someone making fun of Perl.

  2. Ed Spencer says:

    Eek, that’s a lot of anger! Tabs are an interesting one – I agree that they make more sense but annoyingly every editor tends to display them differently (should or be 2 character width or 4? Or sometimes even 8? wtf?).

    That’s why I tend to end up using a double space instead… although some people think 4 is better (I don’t, mainly because I do a lot of JS coding and things can get pretty far indented and I don’t want 1/4 of my screen width taken up by blank space!).

    Incidentally, I’ve never tried sending anything through to core but I don’t think I’d expect the behaviour you do. If I want to update the country I’d do it via city= (some City object) instead of city_id = (some integer). I can see the argument for doing it your way, but for me it’s not what I would expect (after years of Rails conditioning no doubt :p). I guess that’s why plugins are so useful!

  3. matt says:

    Hi Ed,

    I agree that if I was doing a one-off standalone update like that, I’d assign to city= rather than city_id=, but when you’re mass-assigning attributes that come through from form submissions, you’re going to be doing it implicitly all the time – pretty much any time you have a form with a select box on it. That’s how I originally ran into this, when implementing some permission checking: 1) check that this city is one that the user is allowed to edit under; 2) do a mass-assignment including city_id; 3) check that the new city is also one the user is allowed to edit under (but end up getting the old city instead).

    Presumably if you’re unlucky enough to be using an editor that doesn’t let you set your own preferred tab width, it’s also not going to be configurable to let you use the tab key to indent with spaces – so in that situation you’ve basically got to do everything with repeated space bar tapping? Sounds a little bit painful :-)

  4. Or, alternatively, torchbox.city = City.find(2)

    Why are you messing around with direct id assignments? You can do if you want to, but then don’t complain when higher level abstractions don’t work anymore.

  5. matt says:

    Example tweaked now (was previously just setting torchbox.city_id = 2), to emphasise that in lots of real world scenarios direct ID assignment is the sensible thing to do, and requires a lot of faff to avoid…

    The problem with referring to one method or another as being a “higher level abstraction” is that it requires some degree of awareness of Rails internals at the time you’re writing that code… a naïve user would just consider them as two alternative methods. Sure, there are some things (like direct SQL queries) that rightly remain at the bottom of the pile of abstractions and shouldn’t be awarded any ‘magic’ behaviour, but direct ID assignment *is* a standard Rails idiom, and can be made to work alongside the higher-level abstraction with a three line patch in the higher-level code (there’s nothing unusual about higher level abstractions patching into lower-level methods – Rails does it all the time) – so I feel I’m justified in complaining when it gets in the way of me doing my job.

  6. Dan says:

    I both agree and disagree with you Matt. Tabs – i disagree, i don’t like them! But that’s an opinion! Rails core – i agree, both with your patch and your assessment of the current state of development. Browse through various rails tickets and you’ll see a lot that are dismissed off hand without proper consideration, and many more where the core team wander off at a tangent or babble amongst each other without really understanding the ticket or often what rails actually already does. I’d also agree with the content of your patch – i’m sure a lot of people get snared by it, and also the object level abstraction is inherently related to the integer stored beneath – so to me, if the integer of the relationship changes, the higher level abstraction should also change, otherwise you could rapidly get into a messy state. +1 for this patch!

  7. Tom says:

    Tabs: you’re wrong. If you think that code whitespace is “semantic markup” then you’ve got problems that Rails can’t solve.

    Foreign keys: http://github.com/rails/rails/commit/fcf31cb. Happy now?

  8. Carl says:

    @Tom: No, they are semantic. An indentation has meaning, and sensible languages (python) and sensible editors (vim and its brilliant foldmethod=indent) realise that when you indent a line it’s not because you want to improve your Feng Shui or get some practice with typing tab characters but because you want to identify the line as somehow inheriting from the previous one. Typing characters intended to add gaps between words does not do this.

    Also, the commit doesn’t mention this post or indeed the original bug. Shall I open a bug about that?

  9. Dylan Smith says:

    Well, at least Perl does what you mean.

    And there are editors other than vi?

  10. [...] – bookmarked by 6 members originally found by yoroy on 2008-12-20 Some rather opinionated plugins http://matt.west.co.tt/rants/opinionated-plugins/ – bookmarked by 4 members originally found by [...]

  11. [...] Success Metrics Instead of Rankings, Google PageRank … Saved by ramitsethi on Mon 29-12-2008 Some rather opinionated plugins Saved by Aj4x on Sun 28-12-2008 How to Sew a Baby-Doll Top Saved by fekaled on Thu 25-12-2008 [...]

  12. Tony Byrne says:

    Matt,

    I’ve been bitten by this bug in Rails and tried a number of possible solutions before finding your plugin. belongs_to_synchronization is the only thing I’ve tried that works so I’d like to thank you for your efforts.

    Tony.

  13. RailsWTF says:

    I’m really pained by this post and that the lesson you learned [and the lesson you're proffering other people in your situation] is not to go about things in an adult manner in the proper channels but instead to whine and complain and sit back and give up asserting that “Rails has jumped the shark. It happened when it stopped being a framework and started being a collection of opinions instead.” [FWIW _every_ framework, even the holy Merb, is nothing more than a collection of opinions and code that backs those opinions up]. All’s well doesn’t end quite as well when what you’ve preached here is “Don’t try to play ball and be a good sport when you can bitch and pout and make someone else [hat tip at the kindnesses of Jon, Koz, and Pratik] do your work for you”. And sadly this is a lesson too many programmers are willing to implement in their own programming journeys.

    I was going to comment on your repeated insistence that your unique and beautiful snowflake habits are more important than following reasonable best practices as well but the bruises on my forehead remind me how this argument usually turns out.

  14. matt says:

    Where do you get the idea that I was sitting around waiting for someone else to do the work for me? I could have just written the three line workaround, made my code uglier, and quietly cursed Rails for being a bit rubbish. But no, I dug into the Rails core to find out where it was going wrong. I wrote the patch. I submitted it on Lighthouse. I packaged it as a plugin to make it more immediately useful to the wider Rails community. I responded to the feedback I got, and would have happily continued developing it had anyone told me what needed fixing. But instead, I was expected to argue its worth with some people who were too attached to Rails internals to notice or care where it wasn’t catering for real-world programmers, and who had already hand-waved away the explanations I’d already given.

    Yes, I’ll grant you that my “all’s well that ends well” comment only really applies to that one ticket (except actually it doesn’t, because the patch has since been reverted on the grounds that it doesn’t also fix some other stale references that it never claimed to fix in the first place) and not to the wider Rails development process, which based on this experience, is fundamentally broken. Don’t blame me for that though.

    And please do tell me which reasonable best practices I’m neglecting. I’m happy to follow them just as long as someone can explain what’s so reasonable about them (as opposed to them being handed down from on high by someone who purports to know my project better than I do). In the cases of assignment-by-ID and indenting with tabs, no-one’s done that yet.

  15. Tabs are not syntactical. They are not part of the grammar of Ruby. It is impossible for them to have any semantic value. What I believe you are trying to say is “Tabs are meaningful *to me* and I prefer them to spaces,” which is fine, but your argument from semantics is specious and I find the vitriol with which it was delivered to be rather ineffective.

    If your entire team agrees to buck the Rails convention and use tabs, *and* you aren’t concerned with making your code less workable by others in the community, then by all means go for it. However, I would be rather irked if someone converted all the spaces to tabs on an open source project because they prefer it, without regard to the established coding style.

    Your points on the belongs_to behavior have merit. Unfortunately, the general tone of this post is one of passive aggressive frustration and childish temper tantrum throwing and is frankly unbecoming of a developer. Certainly not of the kind of developer I would want on one of my teams, communicating with other developers and with clients. It casts a unfortunate shadow on the work you’ve done on the plugin and patch.

  16. matt says:

    Not sure exactly why people are descending upon the term “semantic” when I never used the word (a spot of Al Gore “I invented the internet” word-twisting perhaps? :-) ), but if I had used it, I would indeed have meant it in the “meaningful to the user” sense. I stand by the HTML analogy: your graphical web browser doesn’t care whether you used <b>, <h3> or <strong>, just as Ruby doesn’t care about your choice of whitespace, but when your document is received by another person in another environment, it does matter what your underlying intent was in choosing to make a bit of text bold, so that they can represent that intent in their preferred way instead. I know coders who absolutely can’t work with two space indents – and why should that be imposed on them, just because the original coder happened to use that as their preferred visual representation of “this line is one syntactic level deeper than the last one”? By using tabs in your files, you’re preserving that intent for the next coder, rather than one particular representation of it.

    “However, I would be rather irked if someone converted all the spaces to tabs on an open source project because they prefer it” – I totally agree; I would never break an established coding standard for the sake of fighting a holy war. (Following one standard, even a stupid one, is much better than mixing them.) But this plugin came about because it was Rails which was breaking our in-house de facto standard of using tabs. De facto, because we have front-end developers who are used to working in things like Macromedia Homesite where the tab key inserts a tab character, and it was so clearly the one sensible way to work that nobody thought to write that down as a standard.

    If you think that the above rant reflects badly on my communication skills – fine. However, I would ask that before you come to that conclusion, you apply the same critical standards to the Rails core team, whose communications with me – here and elsewhere – have boiled down to “I don’t really like your code. Why don’t you go off and find someone who can tell you what’s wrong with it?” Certainly, something has gone wrong in the process here to end up with a potential contributor being completely alienated, and I really hope that Rails doesn’t have such a cult of hero worship around it that people automatically assume that the problem is my attitude.

  17. Alex Fortuna says:

    Matt, I totally agree that 2-space indentation is shi^H^H^H not good enough. I’ve coded on a number of platforms for a number of years and came to a practical conclusion (definitely shared by most other multiplatform developers) that indentation with tabs is the most versatile method.

    More of it, that’s what the tab character was initially intended for! “If not broken, why mend it?”.

    My personal oppinion about those “2-spaces fans” can seem exotic, but I’m no enemy. If I’m wrong, laugh at it and don’t take it too seriously. I think that Ruby and Ruby on Rails is conceived by many of us being “way too revolutionary”. In fact, even more revolutionary than it actually is. So many are glad to destroy everything smelling “old regime” even if it has actually proven itself to be quite solid. Poor tab character is the helpless victim. :)

    My personal counter-arguments against 2 spaces are:

    * There are hundreds of editors in existence. As far as I can tell, almost none of them supports BLOCK INDENT/UNINDENT (mark N lines and indent/unindent them) made of 2-space sequences without a lot of tweaking and personalization. Some editors don’t support it at all.

    Also to my humble knowledge, almost EVERY more or less usable editor supports block indent/unindent made of tabs with no additional tweaking at all. AND EVEN IF IT DOES NOT, you can also do it by hand, since adding or erasing one character a line isn’t a big deal.

    * What about “occasional editing”, when you have to make a couple of hot fixes/improvements after the project has already been deployed on the customer’s resources? Ask a remote hosting administrator immediately bring you a graphical Mac shell with TextMate running on it?

    In situations like this you’ll have to use the hardware and software YOU ARE GIVEN and don’t have chance to ask for more. Experienced programmers have been in such a situation dozens of times. That’s why they don’t rely on tweaking too much.

    You love your Mac, your TextMate, your IDE tweaked to your personal highest grade of comfort? Go code for another 5-10 years and then return to my comment. I’m sure you’ll get rid of all your tweaks by that time. No offense please.

    * As far as I can tell, web templates are normally built up of HTML+CSS+ERB+JS. To my experience, the only way to get a viable template is PROPER INDENTATION OF ALL LOGICAL LAYERS, no matter which language they are in. Yes, if there’s a , and then a and then and then Ruby code in it, indent each layer as if they are all the same language. The simpler the rule — the easier it’s to follow.

    And now tell me, 2-spaces guys, will you ask your colleagues HTML/CSS/JS coders convert to 2-space religion either? Or you’ll mix indentation styles? Or just don’t care about non-Ruby indentations at all?

    All above mentioned possible solutions will lead to TROUBLE AND LOSS OF TIME.

    I’m for freedom of choice. But I’m also for not forgetting the basic roots of programming. If tabs are good in BAD languages, why throwing them out of GOOD ones?

  18. Alex Fortuna says:

    Oops, tag-looking text is not processed properly.

  19. Alex Fortuna says:

    Here’s another plugin to tweak source code indentation: http://github.com/dadooda/indent.

    This one is suitable for both spacebar-lovers and tab-lovers, so let it be peace and friendship. :)

Leave a Reply