[Перевод] Ruby 2.1 в деталях (Часть 3)

ccdb8fbabd2b13bae6145fa4da9c7ac2.pngМетод #singleton_class? для Module/ClassВ классы Module и Class был добавлен метод #singleton_class?, который, как и следовало ожидать, возвращает, является ли получатель мета-классом (singleton) class Example singleton_class? #=> false class << self singleton_class? #=> true end end Более логичный Module#ancestors Метод #ancestors, вызванный по отношению в мета-классу, теперь возвращает массив, содержащий в том числе и мета-классы, что делает его поведение более последовательным. Он также упорядочивает вывод мета-классов, но это выполняется только если модуль был подключен к мета-классу с помощью prepend (а не include). Object.ancestors.include?(Object) #=> true Object.singleton_class.ancestors.include?(Object.singleton_class) #=> true Object#singleton_method Аналогичен #method и #instance_method, но возвращает только методы мета-класса class Example def self.test end

def test2 end end

# returns class method Example.singleton_method (: test) #=> # # doesn’t return instance method Example.singleton_method (: test2) #=> # # doesn’t return inherited class method Example.singleton_method (: name) #=> # example = Object.new

def example.test end

example.singleton_method (: test) #=> #.test> Method#original_name В классах Method и UnboundMethod появился метод #original_name, возвращающий имя метода без псевдонима. class Example def foo «foo» end alias bar foo end

example = Example.new example.method (: foo).original_name #=>: foo example.method (: bar).original_name #=>: foo Example.instance_method (: bar).original_name #=>: foo Mutex#owned? Метод Mutex#owned? больше не является экспериментальным, больше вобщем-то сказать о нем и нечего.Hash#reject Вызов метода Hash#reject в подклассе Hash выведет ворнинг. В Ruby 2.2 вызов #reject в подклассах Hash будет возвращать новый объект Hash, а не объект подкласса. Поэтому в качестве подготовки к этому изменению пока было добавлено предупреждение. class MyHash < Hash end

example = MyHash.new example[: a] = 1 example[: b] = 2

example.reject {|k, v| v > 1}.class #=> MyHash Выведет следующее предупреждение: example.rb:8: warning: copying unguaranteed attributes: {: a=>1, : b=>2} example.rb:8: warning: following atributes will not be copied in the future version: example.rb:8: warning: subclass: MyHash В Ruby 2.1.1 случайно было включено полное изменение, которой возвращает объект Hash и не генерирует ворнинг, но в 2.1.2 это было исправлено назад.Vector#cross_product В класс Vector был добавлен метод cross_product. require «matrix»

Vector[1, 0, 0].cross_product (Vector[0, 1, 0]) #=> Vector[0, 0, -1] Fixnum/Bignum #bit_length Вызов #bit_length по отношению к целому числу вернет количество цифр, необходимых для представления числа в двоичной системе. 128.bit_length #=> 8 32768.bit_length #=> 16 2147483648.bit_length #=> 32 4611686018427387904.bit_length #=> 63 pack/unpack и байтовое представление чисел Методы Array#pack и String#unpack теперь могут работать с байтовым представлением длинных чисел с помощью директив Q_/Q! и q_/q!. # output may differ depending on the endianness of your system unsigned_long_long_max = [2**64 — 1].pack («Q!») #=> »\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF» signed_long_long_min = [-2**63].pack («q!») #=> »\x00\x00\x00\x00\x00\x00\x00\x80» signed_long_long_max = [2**63 — 1].pack («q!») #=> »\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F»

unsigned_long_long_max.unpack («Q!») #=> 18446744073709551615 signed_long_long_min.unpack («q!») #=> -9223372036854775808 signed_long_long_max.unpack («q!») #=> 9223372036854775807 Dir.glob возвращает составные символы Файловая система HFS Plus в Mac OS X использует кодировку UTF8-MAC для имен файлов с разделенными символами, например é представляется в виде e и U+0301, а не просто U+00E9 (с некоторыми исключениями). Dir.glob и Dir[] теперь обратно преобразуют их в UTF8-строки с составными символами. File.write («composed_e\u{301}xample.txt»,») File.write («precomposed_\u{e9}xample.txt»,»)

puts Dir[»*»].map (&: dump) «composed_\u{e9}xample.txt» «example.rb» «precomposed_\u{e9}xample.txt» Улучшенное приведение типов для Numeric#quo Метод Numeric#quo теперь вызывает #to_r на получателе, что должно улучшить поведение при реализации собственных подклассов Numeric. Это также означает, что в случае невозможности приведения будет возбуждено TypeError, а не ArgumentError, что правда не должно стать проблемой при переходе, т.к. TypeError является подклассом ArgumentError.Binding#local_variable_get/_set/_defined? В классе Binding появились методы получения/задания локальных переменных. Это может быть полезным если прямо-таки необходимо использовать именованный аргумент, совпадающий с зарезервированным ключевым словом. def primes (begin: 2, end: 1000) [binding.local_variable_get (: begin), 2].max.upto (binding.local_variable_get (: end)).each_with_object ([]) do |i, array| array << i unless (2...i).any? {|j| (i % j).zero?} end end

