Multiple .rb versions support

The following is just some thoughts I’ve had for setting up a structure
to install multiple versions of the same file on a machine, and support
loading them with the standard require statement. There have been versioning
ideas in the past that pursued a custom requires statement (Dave Thomas
posted one such idea: http://ruby-talk.org/8816). I’m not at all against
ideas like these - it gives the opportunity to be as flexible and clear
as need be. But, I decided to pursue what it would look like working within
the constraints of the existing require statement.

Plus, many of these other threads discussed dependency issues without
addressing having multiple versions installed at the same time.

I’m certainly not in love with everything written here – it’s just what
I’ve come up with so far and thought it needed a good pounding - pound
away :slight_smile:

My idea revolves around a build script that would autogenerate a directory
hierarchy in order to support various require statements. Here’s what
the structure would look like for version 1.0.0 of file.rb:

lib
-file
-file.rb ← finds latest x.x.x version
-1.0.rb ← finds latest 1.x.x version
-1.0.0.rb ← finds latest 1.0.x version
-1
-0
-0
-file.rb ← actual 1.0.0 code

If you think that’s ugly, well, here’s what it would look like with 7
versions of the same file installed:

lib
-file
-file.rb ← finds latest x.x.x version
-1.0.rb ← finds latest 1.x.x version
-1.0.0.rb ← finds latest 1.0.x version
-1.1.rb ← finds latest 1.x.x version
-1.1.0.rb ← finds latest 1.1.x version
-1.1.1.rb ← finds latest 1.1.x version
-2.0.rb ← finds latest 2.x.x version
-2.0.0.rb ← finds latest 2.0.x version
-2.0.1.rb ← finds latest 2.0.x version
-2.1.rb ← finds latest 2.x.x version
-2.1.0.rb ← finds latest 2.1.x version
-2.1.1.rb ← finds latest 2.1.x version
-1

-0

-0
-file.rb ← actual 1.0.0 code
-1
-0
-file.rb ← actual 1.1.0 code
-1
-file.rb ← actual 1.1.1 code
-2
-0
-0
-file.rb ← actual 2.0.0 code
-1
-file.rb ← actual 2.0.1 code
-1
-0
-file.rb ← actual 2.1.0 code
-1
-file.rb ← actual 2.1.1 code

(The above sample directory structure is referred to in the rest of this
text)

While ugly, it does allow the following require statements without any
modification:

require ‘lib/file’
require ‘lib/file/1.0’
require ‘lib/file/1.0.0’
require ‘lib/file/1.1.0’
require ‘lib/file/1.1’
require ‘lib/file/2.0’
require ‘lib/file/2.0.0’

Now another ugly bit. My first idea was that if you specified all three
members of the version structure, that would be an exact match, even if
other
newer versions were installed. But after more thought and reading of other
threads, I decided that all of these would be an ‘at least’ version. Here
are the same require statements with the version they would load.

all of these statements are to be read as, load ‘at least’ this version

with the restriction that you cannot go beyond the parent of the last

member specified

require ‘lib/file’ # loads lib/file/2/1/1/file.rb
require ‘lib/file/1.0’ # loads lib/file/1/1/1/file.rb
require ‘lib/file/1.0.0’ # loads lib/file/1/0/0/file.rb
require ‘lib/file/1.1.0’ # loads lib/file/1/1/1/file.rb
require ‘lib/file/1.1’ # loads lib/file/1/1/1/file.rb
require ‘lib/file/2.0’ # loads lib/file/2/1/1/file.rb
require ‘lib/file/2.0.0’ # loads lib/file/2/0/1/file.rb

  • why does require ‘…/1.1.0’ load 1.1.1 instead of 1.1.0?

Think about this case:

If 1.0.9 is installed – then I need a new feature in 1.1.0, I want to
require 1.1.0 to make sure it’s there, because the feature I use is not in
1.0.9. But – do I want by default this to limit me to 1.1.0 only when 1.1.1
gets installed vs. 1.1.0 or greater? Probably by default I’m just specifying
‘at least’ 1.1.0.

But what about when I want to specify only 1.1.0 and not go greater? Then
require the literal file:

require ‘lib/file/1/1/0/file’

  • why does require ‘…/1.0’ load 1.1.1 and require ‘…/1.0.0’ load 1.0.0?

Same answer as above essentially.

In order to have the by default behavior be to assume ‘at least’ this
instead of exact, plus have some control so ‘at least’ doesn’t go too far,
the at least behavior will not go one increment higher in the parent
version member.

Given the earlier sample dir structure:

“You said 1.0.0, I’m not going to go to anything beyond 1.0.x” => 1.0.0
“You said 1.0, I’m not going to go to anything beyond 1.x” => 1.1.1
“You said 2.0.0, I’m not going to go to anything beyond 2.0.x” => 2.0.1
“You said 2.0, I’m not going to go to anything beyond 2.x” => 2.1.1

  • why not have require ‘…/1.1.0’ be an exact match? Seems clearer.

I agree that it is clearer, but it’s less flexible. There’s no way to offer
on ‘at least’ solution then without customizing the require statement. By
having the ‘at least’ setup, there’s still a way to specify the exact file
by this requires:

require ‘lib/file/1/1/0/file’

This is less desirable from the standpoint of writing the requires
statements and expected results, but it still achieves the original goal
of avoiding a custom requires statement.

  • won’t this perform poorly?

It might. If it’s too poor, then the whole idea is bunk. If you are
concerned about speed, then you can optimize by switching to
requiring the exact path:

require ‘lib/file/1/1/0/file’

  • what about require ‘lib/file/1’ ?

These are not supported as they are redundant:

require ‘lib/file/1’ # loads lib/file/2/1/1/file.rb
require ‘lib/file/2’ # loads lib/file/2/1/1/file.rb

Equivalent to:

require ‘lib/file’

Chris
http://clabs.org

“Chris Morris” chrismo@clabs.org wrote in message news:00f101c2580d$8b586380$22973acc@ntossu2lch

My idea revolves around a build script that would autogenerate a directory
hierarchy in order to support various require statements.

In general, I don’t like seeing versioning managed in the filesystem,
because it adds alot of clutter.

What is the problem that this functionality would solve?

Please humor me, I’ve just never had a problem with Ruby that begged
for integrated versioning.

~ Patrick

In general, I don’t like seeing versioning managed in the filesystem,
because it adds alot of clutter.

What is the problem that this functionality would solve?

Please humor me, I’ve just never had a problem with Ruby that begged
for integrated versioning.

For example, I have an XmlSerialization lib that currently depends on REXML,
and I’ve only tested it with version 1.2.5. Until I get time to verify it
with later versions (I know, 1.2.5 has been gone for quite some time,
there’s internet time and then there’s open source project volunteer time)
If there was a common versioning scheme that REXML used, I could build that
into my xmlserial lib to require an older version. Or … a better example,
if I knew there was a bug in 1.2.4 that caused my lib to fail, then I could
require 1.2.5 or better be used to make sure my stuff worked.

Now … REXML has its own VERSION constant in there I can check – so my
approach is still doable, it just might be nice to have a standard way of
handling dependencies.

Now … let’s say I have need to run the latest REXML for another app I’m
writing, but need to use the latest version. It’d be nice to be able to have
both 1.2.5 installed to support my existing applications, which I don’t have
time to upgrade, plus the latest version for the new app.

.Net has a way of supporting multiple versions of assemblies (.dlls,
essentially) to avoid DLL-Hell, a common Windows problem with multiple
programs overwriting shared .dlls (like msvcrt.dll) in the system directory.

Chris

I have three comments so far:

You could also look at how linux distributions version their .so
files. It’s been used and working much longer than .NET has been
around. (Although the linux way somewhat depends on the fs sanely
supporting symlinks.)

Instead of implicit naming schemes in the file path, maybe you should
just add an optional parameter or two to require (or introduce
vrequire). What the backend does to locate and store files can be
completely transparent to the user. The same with the logic to install
ruby libraries. You would have mkmf install targets automatically
handle backend versioning schemes.

I’m still not sure its worth the work. How would you resolve libraries
depending on libraries of conflicting versions? Wouldn’t the module
and class constants from different versions collide?

  • alan
···

On Tue, Sep 10, 2002 at 07:18:20AM +0900, Chris Morris wrote:

In general, I don’t like seeing versioning managed in the filesystem,
because it adds alot of clutter.

What is the problem that this functionality would solve?

Please humor me, I’ve just never had a problem with Ruby that begged
for integrated versioning.

.Net has a way of supporting multiple versions of assemblies (.dlls,
essentially) to avoid DLL-Hell, a common Windows problem with multiple
programs overwriting shared .dlls (like msvcrt.dll) in the system directory.

Chris


Alan Chen
Digikata LLC
http://digikata.com

You could also look at how linux distributions version their .so
files. It’s been used and working much longer than .NET has been
around. (Although the linux way somewhat depends on the fs sanely
supporting symlinks.)

… and I’m a Windows-er primarily, so perhaps this wouldn’t work. Dunno,
I’ll take a look. (.NET is a similar approach to what I’ve put together.
There’s more to it, but they also have an explorer plug-in that presents a
flat file view of the hierarchy).

Instead of implicit naming schemes in the file path, maybe you should
just add an optional parameter or two to require (or introduce
vrequire).

It was probably buried in my original post, but I was interested in seeing
what a solution would look like that would not require a new require or an
altered one. I know one of the strengths of Ruby is being able to redefine
or tack on our own behavior here, and ultimately I’m pretty sure this would
be a better solution … but I just wanted to see what could be done with
the current require and see how feasible a solution it would be.

With a custom require, then each file would need to require the custom
require library first. So, from that point of view, which is simpler?

require ‘verrequire’
vrequire ‘lib’, AT_LEAST, 1.5

or

require ‘lib/1.5’

It seems in this one case, the latter is more straightforward, but it’s also
less clear. Does it mean exactly 1.5 or at least? On the other hand do I
want to type “require ‘verrequire’” at the top of every file I use? And if
ship this off to someone else, I’ve got one more thing to install. Is that a
big deal? Eh, could be, may not matter at all if I’m already installing
other supporting files.

I’m still not sure its worth the work. How would you resolve libraries
depending on libraries of conflicting versions? Wouldn’t the module
and class constants from different versions collide?

You mean if
A → B
A → C
B → D v.1
C → D v.2

That would be a problem … but that would be a problem regardless of what
version loading mechanism was in place. The problem’s not any easier without
a version loading mechanism – a version loading structure could at least
help track down the problem here.

Chris