Н. В. Морев, 2006-05-21

Разработка web-приложений в среде Ruby on Rails


Содержание

Введение
Язык Ruby
Обзор Ruby on Rails
Model
View
Controller
Среда разработки
Установка
Приложение
Модель данных
Варианты использования
Создание шаблона приложения
Расширение
Критика и сравнение с другими фреймворками
Вывод
Литература

Введение

Ruby On Rails — фреймворк для разработки веб-приложений. Он был разработан фирмой 37signals и выпущен как бесплатный продукт с открытыми исходниками. С использованием RoR этой фирмой были разработаны несколько довольно успешных продуктов, работа с которыми осуществляется по платной подписке или бесплатно: обеспечение совместной работы над проектами, веб-чат, органайзер и другие. За очень короткое время (версия 1.0 была выпущена в декабре 2005 года) этот фреймворк приобрел огромную популярность среди разработчиков небольших и средних веб-приложений. Об этом свидетельствует хотя бы то, что количество возвращаемых Google-ом результатов по запросу «Ruby on Rails» сравнимо с запросом «Struts», являющимся одним из самых известных веб-фреймворков в мире Java, — 13 и 19 миллионов упоминаний соответственно.

Каковы же основные особенности данного фреймворка?

Язык Ruby Интерпретируемый скриптовый язык для быстрого и простого объектно-ориентированного программирования.

Паттерн MVC.  Наиболее широко используемый паттерн для разработки веб-приложений в большинстве фреймворков.

Ajax.  Встроенная поддержка XMLHttpRequest: автоматическая генерация клиентского JavaScript кода, методы Ajax не отличаются от обычных, набор визуальных эффектов (drag-n-drop, автодополнение, сортировки).

Rapid Application Development.  Фреймворк поддерживает концепцию быстрой разработки приложений. Это означает, что вполне функциональное веб-приложение можно разработать буквально за несколько минут. Это приложение будет использовать всевозможные встроенные в систему умолчания (стандартные наименования часто используемых полей в БД, стандартное оформление, и т.д.). После этого можно последовательно вносить изменения в код программы, чтобы довести ее до требуемого состояния. Такой подход очень хорошо согласуется с так называемыми «гибкими» (Agile) методологиями разработки, самая известная из которых Extreme Programming.

Язык Ruby

Ruby — это интерпретируемый скриптовый язык программирования для быстрого и простого объектно-ориентированного программирования. Он включает различные средства для обработки текстовых файлов и решения задач администрирования. Язык Ruby расширяемый, переносимый, свободно-распространяемый.

Характеристики Ruby:

  • Простой синтаксис, частично заимствованный из таких языков, как Ada и Eiffel.
  • Поддержка исключений.
  • Переопределение операторов.
  • Чистый, полностью объектно-ориентированный язык. Все данные являются объектами, без исключений.
  • Возможность добавлять методы к объекту или к классу во время выполнения так, что при необходимости, два экземпляра одного класса могут отличаться друг от друга.
  • Поддерживается только единственное наследование. Но существует понятие модуля, как коллекции методов, которые можно импортировать в класс. Существует мнение, что такой способ — более красивый, чем множественное наследование, которое сложнее и реже используется.
  • Анонимные функции и замыкания (closures).
  • Возможно структурировать код в блоки { ... } или do ... end, которые можно передавать методам в качестве аргумента.
  • Сборщик мусора.
  • Возможность написания расширений на C, с использованием специального API, в котором поддерживается сборка мусора. Есть поддержка генератора C-расширений для скриптовых языков SWIG.
  • Поддерживается два типа целых чисел: малые Fixnum и большие Bignum. При этом программисту не нужно беспокоиться о том, какой тип использовать, преобразование происходит автоматически.
  • Не требуется объявление переменных. Область видимости переменной описывается в ее имени с помощью следующего соглашения: var — локальная переменная, @var — переменная экземпляра класса, $var — глобальная переменная.
  • Независимая от платформы поддержка многопоточности.
  • Язык портирован на все используемые в настоящее время платформы: Linux и другие разновидности UNIX, DOS, все версии MS Windows, MacOS, BeOS, OS/2 и т.д.

