"Here's a Dog." ''"Hi, Dog!"'' "It licks your face, chews your shoes, retrieves things, wags its tail, and loves you 'til death do you part." ''"Great! Just what I want!"'' "It also rides a unicycle." ''"Huh?"'' "But first you'll want it to sing ''Pagliacci''." ''"WTF?"'' ... "Here's a Numeric." ''"Hi, Numeric!"'' "It adds, gets compared with other Numerics, gets raised to a power, and you can get its absolute value and mask its bits." ''"Great! Just what I want!"'' "It also performs the ago operation." ''"Huh?"'' "But first you'll want it to perform the days operation." ''"WTF?"'' ... [More later about confused types, with or without MonkeyPatching, as discovered in RubyOnRails.] ... "Come on, Fido! Sing ''Pagliacci''!" ''"Oooo, ooo oo oooo oooo. Oo oo oo oo oo oooo oooo."'' "Atta boy! Good Dog! ''Good Dog!'' Go on, now! Get your unicycle! Get your unicycle!" ---- The Numeric#days method in RubyOnRails takes the Numeric, multiplies it by the number of seconds in a typical day (ignoring the possibility of DaylightSavingTime shifts and rare LeapSeconds), and returns a Numeric. The Numeric#ago method gets the number of seconds from the beginning of the Unix era until the current time, subtracts the Numeric from it, and returns a Numeric. Hence, 5.days is valid RubyLanguage and it's the number of seconds in 5 typical days. And 5.days.ago is valid Ruby and it's the number of seconds from the beginning of the era until 5 "days" before the current time. (There exists the possibility that this calculation is incorrect on account of politicians arbitrary shifting our clocks and calendars). What I find disturbing is, 5.ago is perfectly valid Ruby (it's the number of seconds from the beginning of the era until 5 seconds before the current time), but it's invalid to the human brain. So is 5.ago.days (a truly large number of seconds != 5.days.ago). So is 5.days.days.days >> 3. So is 17 | 5.days. And, surprisingly, from a "DomainSpecificLanguage" perspective, 17.days.ago.upto 5.days.ago loops a very large number of times, once for each second in 12 days. "Oh," you (whoever you may be) say, "but the ''UnitTest''''''s'' will catch these errors!" Really? When you toss extra methods into a class that never anticipated your "enhancements," what makes you think there's any such thing as a "unit" and how do you come up with test EquivalenceClass''''''es? You know as well as I do that you can't test every permutation and combination of methods and values. So how do you know what tests to write to ensure that 5.days passes, 5.days.ago passes, 5.ago fails, 5.ago.days fails, 5.days.days.days >> 3 fails, and 17 | 5.days fails? "Oh," you say, "but no one will write 5.days.days.days >> 3 because anyone with eyeballs can see from the DomainSpecificLanguage that that's nonsense!" So now you're relying on eyeballs, as if every project under the sun uses CodeReview''''''s and/or PairProgramming and/or enough of EricRaymond's OpenSource eyeballs. I think you've just admitted to the inherent untestability of Ruby code. Welcome to the club. ---- See also ExtraLegsOntoAdog CuteProgramming