RubyHammer: различия между версиями

Содержимое удалено Содержимое добавлено
м Исправление опечаток и коррекция по мелочи , typos fixed: еще → ещё (19), ее → её (27), в нем → в нём (9), того-же → того же, нашелся → нашёлся (6), объем
Строка 328:
Диапазон-мотоцикл (<big><code>..</code></big>) проезжает от первого указанного объекта к его <code>.succ</code> (''succedent'' — «последующему»), и до последнего включительно. Три точки — то же, но мотоцикл остановился прямо перед последним элементом. Ещё раз:
 
<big><code>1..99</code>&equiv;<code>1...100</code></big>
 
{{info|Объекты, имеющие <code>.succ</code> называют [[w:Последовательность|последовательными]]: этим методом можно по текущему элементу достоверно определить следующий.}}
Строка 572:
<source lang=ruby>(1..6).to_a #-> [1,2,3,4,5,6]</source>
 
Есть ещеещё тысяча и один способ, но эти три используются чаще всего.
 
=== Диапазоны ===
Строка 586:
<code>[ 'a'<math>{}^{0}</math>, 'b'<math>{}^{1}</math>, 'c'<math>{}^{2}</math>, 'd'<math>{}^{3}</math>, 'e'<math>{}^{4}</math> ]</code>
 
Такая нумерация называется в Ruby положительной индексацией. Хм, — скажете вы, — а есть ещеещё и отрицательная? Да, есть!
<code>[ 'a'<math>{}^{+0}_{-5}</math>, 'b'<math>{}^{+1}_{-4}</math>, 'c'<math>{}^{+2}_{-3}</math>, 'd'<math>{}^{+3}_{-2}</math>, 'e'<math>{}^{+4}_{-1}</math> ]</code>
 
Плюсы расставлены лишь для красоты. Но вернемся к отрицательной индексации. Каков еееё смысл? Чтобы его пояснить, давайте решим задачку: дан массив, требуется получить предпоследний элемент.
 
<code>maccuB = [ 'a', 'b', 'c', <u>'d'</u>, 'e']
Строка 632:
==== Получение размера массива ====
 
В Ruby массивы динамические: в каждый конкретный момент времени неизвестно сколько в немнём элементов. Чтобы не плодить тайн подобного рода и был реализован метод <code>.size</code>:
 
<code>[1,'считайте',3,'количество',5,6,'зяпятых',2,5]<u>.size</u> #-> 9</code>
Строка 862:
Основную его логику занимает именно чтение нужного формата, кеширование, разбор, десериализация и т.п.
 
Просто реализуйте <code>.each</code> и включите в ваш класс mixin <code>Enumerable</code>. В немнём находится реализация
методов, таких как <code>.inject</code>, <code>.each_with_index</code> и т.п.
 
Строка 914:
noJIHbIu_maccuB<u> == []</u> #-> false</code>
 
ЕщеЕщё можно задать вопрос: твой размер равен нулю?
 
<code>nycTou_maccuB = []
Строка 1091:
Невероятно, но ''от изменения имен переменных, результат остается прежним''. Помните это!
 
Для полноты картины решим ещеещё одну задачку. На этот раз будем искать произведение всех элементов массива:
 
<code>maccuB = [1,2,3,4,5]
Строка 1432:
#-> {5=>1, 1=>3, 2=>4, 3=>1, 4=>1}</code>
 
Страшноватая запись. Поэтому будем разбирать еееё по частям.
 
<code>result.update<u>( { i=>1 } )</u>{ |key,old,new| old+new }</code>
 
Сразу после названия метода (в нашем случае <code>.update</code>) идет передача параметра. Страшная запись <code>{ i => 1 }</code> — это не что иное, как ещеещё один хеш. Ключ его хранится в переменной <code>i</code> (счетчик итератора <code>.inject</code>), а в качестве значения выбрана единица. Зачем? Расскажу чуть позже.
 
{{info|Не обязательно писать именно <code><nowiki>{ i=>1 }</nowiki></code>. Можно "сократить" фигурные скобки и записать <code><nowiki>i=>1</nowiki></code>}}
Строка 1505:
Чуть ранее уже говорилось, что в большинстве случаев индексные массивы удобней ассоциативных.
 
{{Рамка}}Некоторые программисты утверждают, что при больших объемахобъёмах данных лучше использовать двумерный индексный массив. Получается примерно то же, что и хеш (лишь поиск элемента по ключу осуществить сложнее), но обычно программа работает быстрей.
 