Поясним подробнее некоторые использованные выше понятия.

Скриптовыми обычно называют языки, обладающие большинством из следующих характеристик: язык спроектирован для применения в конкретной предметной области или может быть легко модифицирован для этого, обладает упрощенным синтаксисом, не компилируемый, интерпретатор может работать в режиме диалога, простота программирования в ущерб производительности, динамическая типизация.

Анонимная функция — это функция, которая может не иметь имени. Обычно это простая функция, используемая в программе единственный раз. Анонимную функцию можно создавать непосредственно на этапе выполнения программы и передавать в качестве аргумента. Например, сложить два числа с помощью анонимной функции можно так:


irb(main):008:0> proc {|x, y| x + y}.call 4, 5
=> 9

irb(main):025:0> def add(num)
irb(main):026:1>   proc {|x| x+num }
irb(main):027:1> end
=> nil

irb(main):028:0> add10 = add(10)
=> #<Proc:0x07c459e0@(irb):26>

irb(main):031:0> add10.call(10)
=> 20

    

Часто анонимные функции применяют в качестве callback-процедур.


trap "SIGINT", proc { puts "^C was pressed." }

    

Замыкание (closure) — это функция, которая обращается к свободным переменным в собственном контексте. Замыкание — это объединение кода функции и ее собственной области видимости переменных на момент создания. Пример с созданием функции add10, приведенный выше, демонстрирует замыкания. Функция add возвращает динамически созданную функцию, содержащую в себе ссылку на переменную num, которая существовала при создании функции и продолжает существовать далее, при ее вызове. Следующий пример показывает, что в замыкании сохраняется именно область видимости, а не просто значение переменной:


irb(main):045:0> def add_inc(num)
proc {|x| num = num+1; x+num}
end
=> nil
irb(main):048:0> add_inc10 = add_inc(10)
=> #<Proc:0x07be3790@(irb):46>
irb(main):049:0> add_inc10.call(10)
=> 21
irb(main):050:0> add_inc10.call(10)
=> 22
irb(main):051:0> add_inc10.call(10)
=> 23

    

Замыкания могут применяться, например, разработчиками программных библиотек, чтобы позволить пользователю библиотеки расширять ее, передавая замыкания в качестве аргументов определенных функций. С помощью замыканий можно определять управляющие структуры типа ветвлений и циклов. Несколько функций можно замкнуть на одну область видимости, так что они смогут обмениваться между собой значениями.

Обзор Ruby on Rails

Ruby on Rails — это свободно-распространяемый фреймворк для разработки веб-приложений, основанный на архитектуре Model-View-Controller (MVC), и базирующийся на языке программирования Ruby. Его цель — упростить разработку и позволить создавать реальные приложения в меньшем количестве кода, чем в других фреймворках, и с минимальным конфигурированием. Всего это достигается за счет развитых возможностей метапрограммирования языка Ruby.

Основные принципы RoR — исключить повторение кода, несущего одинаковую смысловую нагрузку, и стараться исключить необходимость конфигурирования, вводя общепринятые соглашения, где это возможно. Компоненты RoR интегрированы так, что программисту не требуется писать дескрипторы для их связи между собой или повторять одни и те же определения в реляционной базе данных и в коде программы.

Сам фреймворк и дополнительные расширения для него распространяются через систему RubyGems, стандартизующую формат пакетов и каналы распространения.

Model

