Занятие 6
22. Language Reflection Language Reflection – способность объектов к рефлексии, то есть умение давать информацию об исключительно языковых свойствах объекта – набор методов объекта, класс объекта, суперкласс класса и т.д. С одним такими методами мы уже знакомы: irb> 34.6.class irb> hello.class irb> hello.id Метод #methods класса Object возвращает массив имен всех методов (в виде текстовых объектов), на которые может отвечать данный объект, т.е. это все методы, определенные во всех суперклассах и включенных модулях: irb> hello.methods irb> Fixnum.methods
Метод Object#respond_to? проверяет, может ли объект ответить на метод: irb> 34.respond_to?(+) irb> hello.respond_to?(length) Вместо текстовых аргументов этот метода также может принимать аргумент в виде объекта класса Symbol : irb> 34.respond_to?(:+) irb> hello.respond_to?(:length)
Если можно получить список всех методов объекта, а также определить, отвечает ли объект на определенный метод, было бы грустно, если бы было невозможно послать метод объекту динамически. Все последующие выражения – это один и тот же метод: irb> hello.send(:length) irb> hello.send(length) irb> hello.length Если метод имеет аргументы, в методе #send они следуют после имени метода: irb> hello.send(index, l)
Метод Object#instance_of? возвращает true, если объект является экземпляром класса, указанного в аргументе: irb> 56.instance_of?(Fixnum) irb> 56.instance_of?(Integer) irb> hello.instance_of?(Integer) Метод Object#kind_of? возвращает true, если объект является экземпляром класса, указанного в аргументе, или если класс объекта наследуется от указанного класса (то есть класс – один из суперклассов класса объекта): irb> 56.kind_of?(Fixnum) irb> 56.kind_of?(Integer) irb> hello.kind_of?(Integer)
В классах Module и Class определены свои интересные рефлексивные методы: irb> String.superclass irb> 45.class.superclass irb> Object.superclass Метод #ancestor s возвращает список всех суперклассов и включенных модулей: irb> Fixnum.ancestors
Класс ObjectSpace в Ruby дает доступ к Garbage Collector. Метод ObjectSpace#each_object – это метод класса ObjectSpace, итератор, перебирающий по очереди все существующие объекты. В качестве аргумента ObjectSpace#each_object может принимать класс или модуль, в этом случае будут перебираться только классы или модули (которые в Ruby, как известно, являются объектами), являющиеся наследниками указанного класса или модуля. ObjectSpace.each_object do |object| puts object end
Задания 39) Напишите метод #explore, получающий объект, и возвращающий иерархию классов данного объекта в текстовом виде следующим образом: puts explore(5.7) #=> Float
def explore(obj) klass = obj.class classes = Array.new until klass == nil # Object.class – nil classes.push klass.to_s klass = klass.superclass end return classes.join("
Задания 40) Напишите скрипт, получающий отсортированные имена всех методов всех стандартных классов и модулей. Для получения списка методов класса воспользуйтесь методом Module#public_instance_methods, и помните, что класс Class – это наследник класса Module
require "set.rb" all_methods = Set.new ObjectSpace.each_object(Module) do |class_module| next if class_module == Set class_module.public_instance_methods.each do |meth| all_methods.add meth end puts all_methods.to_a.sort
23. Marshal Модуль Marshal позволяет сохранить объект в виде строки (потока байтов) и затем восстанавливать его из этой строки. Создайте файл – dump.rb : Запустите скрипт и посмотрите содержимое файла data. На месте второго аргумента Marshal.dump может быть любой IO объект, например, Socket. obj = [{"Peter" => 23, "John" => 25}, 45,["yellow", "red"]] File.open("data", "w+"){|file| Marshal.dump(obj, file) }
Создайте файл load.rb, и запустите : Marshal не работает с объектами, содержащими следующие объекты: * IO объекты * Proc объекты * синглтон объекты File.open("data", "r"){|file| obj = Marshal.load(file) puts obj.inspect }
24. Работаем с сетью Как работает HTTP протокол на примере telnet: $ telnet localhost 80 Trying Connected to leikind. Escape character is '^]'. GET /index.html html code here Connection closed by foreign host. $
Объект класса TCPServer ждет соединений и возвращает объект TCPSocket. Простой пример простого веб-сервера: # Должен быть запущен из-под root'а require 'socket' server=TCPServer.new('localhost', 80) while (session = server.accept) # puts session.class #=> TCPSocket puts "Request: #{session.gets}" session.print "HTTP/ /OK\r\nContent-type: + text/html\r\n\r\n" session.print " #{Time.now} + " session.close end
Пишем http клиент: require "socket" socket = TCPSocket.new('localhost', 80) socket.puts "GET /index.html" while line = socket.gets print line end socket.close
Однако есть и более высокоуровневые классы, например, класс Net::HTTP реализует HTTP протокол: require 'net/http' http_connection = Net::HTTP.new('localhost', 80) response, data = http_connection.get('/index.html') puts response.message #response.each {|key, val| # это headers # print key, " => ", val, "\n" #} puts data
require 'socket' server=TCPServer.new('leikind', 81) session = server.accept while true puts "< " + session.gets print "> " answer = gets session.puts answer end Примитивный чат: require "socket" session = TCPSocket.new('leikind', 81) while true print "> " answer = gets session.puts answer puts "< " + session.gets end
25. Пример распределенного вычисления на Ruby
Require "socket" some_object = [ 34, "hello", [23,56,12,["Peter","Jack",0],nil], {"Peter"=>45, "Jack"=>23} ] puts some_object.inspect session = TCPSocket.new('localhost', 81) Marshal.dump(some_object, session) session.close Require 'socket' server=TCPServer.new('localhost', 81) while session = server.accept some_object=Marshal.load(session) puts some_object.inspect session.close end Пересылка объекта по сети:
Require "socket" session = TCPSocket.new('leikind', 81) Marshal.dump(3, session) # будет 3 агрумента Marshal.dump(10, session) # арг 1 Marshal.dump(20, session) # арг 2 Marshal.dump(5, session) # арг 3 session.flush result = Marshal.load(session) puts result session.close Таким же образом мы можем переслать несколько объектов, предварительно переслав количество объектов, и получить какой-то объект в качестве результата: Скрипт, отправляющий объект:
require 'socket' server=TCPServer.new('leikind', 81) while session = server.accept # количество аргументов n = Marshal.load(session) arguments = Array.new 1.upto(n){ # получаем все аргументы arguments.push Marshal.load(session) } sum=0 arguments.each{|arg| sum += arg} # суммируем Marshal.dump(sum, session) # шлем ответ session.close end Скрипт, принимающий объект ( TCPServer ):