From: "Ara.T.Howard" Date: 2005-10-19T00:41:07+09:00 Subject: gem versioning confusion i'm confounded by this: http://docs.rubygems.org/read/chapter/7 section 6.5: 6.5 Examples Let's work through a project lifecycle using our Stack example from above. [eg 0] * Version 0.0.1: The initial Stack class is release. [eg 1] * Version 0.0.2: Switched to a linked=list implementation because it is cooler. [eg 2] * Version 0.1.0: Added a depth method. [eg 3] * Version 1.0.0: Added top and made pop return nil (pop used to return the old top item). [eg 4] * Version 1.1.0: push now returns the value pushed (it used it return nil). [eg 5] * Version 1.1.1: Fixed a bug in the linked list implementation. [eg 6] * Version 1.1.2: Fixed a bug introduced in the last fix. two of these are quite confusing: * Version 0.0.1: The initial Stack class is release. why on earth would all digits begin numbering at zero __except__ build? seems that it should be version 0.0.0 * Version 1.1.0: push now returns the value pushed (it used it return nil). why would this not be 2.0.0 - remembering we're at 1.0.0 from 'eg 3'? clearly code which had done values.inject(stack){|accum, val| accum.push val or accum} would no longer work. i realize this is contrived - but changing a return type is a red flag for non-backward compatibility and the handling in this example seems at odds with that of 'eg 3'. the fact that a return value changed from 'nothing' to 'something' is still a change that some code somewhere may depend upon right? or am i missing something? bang methods are particularly sensitive to this exact kind of change, here is a less contrived example: version : 0.0.0 class List def uniq! u = [] each{|elem| u << elem unless u.include? elem} changed = u.size == self.size self.replace u changed ? self : nil end end list = List[ 42 ] list.uniq! or abort "nothing done!" clearly you cannot promise backwards compatiblity via version : 0.1.0 class List def uniq! u = [] each{|elem| u << elem unless u.include? elem} changed = u.size == self.size self.replace u self end end list = List[ 42 ] list.uniq! or abort "nothing done!" the second __must__ become verion 1.0.0 since the implementation has changed in an incompatible way. in short - method input/output changes __always__ require a major increment unless done extremely carefully as in def notify file end def notify file, lineno = nil end but even this is risky (and wrong) since the client code may well have coded something like this def notify lexer meth = begin lexer.method 'notify' rescue NameError raise "bad lexer object" end case meth.arity when 1 lexer.notify __file__ when -2 lexer.notify __file__, __lineno__ else raise "bad lexer object" end end which is all a fantastic reason to write methods with signatures like: def required_argument, options = {} end since you can accept new options without breaking your method signature -- though you still have to worry about return values, nil or otherwise... can anyone shed some light in here? thanks. -a -- =============================================================================== | email :: ara [dot] t [dot] howard [at] noaa [dot] gov | phone :: 303.497.6469 | anything that contradicts experience and logic should be abandoned. | -- h.h. the 14th dalai lama ===============================================================================