Классы моделей в RoR строятся на основе библиотеки Active Record, которая реализует объектно-реляционное отображение данных, хранящихся в БД. Отображения Active Record обладают следущими возможностями:

  • Автоматическое отображение между классами и таблицами, аттрибутами и колонками. Достаточно просто унаследовать класс от класса ActiveRecord::Base и он автоматически отображается на таблицу с именем, соответствующим имени созданного класса.
  • Отношения между объектами поддерживаются с помощью простых макросов has_many (ссылается на множество объектов другого класса), has_one (ссылка на объект другого класса), belongs_to (другой объект ссылается на этот).
  • Агрегация одних объектов в другие (composed_of).
  • Правила валидации полей объекта.
  • Возможность представления записей в виде списков или деревьев.
  • Возможность задать действия, производимые на различных этапах жизненного цикла объекта (создание, сохранение, удаление, валидация и т.д.) как в самой модели так и в отдельном классе (паттерн Observer).
  • Иерархии наследования.
  • Поддержка транзакций как на уровне объектного представления, так и на уровне базы данных.
  • Отражения (reflections) колонок, ассоциаций, агрегаций.
  • Абстрагирование от конкретной СУБД. Поддерживаются все основные СУБД (MySQL, PostgreSQL, Oracle, SQLServer, DB2).

View

Для отображения пользовательского интерфейса в RoR предусмотрен класс ActionView, реализующий развитую шаблонную систему наподобие JSP, в которой инструкции языка Ruby включаются в текст внутри тегов <% %> или <%= %>. Для отображения шаблона служит функция render, которую можно использовать как внутри контроллера, так и внутри шаблона для отображения подшаблона.

Controller

Классы взаимодействия с пользователем в RoR строятся на основе классов ActionController. В контроллере определяются методы, которые затем становятся доступны через веб по URL вида http://example.com/app/class/method. С каждым методом по-умолчанию связан шаблон представления /app/views/class/method.rhtml. В ActionController определяются различные вспомогательные методы для управления всеми аспектами взаимодействия с пользователем и генерации частоиспользуемого кода, например, для операций Create, Remove, Update, Delete (CRUD) при работе с базой данных.

Среда разработки

В Ruby on Rails прежде всего разработчики предусмотрели вариант, когда не используется никакая интегрированная среда разработки, кроме обычного текстового редактора (рекомендуемые редакторы) и консоли. На этот случай существует набор скриптов для командной строки, которые позволяют легко выполнять наиболее часто встречающиеся при разработке задачи, такие как: создание файловой иерархии нового веб-приложения, создание новых контроллеров, моделей, запуск отладочного сервера веб-приложений и многое другое.

Для разработки и тестирования в состав RoR входит легковесный сервер приложений WEBrick. При внедрении готового приложения рекомендуется использовать Apache HTTP Server или Lighttpd с расширением FastCGI или mod_ruby.

Проект RadRails сводит все эти средства в единую интегрированную среду, которая доступна как в виде расширения для Eclipse IDE, так и в виде отдельной программы.

При использовании Eclipse будет полезно кроме RadRails также установить следующие расширения:

Установка

  1. Скачать и установить язык Ruby.
  2. Скачать и установить систему RubyGems.
  3. С помощью RubyGems установить Ruby on Rails.

Скачать платформу Ruby под Windows можно с официального сайта языка Ruby. Последняя стабильная версия Ruby на момент написания работы — 1.8.4. Дистрибутив имеет объем около 16 МБ. Процедура установки стандартная для Windows-приложений.

Скачать систему установки расширений RubyGems можно с ее сайта. Программа поставляется в виде zip-архива, который необходимо распаковать в любую папку на диске, после чего запустить скрипт установки, введя в командной строке ruby setup.rb.

Для установки Ruby on Rails необходимо ввести в командной строке gem install rails --include-dependencies. При этом система RubyGems скачает все необходимые пакеты и установит их. После чего можно переходить к созданию веб-приложения.

Для разработки можно использовать любой текстовый редактор. Например, процесс настройки Eclipse под RoR описан здесь: http://www.napcs.com/howto/railsonwindows.html.

Приложение

Разрабатываемое приложение представляет собой базу данных абонентов электронной почты, которая предназначена для регистрации абонентов и для конфигурирования программного обеспечения почтового сервера. Полный исходный код приложения находится на github-е (в архиве).

Модель данных

Требуемая почтовым сервером модель:


virtual_id_maps: id, alias
virtual_gid_maps: gid, alias
virtual_mailbox_maps: maildir, alias
virtual_maps: rcpt, alias
transport_maps: transport, domain
authmysqlrc: password, id, gid, login, maildir, info, quota
smtpd_sender_restrictions: action, alias

