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

===============================================================================

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

I personally never release at less than 0.1.0, and often higher than
that.

* 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

That probably should. However, I have also broken that "pattern" from
time when it makes sense. As a specific example, I point to PDF::Writer
1.0 vs. PDF::Writer 1.1 where I had to invert the meaning of certain
values. I do not see a problem with necessarily changing the meaning of
some APIs even in non-major versions. Ideally, this would be done over a
period of time. For example, in the upcoming PDF::Writer 1.1.4, I have
several APIs that warn if the wrong parameters are provided -- but it
fixes the parameters up. In PDF::Writer 1.2, I may cause the wrong
parameters for some of these to fail with a message (particularly those
warnings that have been around since 1.1.0 or 1.1.1).

When PDF::Writer 1.3 comes out, they will *definitely* fail with a
message. By the time PDF::Writer 1.5 comes out, however, these APIs will
be changed and there will be no warnings or messages related to these
items. These are evolutions of a particular API interface.

PDF::Writer 2.0, however, makes no API compatibility guarantees at all.
Yes, they'll likely be the same, but those that change between 1.x and
2.x will be documented but not warned or failed with methods. (This is
particularly true when the new core object model is introduced.)

Similarly, with color-tools 1.3, I removed a number of constants from
the Color namespace and put them in Color::RGB. In 1.3, there is a
const_missing that detects the first time any Color constant is used and
prints a warning. In 1.4, the const_missing warning will appear the
first time each individual Color constant used. In 1.5, I will either
eliminate the const_missing (there have, after all, been two versions of
documentation regarding this) or cause it to print every time an
individual Color constant is accessed.

To me, sane API management isn't about version numbers all of the time.
Going to version 1.0 is an important psychological barrier that should
not just be done because of an incompatible API change. The same applies
from going from 1.0 to 2.0. There is a promise of API maturity when
higher version numbers are applied.

-austin

···

On 10/18/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:
--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

i'm confounded by this:
   * 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

Ok, point taken.

   * 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.

You make a good argument for 2.0.0. Perhaps the example is flawed. My
feeling was that it was unlikely that a method that *always* returned nil
would be used in a value situation, hence returning a value was relatively
safe change to make.

In real life, many API decisions are gray areas where you need to make such
judgement calls (as the rest of your posting points out). However, not using
a clear-cut example in the document is clearly a document bug. I'll address
it when I get a chance.

Thanks for the feedback.

···

On Tuesday 18 October 2005 11:41 am, Ara.T.Howard wrote:

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

thanks for the reply jim. don't get me wrong - i'm not just trying to be
pendatic - i'm in the process of packaging every one of my projects, all 36 of
them, to gems and, therefore, trying to automatic the project. the first task
was figuring out if any of my versions need changing since i was using the
libtool system. all in all the gem version system seems good enough and
addresses my main concern with being able to do

   require_gem 'must_not_break', '~> 2.3'

in production code and know that it'll pick up 2.4 but not 3.0. the trick,
however, is understanding precisely what consitutes re-naming to 2.4, or 3.0!

i had to upgrade to the latest to get '->' operator to work, but have seen
other people say a version back was the way to go. thoughts?

kind regards.

-a

···

On Wed, 19 Oct 2005, Jim Weirich wrote:

On Tuesday 18 October 2005 11:41 am, Ara.T.Howard wrote:

i'm confounded by this: * 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

Ok, point taken.

   * 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.

You make a good argument for 2.0.0. Perhaps the example is flawed. My
feeling was that it was unlikely that a method that *always* returned nil
would be used in a value situation, hence returning a value was relatively
safe change to make.

In real life, many API decisions are gray areas where you need to make such
judgement calls (as the rest of your posting points out). However, not
using a clear-cut example in the document is clearly a document bug. I'll
address it when I get a chance.

--

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

===============================================================================

thanks for the reply jim. don't get me wrong - i'm not just trying to be
pendatic -

No problem ... I really do appreciate the feedback.

i had to upgrade to the latest to get '->' operator to work, but have seen
other people say a version back was the way to go. thoughts?

Umm ... of RubyGems? ... I would recommend the latest and greatest. Why are
some recommending a version back?

···

On Wednesday 19 October 2005 10:01 am, Ara.T.Howard wrote:

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Ara.T.Howard wrote:

i had to upgrade to the latest to get '->' operator to work, but have seen
other people say a version back was the way to go. thoughts?

The only thing I heard that "a version back" was the way to go was Rake
(with people promoting v0.5.4 instead of v0.6), but the issue was
resolved in v0.6.2 so it's a non-issue.