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!

Popularity: 13% [?]

Theoretically Related Posts
  • Email is not a fat pipe. We forget this at our peril.
  • Hire Us
  • “You know CSS, right?”
  • 12 Steps to Testing in Rails
  • Trackbacks

    Use this link to trackback from your own site.

    Comments

    Leave a response

    1. tsonny Tue, 21 Aug 2007 19:08:05 UTC

      hahahaha…laughing at myself via your blog today was relieving. Thanks for sharing the story and the code sample.

    2. jimjim Fri, 24 Aug 2007 06:09:59 UTC

      > Duh, because X will always equal y if you assign it to y instead
      > of checking to see if it == y!

      Or rather, ‘x’ will always be true (x=y => x)… as long as y is true.

      irb(main):001:0> y=1; if x=y then true end
      => true
      irb(main):002:0> y=nil; if x=y then true end
      => nil

      It’s useful for the pattern:
      if md=bar.match(/f(oo)/) then
      p md[1]
      end

    3. [...] Someone asked about my own blog. It is at http://www.thirdbit.net. Sometimes I post reports of idiot bugs I’ve found in my own code, and I’m sure that reading those will cause people in my section no end of amusement. As I [...]

    Comments