Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ruby / Yukihiro Matsumoto_Programming Ruby.doc
Скачиваний:
121
Добавлен:
06.06.2015
Размер:
2.71 Mб
Скачать

Performance Considerations

As we've seen in this section, there are several ways to invoke an arbitrary method of some object: Object#send ,Method#call , and the various flavors ofeval.

You may prefer to use any one of these techniques depending on your needs, but be aware that evalis significantly slower than the others (or, for optimistic readers,sendandcallare significantly faster thaneval).

require "benchmark"   # from the Ruby Application Archive

include Benchmark

test = "Stormy Weather"

m = test.method(:length)

n = 100000

bm(12) {|x|

  x.report("call") { n.times { m.call } }

  x.report("send") { n.times { test.send(:length) } }

  x.report("eval") { n.times { eval "test.length" } }

}

produces:

                  user     system      total        real

call          0.200000   0.000000   0.200000 (  0.206547)

send          0.240000   0.000000   0.240000 (  0.232178)

eval          2.570000   0.000000   2.570000 (  2.560201)

System Hooks

A hookis a technique that lets you trap some Ruby event, such as object creation.

The simplest hook technique in Ruby is to intercept calls to methods in system classes. Perhaps you want to log all the operating system commands your program executes. Simply rename the method Kernel::system [This Eiffel-inspired idiom of renaming a feature and redefining a new one is very useful, but be aware that it can cause problems. If a subclass does the same thing, and renames the methods using the same names, you'll end up with an infinite loop. You can avoid this by aliasing your methods to a unique symbol name or by using a consistent naming convention.]and substitute it with one of your own that both logs the command and calls the originalKernelmethod.

module Kernel

  alias_method :old_system, :system

  def system(*args)

    result = old_system(*args)

    puts "system(#{args.join(', ')}) returned #{result}"

    result

  end

end

system("date")

system("kangaroo", "-hop 10", "skippy")

produces:

Sun Nov 25 23:45:40 CST 2001

system(date) returned true

system(kangaroo, -hop 10, skippy) returned false

A more powerful hook is catching objects as they are created. If you can be present when every object is born, you can do all sorts of interesting things: you can wrap them, add methods to them, remove methods from them, add them to containers to implement persistence, you name it. We'll show a simple example here: we'll add a timestamp to every object as it's created.

One way to hook object creation is to do our method renaming trick on Class#new , the method that's called to allocate space for a new object. The technique isn't perfect---some built-in objects, such as literal strings, are constructed without callingnew---but it'll work just fine for objects we write.

class Class

  alias_method :old_new,  :new

  def new(*args)

    result = old_new(*args)

    result.timestamp = Time.now

    return result

  end

end

We'll also need to add a timestamp attribute to every object in the system. We can do this by hacking class Objectitself.

class Object

  def timestamp

    return @timestamp

  end

  def timestamp=(aTime)

    @timestamp = aTime

  end

end

Finally, we can run a test. We'll create a couple of objects a few seconds apart and check their timestamps.

class Test

end

obj1 = Test.new

sleep 2

obj2 = Test.new

obj1.timestamp

»

Sun Nov 25 23:45:40 CST 2001

obj2.timestamp

»

Sun Nov 25 23:45:42 CST 2001

All this method renaming is fine, and it really does work. However, there are other, more refined ways to get inside a running program. Ruby provides several callback methods that let you trap certain events in a controlled way.

Соседние файлы в папке Ruby