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

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

···

On 12/23/06, Michal Kwiatkowski <constant.beta@gmail.com> wrote:

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.

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

···

On Dec 23, 2006, at 19:00, Michal Kwiatkowski wrote:

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.

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

Mat Schaffer wrote:

> 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.

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

···

On 12/23/06, Michal Kwiatkowski <constant.beta@gmail.com> wrote:

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)

···

--
James.
http://blog.floehopper.org

Hi --

Mat Schaffer wrote:

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.

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

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

···

On Sun, 24 Dec 2006, Michal Kwiatkowski wrote:

On 12/23/06, Michal Kwiatkowski <constant.beta@gmail.com> wrote:

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

dblack@wobblini.net wrote:

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

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

Hi --

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

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?

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

···

On Sun, 24 Dec 2006, Michal Kwiatkowski wrote:

dblack@wobblini.net wrote:

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)