В чём особенности и преимущества Ruby on Rails

Привет. Я использую Rails уже значительное время - 5 лет (это, по-моему, значительное). В этой статье я попробую подытожить свой опыт и ответить на вопрос: почему я все еще использую Rails и почему его должны (или не должны) использовать вы.

Для начала ответим на вопрос из заголовка. Действительно ли Rails популярен сейчас, в середине 2015 года?

Вот эта забавная таблица сравнений по критериям популярности репозитория на GitHub и количества вопросов на StackOverflow (http://hotframeworks.com/) говорит что Rails всё ещё в топе:

frameworks_ranking.png

Согласно Google Trands, Rails тоже популярен в поисковых запросах в сравнении с сопоставимыми фреймворками.

rails_trends.png

Думаю, можно смело сказать, что да, Rails - это популярный инструмент.

Немного истории

Первый релиз Rails вышел в 2004 году и он стал очень горячей темой к 2006 году. В нём видели серебряную пулю, волшебный ингредиент успешного проекта. Со временем мода изменилась, на горизонте появились другие модные вещи, такие как NodeJs, AngularJs и прочие. На Rails начали смотреть трезвее. Он превратился из серебряной пули в просто хороший инструмент.

Следующий график показывает Google Trends в поисковых запросах для Rails с 2004 по 2015

rails_trends_since_2004.png

Что может предложить стек Ruby/Rails менеджеру проекта

Это хороший выбор для быстрого старта проекта. Вы будете поражены скоростью разработки. Вы пишете меньше кода, действительно меньше. Вы будто бабочка - одним взмахом крыла вызываете ураганы. Доступны огромное количество открытых библиотек и инструментов, платформ для развертывания. Есть прекрасная документация и много обучающих ресурсов. Большое сообщество всегда поможет решить возникшую проблему. Экосистема взрослая и стабильная. Стандартные задачи уже давно имеют готовые решения. Многие идеи, решения и подходы, родившиеся в симбиозе Rails и Ruby переняли или адаптировали в других фреймворках. Rails делал и продолжает делать мир веб-разработки лучше.

Важно понимать, что Rails предлагает не только стабильный и зрелый стек технологий и экосистему. Он предлагает постоянное развитие. Rails привносил и популяризировал много удачных решений и подходов. Он повлиял на многие фреймворки, иногда даже очень сильно. Просто вбейте в google "rails like + language". Rails-like фреймворки росли как грибы после дождя для многих популярных платформ - Java, Scala, NodeJs, PHP и даже Erlang. Вообще вся экосистема Ruby/Rails оказала влияние на инструменты разработки на других платформах. Инструменты развертывания приложений, пакетные менеджеры, менеджеры зависимостей, шаблонизаторы и препроцессоры. Если вы хотите быть на острие веб-технологий и не собираетесь ждать, пока это портируют на вашу платформу, тогда Rails - ваш выбор. Постоянное развитие, функциональность и помощь сообщества делают Rails отличным и надежным инструментом.

Предсказуемость, стабильность и надежность делают этот инструмент хорошим выбором для проекта в долгосрочной перспективе. Rails не умрет если “главный maintainer” заскучает и уйдет заниматься другими вещами. Если в проекте уволится один из членов команды, не будет проблемой найти ему замену. Rails уже давно стал зрелым и стабильным инструментом, поэтому и продолжает быть популярным. Выбирая платформу и стек технологий при старте проекта вы должны внимательно присмотреться к Rails: возможно это станет ключевым решением для успеха проекта.

Что может предложить стек Ruby/Rails разработчику

Одна из особенностей подхода Rails это подход Convention over configuration. На практике это означает, что пока вы следуете соглашениям, вы избавлены от конфигурирования и настройки. Во всех компонентах и слоях абстракций. Если какое-то соглашение вам не подходит - окей! - у вас всегда есть способ настроить всё под себя. Фреймворк скрывает от вас сложность проблем и пока вы следуете соглашениям, все работает как по волшебству. Сложности начинаются когда соглашение вам не подходит.

Rails это удивительно модульный и настраиваемый инструмент. У вас есть штатный (предусмотренный разработчиками) способ расширить/переопределить поведение тех или иных компонентов. Вы можете встроиться в процесс отправки писем и модифицировать письмо. Например, вам нужно изменить тему письма так, чтобы на тестовом сервере все письма приходили с постфиксом "[Staging]". Вам надо поддерживать визуальные темы для веб-страниц и динамически определять путь, откуда будут браться html-шаблоны? Легко - вам нужно просто переопределить отвечающий за это компонент (path resolver). Вам надо выполнять какой-то служебный код до начала обработки запроса вашим кодом? Встраиваете свой middleware в очередь стандартных.

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

Вы получаете ORM - ActiveRecord. Как можно догадаться, используется одноименный шаблон доступа к данным. Вашу жизнь сильно облегчат принятые соглашения. Вам не надо ничего конфигурировать. Пока вы руководствуетесь стандартными соглашениями, вы не напишите ни строчки конфигурации (я вру, конечно, вы всё равно должны сконфигурировать доступ к базе данных, указать username, password итд). Пример классов-моделей, они представляют конкретные таблицы базы данных из документации:

class Customer < ActiveRecord::Base
  has_many :orders
end

class Order < ActiveRecord::Base
  belongs_to :customer
end

И это все. Это работает без единой строчки конфигурации. По умолчанию предполагается, что в базе у вас есть таблицы orders и customers. В orders есть внешний ключ с именем customer_id. Первичные ключи называются id. На этом все. Вы соблюдаете соглашения именования и все само работает. Теперь вы можете строить и выполнять SQL-запросы совершенно очевидным и естественным способом:

customer.orders
#=> [order1, order2,...]
order.customer
#=> customer

Окей, идем дальше. Маршрутизация запросов (routes). Это поразительно. Вы можете все, и очень многое из этого “всего” бесплатно. Но это если вы руководствуетесь соглашениями. Пример маршрута из документаци

resources :books

Благодаря магии Rails эта одна скромная строчка способна создать пачку маршрутов для CRUD:

$ bundle exec rake routes | grep book
         books GET      /books(.:format)                 books#index
              POST      /books(.:format)                 books#create
      new_book GET      /books/new(.:format)             books#new
     edit_book GET      /books/:id/edit(.:format)        books#edit
          book GET      /books/:id(.:format)             books#show
             PATCH      /books/:id(.:format)             books#update
               PUT      /books/:id(.:format)             books#update
            DELETE 	    /books/:id(.:format)             books#destroy

Опять-таки, по соглашениям именования у вас должен быть классBooksController и аналогичные методы index/create в нём, т.е. books#index соответствует BooksController#index. Добавьте к этому ограничения (constraints), пространства имен (namespaces), модули (modules).

HTML-формы. Что тут можно улучшить? Нtml есть html, от него никуда не убежишь, но можно попытаться уменьшить боль. Пример создания формы из документации:

<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
  <%= f.text_field :title %>
  <%= f.text_area :body, size: "60x12" %>
  <%= f.submit "Create" %>
<% end %>

Предполагается что @article это ActiveRecord модель, но по факту здесь может быть любой произвольный объект. В том числе, если использовать шаблон FormObject, здесь может быть объект абсолютно отвязанный от слоя ORM.

Миграции базы данных. Здесь все стандартно. Можно, просто и быстро.

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

Развертывание (deploy) приложения на сервер. Есть стандартное решение - capistrano. Пример стандартного скрипта с комментариями: (ссылка на gist).

# config/deploy.rb

# config valid only for current version of Capistrano
lock '3.4.0'

set :application, 'aircat'
set :repo_url, 'git@bitbucket.org:applabsservice/aircat.git'

set :deploy_to, "/var/www/apps/aircat"
set :git_shallow_clone, 1
set :deploy_via, :copy
set :branch, 'master'

set :host,   '127.0.0.1'
set :user,   'deploy'

role :app, %w{deploy@127.0.0.1}
role :web, %w{deploy@127.0.0.1}
role :db,  %w{deploy@127.0.0.1}

namespace :db do
  desc 'Make symlinks'
  task :symlink do
    on roles(:app) do
      execute "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
    end
  end
end

after 'deploy:updating', 'db:symlink'

# дальше идут другие callback'и для перезапуска веб-сервера

Что это дает? При каждом развертывании новая версия приложения копируется в отдельный каталог релиза, рядом с предыдущими. Запускаются миграции, идет компиляция/склейка/минификация статичных css/js файлов, и когда всё готово, веб-сервер переключается на каталог с новым релизом. В случае ошибки на любом из шагов можно просто вернуться на предыдущий релиз. Новый код запускается уже на обновленной базе. Есть нюанс, что во время миграций некоторое время с обновленной базой работает старый код. Но это тоже решается.

Загрузка картинок или видео? Выбирайте из нескольких альтернатив - CarrierWave, Paperclip итд. Пример использования PaperClip:

class Post < ActiveRecord::Base
 
  has_attached_file :image, styles: { standard: "1280x1280>" },
    default_url: "/images/:style/missing.png"
  validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/

  has_attached_file :video
end

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

post.image = File.read("test.jpeg")
post.save

Все, файл помещен в правильный каталог на диске. Если это картинка, её можно обрезать, cконвертировать, сжать. Сделать несколько вариантов разрешений парой магических строчек.

Локализация - да, есть как у всех, всё просто и стандартно.

Тестирование. В стандартной библиотеке Ruby есть фреймворк для unit-тестирования. В Rails из коробки есть поддержка функциональных и интеграционных тестов, но намного круче воспользоваться связкой RSpec для модульных тестов и Capybara + webdriver для интеграционных. Представьте - вы можете в интеграционных тестах запускать настоящий браузер, установленный в системе (например через Selenium Webdriver), или headless браузер на базе WebKit. Второй вариант пока что надежнее, стабильнее и быстрее.

Из минусов для разработки можно выделить следующее:

  • Rails даёт вам соглашения. Вы следуете им. Ваш код лаконичен, красив, покрыт тестами, всё сделано по Rails-way. Но есть и обратная сторона. Вы получаете большую связанность компонентов, толстые контролеры или толстые модели. Rails предлагает шаблон MVC, но ничего сверх этого. Нет никакого стандартного слоя абстракции - это решение ложится на ваши плечи. Если вы заметили, что стандартные компоненты (views, models, controllers) уже перенасыщены несвойственной им логикой, вы должны что-то делать. Какие у вас варианты? Можете начать рефакторить, выносить логику в отдельные слои - об этом уже немало написано. Можно использовать Form-объекты, выносить сложный SQL в независимые объекты, отделить бизнес-логику от ORM/контроллеров.
  • Документация для Rails и популярных библиотек отличная, но это всё равно не избавит вас от изучения их исходного кода, чтобы разобраться с упущенными в документации моментами. Иногда код скажет даже больше.
  • Скорость развития инструментов и библиотек имеет очевидную обратную сторону - вам периодически придётся обновляться до последних версий. Это относится и к версиям Rails, и к библиотекам. Переход на другую мажорную версию библиотеки для тестирования может занять не один и не два дня. Возможен вариант, когда появляется какой-то удачный аналог используемой вами библиотеки. Ваша библиотека постепенно перестает развиваться и поддерживаться, и внезапно вы обнаруживаете, что она не работает в очередной версии Rails и придётся переходить на аналоги. Ничто из этого не специфично только для Rails, но в долгоживущем проекте вы с этим столкнетесь с большой вероятностью.

Когда вам нужен Rails?

  • Вы разрабатываете обычное веб-приложение. Вы ожидаете, что проект будет жить долго. Вам нужно, чтобы инструмент продолжал развиваться и жить, нужна поддержка от сообщества или от какой-нибудь компании, возможность нанять специалиста. В таком случае, Rails - прекрасный выбор. Альтернатив хватает, выбирать есть из чего. Но вы все равно выберете Rails, ведь это по-прежнему модно ;-)
  • Вы предполагаете постоянное изменение требований и функционала, вектора развития проекта. У вас нет постоянной концепции продукта, она меняется и зависит от обратной связи с пользователями. Rails в этом случае отличный выбор.
  • Вам нужно “быстрое прототипирование”. Rails до сих пор хорош для этого. Альтернативы, конечно же, найдутся, но Rails очень хорош и в этом.

Когда вам не нужен Rails?

  • Вы отчётливо понимаете требования к проекту и функционалу. Вы не ожидаете быстрых изменений функционала. К примеру, у вас уже есть прототип или вы уже делали что-то очень похожее и можете сразу отливать архитектуру в бронзе.
  • Вы ищете то, чего никогда не найдёте в динамически типизированном языке, - высокую (высочайшую) скорость работы и низкое потребление ресурсов сервера.
  • У вас уже есть команда профессионалов которые хотят использовать другой фреймворк.

Ну вот и всё. В заключение я добавлю, что получаю большое удовольствие, используя Rails и Ruby. Это очень удобные инструменты, мягкие и податливые, как пластилин. В умелых руках они помогают создавать волшебные вещи.

Связаться