Мнение авторов таково, что у программиста на Руби есть более благородные пути времяпровождения, чем заниматься такой вот псевдооптимизационной ерундой.{{Акмар}}
Строка 1601:
noJIHbIu_xew.empty? #-> false</source>
 
а то ещеещё примут нас за приезжих...
 
{{info|Обратите внимание, что метод <code>.empty?</code> полностью повторяет такой же метод у индексных массивов}}
Строка 1612:
kapmaH<u>.keys.include?( "гаечный" )</u> #-> true</code>
 
В данном примере у нас в <code>kapmaH</code>`e нашелсянашёлся "гаечный" ключ.
 
Но лучше задавать вопрос напрямую, это покажет ваше прекрасное знание языка.
Строка 1739:
Строковый тип является самым популярным в любом языке программирования. Ведь без него невозможно написать любую программу (особенно учитывая, что любая программа &mdash; это строка). При выводе на экран или записи в файл, любой тип данных преобразовывается к строке (явно или неявно). Это значит, что в конечном итоге все сводится к строковому типу. Кстати, и ввод данных тоже осуществляется в виде строки (и только потом преобразовывается в другие типы).
 
{{Начало цитаты}}Студенты 4-го курса МЭТТ ГАИ поступили на подготовительные курсы в МГИУ. Там им начали преподавать основы программирования на Ruby. И одна из заданных им задач была: "Дано число, необходимо поменять порядок цифр на обратный". Задача сложная, но наши студенты об этом не знали и решили еееё преобразованием к строке: <code>ucxogHoe.to_s.reverse</code>. Преподаватели были поражены и впредь запретили им использовать преобразования к строке в своих программах. И все потому, что это сильно упрощало решение и давало студентам огромное преимущество перед остальными слушателями курсов.{{Конец цитаты}}
 
Язык Ruby унаследовал работу со строками из языка Perl (признанного лидера по работе со строками). В частности такой мощный инструмент как «правила» (rules).
Строка 1761:
Давайте будем называть строки в минутах «ленивыми», а строки в лапках — «работящими».
 
{{info|«Вставка» &mdash; это хитрая конструкция, которая вставляется между ограничительными символами (внутрь строки). Она состоит из комбинации решетки и двух ушек ( <code><u>#{</u> 'здесь был Вася' <u>}</u></code> ). Внутри неенеё можно писать не только <code>'Здесь был Вася'</code>, но и любой программный код. Результат программного кода будет преобразован к строке и вставлен вместо «вставки»}}
 
{{Начало цитаты}}
Строка 1838:
{{info|Заниматься оформлением выходных данных стоит только непосредственно перед выводом результата. В остальное время, использование оформительских элементов будет только мешать}}
 
Всем известно, что числа внутри компьютера хранятся в двоичной системе. Но вот парадокс: получить двоичное представление числа иногда очень сложно. В частности, для того, чтобы получить двоичную запись числа необходимо создать строку и записывать результат в неенеё. Это значит, что результатом преобразования в другую систему счисления (из десятичной) будет строка. Давайте посмотрим, как эта задача решается:
 
<code>ucxogHoe_4ucJIo = 123
Строка 1873:
Теперь за свое будущее можно не беспокоиться! Хотя нам может попасться неграмотная кукушка…
 
С делением совсем другая история. Сейчас его в языке просто нет, но этот недостаток уже ощущают многие. Смысл деления состоит в том, чтобы преобразовать строку в массив, разбив еееё по разделителю. Давайте преобразуем речь кукушки в массив и посмотрим, сколько же лет она нам насчитала:
 
<code>pe4b_kykywku = "Ky-ky! Ky-ky! Ky-ky! Ky-ky! Ky-ky! Ky-ky! Ky-ky! ... Ky-ky! Ky-ky! "
Строка 2016:
 
{{Начало цитаты}}
НашелНашёл о<b>ШЫ</b>бку метод <code>.scan</code>,<br />
В массив еееё запомнил.<br />
Учителям он свыше дан!<br />
Зачем его я вспомнил?!
Строка 2080:
=====Квантификатор=====
 
Квантификатор показывает сколько раз может повторяться предыдущий символ (группа, альтернатива или ещеещё какая нечисть). Квантификатор ограничивается парой ушек (фигурными скобками). Примеры квантификаторов:
 
<code>/\w<u>{3}</u>/ #-> три латинских буквы или цифры
Строка 2208:
На этот раз мы просто задействовали символьный класс вместо альтернативы, который описывает первую букву слога с оШЫбкой.
 
Есть ещеещё пару интересных моментов, которые вам необходимо знать. Во время предыдущего примера вас могли посетить следующие вопросы:
* А как получить весь текст, который совпал с правилом?
* Неужели необходимо делать всеобщую группировку?
Строка 2238:
cTpoka.scan(/(?:[-a-z_\.])*@(?:[-a-z])*(?:\.[a-z]{2,4})+/) #-> ["nata@rambler.ru"]</code>
 
Выполните еееё, посмотрите результат, а потом замените любую из группировок (?:…) на (…) и снова взгляните на рузультат.
 
Ну со <code>.scan</code> должно быть все понятно. А вот то, что метод <code>[]</code> начинает тоже правильно искать — пока нет.
Строка 2275:
=== Хитрости ===
====Перенос по словам====
Несколько лет назад (ещеещё при жизни http://ruby-forum.ru) решали мы интересную задачу: как реализовать автоматический перенос на новую строку (wrap). Для тех, кто не застал те времена, уточню задание: дан текст, необходимо, вставить переносы таким образом, чтобы каждая из полученных строк была меньше n (для определенности <code>n=80</code>). Недавно я осознал, что не могу решить эту задачу тем способом, который был нами тогда найден. Я его просто не помнил... Решение нашлось быстро, достаточно было вспомнить, что на англ. языке эта задача именуется коротким и емким словом wrap.
 
<source lang=ruby>class String
Строка 2292:
<source lang=ruby>(.{1,80})( +|$\n?)|(.{1,80})</source>
Очевидно, что оно состоит из четырехчетырёх частей:
* '''(.{1,80})''' — строка длиной от 1 до 80 символов (любых). Результат группировки записывается в <code>$1</code> (один доллар) или <code>"\\1"</code>
* '''( +|$\n?)''' — пробелы или конец строки. Результат группировки записывается в <code>$2</code> (два доллара) или <code>"\\2"</code>. Обратите внимание на запись <code>$\n?</code>, которая означает "конец строки (<code>$</code>), после которого может идти перенос (<code>\n</code>)". Обратите внимание, что <code>$2</code> мы не используем и поэтому можно использовать <code>(?: )</code> (группировку без сохранения результата)
Строка 2430:
Если вы реализуете программу, которой будут пользоваться другие, считается хорошим тоном реализовывать методы-предикаты.
 
ЕщеЕщё одна их прелесть — сочетание с модификаторами выражения:
 
<source lang=ruby>arr = [1,2 ,3]
Строка 2517:
Ruby позволяет создавать анонимные методы и передавать их функциям - такие анонимные методы называются **блоками**. Очень большое количество функций Ruby основано
на использовании блоков - например, итераторы (такие как <code>each</code> и <code>map</code>). Блок - это фактически "функция в функции" - программист определяет
операцию, которую необходимо выполнить, но непосредственно еееё выполнение осуществляет метод, которому блок передается.
 
==== Зачем они нужны ====
Строка 2556:
==== Блоки принимают аргументы ====
 
Другое замечательное свойство блоков — они, как и функции, могут принимать аргументы. В таком случае метод, которому передан блок, сам "решает", что этот блок получит в качестве аргумента. Например, уже продемонстрированный метод <code>.map</code> ещеещё и передает блоку аргумент, который можно захватить следующим образом:
 
<source lang=ruby>puts (1..3).map do | index |
Строка 2894:
{{Начало цитаты}}При разработке своего Rails-приложения мной применялся класс <code>OrderedHash</code>, который работает как стандартный хеш, но при этом имеет упорядоченные ключи. Это позволяет, к примеру, удобно сгруппировать новости по датам, сохраняя порядок дат.''
 
''В какой-то момент моя программа перестала работать''. Почему? В Rails был, для внутренних нужд, добавлен другой класс <code>OrderedHash</code>, но при этом он не соответствовал моему (и даже не соответствовал обычному <code>Hash</code> — некоторых методов в немнём просто не было! Благодаря <code>remove_const</code> мне удалось просто выгрузить их класс и заменить его своим. А тесты в комплекте чужой библиотеки позволили удостовериться, что я ничего не испортил и она с моим "внедренным" классом функционирует нормально.{{Конец цитаты}}
{{Подпись|[[Участник:Julik|Julik]] 01:52, 25 июня 2006}}
 
Строка 2903:
 
== Матрицы и вектора ==
Давно хотел написать историю про то, как использовать [[w:Матрица (математика)|матрицы]] и [[w:Вектор (математика)|вектора]] в Ruby, но руки дошли только сейчас. Мое знакомство с реализацией матричного исчисления в Ruby началось ещеещё в мою бытность студентом [http://www.msiu.ru МГИУ]. И надо сказать, что это знакомство было неудачным. Дело в том, что простейшая программа вычисления определителя матрицы
<source lang=ruby>require 'matrix'
p Matrix[[1,-2,3],[3,4,-5],[2,4,1]].det #-> -50</source>
Строка 2911:
И в некоторых случаях это меня действительно выручало. Но не всегда... стоило появиться делению на 3, как появлялись ненужные остатки и погрешности. И чем больше исходная матрица, тем больше вероятность появления таких остатков. В некоторых случаях это было несущественным, а в остальных приходилось прибегать к услугам специальных математических пакетов (например,[[w:Maxima|Maxima]]). Было жутко неудобно (я тогда писал курсовой, который решал все варианты контрольной работы по предмету <Математическое программирование>. Да простит меня преподаватель, который так и не понял секрета тотальной успеваемости группы) и обидно за такую "кривую реализацию".
 
На этом бы история и закончилась (как позже я узнал, на этом она заканчивалась для многих), но мне в руки попалась книга Programming Ruby 2ed с описанием возможностей стандартной библиотеки версии 1.8.2. Именно там (на стр. 671) я наткнулся на описание библиотеки mathn. Уникальность еееё состоит в том, что она существенно расширяет возможности стандартных чисел, добавляя к ним [[w:Рациональное число|рациональные числа]] (класс <tt>Rational</tt>) и [[w:Комплексное число|комплексные числа]] (класс <tt>Complex</tt>).
{{info|Проще говоря, появляется возможность делить числа без погрешностей (класс <tt>Rational</tt>) и возможность извлекать квадратный корень из отрицательного числа (класс <tt>Complex</tt>)}}
Одновременно с этим она добавляет матричное и векторное исчисления (правда, почему-то в книге дополнительно подключали <tt>complex</tt> и <tt>matrix</tt>). И после этого матричное счисление начинает работать "из коробки" и ещеещё как работать!!! Хотите обратую матрицу? Пожалуйста! Хотите определитель? Нет ничего проще! Обидно только одно... программу к тому времени я уже написал и эти возможности мне были не нужны.
 
Чуть позднее, один из моих студентов написал мне письмо с просьбой объяснить как <работать с матрицами в Руби>? При этом он задал всего три вопроса:
Строка 2929:
 
=== Как послать в матрицу 2-мерный массив? ===
Немножко перефразируем вопрос: <как преобразовать двумерный массив в матрицу>? Пусть ответ будет неожиданным, но это делается при помощи того- же метода <батарейка>:
<source lang=ruby>require 'mathn'
maccuB = [[1, -2, 3], [3, 4, -5], [2, 4, 1]]
Matrix[ *maccuB ]</source>
 
Оператор * преобразует <tt>maccuB</tt> в список параметров, что позволяет существенно упросить процесс создания матрицы. Более того, это единственно удобный метод еееё создания. Все остальные методы создания матрицы (например, метод <tt>.new</tt>) работают не столь изящно и интуитивно.
 
=== Как изменить элемент матрицы? ===
Строка 2970:
{{info|Обратите внимание, что <tt>BekTop</tt> создается точно также, как и матрица. По сути, вектор - это матрица, которая состоит лишь из одной строки. А матрица, в свою очередь - это массив векторов}}
 
Итак, в чемчём соль? Дело в том, что для сложения векторов необходимо сложить их соответствующие координаты. В случае же с массивами происходит конкатенация (сцепление) массивов. Вот именно в этой роли вектора не имеют себе равных. В остальных случаях, замечательно работает массив.
 
=== Хитрости ===
Строка 2977:
 
==== Решение систем линейных уравнений методом Гаусса ====
Как я уже рассказывал раньше, первая моя программа на Ruby (декабрь 2002 года) -- реализация симплексного алгоритма для решения задач оптимизации. Весь фокус в том, что я эту программу нашелнашёл! Это 11 Кбайт и 363 строчки (140 + 223) программного кода. Кстати, если найду подходящее для неенеё место, то обязательно надо будет опубликовать.
 
Но суть не в этом. После того, как я еееё нашелнашёл, у меня появилась идея-фикс: реализовать эту программу заново, но с высоты текущего уровня. Вот только для этого надо вспомнить хотя бы что-то по поводу симплексного алгоритма (чтение моего старого программного кода мне помогло мало). Первое, что пришло мне на ум -- там был метод Гаусса (правда, насколько я помню, не в чистом виде). Вот как раз его мы сейчас и реализуем.
<source lang=ruby>require 'mathn'
ypaBHeHue = [Vector[1,2,1,1],Vector[1,5,6,2],Vector[1,5,7,10]]</source>
Строка 3052:
p ypaBHeHue #-> [Vector[1,0,0,19], Vector[0,1,0,-13], Vector[0,0,1,8]]</source>
 
Ну вот и все... вроде как решение получили. Но было бы замечательно, если бы выводилось не все уравнение, а только столбец свободных членов. Задачка простенькая, но важная. Давайте еееё решим:
<source lang=ruby>p ypaBHeHue.map{ |vector| vector.to_a }.transpose[-1] #-> [19,-13,8]</source>
 
Теперь задачу можно считать решенной! Жаль только, что программа работает только для уравнения 3х4. Надо бы добавить несколько итераторов, чтобы они самостоятельно определяли размерность уравнения. Для этого нужно проследить чередование индексов и записать для них итераторы (хоть я и недолюбливаю <tt>.each</tt>, но для данного случая он пришелсяпришёлся как нельзя кстати). Расписывать преобразование я не буду, так как не вижу в этом большой надобности. Поэтому, смотрите сразу то, что у меня получилось:
<source lang=ruby>require 'mathn'
 
Строка 3081:
Обратите внимание, что <tt>ypaBHeHue</tt> задается через матрицу (которая, не без помощи метода <tt>.row_vectors</tt>, преобразуется в массив векторов). Также обратите внимание, что получить последний столбец можно посредством итератора <tt>.map</tt> и метода "батарейка".
 
Кстати, реализация симплексного алгоритма подразумевает ещеещё несколько интересных задач:
# Если какой либо элемент главной диагонали нулевой, то необходимо переставить строки и/или столбцы, чтобы это исправить.
# Преобразование целевой функции и системы уравнений из текстового формата (<tt>2*x1-x2-x4 > min</tt> и <tt>x1-2*x2-2*x3-x4 <= 10</tt>) в <tt>YAML</tt>
Строка 3135:
После этого, он сильно бьет себя по лбу и выкрикивает нечто вроде «Я идиот!» И все от осознания того, что процесс откладки давно можно было упростить таким способом.
 
А вот другая история. Программист пишет и отлаживает программу, которая все необходимые данных выводит на экран. Но в конечном итоге программа должна запускаться без участия человека и еееё вывод нужно сохранять в файл (для дальнейшей обработки). Переписывать всю программу лень и поэтому в начало своей программы он вставляет парочку волшебных строчек:
<code>$stdout = File.open('выходные данные.txt',<u>'w'</u>)
$stderr = File.open('сообщения об ошибках.txt',<u>'a'</u>)</code>
Строка 3175:
== Сети ==
===Как написать [[w:Троянская программа|троян]]?===
Однажды, один из студентов (<tt>Geka</tt>) попросил меня рассказать о том, как создать простейшее клиент-серверное приложение на Ruby. Перед тем, как подойти ко мне он уже облазил несколько форумов, но у него все равно осталось много вопросов. Отчаявшись он наконец подошелподошёл ко мне...
 
{{Начало цитаты}}
Строка 3182:
 
====Построение серверной части====
На руках у него уже была серверная часть программы, которая позволяла манипулировать удаленной файловой системой (как потом оказалось, ему еееё презентовал <tt>Invalid</tt>). Давайте на неенеё посмотрим...
 
<code>require 'socket'
Строка 3264:
Этот программный код создает сервер, который будет прослушивать порт <tt>3000</tt>. В качестве порта может использоваться любой другой (например, <tt>31337</tt>). Менять имя хоста (<tt>localhost</tt>) не нужно, если только у Вас не несколько сетевых интерфейсов. Если у Вас их все таки несколько, то ничего по поводу смены хоста Вам объяснять не надо. Вы и так все, скорее всего, знаете ...
* <code>if (<u>session = server.accept</u>) ... end</code>
При помощи такой нехитрой комбинации ловится соединение с сервером. Обратите внимание, что в примере студента использовалась конструкция <code>while</code>. ЕеЕё пришлось упразднить, т.к. большой необходимости в ней не было. Как только вызов <code>server.accept</code> возвращает значение, то это означает, что к серверу подсоединился клиент. В переменную <code>session</code> записывается указатель на соединение. С ним-то мы и будем работать дальше...
* <code>cmd, arg = <u>*</u>session.gets.chomp.split</code>
Данный код интересен тем, что в программе студента для его реализации задействовано аж три строчки. И все лишь потому, что он не знал, что такое оператор <tt>*</tt> (звездочка). Этот оператор преобразует массив в список параметров. Тем самым он освобождает нас от нуждого присваивания. Сам же код получает от клиента строку, которая интерпретируется им как "команда и аргумент, разделенные пробелом". Обратите внимание, что работа идет с переменной <code>session</code>, а не <code>server</code>.
Строка 3273:
{{info|Для того, чтобы прекратить работу сервера необходимо нажать комбинацию клавиш <tt>Ctrl+C</tt> или <tt>Ctrl+Break</tt>. Команду отключения данный сервер не поддерживает. Команда <tt>shutdown</tt> относится к клиенту}}
 
Несмотря на то, что данный сервер вполне рабочий, у него есть один существенный недостаток - он не работает для нескольких клиентов. Для того, чтобы это реализовать необходимо обрабатывать каждое соединение с клиентом в отдельном потоке. Кто изучал мультипроцессорное программирование, тот понимает о чемчём речь. Вот только не стоит сразу кидаться в книжный магазин за необходимой литературой. В составе дистрибутива Ruby уже есть замечательная библиотека <tt>gserver</tt>, которая как раз и занимается тем, что реализует обработку запросов клиентов в отдельных потоках. Для демонстрации еееё работы перепишем предыдущую программу под <tt>gserver</tt>.
 
<code><u>require 'gserver'</u>
Строка 3331:
Как мы уже видели выше, серверная часть полностью определяет функциональность всего клиент-серверного приложения. Клиентская часть же лишь принимает от пользователя запросы, пересылает их серверу и получает ответ, который передает пользователю.
 
Хотя Geka и имел в своем распоряжении клиентскую программу, еееё мы рассматривать не будем, т.к. с тех пор его сервер претерпел некоторые изменения и прежняя клиентская часть не может работать с нынешней серверной. Мы напишем новую клиенскую часть "с нуля".
 
Для соединения с сервером по протоколу [[w:TCP/IP|TCP/IP]] используется класс <tt>TCPSocket</tt> из библиотеки <tt>socket</tt>. Для того, чтобы наша задача была конкретней, мы заставим сервер (при помощи нашего клиента) выполнить следующие команды: <tt>ls</tt>, <tt>cd ..</tt>, <tt>ls</tt> и <tt>shutdown</tt>. Вывод результата выполнения этих команд мы будем осуществлять на экран, но пользователь не получит возможности изменять эту последовательность действий (кроме как исправив программу). Он увидит только результат. Логика здесь следующая: зачем заставлять пользователя вводить команды, если это может делать программа? Если же пользователю нужна другая последовательность команд, то пусть использует telnet или правит клиентскую часть под свои нужды.
Строка 3363:
}</code>
 
Уже лучше... по крайней мере программа работает. Но давайте поразмышляем над ситуацией, которая произошла с методом <code>.read</code>. Если немного подправить сервер и выдавать после каждой передачи этот символ, то программа с <code>.read</code> могла бы с успехом работать. Какова здесь мораль? А мораль в том, что для успешной работы необходимо с сервера передавать сигнал, который означал бы "последняя строка, которую я передаю клиенту". Чтобы клиент не пытался читать данные с сервера, а начинал их передачу. Вполне естественно, что добавление такого сигнала означает модификацию сервера. В качестве сигнала последней строки мы будем использовать строку <tt>+ОК</tt>. Почему именно такую? Просто видел еееё где-то, вот и решил использовать. Если хотите, то можете использовать свою строку. Только не забудьте об этом, когда будете писать программу-клиент. Вот модифицированный сервер:
 
<code>require 'gserver'
Строка 3403:
</code>
 
Была добавлена лишь одна команда (хотя, самые внимательные могут заметить, что ещеещё и метод <code>.print</code> был заменен на <code>.puts</code>) - <code>session.puts <u>"+OK"</u></code>. Она будет выполнятся после каждой передачи данных от сервера к клиенту. Тем самым мы будем извещать клиента о том, что передача завершается. Теперь займемся переписыванием клиента. Необходимо исправить код там, где происходит чтение, чтобы он учитывал строки <tt>+OK</tt>.
 
<code>require 'socket'
Строка 3530:
<nowiki>loop{</nowiki> <u>system</u>( gets )<nowiki>{ |str|</nowiki> puts str <nowiki>} }</nowiki></code>
 
Способ не совсем честный, но нет причин о немнём не рассказать. Данный способ использует метод <code>system</code>, который вызывает внешнюю программу (в данном случае <tt>telnet</tt>). Далее все введенное с клавиатуры уходит в программу <tt>telnet</tt>, а выдаваемое на экран берется из результата работы этой программы.
 
После запуска данной программы надо ввести имя хоста и порт. Далее, можно вводить команды, которые поддерживает сервер (в нашем случае <tt>ls</tt>, <tt>cd</tt> и <tt>shutdown</tt>).
Строка 3536:
 
===Как создать сетевой блокнот?===
Идея написать подобную программу появилась после прочтения статьи [http://doci.nnm.ru/php_forall_/22.11.2006/sozdaem_svoj_onlajn_bloknot/ Создаем свой online-блокнот]. Продемонстрированная там программа предельно проста, но на еееё примере очень удобно показать работу с [[w:ERB|ERB]]. А учитывая тот факт, что ERB используется в составе инструментария [[w:Ruby on Rails|Руби на Рельсах]], то ценность этой статьи становится очевидной.
====Первое приближение====
В первом приближении мы попытаемся реализовать ту же самую функциональность, что и описана в статье. Вот только [[w:PHP|PHP]] подразумевает наличие веб-сервера, который будет заниматься интерпретацией его команд. В нашем же примере мы самостоятельно поднимем веб-сервер (написанный на Ruby), чтобы не заморачиваться с настройкой стороннего.
Строка 3591:
{{Внимание|Обработка кода внутри тегов ERB идет только во время обработки шаблона (вызова метода <tt>.result</tt>)}}
 
Не знаю почему, но ERB для меня это "PHP, только на Ruby". Эта фраза обладает столь магическим свойством, что после неенеё собеседнику все становится понятным. Вот и вам советую воспринимать ERB как "PHP на Ruby".
 
{{info|Библиотека WEBrick поддерживает PHP-скрипты благодаря [[w:CGI|CGI]]-сервлету}}
Строка 3617:
: Создаем переменную <tt>wa6JIoH</tt> и присваиваем ей строку, которая по совместительству является ERB-шаблоном. Внутри строки можно заметить тег <tt><nowiki><%= ... %></nowiki></tt> внутри которого осуществляется считывание файла <tt>notepad.txt</tt>. Результат считывания будет вставлен вместо тега <tt><nowiki><%= ... %></nowiki></tt> во время обработки ERB-шаблона.
* <code>resp.body = <u>ERB.new( wa6JIoH ).result</u></code>
Создаем объект ERB и передаем туда подготовленный ERB-шаблон. Обрабатываем его методом <tt>.result</tt> и результирую строку передаем методу <tt>.body=</tt>, который подставляет еееё в качестве ответа на запрос.
 
И что мы получаем в результате? Подключили "лишнюю" библиотеку и создали "лишнюю" переменную? Не будем спешить с выводами. Использование библиотеки ERB позволяет вынести шаблон во внешний файл. Тем самым мы очищаем Ruby-код от HTML-кода.
Строка 3637:
</html></source>
 
Переменную шаблон мы убираем, а вместо неенеё вставим считывание файла c ERB-шаблоном (<tt>index.html</tt>).
 
<code>require 'webrick'
Строка 3673:
На этом все... из чего состоит наша программа теперь?
* <tt>notepad.rb</tt>. Программа-сервер. Назвать файл можно на свое усмотрение. Главное, чтобы работал. Содержит логику, которая осуществляет конфигурирование и запуск сервера.
* <tt>index.html</tt>. ERB-шаблон. В немнём содержится вся логика программы, кроме реализованной в программе-сервере.
* <tt>notepad.txt</tt>. Файл данных. В немнём содержатся записи, которые мы вводим и отображаем посредством нашей программы.
 
В качестве задания для самостоятельной проработки, предлагаю вам реализовать не только ввод и редактирование, но и просмотр без возможности редактирования. Подсказка: подключайте второй сервлет.
Строка 3702:
end
p ping(ARGV[0] || "localhost")</source>
Итак, давайте разберем, что делает наш метод. Он создает соединение посредством класса TCPSocket и тут же закрывает его. Если соединение проходит слишком долго (хост не существует в сети) или произошла какая-то другая ошибка (не поддерживается протокол или ещеещё что-то), то метод возвращает <code>false</code>. Если удаленный хост явно отверг наш запрос или принял его, то мы возвращаем <code>true</code>. Как видите, ничего сложного в этом нет.
 
=== Простейший датчик работы службы ===
Строка 3711:
* баннер службы.
 
ЕеЕё я решил писать в логи следующим образом... Каждый день будет создаваться файл лога и каждый час в него будет писаться информация о работе службы. В качестве планировщика заданий использовался виндовый [http://www.nncron.ru/ Cron], который запускал мою программу в нужное время. Для начала я написал программу, которая соединяется со службой SMTP и получает от него баннер:
<source lang=ruby>require 'socket'
 
Строка 3738:
f.puts( "#{sprintf( '%02d',Time.now.hour )} | #{end_time-begin_time} | #{request}" )
}</code>
Великолепно! Программа работает... но иногда зависает. И тогда меня посетила ещеещё одна мысль: на соединение отводить всего одну секунду (не уложился — сам дурак). Если соединение зависало, то в файл записывалось <tt>timeout</tt>. Чтобы "отводить время на выполнение операции" нужно задействовать библиотеку <tt>timeout</tt>. Она у меня входила в состав дистрибутива. Итак, переписываем нашу программу:
<code><u>require 'timeout'</u>
require 'socket'
Строка 3793:
}
}.real</source>
Или можно просто вместо <tt>Benchmark.measure</tt> использовать <tt>Benchmark.realtime</tt>. Теперь надо бы разделить ошибки по таймауту и ошибки соединения. Для этого надо добавить лишь ещеещё один блок <code>;rescue</code>. Помимо этого мне не понадобится все сообщение от службы. Мне и кода сообщения достаточно. Итак, смотрим, что получилось:
<code>require 'socket'
require 'benchmark'
Строка 3873:
 
====Запрос заголовка====
Во время скачивания передается не только сама страница (<b>тело сообщения</b> или на англ. body), но и техническая информация (<b>заголовок</b> или на англ. head). Мы ещеещё не раз будем свидетелями такого поведения в протоколах сети Интернет. В заголовке содержится информация об успешности запроса (код возврата), типе передаваемых данных, кодировке, HTTP-сервере и так далее. Для примера, мы будем производить HTTP-запрос и получать ответ только в виде заголовка. Запись заголовка в файл производить не будем, так как в реальной практике этот прием практически не используется. Сначала «потренируемся на кошках», то есть на классе Net::HTTP:
 
<code>require 'net/http'
Строка 3962:
{{info|Очень часто необходимо осуществить передачу данных между компьютерами по сети. Для этого вполне подойдет <tt>WEBrick</tt>. Быстро напишите веб-сервер, который будет просматривать нужную директорию. Теперь, для вас это больше не проблема!}}
 
Но вернемся к заказанной мне программе. Она будет состоять из двух частей: обычная <tt>html</tt>-страница (<tt>index.html</tt>) и [[w:Сервлет|сервлет]] (<tt>/input</tt>). Страница <tt>public_html/index.html</tt> будет содержать форму в которую будут вводится исходные данные. ЕеЕё задачей будет формирование запроса для сервлета. Сервлет <tt>/input</tt> будет получать номера подразделений, а выдавать их названия и ФИО действующих начальников. Вот код <tt>public_html/index.html</tt>:
 
<source lang="html4strict"><nowiki><HTML><BODY><form </nowiki><u>action='/input' method='post'</u><nowiki>><div align=center>
Строка 4015:
{{info|Geka, когда увидел этот код, воскликнул: "И не лень было такой код писать?" На что я ему резонно ответил, что этот код является результатом программы-генератора, которую я не привожу здесь, чтобы не отвлекать внимание читателя от более интересной темы}}
 
Хотя код и довольно объемныйобъёмный, но я решил поместить его целиком, чтобы программа у вас работала точно также, как и у меня. Первое, на что следует обратить внимание, это параметры [[w:Тег (языки разметки)|тега]] <tt>FORM</tt>. Именно он занимается тем, что подготавливает данные для сервлета и переправляет их ему.
* <code>action='<u>/input</u>'</code>
: Адрес, по которому будут передаваться данные. Это может быть как [[w:CGI|CGI]]-приложение, так и сервлет. В нашем случае это сервлет <tt>/input</tt>.
Строка 4110:
 
====Сервлетки в рубиновом соусе====
Мы уже неоднократно упоминали понятие [[w:Сервлет|сервлет]], но особенно на немнём не останавливались. Давайте сейчас рассмотрим типовые сервлеты, которые имеются в стандартной поставке <tt>WEBrick</tt>
* '''Файловый сервлет'''
: Реализует взаимосвязь запроса с реальным файлом (или директорией). В самом первом примере мы использовали файловый сервлет, когда передавали <code>:DocumentRoot => 'public_html'</code> в качестве параметра методу <code>.new</code>. Это было равнозначно созданию файлового сервлета на корневой директории веб-сервера. Функциональность файлового сервлета описана в классе WEBrick::HTTPServlet::FileHandler.