From: dblack@... Date: 2006-12-24T22:31:55+09:00 Subject: Re: Reverting module changes Hi -- On Sun, 24 Dec 2006, Michal Kwiatkowski wrote: > 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? A couple of things, but be warned I am still early in my coffee today :-) 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 -- 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)