primes (end: 10) #=> [2, 3, 5, 7] Или если вы хотите использовать хэш для задания переменных, например при выполнении шаблона: def make_binding (hash) b = TOPLEVEL_BINDING.dup hash.each {|k, v| b.local_variable_set (k, v)} b end

require «erb»

cover = %Q{

<%= title %>

\n

<%= subtitle %>

} locals = {: title => «Hitchhiker’s Guide to the Galaxy», : subtitle => «Don’t Panic»}

ERB.new (cover).result (make_binding (locals)) #=> »

Hitchhiker’s Guide to the Galaxy

\n

Don’t Panic

» Методы класса CGI теперь доступны из модуля CGI: Util Класс CGI имеет несколько полезных методов экранирования url и html строк. Они были перенесены в модуль CGI: Util, который может быть включен в другие классы или скрипты. require «cgi/util»

CGI.escape («hello world!») #=> «hello+world%21»

include CGI: Util

escape («hello world!») #=> «hello+world%21» Digest: Class.file передает аргументы конструктору Различные классы модуля Digest имеют метод для создания дайджеста для файла, этот метод был изменен, и теперь он передает конструктору все переданные аргументы кроме имени файла. Т.е. вместо: require «digest» Digest: SHA2.new (512).hexdigest (File.read («example.txt»)) #=> «f7fbba…» Можно написать: require «digest» Digest: SHA2.file («example.txt», 512).hexdigest #=> «f7fbba…» Net: SMTP#rset Теперь можно отменить SMTP-транзакцию, послав команду RSET с помощью метода Net: SMTP#rset. require «net/smtp»

smtp = Net: SMTP.start («some.smtp.server») notification = «Hi %s,\n …»

users.each do |user| begin smtp.mailfrom («noreply@example.com») smtp.rcptto (user.email) smtp.data (sprintf (notification, user.name)) rescue smtp.rset end end

smtp.finish open-uri поддерживает повторяющиеся заголовки open-uri позволяет с помощью метода Kernel#open открывать ресурсы по URI, и расширяет возвращаемое значение с помощью OpenURI: Meta, куда был добавлен новый метод #metas, возвращающий массив значений, если заголовок был задан несколько раз, например set-cookie. require «open-uri»