Эту модель можно свести к следующим таблицам MySQL:


CREATE TABLE `mailboxes` (
	`id` int(11) NOT NULL,
	`gid` int(11) NOT NULL,
	`login` varchar(255) NOT NULL,
	`password` varchar(255) NOT NULL,
	`alias` varchar(255) NOT NULL,
	`maildir` varchar(255) NOT NULL,
	`quota` int(11) NOT NULL default '0',
	`info` varchar(255) default NULL,
	PRIMARY KEY  (`id`),
	UNIQUE KEY `alias` (`alias`)
);

CREATE TABLE `remote_aliases` (
	`alias` varchar(255) NOT NULL,
	`rcpt` varchar(255) NOT NULL,
	UNIQUE KEY `alias` (`alias`)
);

CREATE TABLE `transports` (
	`domain` varchar(255) NOT NULL,
	`transport` varchar(255) NOT NULL,
	UNIQUE KEY `domain` (`domain`)
);

CREATE TABLE `rcpt_accesses` (
	`alias` varchar(255) NOT NULL,
	`action` varchar(255) NOT NULL,
	UNIQUE KEY `alias` (`alias`)
);

Создадим соответствующие этим таблицам классы моделей приложения.


MailDB> ruby script/generate model Mailbox
MailDB> ruby script/generate model RemoteAlias
MailDB> ruby script/generate model Transport
MailDB> ruby script/generate model RcptAccess

Варианты использования

Перечислим возможные сценарии использования программы:

  • добавление почтового ящика
  • редактирование почтового ящика
  • удаление почтового ящика
  • просмотр информации о ящике

Создание шаблона приложения

Рисунок 1. Результаты работы генератора scaffold

Результаты работы генератора scaffold

Запустим генератор scaffold:


MailDB> ruby script/generate scaffold Mailbox

Этот генератор создаст шаблон будущего приложения. Получившийся код всего в 60 строк (без шаблонов страниц) представляет собой простой интерфейс к БД, умеющий выдавать список записей, просматривать отдельные записи, создавать, изменять и удалять записи (Результаты работы генератора scaffold). Далее будем добавлять к приложению отдельные функции, демонстрирующие основные возможности RoR.

Расширение

Хотя наше приложение уже полностью функционально, нам все же необходимо добавить туда некоторые возможности, облегчающие его использование.

Изменение списка выводимых колонок в списке пользователей.  Если открыть шаблон страницы списка пользователей, там можно увидеть следующие строки:


<% for column in Mailbox.columns %>
  <th><%= column.human_name %></th>
<% end %>
...
<% for column in Mailbox.columns %>
  <td><%=h mailbox.send(column.name) %></td>
<% end %>

Чтобы показывать только интересные нам строки, добавим в класс модели:


@column_names_to_show = ["id", "login", "alias", "maildir", "quota", "info"]

def self.columns_to_show
    unless @columns_to_show
        @columns_to_show = columns
        @columns_to_show.delete_if {|column| not @column_names_to_show.include? column.name }
    end
    @columns_to_show
end    

И после этого меняем в шаблоне columns на columns_to_show. Этот пример также демонстрирует использование в языке Ruby блоков там, где мы использовали бы цикл в Java.

Изменение списка полей в форме создания пользователя.  Для этого необходимо всего лишь отредактировать шаблон формы, хранящийся в файле _form.rhtml, удалив ненужные поля.

Задание значений по-умолчанию для формы.  Эти значения можно задать в методе new контроллера:


def new
  @mailbox = Mailbox.new
  @mailbox.login = "p3cc"
  @mailbox.password = random_password(8)
  @mailbox.alias = ".elcom.ru"
end

Здесь мы используем метод random_password, который задается в контроллере application.rb. В нем определяются методы, доступные из всех контроллеров приложения.

Значения по-умолчанию для полей, которые пользователю не предлагается заполнять самостоятельно, описываются в методе create, который вызывается при создании нового объекта.

