Ruby Advanced
Часть I Марченко Светлана
Ruby Advanced
Графические интерфейсы для Ruby Потоки в Ruby Сценарии и системное администрирование Тестирование и отладка Создание пакетов и распространение программ Ruby и Web приложения Ruby tricks
Графические интерфейсы для Ruby (Tk, GTK+, FOX, QtRuby)
Ruby/Tk Tk — платформо-независимая система
Perl, Tcl (Perl/Tk)
Переносимость, стабильность
Корневой контейнер с виджетами + геометрический менеджер(pack, grid, place)
Блоки вычисляются в контексте вызываемого объекта (instance_eval), а не в контексте вызывающей программы
Ruby/GTK2
Ruby/GTK2 (GIMP Toolkit) Лежит в основе графического менеджера GNOME, но MS Windows, MAC OS X c X Window System
Расширение Ruby/GTK2 (GTK+ 2.0) НЕ ПУТАТЬ c Ruby/GTK (GTK+ 1.2), которое несовместимо и считается устаревшим
GNU LGPL
Концепция фреймов, диалогов, окон и менеджеров размещения
Располагает богатым набором виджетов (стандартные + более сложные, например, деревья, многоколонные списки)
GTK+ написана на С, но спроектирована в ОО манере
Ruby/GTK2 предоставляет объектно-ориентированный API
GTK2 написана вручную → API выдержан в духе Ruby (с использованием блоков, необязательных аргументов и т.д)
GTK+ на базе Glib, Pango, ATK, Cairo, GDK
Графические интерфейсы для Ruby
Недостатки недостаточно богатый набор стандартных диалоговых окон
все строки должны быть в UTF-8 (в начало сценария $KCODE= "U")
FXRuby (FOX — Free Objects for X) относительно новая технология акцент на быстродействие и межплатформенную совместимость
не обертка платформенного API
написана на С++, объектно-ориентированная
FXRuby — привязка к Ruby библиотеки FOX (Lyle Johnson)
также перенесена на платформу MS Windows
парадигма сообщение/получатель
парадигма автоматического обновления
широкие возможности(связь между приложениями и его окружением, сигналы)
гибкость (поддерживает API для работы с OpenGL) Графические интерфейсы для Ruby
QtRuby акцент на кросс-платформенность (Windows, Mac, UNIX)
Qt (GPL and Commercial License)
концепция сигналов и слотов
Qt написан на С++, для Ruby вводится некая избыточность для возможности «рубистских »выражений Qt::Widget::minimumSizeHint Qt::Widget::minimum_size_hint
widget.setMinimumSize(50); widget.minimumSize = 50; widget.minimum_size = 50;
a.isVisible();
a.visible? Потоки в Ruby
Потоки определены на пользовательском уровне и не зависят от ОС Создание потоков thread = Thread.new #действия этого потока end Передача им входной информации (параметры, передаваемые в блок) Останов Синхронизация – может быть очень дорогой
Создание потоков и манипулирование ими
new возвращает объект типа Thread (синоним fork) a = 1 b = 2 c = 3 thread2 = Thread.new(a,b) do |a, x| с = 4 # доступ ко всем переменным из внешней области видимости а = 2 # любые другие манипуляции end Доступ к локальным переменным потока через специальный механизм (организован как хэш) из любого места видимости объекта Thread Thread.current[:var1] = «var_value» #внутри потока Thread.current[«var2»] = 3 x = thread2[:var1] y = thread2.key?(«var2») # проверяет используется ли в потоке указанное имя
Опрос и изменение состояния потока
list — массив «живых» потоков main — возвращает ссылку на главный поток current — позволяет потоку идентифицировать себя exit, pass, start, stop, kill — позволяют управлять потоком изнутри и извне Thread.exit, Thread.pass Не существует метода экземпляра stop → поток только может приостановить собственное выполнение, но не выполнение другого потока alive?, stop?, status (run, sleep, nil) $SAFE, safe_level join — не может быть вызван самим потоком у себя, только у другого потока Thread.list.each{ |t| t.join if t != Thread.main } интерпретатор обнаружит тупиковую ситуацию: два потока вызвали друг у друга join метод value неявно вызывает join
Опрос и изменение состояния потока (пример)
max = 1000 thr = Thread.new do sum = 0 1.upto(max) {|i| sum += i} sum end guess = (max*(max+1))/2 if guess == thr.value puts «правильно » else puts «неправильно » end
Обработка исключений и группы потоков Theread.abort_on_exception = true (t1.abort_on_exception = true) Группы позволяют логически связывать потоки (по умолчанию все потоки в группе Default) file_threads = ThreadGroup.new file_threads.add f1 file_threads.add f2 В любой момент поток принадлежит только одной группе class ThreadGroup (new, add, list) В класс ThreadGroup можно добавлять свои методы, например: возобновление потоков в группе групповое ожидание групповое завершение class ThreadGroup def join #... end
... end Синхронизация потоков
Критические секции — простейший способ синхронизации Thread.critical = true #код , который хотим защитить Thread.critical = false Применять только в самых простых случаях! Возможны такие комбинации операций с потоками, что поток планируется даже, если другой поток в критической секции. Синхронизация доступа к ресурсам (mutex.rb) lock, try_lock, unlock mutex = Mutex.new mutex.lock #если мьютекс занят , ждет освобождения #something mutex.unlock
Синхронизация потоков mutex.rb
if mutex.try_lock #не ждет освобождения , возвращает false, если занят #something else #something end
mutex.synchronize do #something end synchronize захватывает мьютекс и автоматически освобождает его объекты могут выступать в роли мьютекса Библиотека mutex_m с модулем Mutex_m, который расширяет пользовательский класс: у объектов класса можно вызывать методы мьютекса
Синхронизация потоков
Библиотека sync.rb предоставляет еще один способ синхронизации: блокировка со счетчиком locked?, shared?, exclusive?, lock, unlock, try_lock Условная переменная — используется вместе с мьютексами (очередь потоков) require 'thread' @music = Mutex.new @violin = ConditionalVariable.new @violins_free = 2 @music.synchronize do @violin.wait(@music) while @violins_free == 0 #... end
Синхронизация потоков monitor.rb
Monitor(monitor.rb) — еще один способ синхронизации: захваты одного и того же мьютекса могут быть вложенными Например, может понадобиться при рекурсивном вызове метода применяется для расширения класса class ConditionalVariable в библиотеке monitor.rb — расширенный по сравнению с условными переменными в Thread: new_cond — создание условной переменной
wait_until, wait_while — методы, блокирующие поток в ожидании выполнения условия
wait(timeout) - можно определить timeout при ожидании (в секундах)
Сценарии и системное администрирование
Универсальность Ruby позволяет использовать его для взаимодействия с ОС на высоком уровне (все, что есть в bash, можно реализовать с Ruby) Запуск внешних программ
Перехват вывода программы
Манипулирование процессами
Стандартный ввод/вывод
Не всегда удобно использовать из-за сложности синтаксиса (использование различных библиотек) Библиотека Shell (не всегда работает на Windows) ActiveScriptRuby — язык, предоставляющий мост между COM и Ruby для Windows приложений Ruby можно использовать в html
Сценарии и системное администрирование shell.rb
sh = Shell.new #создали объект , ассоциированныйс текущимкаталогом sh1 = Shell.cd (''/tmp/hal'') #объект , ассоциированный с указанным каталогом
Встроенные команды (например, echo, cat, tee) возвращают объекты класса Filter sh.cat (''/etc/modt'') > STDOUT ! Автоматически shell вывод команд никуда не направляет Class Filter понимает, что такое перенаправление ввода/вывода
Операторы >, <, |
Метод def_system_command позволяет «инсталлировать» системные команды по своему выбору Shell.def_system_command ''ls''
Различные сценарии
AllInOneRuby — дистрибутив Ruby в одном файле (интерпретатор, системные классы, стандартные библиотеки) Tar2RubyScript — программа получает дерево каталогов и создает самораспаковывающийся архив: Ruby программа и tar архив (если не библиотека, нужен файл init.rb) Созданный архив можно запустить на любой машине, где установлен Ruby
RubyScript2Exe — преобразует Ruby приложение в один двоичный файл(подходит для Windows, Linux, Mac OS X): Не компилятор
Собирает файлы, являющиеся частью установленного дистрибутива Ruby на вашей машине (не нуждается в кросс-компиляции)
Исполняемый файл «усечен» - в нем нет неиспользуемых библиотек
Не требует установленного Ruby для запуска, т. к. включает интерпретатор Ruby, среду исполнения и необходимые внешние программы
Модуль Etc — получает информацию из /etc/passwd и /etc/group login пользователя, от имени которого запущена программа
Структуру passwd (name, dir, shell) Аналогичные функции для групп Тестирование и отладка
Библиотека Test::Unit (включена в дистрибутив с 2001 года) для анализа тестового кода применяется отражение
для наследника класса Test::Unit::TestCase все методы, имена которых начинаются с test считаются тестовыми
тесты выполняются в алфавитном порядке
методы setup, teardown используются для создания особой среды выполнения тестов, вызываются для каждого теста
утверждения assert_equal (expected, actual)
любой исполнитель тестов можно вызвать методом run и передать параметр, описывающий набор тестов
Тестирование и отладка
Комплект инструментов ZenTest Основной инструмент — исполняемая программа, которая генерирует файл с тестами тестируемый класс — основа для создания тестового класса: Создается класс с именем тестируемого класса и префикса Test, наследованный от Test::Unit::TestCase, и все методы c префиксом test_
Разработчику (или тестеру) остается только реализовать тело тестовых методов unit_diff — программа, которую следует вызывать как фильтр после тестовой программы autotest — прогоняет все тесты по мере их обновления multiruby — позволяет тестировать код относительно разных версий Ruby
Тестирование и отладка
Отладчик Ruby (библиотека debug) ruby -rdebug myfile.rb {rdb:1} list, step, break, catch, delete, help, next, quit позволяет просматривать стек вызовов и перемещаться по нему Использование irb библиотека ruby-breakpoint (метод breakpoint) не входит в стандартный дистрибутив сеанс irb (программа интерактивной работы с Ruby) — вызывается, когда в процессе исполнения встречается точка прерывания, также позволяет вызывать ранее определенные методы и изменять значения переменных клиент drb - позволяет удаленно отлаживать программу Ruby, работающую в другом процессе
Тестирование и отладка
Измерение покрытия кода rcov собирает статистику выполнения программы (в каталоге coverage) — очень простая утилита в файле index.html предоставляет сводную статистику и ссылки на исходные тексты, где подсвечена каждая строка, которая выполнялась хотя бы один раз имеет параметры для настройки предоставляет API для написания своих инструментов анализа Измерение производительности профилировщик profile — выдает статистику выполнения программы, какие методы как часто вызывались и какое время это заняло библиотека benchmark — при вызове можно передать блок кода, на выходе: время, затраченное процессором в режиме пользователя, в режиме ядра, полное затраченное время Метод inspect — предназначен для вывода объектов в виде, удобном для чтения человеком
Создание пакетов и распространение программ
Rdoc — инструмент документирования для Ruby Можно использовать даже, если в исходном тексте нет комментариев (анализирует текст программы и собирает информацию о классах, модулях, методах и т.д.)
На выходе каталог doc и html-файлы в нем
Ассоциирует комментарии с конкретными частями программы
Документация API ссылается на сам код
Обладает собственным механизмом разметки, чтобы не редактировать выходной html-файл вручную
Модификаторы документации - указывают, какие части текста нужно документировать
:doc
:nodoc, :nodoc: all
:notnew — предотвращает документирование метода new
Установка и подготовка пакета setup.rb
Библиотека setup Создать дерево каталогов с определенной структурой(lib, ext, bin, data and etc.)
3 основных этапа — config, setup, install
lib/foobar/pre-setup.rb
pre или post-[имя задачи].rb (config, setup, install, test, clean, dist-clean)
hook API, упрощающее решение ряда задач (установить значение конфигурационного параметра, получить путь к текущему исходному каталогу ) Файл на верхнем уровне дерева каталогов metaconfig содержит глобальные конфигурационные параметры (config API)
Установка и подготовка пакета RubyGems
Простота установки и удаления gem-пакетов Поддержка нескольких версий Управление зависимостями Механизм запросов и поиска пакетов Специальное именование gem-пакетов (описательное_слово-номер_версии) Создание gem-пакета: Дерево каталогов со специальной структурой
Спецификация gemspec — исполняемый файл на Ruby name, version, author, platform, summary, test_file, add_dependency
Собственно создание пакета: выполнить созданный gemspec или вызвать gem build и передать файл спецификации — результат один и тот же
Распространение: сайт RubyForge, архив приложений Ruby (RAA)
Ruby и Web приложенияB
Программирование CGI(Common Gateway Interface) Библиотека cgi.rb включена в стандартный дистрибутив (вывод и обработка форм, поддержка работы с cookies и сеансами пользователей)
FastCGI (можно использовать недостающие функции из библиотеки cgi.rb)
Web серверы WEBrick — библиотека создания полноценного HTTP-сервера
Входит в стандартный дистрибутив
Ничего не знает о деталях Web приложений
Оперирует сервлетами, работающими независимо друг от друга
Обработчики монтирования и обработчики сигналов
Mongrel — основная цель: повысить производительность по сравнению с WEBrick
Часто используют с Rails
Можно запустить как приложение(start, stop, restart)
Система GemPlugin — автозагружаемые gem-пакеты расширяют функциональность сервера Ruby и Web приложения
Ruby On Rails — каркас для Web, построен на основе MVC (быстрая разработка Web приложений) Nitro — комплект инструментов для разработки Web приложений Поддерживает много архитектур и паттернов
Облегчает разработку сложных приложений с поддержкой DB (ORM Og)
DRY (Don`t Repeat Yourself)
Wee (Web Engineering Easy) - «каркас для создания очень динамичных, компонентных Web приложений, на дизайн которого оказал большое влияние продукт Seaside» Использовать вместе с Nitro
В основе — идея компонентов
Каркас IOWA (Interpreted Objects for Web Applications) Работает как фоновый процесс, прослушивающий сокет в ожидании запросов
Включает различные адаптеры (CGI, Mongrel, WEBrick)
Ruby tricks
Единообразно обработать коллекции и объекты, не являющиеся коллекциями (обычный объект рассматривается как коллекция, содержащая только один элемент — сам объект) [*items].each do |item| # ... end
=== по сути является алиасом вызова объекта класса Proc, это означает, что объекты Proc можно использовать таким образом: def multiple_of(factor) Proc.new{|product| product.modulo(factor).zero?} end case number when multiple_of(3) puts "Multiple of 3" when multiple_of(7)
puts "Multiple of 7" end Ruby tricks
Создать Hash для одномерного массива fruit = ["apple","red","banana","yellow"] => ["apple", "red", "banana", "yellow"]
Hash[*fruit] => {"apple"=>"red", "banana"=>"yellow"}
match, text, number = *"Something 981".match(/([Az]*) ([09]*)/)
Job = Struct.new(:name, :occupation) tom = Job.new("Tom", "Developer") name, occupation = *tom
1.upto(100) do |i| puts i if (i == 3)..(i == 15) end Ruby tricks
|| and && string &&= string + "suffix" эквивалентно if string string = string + "suffix" end convert a Fixnum into any base up to 36 >> 1234567890.to_s(24) => "6b1230i"
>> 1234567890.to_s(36) => "kf12oi"
Источники
Хэл Фултон «Программирование на языке Ruby» http://ruby-gnome2.sourceforge.jp/ - справочное руководство Ruby/GTK2 http://www.gtk.org/ http://fxruby.org/ - сайт FXRuby http://www.erikveen.dds.nl/ruby.html - сайт автора пакетов AllInOneRuby, Tar2RubyScript, RubyScript2Exe http://ruby-doc.org/ http://stackoverflow.com/questions/63998/hidden-features-of-ruby - Hidden features of Ruby