Приветствую всех гостей и постояльцев на своем техническом, иногда не совсем, блоге. Кто читает меня в твиттере, наверное, помнят то, что вчера у меня было первое в жизни собеседование на ruby on rails вакансию, кто не читает, милости просим дружить в твиттере.
Встретились мы сначала с генеральным директором, он рассказал о проекте, пока его не буду называть, но это компания, которая занимается развитием сайта, посвященному туризму: туры, бронирование отелей, такси и прочей смежной тематикой. Обсудили нетехнические вопросы. Затем подошел старший программист и по большей части мы уже общались непосредственно с ним. Конечно, сказывалось отсутствие большого опыта программирования на ruby и ruby on rails, но на большую часть вопросов, как мне кажется, я ответил правильно. Вопросы были следующего характера:
- какие виды связей и как их создать в rails
- чем отличается include от extend’а
- имеется ли множественное наследование в ruby
Параллельно с вопросами я рассказывал о своих проектах и на ruby, и на php. Рассказал почему захотел перейти с php на ruby, как решал задачу из предыдущего поста тоже рассказал :).
После собеседования мне дали тестовое задание, оно заключается в следующем: необходимо без сторонних гемов, плагинов и библиотек реализовать простое приложение по управлению записями (обычный CRUD), только все это дело должно располагаться на одной странице, все запросы должны происходить в фоне (ajax) с применением unobtrusive javascript’а. Еще мне нужно было засечь сколько времени займет все это дело.
На ознакомление с мат. частью (railscasts, guides.rubyonrails и пр.), так сказать, ушло около двух часов, на реализацию проекта столько же. Что получилось и ссылку на репозиторий вы можете увидеть в конце данной заметки.
Итак, начнем. Единственную “плюшку”, которую я применил в проекте был gem ‘twitter-bootstrap-rails’ для придания “красивости”, на конечную функциональность он никак не повлиял.
Сначала, как и всегда, создаем проект rails new unobtrusive && unobtrusive. Вписываем в Gemfile, что будем использовать twitter-bootstrap-rails gem ‘twitter-bootstrap-rails’ и делаем bundle, чтобы обновить Gemfile.lock
Далее установим нужные стили, js-файлы, написав в консоли rails g bootstrap:install и обновим наш шаблон application командой rails g bootstrap:layout application fluid.
Скаффолдом создадим все необходимое для задачи rails g scaffold item name:string description:string, теперь мы можем создавать, удалять и редактировать, но без ajax’а. Также давайте добавим красоты нашим item’ам rails g bootstrap:themed Items. Сейчас можно зайти на /items своего приложения и увидеть что получилось. Но нам пока рано останавливаться.
Давайте добавим валидацию, которую я упустил, в файл app/models/item.rb
1 2 3 4 |
|
Первой строкой мы добавляем, как, наверное, ясно из название, наличие и имени, и описания, второй мы проверям уникальность имени, чтобы не было двух айтемов с одним и тем же именем.
Далее давайте немного изменим нашу форму app/views/items/_form.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Небольшого изменения коснулась первоя строка, а именно добавился атрибут :remote => true, что сообщает рельсам задейстовать ujs (unobtrusive javascript). Также был добавлен тег с id item_errors, где мы будем выводить все ошибки валидации нашей модели.
Так как у нашего приложение будет только одна страница, можно смело избавляться от других вьюшек, заодно изменим наш контроллер app/controllers/items_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
Тут я, конечно, перемудрил с before_filter’ами, но это то, как смог сделать за два часа. Как видите, контроллер шан стал намного полегче от того состояния, в котором он был после скаффолдинга: мы избавились от редиректов и вынести многое в before_filter’ы.
Далее я добавил в шаблон app/view/layouts/application.html.erb рядом с тем где выводится контент экшнов, вывод flash сообщения.
1 2 3 4 |
|
Это не весь шаблон, а только измененная его часть.
Дело за вьюшками, создадим _items.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Здесь нет тоже ничего необычного, кроме атрибута :remote => true у ссылок. Тем самым мы сообщаем рельсам, чтобы они инициировали ajax запрос, вместо обычного get’а.
Так как мы создали все необходимые partial’ы, мы модем изменить наш index view.
1 2 3 |
|
Который только собирает воедино нашу форму и список айтемов. Если вы запустите сейчас приложение и попытаетесь создать что-либо или отредактировать, получите множество сообщений об ошибках, связанных с отсутствием нужных шаблонов (если вы удалили все вьюшки, кроме index.html.erb).
Теперь переходим к магии: вмето того, чтобы отдавать html файлы, мы будем отдавать javascript, который rails вставит в нушное место. Вместо create.html.erb, у нас будет app/views/items/create.js.erb
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Вкратце мы делаем следующее: если есть ошибки, то выводим их, если нет, то очищаем наши текстовые поля и обновляем список айтемов.
Самая длинная строчка в этом файле мне не очень понравилась, может можно как-то попроще, покороче?
Если запустить наше приложение, то вы сможете создавать новые записи, видеть сообщения об успешном или неуспешном (если возникли ошибки валидации) создании.
Также поступим с двумя оствшимися вьюшками app/views/items/edit.js.erb
1
|
|
Где мы просто обновляем наш список элементов.
и app/views/items/destroy.js.erb
1 2 3 |
|
Вот и все :) Заранее хочу извиниться за сумбурное, местами беглое, изложение, но я сейчас нахожусь в ожидании ответа от работодателя и не могу спокойно сидеть на месте :).
Репозиторий доступен, как всегда на github, работающее приложение на heroku
P.S. деплой на heroku
- запускаем
RAILS_ENV=production bundle exec rake assets:precompile
, чтобы скомпилировать наши стили и javascript’ы - заменяем в Gemfile sqlite3 на pg и далеем bundle
- создаем приложение на heroku
heroku create --stack cedar
(у вас приложение должно быть под git’ом и вы должны войти в heroku аккаунт) git push heroku master
отправляем наше приложение за тысячи километров :)- на heroku сайт пишут, что нужно сделать
heroku run rake db:migrate
, но у меня с этим не вышло, поэтому я запустил эту же команду в фоновом режимеheroku run:detached rake db:migrate
- открываем приложение
- наслаждаемся
- и еще раз наслаждаемся :)