instiki/vendor/plugins/rexml/lib/require_with_version.rb
Jacques Distler 38ae064b8a Bundle Latest REXML
Sam Ruby has been doing a bang-up job fixing the bugs in REXML.
Who knows when these improvements will trickle down to vendor distributions of Ruby.
In the meantime, let's bundle the latest version of REXML with Instiki.
We check the version number of the bundled REXML against that of the System REXML, and use whichever is later.
2008-01-11 23:53:29 -06:00

167 lines
6.9 KiB
Ruby

# FUNDAMENTAL FLAWS:
# The order of $: must be preserved. Therefore, there are two sort
# criteria: Versioned files are sorted high; after that, the sort order
# is the order of $:.
# We must preserve the load path; if rexml-2.4 is required in one place,
# all rexml/* packages should be loaded from there.
############################################################################
# #
# This is based on Phil Tomson's #
# ("ptkwt!shell1#aracnet#com".tr("!#","@.")) #
# code. The changes I made are: #
# 1) The Version class is now a member of the Kernel module, to avoid #
# name space conflicts. #
# 2) Version::to_s() returns the original string, not a comma-separated #
# string. #
# 3) The versioning is package based, not file based. In fact, with #
# this, you can't version individual files. AFAIC, this is better, #
# since versioning on individual files is much more tedious than #
# package-based versioning, and it is arguably less useful and less #
# commonly desired. #
# 4) Versions can have arbitrary length. EG: 2.7 < 2.7.1, and "2" #
# matches any version that starts with "2", such as "2.5.2.6.7" #
# #
# The rules are these: #
# 1) All of the locations in $: will be searched #
# 2) The highest version of the package found that satisfies the #
# requirements will be used. #
# 3) If there is no versioned package, or no version matches, we default #
# to the normal Ruby require mechanism. This maintains backward #
# compatible behavior. #
# 4) The packages must be installed as foo-x.y.z. The cardinality of #
# the version is not significant, and packages that do not match this #
# naming pattern match by default. #
# #
# Rule (1) and (2) mean that the highest matching version anywhere in #
# the search path will be used. Rule (3) and (4) mean that even if #
# packages are not installed with this naming convention, programs that #
# use require_version will still work. #
# #
# Usage: #
# To use this, require this module. Then use require_with_ver, instead #
# of require in your files. #
# #
# Examples: #
# require_version('rexml/document'){|v| v > '2.0' and v < '2.5'} #
# require_version('rexml/document'){|v| v > '2.0'} #
# require_version('rexml/document') #
# require_version('rexml/document'){|v| v > '2.0'} #
# require_version('rexml/document'){|v| v >= '1.0' and v < '2.0'} #
# require_version('rexml/document'){|v| v >= '1.0' and v < '2.0' and #
# v != '1.7'} #
# require_version('rexml/document'){|v| (v >= '1.0' and #
# v < '2.0' and #
# v != '1.7') or #
# v == '3.0.1'} #
# require_version('rexml/document'){|v| v.to_s =~ /^2.[02468]/} #
# #
############################################################################
module Kernel
#########################################################
# Version - takes a string in the form: 'X1.X2.X3...Xn' #
# (where 'Xn' is a number) #
#########################################################
class Version
include Comparable
def initialize(str)
@vs = str.split('.').map!{|i| i.to_i}
end
def [](i)
@vs[i]
end
def to_s
@vs.join('.')
end
def <=>(other)
if other.class == String
other = Version.new(other)
end
@vs.each_with_index { |v,i|
return 1 unless other[i]
unless v == other[i]
return v <=> other[i]
end
}
return 0
end
end
alias :old_require :require
@@__versioned__ = {}
def require(file,&b)
path = file.split('/')
root = path[0]
rest = path[1..-1].join('/')
unless @@__versioned__[root]
package = File.dirname( file )
files = []
$:.each {|dir|
if File.exists? dir
fileset = Dir.new(dir).entries.delete_if {|f|
fpath = File.join( dir, f )
!(File.directory?(fpath) and f =~ /^#{root}(-\d(\.\d)*)?$/)
}
fileset.collect!{ |f| File.join( dir, f ) }
files += fileset
end
}
if files.size > 0
@@__versioned__[root] = files.uniq.sort{|x,y|
File.basename(x) <=> File.basename(y)
}
@@__versioned__[root].reverse!
else
@@__versioned__[root] = [root]
end
end
base = @@__versioned__[root][0]
if b #block_given?
p @@__versioned__[root]
base = @@__versioned__[root].delete_if { |f|
l = File.basename(f)
l.include?('-') and yield( Version.new( l.split('-')[1] ) ) and
Dir.new(f).entries.include?( rest+".rb" ) ? false : true
}
p base
base = base[0]
end
#old_require "#{base}/#{rest}"
puts <<-EOL
old_require "#{base}/#{rest}"
EOL
end
end
#=begin
# For testing
if $0 == __FILE__
$: << "./"
puts "\n\nv > '2.0' and v < '2.5'"
require('rexml/document'){|v| v > '2.0' and v < '2.5'}
puts "\n\nv > '2.0' and v < '3'"
require('rexml/document'){|v| v > '2.0' and v < '3'}
=begin
puts "\n\nv > '2.0'"
require('rexml/document'){|v| v > '2.0'}
require('rexml/document')
puts "\n\nv > '2.0'"
require('rexml/document'){|v| v > '2.0'}
puts "\n\nv >= '1.0' and v < '2.0'"
require('rexml/document'){|v| v >= '1.0' and v < '2.0'}
puts "\n\nv >= '1.0' and v < '2.0' and v != '1.7'"
require('rexml/document'){|v| v >= '1.0' and v < '2.0' and v != '1.7'}
require('rexml/document'){|v| (v >= '1.0' and
v < '2.0' and
v != '1.7') or
v == '3.0.1'}
puts "\n\nv.to_s =~ /^2.[02468]/"
require('rexml/document'){|v| v.to_s =~ /^2.[02468]/}
require('rexml/parsers/baseparser' )
=end
end
#=end