- PVSM.RU - http://www.pvsm.ru -

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

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


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

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

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

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

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

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

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

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

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

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

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

Вместо этого, учитывая мои обсуждения 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 [7], который помог всему этому уложится в голове; можете посмотреть тут [8] — вкратце: классы определяются в процессе исполнения кода, это означает, что можно программно определять классы, и даже работать с ними в процессе определения.

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

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

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

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

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

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

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

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

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

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

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

И снова, с помощью Google, я нашел гем Paperclip [13]. Расширение для 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 [14], но он не предполагает, что вы используете ZF2 контроллеры/роутеры, и не прописывает соответсвующие роуты, как и не генерирует вьюхи, не встраивается в механизм аутентификации, не интегрируется с адаптером БД. Поэтому, как мне кажется, Rails (а следовательно и Ruby), замечательное средство для быстрой разработки. (Если Вы любите поохотиться на гемы, на The Ruby Toolbox [15] можно найти список гемов, отсортированных по сферам применениям и популярности, что довольно удобно!)

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

Один из основных факторов, почему PHP хорош для веба, это то, что его архитектура (Shared nothing architecture [16]) замечательно подходит для горизонтального масштабирования, и, похоже, лучше справляется с параллелизмом прямо из коробки — хотя 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, а также применить на практике основные идеи, которые я почерпнул в процессе своего путешествия в новую среду.

Автор: Deshene

Источник [17]


Сайт-источник PVSM.RU: http://www.pvsm.ru

Путь до страницы источника: http://www.pvsm.ru/ruby/34076

Ссылки в тексте:

[1] RDF: http://www.w3.org/TR/REC-rdf-syntax/

[2] OWL: http://www.w3.org/TR/owl-features/

[3] SPARQL: http://www.w3.org/TR/rdf-sparql-query/

[4] Document Type Definition: http://ru.wikipedia.org/wiki/DTD

[5] Distill: http://distill.engineyard.com/

[6] Zend Framework 2: http://framework.zend.com/

[7] mkb: https://twitter.com/mkb

[8] тут: https://twitter.com/dshafik/status/308369397280559105

[9] Getting Started with Rails: http://guides.rubyonrails.org/getting_started.html

[10] Devise: https://github.com/plataformatec/devise

[11] Omniauth: https://github.com/intridea/omniauth

[12] гема: http://en.wikipedia.org/wiki/RubyGems

[13] Paperclip: https://github.com/thoughtbot/paperclip

[14] OAuth компонент для Zend Framework 2: https://github.com/zendframework/ZendOAuth

[15] The Ruby Toolbox: https://www.ruby-toolbox.com/

[16] Shared nothing architecture: http://en.wikipedia.org/wiki/Shared_nothing_architecture

[17] Источник: http://habrahabr.ru/post/179395/