«За кулисами» Ruby предпринимает согласованные меры, чтобы операции ввода/вывода не блокировали выполнение программы. В большинстве случаев для управления вводом/выводом можно пользоваться потоками — один поток может выполнить блокирующую операцию, а второй будет продолжать работу.
Это немного противоречит интуиции. Потоки Ruby работают в том же процессе, они не являются платформенными потоками. Быть может, вам кажется, что блокирующая операция ввода/вывода должна приостанавливать весь процесс, а значит, и все его потоки. Это не так — Ruby аккуратно управляет вводом/выводом прозрачно для программиста.
Но если вы все же хотите включить неблокирующий режим ввода/вывода, такая возможность есть. Небольшая библиотека io/nonblock
предоставляет методы чтения и установки для объекта IO
, представляющего блочное устройство:
require 'io/nonblock'
# ...
test = mysock.nonblock? # false
mysock.nonblock = true # Отключить блокирующий режим.
# ...
mysock.nonblock = false # Снова включить его.
mysock.nonblock { some_operation(mysock) }
# Выполнить some_operation в неблокирующем режиме.
mysock.nonblock(false) { other_operation(mysock) }
# Выполнить other_operation в блокирующем режиме.
10.1.16. Применение метода readpartial
Метод readpartial
появился сравнительно недавно с целью упростить ввод/вывод при определенных условиях. Он может использоваться с любыми потоками, например с сокетами.
Параметр «максимальная длина» (max length) обязателен. Если задан параметр buffer, то он должен ссылаться на строку, в которой будут храниться данные.
data = sock.readpartial(128) # Читать не более 128 байтов.
Метод readpartial
игнорирует установленный режим блокировки ввода/вывода. Он может блокировать программу, но лишь при выполнении следующих условий: буфер объекта IO пуст, в потоке ничего нет и поток еще не достиг конца файла.
Таким образом, если в потоке есть данные, то readpartial
не будет блокировать программу. Он читает не более указанного числа байтов, а если байтов оказалось меньше, то прочитает их и продолжит выполнение.
Если в потоке нет данных, но при этом достигнут конец файла, то readpartial
немедленно возбуждает исключение EOFError
.
Если вызов блокирующий, то он ожидает, пока не произойдет одно из двух событий: придут новые данные или обнаружится конец файла. Если поступают данные, метод возвращает их вызывающей программе, а в случае обнаружения конца файла возбуждает исключение EOFError
.
При вызове метода sysread
в блокирующем режиме он ведет себя похоже на readpartial
. Если буфер пуст, их поведение вообще идентично.
10.1.17. Манипулирование путевыми именами
Основными методами для работы с путевыми именами являются методы класса File.dirname
и File.basename
; они работают, как одноименные команды UNIX, то есть возвращают имя каталога и имя файла соответственно. Если вторым параметром методу basename
передана строка с расширением имени файла, то это расширение исключается.
str = '/home/dave/podbay.rb'
dir = File.dirname(str) # '/home/dave'
file1 = File.basename(str) # 'podbay.rb'
file2 = File.basename(str,'.rb') # 'podbay'
Хотя это методы класса File
, на самом деле они просто манипулируют строками.
Упомянем также метод File.split
, который возвращает обе компоненты (имя каталога и имя файла) в массиве из двух элементов:
info = File.split(str) # ['/home/dave','podbay.rb']
Метод класса expand_path
преобразует путевое имя в абсолютный путь. Если операционная система понимает сокращения ~
и ~user
, то они тоже учитываются.
Dir.chdir('/home/poole/personal/docs')
abs = File.expand_path('../../misc') # '/home/poole/misc'
Если передать методу path
открытый файл, то он вернет путевое имя, по которому файл был открыт.
file = File.new('../../foobar')
name = file.path # '../../foobar'
Константа File::Separator
равна символу, применяемому для разделения компонентов путевого имени (в Windows это обратная косая черта, а в UNIX — прямая косая черта). Имеется также синоним File::SEPARATOR
.
Метод класса join
использует этот разделитель для составления полного путевого имени из переданного списка компонентов:
path = File.join('usr','local','bin','someprog')
# path равно 'usr/local/bin/someprog'.
# Обратите внимание, что в начало имени разделитель не добавляется!
Не думайте, что методы File.join
и File.split
взаимно обратны, — это не так.
10.1.18. Класс Pathname
Следует знать о существовании стандартной библиотеки pathname
, которая предоставляет класс Pathname
. В сущности, это обертка вокруг классов Dir
, File
, FileTest
и FileUtils
, поэтому он комбинирует многие их функции логичным и интуитивно понятным способом.
path = Pathname.new('/home/hal')
file = Pathname.new('file.txt')
p2 = path + file