Валидация полей формы.  В Rails валидация полей производится на уровне класса модели. Для этого существуют простые макросы:


class Mailbox < ActiveRecord::Base
    validates_presence_of :login, :password, :alias
    validates_uniqueness_of :login, :alias
    validates_format_of :alias, :with => /^((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
...

Рисунок 2. Ошибки валидации полей формы

Ошибки валидации полей формы

Проверим работоспособность валидации, указав дублирующие или неверные значения полей. Приложение укажет нам на эти ошибки, высветив ошибочно заполненные поля (Ошибки валидации полей формы).

Установка внешней библиотеки.  Пароли почтовых ящиков хранятся в базе данных в зашифрованном виде. Чтобы автоматически шифровать их при добавлении, необходимо установить соответствующую библиотеку. На репозитории библиотек Ruby Application Archive находим требуемую библиотеку md5_crypt и видим упоминание, что она перенесена в коллекцию методов Ruby Facets. С сайта скачиваем пакет с расширением .gem и устанавливаем его утилитой gem. После этого его можно использовать в методе create:


require 'facets/more/crypt'
...
@mailbox.password = Crypt.crypt(@mailbox.password, :md5)

Ajax.  Добавим функциональность удаление строки без перезагрузки страницы. Для этого сначала добавим уникальный аттрибут id в HTML-код каждой строки таблицы.


<tr id="mbox-<%= mailbox.id %>">

Включим в шаблон страницы все необходимые библиотеки JavaScript:


<%= javascript_include_tag :defaults %>

И назначим действие, выполняемое при удалении строки:


<td class="controls"><%= link_to_remote 'Удалить',
    :url => { :action => 'destroy', :id => mailbox.id },
    :success => update_element_function( 'mbox-' + mailbox.id.to_s,
        :action => :remove) %></td>

Web Services.  Сгенерируем код, реализующий веб-сервис. Для простоты веб-сервис реализует только операцию удаления пользователя.


MailDB> script/generate web_service Mailboxes destroy
      create  app/apis/
      exists  app/controllers/
      exists  test/functional/
      create  app/apis/mailboxes_api.rb
overwrite app/controllers/mailboxes_controller.rb? [Ynaq] n
        skip  app/controllers/mailboxes_controller.rb
      create  test/functional/mailboxes_api_test.rb

В класс контроллера добавим строку:


class MailboxesController < ApplicationController
  web_service_api MailboxesApi

Веб-сервис готов. Чтобы получить его WSDL-описание, достаточно направить браузер по адресу http://localhost:3000/mailboxes/wsdl.

Критика и сравнение с другими фреймворками

Существует большое количество фреймворков для разработки веб-приложений для самых разных языков. Понятие о том, какой набор функций должен реализовывать такой фреймворк более или менее устоялось: разделение различных аспектов разработки с помощью паттерна MVC, средства валидации форм, средства для работы с БД, объектно-реляционное отображение данных, отображение URL на методы контроллера и т.д. Даже если сам фреймворк не полностью реализует этот набор, можно воспользоваться сторонними библиотеками. Поэтому сравнивать фреймворки по набору функций не имеет большого смысла.

Главная отличительная черта Ruby on Rails, на которую делается упор разработчиками и популяризаторами этого фреймворка, — это значительное более простая и быстрая разработка приложений, что сказывается на общей стоимости разработки. Добиваются этого, используя свойства языка Ruby: динамическая типизация, ООП, удобные управляющие конструкции, возможность генерировать код на этапе выполнения. Естественно, за это приходится платить более низкой скоростью работы. Утверждается, что дополнительные затраты на вычислительные ресурсы окупаются снижением стоимости разработки приложения.

На сегодняшний день один из общепризнанных индустриальных стандартов в веб-разработке — язык Java и сопутствующие технологии. Сравним фреймворки, основанные на языка Java и Ruby on Rails.

Рисунок 3. Архитектура Rails и J2EE

Архитектура Rails и J2EE

Рисунок показывает, что фундаментальных архитектурных отличий у этих двух подходов практически нет. Различия заключаются в том, как это все реализовано.

Контроллер.  Чтобы указать какой код выполнять при вводе пользователем определенного URL, в Struts программисту необходимо написать специальный XML-дескриптор, содержащий отображения URL на Action-классы. Rails же находит соответствующий класс ActionController и его метод автоматически, руководствуясь разумными общепринятыми правилами. Например адрес вида http://example.com/order/delete/4 будет обработан экземпляром класса OrderController из файла app/controllers/order_controller.rb, будет вызван его метод {\tt delete}, а значение 4 будет передано в качестве переменной экземпляра класса.

Действия.  В Struts код каждого действия должен храниться в отдельном классе, наследующем класс Action. Сделано это так для того, чтобы в условиях статической типизации можно было единообразно вызывать различные действия, пользуясь единым интерфейсом Action. Однако, логичнее и проще было бы реализовывать несколько действий, производимых над одним объектом (save, delete, list), в виде методов одного класса, как это и сделано в Rails.

Сохраняемость.  Самым популярным фреймворком хранения объектов в реляционной БД в мире Java является Hibernate. ActiveRecord, входящий в Rails, реализует аналогичный набор функций.

Чтобы задать сохраняемый объект в Hibernate необходимо: 1) описать класс этого объекта, задав все поля и методы-аксессоры для них; 2) описать отображение реляционной таблицы на этот объект в XML-дескрипторе. В Rails же всего этого не требуется. Rails автоматически определяет, что класс Order, унаследованный от ActiveRecord::Base, отображается на таблицу orders, и самостоятельно подхватывает из базы имена полей класса и задает методы-аксессоры.

