Annals of Idiotic Bugs

Posted by amy on August 21, 2007

I’ve decided I like bugs. Not in production code, of course, but in development code, yay for bugs. Fixing my code teaches me far more than writing it did. Perhaps that’s because, with example code and snippets and tutorials all over the internet, much of the code that I write I don’t really write. I copy something I saw from something else and modify it to suit my own needs, or I look it up in a recipe book, or I see someone else’s solution on a mailing list… When you’re new to a language, you often use code that you don’t quite understand. I’m curious, so I try to understand it, but I don’t always have the time.

But then you get the bugs. And to fix the bug, you have to understand the code. (no, I know, that’s not really true. you can fiddle around with bug fixes just like regular code, and find someone else’s fix and implement it and it works and you’re in a hurry so you don’t care why it works, just that it does…)

Anyway, most normal developer bugs are stupid. I suppose there are great developers out there whose bugs are subtle and interesting. Most of mine, anyway, are quite stupid.

Here are some of my stupid bugs:

1) Implemented a slideshow. Stored stuff in the session to use during the slideshow. At end of slideshow, remembered to clear the slideshow stuff out of the session. Yay for me! Oops, child calling, check in code!

Max: “Um, Amy, how come playing a slideshow deletes all the photos in the slideshow?”

Me: “Dunno, maybe it has something to do with clearing the session? Why would deleting a thing from a session destroy it?”

Here’s what I had to clear the slideshow out of the session:
session[:slideshow].clear

Here’s what I should have had:
session[:slideshow] = nil

Oh, and here’s what was in the slideshow:
slideshow = domain_object.photos

So it’s not like my slideshow was just an array of any old ActiveRecord objects. It was a particular other ActiveRecord object’s personal array: i.e., an ActiveRecord AssociationCollection. AssociationCollections have a method called clear. Clear removes all the objects from the collection, like delete_all. Except delete_all doesn’t, according to the doc, destroy records, it just deletes them. And assoc_collect_.clear definitely destroys them, because not only did calling it kill the database records for my photos, it also killed the actual photos on the filesystem. So are clear and destroy_all the same thing? The doc is thin, so I went to the source:

# File vendor/rails/activerecord/lib/active_record/associations/association_collection.rb, line 67
67:       def clear
68:         return self if length.zero? # forces load_target if hasn't happened already
69:
70:         if @reflection.options[:dependent] && @reflection.options[:dependent] == :delete_all
71:           destroy_all
72:         else
73:           delete_all
74:         end
75:
76:         self
77:       end

So, domain_object.association_collection.clear does to the collection what domain_object.destroy would do, which is either to delete or destroy the associated records depending on what options you set in your call to has_many:

:dependent - if set to :destroy all the associated objects are destroyed alongside this object by calling their destroy method. If set to :delete_all all associated objects are deleted without calling their destroy method. If set to :nullify all associated objects’ foreign keys are set to NULL without calling their save callbacks.

I’d set :dependent => :destroy_all, so that’s what calling clear did. Oopsie!

Why did I use .clear anyway? Apparently I was thinking of my rake task rake db:sessions:clear.

In any case I didn’t want to be storing whole photo objects in my session anyway. I just wanted IDs: session[:slideshow] = domain_object.photos.collect { |p| p.id }

2) Wrote some boilerplate code for a create action. On failed validation, Rails complains there’s no create template to render. Why are you trying to render a create template, Rails, when I clearly ask you to render new upon failed validations:

render :action => new

Uh, because I told rails to render :action => nil. So rails defaulted to its standard "render the template whose name is the same as the action invoked" and went looking for a create template. What I needed to be saying was:

render :action => 'new'

Problem fixed. That one took me way too long to track down. I wish Ruby would complain to me about things that look like local variables but don’t seem to have any values attached to them. I’m not quite used to bug fixing a dynamic language, without the crutch of the compiler.

Right, what else?

3) If I want, temporarily, a list of all the photos in both a & b, where a & b are two different ActiveRecord objects, I should not do the following to get the list:

list = a.photos << b.photos

This moves all the photos in b over to a, permanently, in the database. This is one of the pitfalls of the syntactic sugar of methods that look like plain old operators. They’re not, really. ActiveRecord sometimes does more stuff than you want or expect it to, behind the scenes. Not a save or update in that code to warn me that I’m calling the db, but I am.

4) Oh, and the stupidest thing of all:


if  x = y
	#do stuff
else
	# do some other stuff
end

How come the ‘other stuff’ is never getting done!??
Duh, because X will always equal y if you assign it to y instead of checking to see if it == y!

Conclusion

This concludes the first edition of: Amy reports dumb things she did in Ruby code so you don’t have to do them too. C’mon, be original, and come up with your own bugs! I do not release my bugs under a Creative Commons license. I want royalties on all software including these particular bugs. I am going to implement DRM on these bugs. Don’t you dare steal my buggy code! It’s mine, mine, all mine!

10 things you should know about method_missing

Posted by amy on August 01, 2007

[update, 9:27 a.m., 8/1/2007: welcome, reddit readers. annoyed at me for calling this post “10 things”? Well, then you’ll really hate me after reading this post!]

1. method_missing is a Ruby kernel method and everyone should know about it.

