[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Override methods from a module

Drew Olson

10/15/2007 10:27:00 PM

All -

I'd like to create a module that adds some specific functionality to my
setup and teardown methods within a few of my test cases. I'd like to be
able to do something to the effect of:

module Stuff
method_alias :old_setup, :setup
def setup
old_setup
# new stuff goes here
end
end

class MyTest
include Stuff
def setup
# original setup stuff here
end
end

The above code is just psuedo code and doesn't work for a number of
reasons. However, I was wondering if there is a best practice for this
type of behavior.

Thanks,
Drew
--
Posted via http://www.ruby-....

7 Answers

Phrogz

10/15/2007 10:51:00 PM

0

On Oct 15, 4:27 pm, Drew Olson <olso...@gmail.com> wrote:
> All -
>
> I'd like to create a module that adds some specific functionality to my
> setup and teardown methods within a few of my test cases. I'd like to be
> able to do something to the effect of:
>
> module Stuff
> method_alias :old_setup, :setup
> def setup
> old_setup
> # new stuff goes here
> end
> end
>
> class MyTest
> include Stuff
> def setup
> # original setup stuff here
> end
> end

I've never done this before, but here's a hack that sort of does what
I think you want:

module Foo
def initialize( *args )
self.extend Foo::Stuff
end
module Stuff
def bar
puts "from module"
super
end
end
end

class Bar
include Foo
def initialize( name )
@name = name
super
end
def bar
puts "from class"
end
end

goof = Bar.new( 'Goofy' )
goof.bar
#=> from module
#=> from class

__END__

Note that this requires calling super in the initialize of the source
class, which may break other ancestor modules/classes expecting
different arguments to be passed in. Ideally the module would latch
onto and wrap the initialize method itself when included in the class,
but I was too lazy to do that.

Phrogz

10/15/2007 10:57:00 PM

0

On Oct 15, 4:51 pm, Phrogz <phr...@mac.com> wrote:
> Note that this requires calling super in the initialize of the source
> class, which may break other ancestor modules/classes expecting
> different arguments to be passed in. Ideally the module would latch
> onto and wrap the initialize method itself when included in the class,
> but I was too lazy to do that.

Here's a slightly cleaner version, showing that you can fully wrap the
class method.

module Foo
def initialize( *args )
self.extend Foo::Stuff
end
module Stuff
def bar
puts "before class"
super
puts "after class"
end
end
end

class Bar
include Foo
def initialize
super
end
def bar
puts "in class"
end
end

goof = Bar.new
goof.bar
#=> before class
#=> in class
#=> after class

Drew Olson

10/15/2007 11:10:00 PM

0

Gavin Kistner wrote:
> goof = Bar.new
> goof.bar
> #=> before class
> #=> in class
> #=> after class

Gavin -

Thanks for the responses. I think you're examples are interesting,
however I'm not sure this will help me as my objects are already in
inheritance chains. Ideally, I want to be able to include a module that
will "magically" add functionality to existing methods without forcing
any other code changes. Obviously this presents some challenges.
Initially, I figured I would be able to alias the current method,
override the existing method in the module and then call the original
version when I was done. However, because I was including the module at
the top of the class it was not yet able to see that the setup/teardown
methods existed yet.

Your examples definitely help and may have helped me with another way of
looking at the problem but I'm not sure they'll work as my objects are
already extending parent objects.

- Drew
--
Posted via http://www.ruby-....

Drew Olson

10/15/2007 11:52:00 PM

0

Drew Olson wrote:
> Your examples definitely help and may have helped me with another way of
> looking at the problem but I'm not sure they'll work as my objects are
> already extending parent objects.
>
> - Drew

Sorry about the previous response. Apparently lack of sleep results in
horrendous grammar. Below is the solution I've figured out for the
problem. It seems to work the way I was hoping for. Please let me know
if this makes sense/how this could be improved.

Thanks,
Drew

module Stuff
def self.included mod
@@mixer_class = mod
end

def initialize
super
@@mixer_class.class_eval do
alias_method :old_setup, :setup
alias_method :old_teardown, :teardown

define_method(:setup) do
old_setup
puts "new setup"
end

define_method(:teardown) do
old_teardown
puts "new teardown"
end
end
end
end

class Tester
def common
puts "this is in all tests!"
end
end


class PlainTester < Tester
def setup
puts "plain setup"
end

def teardown
puts "plain teardown"
end
end

class ExtraFunTester < Tester
include Stuff
def setup
puts "old setup"
end

def teardown
puts "old teardown"
end
end

p = PlainTester.new
e = ExtraFunTester.new

p.setup
p.teardown

e.setup
e.teardown
--
Posted via http://www.ruby-....

Dave Rothlisberger

5/14/2008 11:25:00 PM

0

> Please let me know if this makes sense/how this could be improved.
>
> module Stuff
> def self.included mod
> @@mixer_class = mod
> end
>
> def initialize
> super
> @@mixer_class.class_eval do
> alias_method :old_setup, :setup
> alias_method :old_teardown, :teardown
>
> define_method(:setup) do
> old_setup
> puts "new setup"
> end
>
> define_method(:teardown) do
> old_teardown
> puts "new teardown"
> end
> end
> end
> end

A slightly nicer way to write the above:

module Stuff
def self.included(mod)
mod.class_eval do
alias_method_chain :setup, :new_feature
end
end

def setup_with_new_feature
setup_without_new_feature
puts 'new setup'
end

def teardown_with_new_feature
teardown_without_new_feature
puts 'new teardown'
end
end
--
Posted via http://www.ruby-....

Rick DeNatale

5/15/2008 12:43:00 AM

0

On Wed, May 14, 2008 at 7:24 PM, Dave Rothlisberger <dave@rothlis.net> wrote:
>> Please let me know if this makes sense/how this could be improved.
>>
>> module Stuff
>> def self.included mod
>> @@mixer_class = mod
>> end
>>
>> def initialize
>> super
>> @@mixer_class.class_eval do
>> alias_method :old_setup, :setup
>> alias_method :old_teardown, :teardown
>>
>> define_method(:setup) do
>> old_setup
>> puts "new setup"
>> end
>>
>> define_method(:teardown) do
>> old_teardown
>> puts "new teardown"
>> end
>> end
>> end
>> end
>
> A slightly nicer way to write the above:
>
> module Stuff
> def self.included(mod)
> mod.class_eval do
> alias_method_chain :setup, :new_feature
> end
> end
>
> def setup_with_new_feature
> setup_without_new_feature
> puts 'new setup'
> end
>
> def teardown_with_new_feature
> teardown_without_new_feature
> puts 'new teardown'
> end
> end

alias_method_chain, nice as it is, is part of activesupport (i.e. part
of Rails) and is not standard Ruby.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

Rick DeNatale

5/15/2008 12:56:00 AM

0

On Mon, Oct 15, 2007 at 7:52 PM, Drew Olson <olsonas@gmail.com> wrote:
> Drew Olson wrote:
>> Your examples definitely help and may have helped me with another way of
>> looking at the problem but I'm not sure they'll work as my objects are
>> already extending parent objects.
>>
>> - Drew
>
> Sorry about the previous response. Apparently lack of sleep results in
> horrendous grammar. Below is the solution I've figured out for the
> problem. It seems to work the way I was hoping for. Please let me know
> if this makes sense/how this could be improved.
>
> Thanks,
> Drew
>
> module Stuff
> def self.included mod
> @@mixer_class = mod
> end
>
> def initialize
> super
> @@mixer_class.class_eval do
> alias_method :old_setup, :setup
> alias_method :old_teardown, :teardown
>
> define_method(:setup) do
> old_setup
> puts "new setup"
> end
>
> define_method(:teardown) do
> old_teardown
> puts "new teardown"
> end
> end
> end
> end
>
> class Tester
> def common
> puts "this is in all tests!"
> end
> end
>
>
> class PlainTester < Tester
> def setup
> puts "plain setup"
> end
>
> def teardown
> puts "plain teardown"
> end
> end
>
> class ExtraFunTester < Tester
> include Stuff
> def setup
> puts "old setup"
> end
>
> def teardown
> puts "old teardown"
> end
> end

Not a general solution:

module Stuff
def self.included mod
@@mixer_class = mod
end

def initialize
super
@@mixer_class.class_eval do
alias_method :old_setup, :setup
alias_method :old_teardown, :teardown

define_method(:setup) do
old_setup
puts "new setup"
end

define_method(:teardown) do
old_teardown
puts "new teardown"
end
end
end
end

class Tester
def common
puts "this is in all tests!"
end
end


class PlainTester < Tester
def setup
puts "plain setup"
end

def teardown
puts "plain teardown"
end
end

class ExtraFunTester < Tester
include Stuff
def setup
puts "old setup"
end

def teardown
puts "old teardown"
end
end

class FunkedUpTester < Tester
include Stuff
def setup
puts "old setup"
end

def teardown
puts "old teardown"
end
end


p = PlainTester.new
e = ExtraFunTester.new
f = FunkedUpTester.new

puts "p.setup"
p.setup
p.teardown

puts "e.setup"
e.setup
e.teardown

puts "f.setup"
f.setup
f.teardown

RubyMate r8136 running Ruby r1.8.6
(/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby)
>>> untitled

p.setup
plain setup
plain teardown
e.setup
old setup
old teardown
f.setup
SystemStackError: stack level too deep

method setup in untitled document at line 13
method old_setup in untitled document at line 13
method setup in untitled document at line 13
method old_setup in untitled document at line 13
method setup in untitled document at line 13
method old_setup in untitled document at line 13
method setup in untitled document at line 13
,