f = open («http://google.com») f.meta[«set-cookie»].class #=> String f.metas[«set-cookie»].class #=> Array f.metas[«set-cookie»].length #=> 2 Запись в файл через Pathname В класс Pathname добавлены методы #write и #binwrite для записи файлов. require «pathname»

path = Pathname.new («test.txt»).expand_path (__dir__) path.write («foo») path.write («bar», 3) # offset path.write («baz», mode: «a») # append Tempfile.create В классе Tempfile теперь есть метод, аналогичный методу new, но вместо того, чтобы возвращать объект Tempfile, использующий финализатор (finaliser), удаляющий файл, когда объект подвергается сборке мусора, метод create передает объект File в блок, после выполнения которого файл удаляется. require «tempfile»

path = nil Tempfile.create («example») do |f| f #=> # path = f.path end File.exist?(path) #=> false Поддержка группового вещания в Rinda Теперь классы Rinda Ring могут слушать/соединяться с групповыми адресами.Ниже пример использования Rinda для создания простого сервиса, прослушивающего групповой адрес 239.0.0.1

require «rinda/ring» require «rinda/tuplespace»

DRb.start_service

tuple_space = Rinda: TupleSpace.new server = Rinda: RingServer.new (tuple_space, [»239.0.0.1»])

DRb.thread.join Регистрация сервиса: require «rinda/ring»

DRb.start_service ring_finger = Rinda: RingFinger.new ([»239.0.0.1»]) tuple_space = ring_finger.lookup_ring_any

tuple_space.write ([: message_service, «localhost», 8080])

# start messaging service on localhost:8080 Получение адреса сервиса: require «rinda/ring»

DRb.start_service ring_finger = Rinda: RingFinger.new ([»239.0.0.1»]) tuple_space = ring_finger.lookup_ring_any

_, host, port = tuple_space.read ([: message_service, String, Fixnum])

# connect to messaging service У меня возникли некоторые проблемы со строкой tuple_space = ring_finger.lookup_ring_any и мне пришлось использовать: tuple_space = nil ring_finger.lookup_ring (0.01) {|x| break tuple_space = x} Задание дополнительных HTTP-опций для XMLRPC XMLRPC: Client#http возвращает объект Net: HTTP, используемый клиентом для задания некоторых конфигурационных опций, которые не могут быть заданы через сеттеры. client = XMLRPC: Client.new («example.com») client.http.keep_alive_timeout = 30 # keep connection open for longer # use client … URI.encode_/decode_www_form обновлены под стандарт WHATWG Методы URI.encode_www_form и URI.decode_www_form были обновлены для соответствия стандарту WHATWG.URI.decode_www_form больше не воспринимает ; в качестве разделителя, & единственный разделитель по умолчанию, но можно задать значение разделителя с помощью именованного аргумента separator:.

require «uri» URI.decode_www_form («foo=1; bar=2», separator:»;») #=> [[«foo»,»1»], [«bar»,»2»]] URI.decode_www_form теперь также может успешно декодировать вывод URI.encode_www_form, когда его значение nil. require «uri»

string = URI.encode_www_form (foo: 1, bar: nil, baz: 3) #=> «foo=1&bar&baz=3» URI.decode_www_form («foo=1&bar&baz=3») #=> [[«foo»,»1»], [«bar»,»], [«baz»,»3»]] RbConfig: SIZEOF Новый метод RbConfig: SIZEOF возвращает размер C-типов. require «rbconfig/sizeof»

RbConfig: SIZEOF[«short»] #=> 2 RbConfig: SIZEOF[«int»] #=> 4 RbConfig: SIZEOF[«long»] #=> 8 Установка категории логгирования в Syslog: Logger Syslog: Logger, Logger-совместимый интерфейс для Syslog, получил возможность установки категории. require «syslog/logger»

facility = Syslog: LOG_LOCAL0 logger = Syslog: Logger.new («MyApp», facility)

logger.debug («test») CSV.foreach без блока возвращает перечислитель CSV.foreach, вызванный без блока в качестве аргумента, возвращает перечислитель, но при использовании в течении длительного времени это приводило к IOError, это было исправлено. require «csv»

enum = CSV.foreach («example.csv»)

enum.next #=> [»1», «foo»] enum.next #=> [»2», «bar»] enum.next #=> [»3», «baz»] OpenSSL bignum OpenSSL: BN.new теперь принимает в качестве аргументов не только строки, но и целые числа. require «openssl»

OpenSSL: BN.new (4_611_686_018_427_387_904) #=> # Аргумент size Enumerator.new принимает любой вызываемый объект Enumerator.new принимает аргмент size, который может быть как целым числом, так и объектом с методом #call. Однако до 2.0.0 метод по факту работал только с целыми числами и Proc-объектами. Теперь это исправено и работает как сказано в документации. require «thread»

queue = Queue.new enum = Enumerator.new (queue.method (: size)) do |yielder| loop {yielder << queue.pop} end queue << "foo" enum.size #=> 1 Удалена библиотека curses curses была удалена из стандартной библиотеки и доступна в качестве гема.Методы класса в TSort Класс TSort может быть полезен в определении порядка выполнения задач из списка зависимостей. Однако использовать его довольно хлопотно, для итого нужно реализовать класс, включающий TSort, а также методы #tsort_each_node и #tsort_each_child.Но теперь TSort стало удобнее использовать с, например, хэшами. Методы, доступные как методы экземпляра теперь доступны в самом модуле, принимая два вызываемых объекта, один в качестве замены #tsort_each_node, второй — #tsort_each_child.

require «tsort»

camping_steps = { «sleep» => [«food», «tent»], «tent» => [«camping site», «canvas»], «canvas» => [«tent poles»], «tent poles» => [«camping site»], «food» => [«fish», «fire»], «fire» => [«firewood», «matches», «camping site»], «fish» => [«stream», «fishing rod»] }

all_nodes = camping_steps.to_a.flatten each_node = all_nodes.method (: each) each_child = → step, &b {camping_steps.fetch (step, []).each (&b)} puts TSort.tsort (each_node, each_child) Выведет: stream fishing rod fish firewood matches camping site fire food tent poles canvas tent sleep TCP Fast Open В Ruby 2.1 добавлена поддержка TCP Fast Open, если он доступен в вашей системе. Для проверки на наличие его в системе можно проверить существование констант Socket: TCP_FASTOPEN и Socket: MSG_FASTOPEN.Сервер:

require «socket»

unless Socket.const_defined?(: TCP_FASTOPEN) abort «TCP Fast Open not supported on this system» end

server = Socket.new (Socket: AF_INET, Socket: SOCK_STREAM) server.setsockopt (Socket: SOL_TCP, Socket: TCP_FASTOPEN, 5) addrinfo = Addrinfo.new (Socket.sockaddr_in (3000, «localhost»)) server.bind (addrinfo) server.listen (1)

socket = server.accept socket.write (socket.readline) Клиент: require «socket»

unless Socket.const_defined?(: MSG_FASTOPEN) abort «TCP Fast Open not supported on this system» end

socket = Socket.new (Socket: AF_INET, Socket: SOCK_STREAM) socket.send («foo\n», Socket: MSG_FASTOPEN, Socket.sockaddr_in (3000, «localhost»)) puts socket.readline socket.close Первая часть Вторая часть

© Habrahabr.ru