Pull to refresh

Легковесные веб-приложения на Ruby

Reading time5 min
Views8.7K

Быстрая разработка


Вдохновленный постами на западных блогах вроде «Clone TinyURL with 40 lines of Ruby» или «Clone Pastie in 15 Minutes with Sinatra & DataMapper» я решил попробовать пройти и заодно описать весь процесс реализации легковесного веб-приложения на руби, от проектирования до деплоймента.



Инструменты


Для руби есть огромное количество различных инструментов для быстрой разработки. Я остановился на следующих:

Sinatra — DSL для веба. Легковесный фреймворк, работающий по принципу «convention over configuration». Позволяет быстро и легко разрабатывать веб-приложения, и легко дополняется всем, что только может вам понадобиться. Основа нашего приложения.

DataMapper — ORM, главный конкурент ActiveRecord. В чем-то уступает, в чем-то превосходит вышеназванный, прекрасно работает с разными базами данных, легко конфигурируется и встраивается.

HAML — HTML для программистов. Язык разметки, чуть более красивый чем традиционный erb, генерирует чистый и валидный xhtml. Содержит в себе эквивалент для CSS — SASS.

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

Что будем писать?


Выбрав инструменты, я задумался, а что же, собственно, написать? И решил, что это будет инструмент для организации своей комикс-ленты. В таком приложении имеет место и клиентский функционал, и админ-панель, и генерация rss-фида для подписки, что позволит затронуть разные аспекты разработки, и приблизить ее к реальным задачам. Ну и еще я люблю веб-комиксы:)

Разбор кода


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

Для начала разберем структуру проекта, она очень проста:
comics.rb
config.ru
models.rb
public
views


Файл models.rb содержит в себе модели, конфигурацию базы данных и все что касается работы с ней. comics.rb содержит весь код для синатры. Так-же синатра по умолчанию подхватывает папки views, содержащую представления на haml и public с файлами доступными из веба (javascript, изображения).

Начнем с моделей.
models.rb
  1. DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3:///#{Dir.pwd}/comics.db")

Параметры БД на heroku содержатся в ENV['DATABASE_URL'], если такой переменной нет, то создаем sqlite-базу в каталоге с проектом. Править в исходниках ничего не придется.

models.rb
  1. class DateTime
  2. def rfc822
  3. self.strftime "%a, %d %b %Y %H:%M:%S %z"
  4. end
  5. end

Спецификация RSS 2.0 требует дату в формате RFC #822. Для этого добавим объектам класса DateTime метод rfc822, который отформатирует timestamp нужным образом, и именно его будем использовать в дальнейшем в представлениях.

comics.rb
  1. def protected!
  2. response['WWW-Authenticate'] = %(Basic) and \
  3. throw(:halt, [401, "Not authorized]) and \
  4. return unless authorized?
  5. end
  6. def authorized?
  7. comics = Comics.first
  8. @auth ||= Rack::Auth::Basic::Request.new(request.env)
  9. @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == [comics.login, comics.password]
  10. end

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

comics.rb
  1. get '/rss.xml' do
  2. content_type 'application/rss+xml', :charset => 'utf-8'
  3. @comics = Comics.first
  4. @strips = Strip.all :limit => 10
  5. haml(:rss, :layout => false)
  6. end

Отдача rss-фида. Меняем Content-Type, и добавляем: layout => false, чтобы фид не отрендерился в layout.

Теперь несколько хинтов в представлениях.
layout.haml
  1. %title= "#{@comics.title} — #{@strip.title}" rescue @comics.title

Если не использовать здесь механизм исключений, то при пустой переменной @strip нас остановит ошибка NoMethodError, так как у класса nil нет метода title. В руби такие штуки надо всегда держать в голове.

models.rb
  1. class Strip
  2. # объявляем property
  3. def next
  4. Strip.first(:created_at.gt => self.created_at, :order => [:created_at.asc])
  5. end
  6. def previous
  7. Strip.first(:created_at.lt => self.created_at)
  8. end
  9. def get_id
  10. self.id
  11. end
  12. default_scope(:default).update(:order => [:created_at.desc])
  13. end

layout.haml
  1. - tonext = "/#{@strip.next.get_id}" rescue "#"
  2. - toprevious = "/#{@strip.previous.get_id}" rescue "#"

Механизм понятен — поиск следующего и предыдущего стрипа для того, который смотрим сейчас, и вывод ссылок на них в представлении. Почему надо было делать отдельный метод get_id, вместо того чтобы напрямую использовать существующий id? Дело в том, что если мы смотрим последний на текущий момент стрип, то метод next вернет nil. А у nil в свою очередь есть метод id, который не долго думаю вернет «4». Можете убедиться в этом сами, поэкспериментировав в irb.

На этом с разбором можно и закончить, с удовольствием отвечу на любые вопросы по коду, и отреагирую на критику в комментариях.

Деплоймент


Написанное приложение легко запустится как и любое другое приложение на синатре командой ruby comics.rb. Но мы ведь хотим показать его миру, и в этом нам поможет Heroku. Регистрируемся на heroku, и устанавливаем себе на локальную машину gem heroku. Теперь пишем конфиг для Rack:
config.ru
  1. require 'comics'
  2. run Sinatra::Application

Следующим шагом создаем приложение на heroku, и пушим туда код. Условимся, что приложение уже лежит у вас в git-репозитории:
heroku create comics
git push heroku master

Осталось только забить базу данных начальными данными, для этого в models.rb есть метод install:
heroku console
install

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

Ссылки


Comics на GitHub
Демо на Heroku (Админка, пароль по запросу в хабрапочту).
Tags:
Hubs:
Total votes 49: ↑47 and ↓2+45
Comments24

Articles