2. Rails implements some of its funkiest magic with method_missing. When you ask your model to find_by_freaky_column_name, and it does so, that’s because ActiveRecord::Base overrides the kernel’s method_missing method.

3. method_missing is the method of last resort. When you send a message to a Ruby object, Ruby looks for a method to invoke with the same name as the message you sent. (There are a bunch of different ways to send the message, but the most common one is just obj.method_name. But you can make the fact that you are sending a message explicit by using obj.send(:method_name)). First it looks in the current self object’s own instance methods. Then it looks in the list of instance methods that all objects of that class share, and then in each of the included modules of that class, in reverse order of inclusion. Then it looks in that class’s superclass, and then in the superclass’s included modules, all the way up until it reaches the class Object. If it still can’t find a method, the very last place it looks is in the Kernel module, included in the class Object. And there, if it comes up short, it calls method_missingYou can override method_missing anywhere along that method lookup path, and tell Ruby what to do when it can’t find a method.

4. Sometimes people use method_missing to implement some default behavior they want to occur; more often they seem to use it as a kind of method factory or dispatcher. They have a class or module that they want to have many methods that do generally the same kinds of things, they’re not sure in advance which methods people will actually want to call, or there are so many of them, and they’re all so similar, that it seems like a waste to implement them all by hand. So they just say, okay, if someone gives me a method that doesn’t exist, I’ll assume that they’re trying to do this general kind of thing, and I’ll make them a method based on their name that does the thing they probably want it to do.Sometimes they make a new method and add it to the class (factory); sometimes they just do what needs to be done in method_missing itself (dispatcher).

5. Basically, method_missing is one of the features of Ruby that make it remarkably easy to create your own DSL for fun and profit. DSLs are Domain Specific Languages, which, if you didn’t know — and of course, dear reader, you did, because you are oh-so-up on all the oh-so-fashionable programming paradigms, are the big thing that Martin Fowler is talking about a lot these days.

6. Here’s the kernel’s method_missing implementation. It’s in C, so I have no freakin’ clue what it’s doing, but I know what the result is: a NoMethod error:

irb(main):227:0> "thingy".invoke_method_missing_for_demo_purposes("arg", "other arg")
NoMethodError: undefined method `invoke_method_missing_for_demo_purposes' for "thingy":String from (irb):227

7. Now here’s Jamis Buck showing us how ActiveRecord::Base overrides method_missing. It’s a long but fascinating article that takes you step-by-step through some Rails source code. Go look at it now.

8. method_missing is said to be slower than calling an actually-already existing method, unsurprisingly, given that it’s by definition invoked only after Ruby’s looked everywhere else for your method. The speed issue may or may not matter for your intended use, and my impression is that it usually doesn’t. #{usual disclaimer about benchmarking, using profilers, and not prematurely optimizing your code.}

9. method_missing can also be a bit opaque to users of your code, since dynamically created methods are, of course, not documented in the API.

10. The use of method_missing falls under the general technique of metaprogramming, which is a big deal in Ruby, and not something ordinary folks ever fool around with in many other languages (like Java). At least, I never fooled around with it in Java. But hey, what do I know? What is metaprogramming? C’mon, people, it’s right up there with DSLs in the list of things the kewl kids know. But actually, it really is pretty cool, and it really is pretty much the way Ruby works. And it’s not as difficult as (but waaaay more useful than) say, Lacan, despite the poststructuralist-sounding name and the fact that the metaprogramming wikipedia article makes it sound very fancy.

Ignore the wikipedia article, listen to _why, in a discussion of method_missing: “I don’t think the idea here is to save memory or speed. The idea behind metaprogramming is to teach Ruby your conventions and let it do some guessing, in order to save you some code.”

Don’t trust that _why and all his chunky bacon? Here’s David Black, equally reassuring:

One of the great accomplishments of Ruby is that its object and class model bring about a wonderful unity and simplicity. Even things that might seem like they should be hard or obscure are actually very transparent, once you have the hang of how the model works.Unfortunately, there’s a trend toward drawing an increasingly sharp line between regular programming and “metaprogramming” in Ruby discussions, where metaprogramming is understood to mean… well, I’m not sure what it’s understood to mean, the way it’s getting used in connection with Ruby, and that’s the problem.There’s no natural separation between programming and metaprogramming in Ruby, and no reason to make life harder by breaking them apart. The thrust of Ruby’s design is to dissolve complexities, so that even things that appear to be “wizardly” or full of “dark magic” actually make perfect sense in terms of a relative small number of underlying language principles.

So here’s your takeaway #{max gagging sound}: method_missing is cool. It’s metaprogramming. It’s used to save programmer time and typing, and to make DSLs or provide default behavior. Most famously in ActiveRecord dynamic finders. Metaprogramming is not scary, and method_missing, like other cool Ruby features, may or may not float your boat. That’s cool, either way.

_why says: “I never use method_missing. Maybe twice. And both times I didn’t use it, regretted it, forcefully ejected the code from a moving vehicle, shed nary a tear.” Then he goes on to enumerate some of the very cool ways he’s seen method_missing used elsewhere, in non-_why code.

So: know it, love it, use it, hate it, kill it, eat it, break it, think it. As they say at Ari’s preschool: “Have fun, be safe, and USE YOUR IMAGINATION!” Matz wants you to.