From: Gavin Kistner Date: 2006-09-07T03:13:31+09:00 Subject: What Not to Do (a cautionary tale) I wrote a little code generation system for my company. It's a small script that reads in an XML file and an ERB template, feeds the XML file to the template, and writes out the result to a file. To help those in the company who don't know XPath, I extended REXML with a couple convenience methods that let you search for elements by name and/or attributes. class REXML::Element # Find all descendant nodes with a specified tag name and/or attributes def find( tag_name='*', attributes_to_match={} ) self.each_element( ".//#{REXML::Element.xpathfor(tag_name,attributes_to_match)}" ){} end # Find all child nodes with a specified tag name and/or attributes def kids( tag_name='*', attributes_to_match={} ) self.each_element( "./#{REXML::Element.xpathfor(tag_name,attributes_to_match)}" ){} end ... end This worked very nicely. Template writers could simply do something like: In this file I see the following classes: <%= root.find( 'class' ).map{ |el| el.attributes[ 'name' ] }.join( ', ' ) %> All was fine under 1.8.4. I just upgraded to 1.8.5 and all templates using #find broke. After a bit of sleuthing, it looks like REXML changed the Document#root method in a way that caused it to call Elements#[], and that method uses @element.find, mixed in from Enumerable. My Element#find was overriding Enumerable#find, and all hell broke loose as a result. Moral of the story (pick one or more): [ ] Don't extend someone else's class with common method names [ ] Don't upgrade your Ruby environment unless you need to