Масштабирование.  Понятие масштабируемости не связано напрямую со скоростью выполнения. Масштабируемость означает возможность получить гарантированный прирост производительности, добавив дополнительные вычислительные ресурсы (например, дополнительный сервер). Приложения, написанные на RoR, масштабируются также просто или сложно, как и на PHP, Perl, Java. Т.е. в зависимости от того, предусмотрено ли это в самом приложении.

Производительность.  Скорость выполнения приложений на Ruby меньше, чем на Java. Это связано с интерпретируемостью языка и тем, что большее количество действий откладывается на этап выполнения. Естественно, это проблема решается кэшированием на разных уровнях. Однако, скорость отличается не сильно (не на порядки), а некоторые разработчики даже умудряются превзойти Java по этому показателю. Кроме того, готовится к выпуску новый, оптимизированный по скорости интерпретатор Ruby.

Вывод

Ruby on Rails — относительно новый и многообещающий фреймворк. Он не заменит общепризнанные и испытанные компоненты, которые предоставляет платформа Java, для разработки приложений уровня enterprise. Однако, он вполне может и должен потеснить такие широко использумые инструменты, как PHP и Perl, для малых и средних веб-приложений, благодаря простоте и широким возможностям языка Ruby и полнофункционального интегрированного фреймворка Ruby on Rails.

Java-сообщество также пытается не отставать от общей тенденции к упрощению разработки. Так в новой версии языка Java реализованы дополнительные возможности для этого (generics, аннотации), а в сфере веб-разработки запущен проект Trails. Этот проект вдохновлен Rails. В нем пытаются упростить разработку с помощью динамического генерирования очевидных частей кода. При этом не разрабатывают полностью интегрированный фреймворк с нуля, а используют уже существующие технологии типа Spring, Tapestry, Hibernate.

Литература

Ruby on Rails. URL: http://rubyonrails.org/.

Ruby Home Page. URL: http://ruby-lang.org/.

Java. URL: http://java.sun.com/.

Open Source Web Frameworks in Java. URL: http://java-source.net/open-source/web-frameworks.

Aaron Rustad. Ruby on Rails and J2EE: Is there room for both?. URL: http://www-128.ibm.com/developerworks/java/library/wa-rubyonrails/index.html?ca=drs-. IBM developerWorks.

Обновлено Tue Apr 15 21:27:20 2014 +0400