Reverting module changes

Hi,

I want to mock standard Kernel.system with my own method and then
revert back to the original version. I was able to do the former, but
not the latter. Currently I’m overwriting Kernel.system method
definition with:

def system_should_return what
Kernel.module_eval “def system(*args) #{what.inspect} end”
end

I don’t a have idea how going back to the original state can be done.
Thanks in advance for any help on this.

Cheers,
mk

On 12/23/06, Michal K. [email protected] wrote:

I don’t a have idea how going back to the original state can be done.
Thanks in advance for any help on this.

Maybe there’s something more elegant. But I’d first alias the
original method, redefine it, then when I was done, I’d redefine it
again to just call the alias.
-Mat

Mat S. wrote:

I don’t a have idea how going back to the original state can be done.
Thanks in advance for any help on this.

Maybe there’s something more elegant. But I’d first alias the
original method, redefine it, then when I was done, I’d redefine it
again to just call the alias.

Thanks, aliasing was what I was looking for. Does code below look OK or
can be improved?

def system_should_return what
Kernel.module_eval <<-EOV
alias_method :orig_system, :system
def system(*args)
#{what.inspect}
end
EOV
end

def restore_system_behaviour
Kernel.module_eval <<-EOV
def system()
orig_system
end
EOV
end

Cheers,
mk

Hi –

On Sun, 24 Dec 2006, Michal K. wrote:

def system_should_return what
def system()
orig_system
end
EOV
end

I like to avoid the string version of module_eval (and similar), and
use the block version instead, where possible. To do that with your
code, you could do:

def system_should_return(what)
Kernel.module_eval do
alias_method :orig_system, :system
define_method(:system) {|*args| what.inspect }
end
end

def restore_system_behaviour
Kernel.module_eval do
alias_method :system, :orig_system
end
end

Note also that there’s a library, available via RAA, that lets you do
temporary changes to core behaviors:

http://raa.ruby-lang.org/project/import_module/

Actually there a couple of such libraries (including my Ruby
Behaviors), but that one is the most full-featured. It might come in
handy if you do a lot of this or need thread safety.

David

[email protected] wrote:

def restore_system_behaviour
Kernel.module_eval do
alias_method :system, :orig_system
end
end

I’ve tried to make a more versatile version of this that works for any
module and method. I’ve ended up with the following code:

class Module
class Stub
def initialize procedure
@procedure = procedure
end

def and_return value
  @procedure.call value
end

end

def override! method
Stub.new(lambda do |value|
alias_method((“orig_” + method.to_s).to_sym, method)
define_method(method) { value }
end)
end

def restore! method
alias_method(method, (“orig_” + method.to_s).to_sym)
end
end

With this code, to override the system method you can write (syntax
inspired by RSpec):

Kernel.override!(:system).and_return false

and then to restore:

Kernel.restore! :system

Can you suggest any improvements to this code?

Note also that there’s a library, available via RAA, that lets you do
temporary changes to core behaviors:

http://raa.ruby-lang.org/project/import_module/

Wow, very cool. Thanks!

Cheers,
mk

The Stubba portion of Mocha (http://mocha.rubyforge.org) allows you to
temporarily replace the implementation of a Module method within a
test.

require ‘test/unit’
require ‘test/unit/ui/console/testrunner’
require ‘rubygems’
require ‘stubba’

class Test1 < Test::Unit::TestCase

def test_should_force_system_to_return_false
Kernel.stubs(:system).returns(false)
assert_equal false, Kernel.system(‘echo’)
end

end

class Test2 < Test::Unit::TestCase

def test_should_not_be_affected_by_other_test
assert_equal true, Kernel.system(‘echo’)
end

end

class OrderedTests < Test::Unit::TestCase

def self.suite
suite = Test::Unit::TestSuite.new(‘OrderedTests’)
suite << Test1.suite
suite << Test2.suite
end

end

Test::Unit::UI::Console::TestRunner.run(OrderedTests)

On Dec 23, 2006, at 19:00, Michal K. wrote:

Thanks in advance for any help on this.
Its simpler to not overwrite Kernel#system.

If your class looks something like:

$ cat runner.rb
class Runner

def run(command)
puts command
system command
end

end

Use open classes and inheritance to add a system that works when you
want it. Restoring the real system for the Runner class is as simple
as removing the method again.

$ cat test_runner.rb
require ‘test/unit’
require ‘runner’

class Runner
attr_accessor :commands, :results
def system(command)
@commands << command
@results.shift
end
end

class TestRunner < Test::Unit::TestCase

def setup
@runner = Runner.new
@runner.commands = []
@runner.results = []
end

def test_run
@runner.results << false

 assert_equal false, @runner.run("exit 1")

 assert @runner.results.empty?
 assert_equal 1, @runner.commands.length
 assert_equal 'exit 1', @runner.commands.first

end

end

$ ruby test_runner.rb
Loaded suite test_runner
Started
exit 1
.
Finished in 0.000369 seconds.

1 tests, 4 assertions, 0 failures, 0 errors

If you want to get rid of the “exit 1” use util_capture from
ZenTest’s test/zentest_assertions.rb


Eric H. - [email protected] - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

Hi –

On Sun, 24 Dec 2006, Michal K. wrote:

end
class Module
def override! method

With this code, to override the system method you can write (syntax
inspired by RSpec):

Kernel.override!(:system).and_return false

and then to restore:

Kernel.restore! :system

Can you suggest any improvements to this code?

A couple of things, but be warned I am still early in my coffee today
:slight_smile:

You don’t need to_sym in those calls to alias_method; it will take a
string. In fact, you can do this:

“orig_#{method}”

which will work for either.

The ! is generally used for methods that come in pairs: override and
override! It indicates that one is the “dangerous” version of the
operation. In this case, there’s only one of each, and the name
itself describes what’s happening, so I’d lose the !'s. (I’ll keep
them in my examples below, though.)

I would avoid “and_return”, which is a kind of odd method name (it
leaves me thinking: don’t methods always return something?). It’s
better just to tell the method what you want, and not string it across
multiple method calls. Just give it two arguments, or a hash of
arguments.

Here’s another version, for your reading pleasure. Use only as
desired.

class Module

def override(opts)
method, value = opts.values_at(:method, :value)
alias_method “orig_#{method}”, method
define_method(method) { value }
end

def restore(method)
alias_method method,“orig_#{method}”
end

end

Kernel.override(:method => “system”, :value => 3)
puts system
Kernel.restore(:system)
system(“date”)

What would be more general would be a way to specify the new behavior
arbitrarily, with a code block, which the import-module library does.

David