[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Tricks with method as superclass

Clifford Heath

1/31/2007 5:33:00 AM

I'm intrigued by the way Camping uses a method invocation to
serve as a superclass, and while exploring, possibly found a
bug? At any rate, some behaviour I can't explain... but first
a simple example of what Camping does:

class Foo
def initialize(*args)
puts "Initializing Foo"
end
end
=> nil

def Foo(*args)
puts args.inspect
Class.new(Foo) # Interesting that this works...
end
=> nil

class Bar < Foo("Hi there!")
end
["Hi there!"]
=> nil

Bar.new
Initializing Foo
=> #<Bar:0x2f84358>

Ok, all's well, and it's basically what Camping does.
Now what if I want to pass a *block* to the method
that will return my superclass?

def Foo(*args, &block)
puts args.inspect
block.call if block
Class.new(Foo)
end
=> nil

class Bar < Foo("Hi There!") {
puts "Surprise!"
}
end
["Hi there!"]
Surprise!
=> nil

Cool, that worked well. Now I want to keep the args and block passed
that was passed to the method, so I tried the following, which makes
an anonymous subclass of Foo, and Bar is a subclass of that:

class Foo
def initialize(*args)
puts "Initializing Foo"
end
end
=> nil

def Foo(*args, &block)
puts args.inspect
block.call if block
Class.new(Foo).instance_eval <<-END
def initialize
# I'm going to do something with block and args here
puts "Double surprise!"
end
self
END
end
=> nil

class Bar < Foo("Hi there!") {
puts "Surprise!"
}
end
["Hi there!"]
Surprise!
=> nil

Bar.new
Initializing Foo
=> #<Bar:0x2f776b0>

What happened to my Double surprise? It seems that instance_eval hasn't
caused Bar.new to use my extra layer of initialize... The instance of
Bar has the anonymous superclass in it's hierarchy, but the initialize()
doesn't appear in the call stack. What gives here?

Clifford Heath.

5 Answers

Ara.T.Howard

1/31/2007 5:51:00 AM

0

Max Muermann

1/31/2007 5:59:00 AM

0

> Cool, that worked well. Now I want to keep the args and block passed
> that was passed to the method, so I tried the following, which makes
> an anonymous subclass of Foo, and Bar is a subclass of that:
>
> class Foo
> def initialize(*args)
> puts "Initializing Foo"
> end
> end
> => nil
>
> def Foo(*args, &block)
> puts args.inspect
> block.call if block
> Class.new(Foo).instance_eval <<-END
> def initialize
> # I'm going to do something with block and args here
> puts "Double surprise!"
> end
> self
> END

This is really cool. Regarding the double surprise problem, class_eval
instead of instance_eval (and a call to super) does the trick:

Class.new(Foo).class_eval <<-END
def initialize
super
# I'm going to do something with block and args here
puts "Double surprise!"
end
self
END

=> ["Hi there!"]
Surprise!
Initializing Foo
Double surprise!

--max

Clifford Heath

1/31/2007 6:26:00 AM

0

ara.t.howard@noaa.gov wrote:
> # Class.new(Foo).instance_eval <<-END
> Class.new(Foo).module_eval <<-END

Thanks Ara - super-quick response too! Caught out by the singleton,
which I didn't expect to find on a Class instance. Here's a complete
example that shows most of what's possible, assuming the attachment
comes through.

Clifford Heath.
# Start with a real base class
class Base
def initialize(*args, &block)
# puts "Base::initialize #{args.inspect} &#{block.inspect}"
print("Base::")
block.call(*args) if block
end

# Return a new anonymous class derived from Base, with class variables and initialize
def self.augment(*args, &block)
Class.new(self).class_eval {
@@_base_args = args
@@_base_block = block
# puts "new class is #{self.object_id} #{args.inspect} &#{block.inspect}"

def initialize(*args, &block)
# puts "Constructing instance with base #{@@_base_args.inspect} &#{@@_base_block.inspect}, instance #{args.inspect} &#{block.inspect}"
@@_base_block.call(*@@_base_args) if @@_base_block
super(*args, &block)
end

self
}
end
end

# Here's the method used to create the custom base class:
def Base(*args, &block)
puts "Defining funky magic with #{args.inspect} &#{block.inspect}"

Base.augment(*args, &block)
end

# A subclass with neither args nor block:
class Sub0 < Base
end
Sub0.new { puts "No augmented class, use Base alone" }

# A subclass with just args:
class Sub1 < Base("arg1")
end
Sub1.new("Thinking...") { |*args| puts "#{args.inspect}: Ok" }

# A subclass with just a block:
class Sub2 < Base { print "Hi" }
end
Sub2.new { puts " there!" }

# A subclass with args and a block:
class Sub3 < Base("arg1", "arg2") { |*args|
puts "#{args.inspect}: getting things all ready for you..."
}

def initialize(*args, &block)
super(*args, &block)

puts "#{args.inspect}: fun for all the family!"
end
end

Sub3.new("sub1", "sub2") { |*args|
puts "#{args.inspect}: still working"
}

Clifford Heath

1/31/2007 6:55:00 AM

0

Max Muermann wrote:
> This is really cool.

Thanks, I agree. Ara beat you to it, but I thought I'd
post a simpler example too. The cute thing about this is
that you can use method_missing magic to make the attached
block use a different DSL than the class body.

$ cat superclass-method-simple.rb
class Foo
def initialize(*args)
puts "Initializing a Foo"
end
end

def Foo(*args, &block)
Class.new(Foo).class_eval <<-END
@@_args = args
@@_block = block
def initialize
super
puts @@_args.inspect
@@_block.call if @@_block
end
self
END
end

class Bar < Foo("Hi there!") {
puts "Surprise!"
}
end

puts "All ready, here goes:"
Bar.new

$ ruby superclass-method-simple.rb
All ready, here goes:
Initializing a Foo
["Hi there!"]
Surprise!
$

Cute, huh? :-)

Steve de Mena

6/9/2013 3:35:00 AM

0

On 6/7/13 11:15 PM, Matthew B. Tepper wrote:

> Even before the rise of the internet, I found it was more cost-effective to
> buy HMUSA-distributed labels from the UK, as the savings (plus the absence
> of VAT) more than made up for shipping charges. And by planning purchases
> around the appearances of Borders 40%-off coupons, I was able to assemble
> the 1955 Keilberth Bayreuth "Ring" for what I considered an okay price.
> This may make me the man who "knows the price of everything and the value
> of nothing," but I like to save money, so sue me. Capitalism at work!
>
> Matthew, "I'll make it up in quantity!"
>

If your point is that if HM USA never printed catalogs (in COLOR!) and
threw those $100,000 yacht parties (which I'm sure included hookers
and cocaine served on silver platters), and charged $3 less wholesale
for their CDs, their balance sheets at the end of the year would have
shown more profit, I don't know how any of us can agree or disagree,
as we simply don't have the numbers available to us to know what their
profits are.

Steve