[Перевод] Ruby 2.1 в деталях (Часть 3)
Метод #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) #=> #
def example.test end
example.singleton_method (: test) #=> #
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
\nDon’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 #=> #
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) #=> #
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 Первая часть Вторая часть