Skip to content

Instantly share code, notes, and snippets.

@cmsd2
Created June 17, 2011 09:09
Show Gist options
  • Save cmsd2/1031105 to your computer and use it in GitHub Desktop.
Save cmsd2/1031105 to your computer and use it in GitHub Desktop.
Revision comparison using y combinator
#!/usr/bin/ruby -w
# Revision class
# Designed to function in version comparisons, where the
# version number matches /\d+(\.\d+)*/
# Author: Chris Dawes, Aupeo GmbH <chris@aupeo.com>
# Based on original by: Hugh Sasse, De Montfort University <hgs@dmu.ac.uk>
# And using: http://weblog.raganwald.com/2007/02/guest-blogger-tom-moertel-derives-y.html
def y(&f)
lambda { |x| x[x] } [
lambda { |yf| lambda { |*args| f[yf[yf]][*args] } } ]
end
class Revision
attr :contents
def self.comp(c)
y { |x| lambda { |a,b| as = a.split(/\./); bs = b.split(/\./); return true if as.empty? && bs.empty?; af = as.shift.to_i; bf = bs.shift.to_i; return af == bf ? x[as.join('.'), bs.join('.')] : c[af, bf] } }
end
def self.rev_gte(a, b)
self.comp(lambda { |x, y| x >= y })[a, b]
end
def self.rev_lte(a, b)
self.comp(lambda { |x, y| x <= y })[a, b]
end
def to_i
@contents.collect { |j| j.to_i }
end
def to_s
astring = @contents.collect { |j| j.to_s }
astring.join(".")
end
def <(x)
!self.class.rev_gte(self.to_s, x.to_s)
end
def >(x)
!self.class.rev_lte(self.to_s, x.to_s)
end
def ==(x)
!((self < x) || (self > x))
end
def initialize(thing)
if thing.class == Revision
@contents = thing.contents.dup
# dup so we get a new copy, not a pointer to it.
elsif thing.class == String
result = thing.split(".").collect {|j| j.to_i}
@contents = result
elsif thing.class == Float
@contents = thing.to_s.split(".").collect {|j| j.to_i}
else
raise "cannot initialise to #{thing}"
end
end
end
if __FILE__ == $0
# Perform RubyUnit tests
require "runit/testcase"
require 'runit/cui/testrunner'
require 'runit/testsuite'
class Testing_class < RUNIT::TestCase
def test_a
a = Revision.new("1.2.3")
assert_equal([1,2,3], a.contents, "initialize failed #{a.contents}")
a = Revision.new("2.4.8")
b = Revision.new(a)
assert_equal([2,4,8], b.contents, "initialize failed #{a.contents}")
a = Revision.new(2.3)
assert_equal([2,3], a.contents, "initialize failed #{a.contents}")
end
def test_equal
a = Revision.new("1.2.1")
b = Revision.new("1.2.1")
assert(a == b, "supposedly identical Revisions are not.")
a = Revision.new("2.2.2")
b = Revision.new("2.2.1")
assert(!(a == b), "supposedly inidentical Revisions are not.")
a = Revision.new("1.0")
b = Revision.new("1.0.0")
assert((a == b), "supposedly identical Revisions are not.")
end
def test_less_than
a = Revision.new("1.2.1")
b = Revision.new("1.2.2")
assert(a < b, " 1.2.1 not less that 1.2.2 ")
a = Revision.new("1.2.2")
b = Revision.new("1.3.2")
assert(a < b, " 1.2.2 not less that 1.3.2 ")
a = Revision.new("1.2.2")
b = Revision.new("2.2.2")
assert(a < b, " 1.2.2 not less that 2.2.2 ")
a = Revision.new("1.2.3.4")
b = Revision.new("1.2.3.5")
assert(a < b, " 1.2.3.4 not less that 1.2.3.5 ")
a = Revision.new("1.2.3")
b = Revision.new("1.2.3.5")
assert(a < b, " 1.2.3 not less that 1.2.3.5 ")
# check comparison is numeric, not lexical
a = Revision.new("1.2.3")
b = Revision.new("1.12.3")
assert(a < b, " 1.2.3 not less that 1.2.3.5 ")
end
def test_greater_than
a = Revision.new("1.2.2")
b = Revision.new("1.2.1")
assert(a > b, " 1.2.1 not less that 1.2.2 ")
a = Revision.new("1.3.2")
b = Revision.new("1.2.2")
assert(a > b, " 1.2.2 not less that 1.3.2 ")
a = Revision.new("2.2.2")
b = Revision.new("1.2.2")
assert(a > b, " 1.2.2 not less that 2.2.2 ")
a = Revision.new("1.2.3.5")
b = Revision.new("1.2.3.4")
assert(a > b, " 1.2.3.4 not less that 1.2.3.5 ")
a = Revision.new("1.2.3.5")
b = Revision.new("1.2.3")
assert(a > b, " 1.2.3 not less that 1.2.3.5 ")
# check comparison is numeric, not lexical
a = Revision.new("1.12.3")
b = Revision.new("1.2.3")
assert(a > b, " 1.2.3 not less that 1.2.3.5 ")
end
end
RUNIT::CUI::TestRunner.run(Testing_class.suite)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment