Изучая Rails (ну, и Ruby)

Источник: habrahabr
Deshene


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


После двух лет своих занятий с PHP, я взял себе небольшой отпуск и выучил ColdFusion, который крутился поверх Java EE платформы. Собственно, поэтому я также поковырался в Java, так как ColdFusion можно расширять, используя Java компоненты.

 

Потом, естественно, было неизбежное погружение в JavaScript, приправленное порядочной порцией CSS, семантических Веб-технологий (RDFOWL и SPARQL), XML, XPath и XSL (XSL:FO и XSLT), и не будем забывать про SQL. Черт, да я могу написать (и писал) DTD (Document Type Definition - прим. переводчика)!

Не так давно, после того как я начал работать в EngineYard над платформой Orchestra PHP, я выучил Python. (Да, мы используем Python, для некоторых частей нашего PHP стека. Почему? Потому что это лучший инструмент для решения некоторых задач.)

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

Я всегда изучал новые инструменты (будь это демоны, утилиты, библиотеки, языки или сервисы) и судил их по нескольким критериям:

  • Насколько хорошо инструмент написан?
  • Что у него с безопасностью?
  • Сколько в нем открытых багов?
  • Как реагировало сообщество на предыдущие проблемы (были ли они [члены сообщества] отрыты, дружелюбны, вежливы, оперативны)?
  • Достаточен ли функционал для решения моих проблем?
  • Не слишком ли избыточен функционал для решения моих проблем?

В конце концов, все сводится к тому, правильный ли это инструмент для решения поставленной задачи?

Именно поэтому, когда в конечном счете дело доходит до написания веб-сайтов, я выбираю именно PHP.  Да познай свой инструмент хорошо, и да окупится тебе это сторицей!

Потом я пришел в EngineYard и познакомился с кучей потрясающих инженеров, которые выбрали для себя Ruby.

Даже сейчас, через более чем год работы в EngineYard, я узнаю что-то о Ruby или Rails. Конечно, я прочитал много кода на Ruby, в процессе code review, или просто из интереса, как что-то было реализовано. Я даже немного похачил Rails, но это было в основном скопировать-вставить-чуть_чуть_подкрутить.

Потом подоспел Distill, и нам понадобился сайт. Примерно 3 недели, чтобы все сделать, причем без отрыва от основных задач и без какой-либо спецификации на используемые технологии; в обычной ситуации я бы выбрал PHP и, вероятно, Zend Framework 2, и сделал бы все за пару дней.

Вместо этого, учитывая мои обсуждения Distill и чего мы хотим достичь (с прицелом на решения, а не технологии) на ближайшие месяцы, я решил уцепиться за предоставившуюся возможность попробовать Rails (ну, и Ruby). Это был небольшой проект с ограниченным функционалом, к тому же я мог быстро откатиться к PHP, если бы встретил слишком много проблем с RoR. К счастью, окруженный, буквально, десятками потрясающих опытных разработчиков, я всегда мог найти человека, которому, я бы мог задать вопросы. Но, как Вы увидите далее, помощь мне не особенно и понадобилась. 

Детали реализации


Смысл данной статьи не в том, как я выучил Ruby или Rails, но в том, что я вынес из полученного опыта. Хотя, кое-что относительно RoR, тоже хотелось бы сказать.

После PHP, я определенно столкнулся с вещами, вызывающими WTF! реакцию:

  • Скобки опциональны при вызове метода и часто не используются, однако в некоторых случаях (например, вложенные вызовы) они нужны.
  • Очень много вариантов "if not". Например: if !<condition>if not <condition> и unless <condition>.
  • Имена методов могут содержать ? и !, и есть соглашения, что методы с ? на конце возвращают булево значение. Это не оператор. Это часть имени, например, foo.empty?. Методы заканчивающиеся на ! обычно изменяют целевой объект, т.е. foo.downcase! изменяет foo, в то время как foo.downcase просто возвращает результат, по этой причине такие методы в некотором смысле деструктивны.
  • Неявное возвращаемое значение: в качестве результата выполнения метода возвращается результат выполнения последнего выражения из тела метода.

В итоге, получаешь что-то вроде этого (реальный код на некотором этапе разработки сайта Distill, который возможно изменился к моменту запуска):

class Speaker < ActiveRecord::Base belongs_to :user has_many :proposals attr_accessible :user, :bio, :email, :name, :id, :website, :photo has_attached_file :photo, :styles => { :medium => "300x300>", :thumb => "120x120>" } validates_attachment_content_type :photo, :content_type => /^image\/(png/gif/jpeg)/ validates :bio, :email, :name, :photo, :presence => true validates :email, :format => { :with => /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/, :message => "Must be a valid email address" } end
Пройдемся детально:

  • Строка 1: Мы определяем класс Speaker, который наследуется (<) от класса Base в (::) модулеActiveRecord.
  • Строки 2, 3, 5, 7, 8, 9, 10: все это вызовы методов.
  • Строка 15: закрываем определение класса (end).

"Подожди, вызовы методов, говоришь?" 
Именно так! 
"Это безумие! Мы же все еще в определении класса!" 
(Нет, это Руби! ^_^ - прим. переводчика)

Во первых, важно отметить, что в Ruby неявный self, который, как и self:: в PHP, вызывает методы статически (это самая простая аналогия). Это означает, что belongs_to :user равнозначно self.belongs_to :user. Что тут странно, так это то, что эти методы (которые, вообще говоря, наследованы) вызываются в процессе определения класса. Эти методы могут быть определены (например, def self.foo) и вызваны (после определения) внутриопределения того же самого класса, или унаследованы от его предка. Эти методы изменяют сам объект класса.

Ремарка: пока я писал этот пост, я наконец-то сам в полной мере осознал, что написано в предыдущем абзаце, и потвиттился с коллегой по EngineYard mkb, который помог всему этому уложится в голове; можете посмотреть тут- вкратце: классы определяются в процессе исполнения кода, это означает, что можно программно определять классы, и даже работать с ними в процессе определения.

Таким образом, то, что я думал, является свойством (validates), которое магически было определено дважды, на самом деле вызов метода - вспомните, я ранее упоминал про опциональность скобок при вызове метода. ( Poetry mode  это действительно очень удобно и красиво, но вспоминайте все же время от времени последние слова дяди Человека-Паука - "Большая сила - большая ответственность!" - прим. переводчика)

И что получилось?


Ну, я сделал сайт. Безопасный, читаемый (код), приятный в использовании сайт. Не больше, чем я мог бы сделать с PHP, но было несколько вещей, которые просто снесли мне крышу.

Для большей части приложений мы имеем дело с обычным CRUD. Показать форму, принять данные, сохранить данные, показать их позднее. Не было сложных структур данных, о которых мне нужно было волноваться, или чего-нибудь еще такого. Ruby/Rails или PHP/Zend Framework 2 - да, без разницы.  Вероятно, я бы даже на ***bash-скриптах*** смог бы все написать.

Я прочитал почти полностью Getting Started with Rails, адаптируя в процессе под свои нужны.

Я думал наиболее сложными будут следующие две вещи:

  1. OAuth с разными провайдерами (Github, Facebook и Twitter).
  2. Хранение загруженных изображений в S3.

OAuth с разными провайдерами


Чтобы решить данную проблему, я использовал отточенный временем навык гугления. В процессе, я наткнулся наDevise и Omniauth; два гема, которые реализовывали пользовательскую аутентификацию и OAuth, соответственно.

Интегрировать эти гемы для человека, который едва знаком с Ruby или Rails, достаточно хитрая задача, но у меня получилось, и я, честно говоря, был шокирован. Они не просто разобрались с OAuth, но также позаботились о роутах, вьюхах, формах, схеме БД - в значительной степени обо всем. Мне пришлось написать собственные обработчики, чтобы записывать пользователей в базу и обрабатывать данные, полученные от сервисов (имя, email), но ничего слишком сложного.

Загрузка изображений в S3


И снова, с помощью Google, я нашел гем Paperclip. Расширение для Active Record с файловыми вложениями.

С помощью Paperclip, даже не зная как реализовать загрузку файлов в Ruby/Rails, я смог решить поставленную задачу: сохранить различные подробности в базе, создать множественные миниатуры и загрузить все это в S3, и всего за несколько минут. По существу, после конфигурирования (которое включает в себя указание адаптера хранилища в S3, учетных данных и пути), и вызова rake, последующие несколько строчек решили все:

has_attached_file :photo, :styles => { :medium => "300x300>", :thumb => "120x120>" } validates_attachment_content_type :photo, :content_type => /^image\/(png/gif/jpeg)/
Тут мы называем вложение как "photo", указываем требуемые версии (300x300 и 120x120) и проверяем, что загружаются png, gif или jpeg.

Что я вынес из этого?


Ну, мне по прежнему нужно многому научится. Ruby это не просто другой синтаксис, в отличии от моего опыта с Python. Я теперь, как говорится, чувствую, что могу разумно рассуждать о некоторых преимуществах Rails над его PHP-коллегами.

Одно из значительных преимуществ - это набор замечательных гемов, которые работают с Rails прямо из коробки, и могут привнести гораздо больше, чем средняя PHP библиотека, как из-за широкого распространения Rails с среде Ruby разработчиков, так и из-за того, что люди стараются придерживаться стандартизированного инструментария. Например, есть OAuth компонент для Zend Framework 2, но он не предполагает, что вы используете ZF2 контроллеры/роутеры, и не прописывает соответсвующие роуты, как и не генерирует вьюхи, не встраивается в механизм аутентификации, не интегрируется с адаптером БД. Поэтому, как мне кажется, Rails (а следовательно и Ruby), замечательное средство для быстрой разработки. (Если Вы любите поохотиться на гемы, наThe Ruby Toolbox можно найти список гемов, отсортированных по сферам применениям и популярности, что довольно удобно!)

Я также думаю, что нужно отделять фреймворк от языка. Как PHP или Python, Ruby - это язык программированияобщего назначения. Хотя PHP создавался с прицелом на работу в веб-окружении, это означает только, что он обеспечивает простой доступ к этому веб-окружению (GET/POST/Cookies, встроенная поддержка сессий, PUT/POST сырых данных, серверная среда, и т.д.) Для меня это значит только то, что я мог бы написать Distill на любом языке из трех упомянутых выше.

Один из основных факторов, почему PHP хорош для веба, это то, что его архитектура (Shared nothing architecture) замечательно подходит для горизонтального масштабирования, и, похоже, лучше справляется с параллелизмом прямо из коробки - хотя Ruby движется семимильными шагами в этом направлении (с такими проектами, как rubinius и jruby). Это не означает, что Ruby или Python не могут или не хотят масштабироваться, просто, если посмотреть на кривую обучения, то точка, с которой Вы сможете делать это правильно, находится гораздо дальше. Конечно, работа с Engine Yard Cloud (в частности, для деплоя) избавила меня от этих проблем.

Итак, язык не так уж много значит, но что по поводу фреймворков? Смог бы я создать тот же самый веб-сайт, используя ZF2? Да. Было бы это проще? В некоторых моментах - да, учитывая, что я не так хорошо знаю Ruby и Rails. Однако я не думаю, что смог бы также быстро реализовать OAuth и подключить хранилище S3 при прочих равных. Теперь, возвращаясь к ZF2, я замечаю, что делаю гораздо больше рутины, такой как генерация форм, скелета, обновлений схемы, и т.д., как минимум, в самом начале процесса.

Означает ли это, что я перехожу на Ruby/Rails? Вряд ли. PHP все еще мой любимчик, хотя бы потому, что зная его так хорошо, я могу практически без усилий реализовывать свои идеи.

Вернусь ли я к Ruby/Rails снова? Возможно не для своих собственных проектов - я, как правило, работаю с друзьями из PHP сообщества. Но, при работе с моими замечательными коллегами из Engine Yard - непременно! Для меня, наиболее сильная черта Ruby/Rails, это сообщество, к знаниям которого я имею доступ. Вне зависимости от ответа на этот вопрос, изучение нового языка - и, что более важно, изучение лучших практик использования этого языка - надеюсь, делает меня лучшим разработчиком.

Это очень просто - зациклиться на языке, на сообществе, и думать, что мы учимся, потому что мы работаем на технологической периферии, с такими вещами как серверы баз данных, системы кеширования и веб-сервисы… и мы учимся, но как-то односторонне ("The PHP Way" или "The Ruby Way"); смотрим на мир через шоры, ограничения, основанные на тех вещах, с которыми мы чувствуем себя комфортно.

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

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


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=33141