How to call super with different arguments

Some code:

class Awk < String
include Comparable
@@intpattern = /^\s*[-+]?\d+$/
@@casesensitive = false

def self.casesensitive(val)
@@casesensitive = val
end

def initialize(str)
super(str.to_s)
end

def <=>(other)
if @@intpattern =~ self && @@intpattern =~ other
to_i <=> other.to_i
else
if @@casesensitive
super
else
# What to use here ?
String.new(downcase) <=> String.new(other.downcase)
end
end
end

def ==(other)
(self <=> other) == 0
end
end

a = Awk.new(‘a’)
b = Awk.new(‘A’)

if a == b
puts 'equal’
end
a = Awk.new(‘21’)
b = Awk.new(‘3’)
if a > b
puts "OK"
end

The problem I’m having is in the case insensitive case: it’s somehow
asymmetrical: while to_i return an integer, to_s does not return a
String but an Awk (as does downcase).
This means I can’t use
downcase <=> other.downcase
as I’s like to. Is it possible to use some variation of super ?
The above construct works, but strikes me as expensive and clumsy.

Han Holl

Seems unlikely, since ‘downcase’ returns a different object; <=> is an
instance method so it needs to operate on ‘self’

Personally I’ve become very sold on ‘has_a’ rather than ‘is_a’
relationships. Taking this approach literally in your case gives:

require ‘delegate’
class Awk < DelegateClass(String)
include Comparable
@@intpattern = /^\s*[-+]?\d+$/
@@casesensitive = false

def self.casesensitive(val)
  @@casesensitive = val
end

def initialize(str)
  super(str)
end

def <=>(other)
  if @@intpattern =~ to_s && @@intpattern =~ other.to_s
    to_i <=> other.to_i
  elsif @@casesensitive
    to_s <=> other.to_s
  else
    to_s.downcase <=> other.to_s.downcase
  end
end  

end

However, I can certainly see why you’d want to subclass String in this
example: it just seems the obvious thing to do, and you might be concerned
about the overhead of delegation.

But in general, I find that having an instance variable (it’s “@obj” in the
above example) even for a simple wrapper class that just adds a couple of
methods and delegates everything else can actually be extremely useful. It
allows your wrapper to take on the characteristics of @obj whatever it is,
rather than being restricted to String (say).

A similar result - adding a few methods and leaving everything else
unchanged - can be obtained with singleton classes. How about this approach:

module Awk
@@intpattern = /^\s*[-+]?\d+$/
@@casesensitive = false

def self.casesensitive(val)
  @@casesensitive = val
end

def <=>(other)
  if @@intpattern =~ to_s && @@intpattern =~ other.to_s
    to_i <=> other.to_i
  elsif @@casesensitive
    to_s <=> other.to_s
  else
    to_s.downcase <=> other.to_s.downcase
  end
end
def ==(other)
  (self <=> other) == 0
end

end

a = ‘a’
a.extend Awk
b = ‘A’
b.extend Awk

if a == b
puts ‘equal’
end
a = ‘21’.extend Awk
b = ‘3’.extend Awk
if a > b
puts “OK”
end

That looks pretty neat to me…

Regards,

Brian.

···

On Fri, Mar 28, 2003 at 07:02:09AM +0900, Han Holl wrote:

The problem I’m having is in the case insensitive case: it’s somehow
asymmetrical: while to_i return an integer, to_s does not return a
String but an Awk (as does downcase).
This means I can’t use
downcase <=> other.downcase
as I’s like to. Is it possible to use some variation of super ?

A similar result - adding a few methods and leaving everything else
unchanged - can be obtained with singleton classes. How about this approach:

Actually that didn’t work when case-sensitive was true, because of the loop
on <=>, but in this case you can use super:

def <=>(other)
if @@intpattern =~ self && @@intpattern =~ other
to_i <=> other.to_i
elsif @@casesensitive
super
else
downcase <=> other.downcase
end
end

Instead of ‘super’ you can do ‘dup <=> other’. Note that ‘downcase’ also
duplicates the object: it’s the same as dup.downcase!

It seems that duplicating an object of a singleton class gives you an object
of the original class, which is fortunately exactly what we needed. I
suppose if it didn’t, it would no longer be a singleton :slight_smile:

Regards,

Brian.

Hmm. With all that excitement about singleton classes, I forgot the most
basic way of getting access to a method which you have overridden: alias.

Taking the original code from [ruby-talk:68080], probably all you wanted was

alias :oldcmp <=>
def <=>(other)
if @@intpattern =~ self && @@intpattern =~ other
to_i <=> other.to_i
else
if @@casesensitive
super
else
downcase.oldcmp(other.downcase)
end
end
end

I will try to think twice before posting in future… it’s just that the
posting helps me to think :slight_smile:

Regards,

Brian.

Brian Candler wrote:

Hmm. With all that excitement about singleton classes, I forgot the most
basic way of getting access to a method which you have overridden: alias.

Taking the original code from [ruby-talk:68080], probably all you wanted was

alias :oldcmp <=>
def <=>(other)
if @@intpattern =~ self && @@intpattern =~ other
to_i <=> other.to_i
else
if @@casesensitive
super
else
downcase.oldcmp(other.downcase)
end
end
end

Yes! Indeed, this was all I wanted.
I don’t know why I keep forgetting about alias.

Thanks a lot.

Cheers,

Han Holl

···