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

Looking at Classes

Knowing about objects is one part of reflection, but to get the whole picture, you also need to be able to look at classes---the methods and constants that they contain.

Looking at the class hierarchy is easy. You can get the parent of any particular class using Class#superclass . For classesandmodules,Module#ancestors lists both superclasses and mixed-in modules.

klass = Fixnum

begin

  print klass

  klass = klass.superclass

  print " < " if klass

end while klass

puts

p Fixnum.ancestors

produces:

Fixnum < Integer < Numeric < Object

[Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]

If you want to build a complete class hierarchy, just run that code for every class in the system. We can use ObjectSpaceto iterate over allClassobjects:

ObjectSpace.each_object(Class) do |aClass|

   # ...

end

Looking Inside Classes

We can find out a bit more about the methods and constants in a particular object. Instead of just checking to see whether the object responds to a given message, we can ask for methods by access level, we can ask for just singleton methods, and we can have a look at the object's constants.

class Demo

  private

    def privMethod

    end

  protected

    def protMethod

    end

  public

    def pubMethod

    end

  def Demo.classMethod

  end

  CONST = 1.23

end

Demo.private_instance_methods

»

["privMethod"]

Demo.protected_instance_methods

»

["protMethod"]

Demo.public_instance_methods

»

["pubMethod"]

Demo.singleton_methods

»

["classMethod"]

Demo.constants - Demo.superclass.constants

»

["CONST"]

Module.constants returnsallthe constants available via a module, including constants from the module's superclasses. We're not interested in those just at the moment, so we'll subtract them from our list.

Given a list of method names, we might now be tempted to try calling them. Fortunately, that's easy with Ruby.

Calling Methods Dynamically

C and Java programmers often find themselves writing some kind of dispatch table: functions which are invoked based on a command. Think of a typical C idiom where you have to translate a string to a function pointer:

typedef struct {

  char *name;

  void (*fptr)();

} Tuple;

Tuple list[]= {

  { "play",   fptr_play },

  { "stop",   fptr_stop },

  { "record", fptr_record },

  { 0, 0 },

};

...

void dispatch(char *cmd) {

  int i = 0;

  for (; list[i].name; i++) {

    if (strncmp(list[i].name,cmd,strlen(cmd)) == 0) {

      list[i].fptr();

      return;

    }

  }

  /* not found */

}

In Ruby, you can do all this in one line. Stick all your command functions into a class, create an instance of that class (we called it commands), and ask that object to execute a method called the same name as the command string.

commands.send(commandString)

Oh, and by the way, it does much more than the C version---it's dynamic. The Ruby version will find new methods added at runtime just as easily.

You don't have to write special command classes for send: it works on any object.

"John Coltrane".send(:length)

»

13

"Miles Davis".send("sub", /iles/, '.')

»

"M. Davis"

Another way of invoking methods dynamically uses Methodobjects. AMethodobject is like aProcobject: it represents a chunk of code and a context in which it executes. In this case, the code is the body of the method, and the context is the object that created the method. Once we have ourMethodobject, we can execute it sometime later by sending it the messagecall.

trane = "John Coltrane".method(:length)

miles = "Miles Davis".method("sub")

trane.call

»

13

miles.call(/iles/, '.')

»

"M. Davis"

You can pass the Methodobject around as you would any other object, and when you invokeMethod#call , the method is run just as if you had invoked it on the original object. It's like having a C-style function pointer but in a fully object-oriented style.

You can also use Methodobjects with iterators.

def double(a)

  2*a

end

mObj = method(:double)

[ 1, 3, 5, 7 ].collect(&mObj)

»

[2, 6, 10, 14]

As good things come in threes, here's yet another way to invoke methods dynamically. The evalmethod (and its variations such asclass_eval,module_eval, andinstance_eval) will parse and execute an arbitrary string of legal Ruby source code.

trane = %q{"John Coltrane".length}

miles = %q{"Miles Davis".sub(/iles/, '.')}

eval trane

»

13

eval miles

»

"M. Davis"

When using eval, it can be helpful to state explicitly the context in which the expression should be evaluated, rather than using the current context. You can obtain a context by callingKernel#binding at the desired point.

class CoinSlot

  def initialize(amt=Cents.new(25))

    @amt = amt

    $here = binding

  end

end

a = CoinSlot.new

eval "puts @amt", $here

eval "puts @amt"

produces:

$0.25USD

nil

The first evalevaluates@amtin the context of the instance of classCoinSlot. The secondevalevaluates@amtin the context ofObject, where the instance variable@amtis not defined.

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