Набор Ruby библиотек для CMS и сайта медиа издания



    Набор библиотек для разработки CMS медиа издания практически ничем не отличается от любого другого приложения. На примере приложения для Ленты и Ведомостей мы решили прокомментировать выбор каждой библиотеки. Описание составлено в формате обсуждения каждого гема.

    Предыдущая обзорная статья: Перезапуск медиа издания: обзор

    CMS Ленты.ру


    Написанное про Ленту было актуально до апреля 2014 года.

    gem 'unicorn'

    foxweb: Здесь должно быть объяснение, почему не Puma.
    7even: Его не существует.
    zaur: Это мой консервативный подход не даёт парням свободы, чтобы раскрыть потенциал веб-сервера Puma. Я предпочитаю использовать классическую модель работы веб-сервера, когда работает несколько процессов. А модель, где обработчики конкурентно обрабатывают запросы в разных потоках в классическом интерпретаторе, мне не нравится. Отсутствие уверенности в потокобезопасности MRI-интерпретатора и нежелание всех переходить на jRuby или Rubinius предопределило выбор.
    7even: Готов перейти на рубиниус.
    foxweb: Ну теперь-то да.
    zaur: 7even, совсем недавно был другого мнения.
    7even: Рубиниус быстро развивается. Еще несколько месяцев назад он падал на конструкциях вида %i(foo bar).

    gem 'newrelic_rpm'
    gem 'newrelic-redis'

    foxweb: Наше Rails-приложение и очередь сообщений в Redis работали под постоянным мониторингом сервиса NewRelic. Если позволяют финансы, обязательно стоит использовать этот инструмент на всех критичных проектах. NewRelic сохраняет часы и трудодни, спокойный сон разработчиков, а некоторых спасает от увольнения и даже от физических увечий. До того, как кто-нибудь из редакции прибегал с криками “Ничего не работает!”, мы уже знали, что именно, на каком сервере, по какой причине не работает, кто сломал, кто будет чинить и какая часть тела будет ампутирована. Некоторые графики мы вывели на HTML-страницу, которая 24 часа в сутки крутилась на большом мониторе на стене. Все всегда знали “что у нас с сайтом”. Косяки одного разработчика видит вся команда. NewRelic максимально сокращает время реакции “упало — подняли”. Для онлайн-СМИ это особенно критично.
    zaur: При настройке модуля надо не забывать исключать тестовое и дев окружение. Делается в конфигурационных файлах.



    gem 'rails', '~> 3.2'

    foxweb: Это Rails.
    zaur: При этом старые рельсы. На новые так и не перешли, всё боялись, что “всё сломается”.
    foxweb: Если быть точнее, большая часть гемов на тот момент не была адаптирована под Rails 4.

    gem 'faye'

    foxweb: Faye — простая реализация WebSockets, pub/sub заточенный на обмен сообщениями между разными пользователями, сидящими на одном сайте. Изначально это такой удобный чатик на вебсокетах. В нашем проекте Faye отправлял/получал сообщения между редакторами, сидящими в админке. Кто-то сохранил новый текст, кто-то залил 10 картинок — получили сообщения. Через какое-то время сервер сообщает, что картинки сгенерировались и опубликовались — получили сообщения все, кто занимается в данный момент именно этой статьёй. Всё это было очень удобно для редакции, повысило уровень взаимодействия и ускорило работу редакции в целом. Не нужно было писать в скайп “пацаны, никто не трогайте эту новость, я её сейчас сохраняю!” или “го в галерею, я создал!” Всегда было видно, какие редакторы в данный момент правят именно эту статью. В сочетании с системой журналирования и версионирования, а также сервиса сравнения по diff, коллективная работа редакции над одними и теми же материалами стала по-настоящему удобной. Единственное, что мы не успели реализовать — разделение по ролям и уровням доступа в системе оповещений. Было бы интересно сделать так, чтобы, к примеру, главный редактор мог видеть вообще всё на особой странице, при этом нигде не “светиться”, а стажёры получали бы сообщения определённого вида. Хотя, большой объём работы ушёл на то, чтобы faye нельзя было использовать со стороны. Всякое бывает. Например, была реализована в какой-то степени авторизация через cookie и токены, хранящиеся в Redis. Токен привязывался к учётной записи конкретного редактора. Благодаря этому редакция могла видеть сообщения как бы от лица автора — “Василий Петров сохранил документ”. В базовой версии faye никак не занимается авторизацией пользователей и защитой сообщений, а также вообще никак не связан с окружением Rails.
    ksavelyev: Ну на самом деле не очень простая, всем хорош Faye, но не масштабируется.

    gem 'faye-redis'

    foxweb: Плагин для faye, позволяющий использовать в качестве промежуточного хранилища для pub/sub сообщений, как видно из названия, Redis. У нас он использовался только для авторизации и хранения токенов (кажется).
    zaur: …и для хранения очереди фоновых процессов, таких как Resque или Cloudy (самописная поделка для работы с видео).

    gem 'thin'

    foxweb: Thin — это сервер для Faye.
    zaur: Не ваша любимая Puma.
    7even: А вот тут zaurа не смущает асинхронный сервер :)
    ksavelyev: У Заура не было выбора, по другому Faye не работает ;)

    gem 'pg'

    foxweb: Славный переезд с MySQL на Postgres. Сделали отдельную ветку в git, там всё наладили, протестировали и смерджили. Сначала сдампили структуру в SQL-файл. Естественно, он оказался непригоден для импорта напрямую в Postgres. Пришлось заменить, например, кавычки по всему файлу, какие-то вещи правились вручную. Думаю, 7even напишет большой рассказ о том, почему MySQL в таких проектах — плохо.
    7even: В MySQL нет hstore :) Вообще, если серьезно, Postgres в Ruby-сообществе намного популярнее и, как следствие, нагуглить решение любой проблемы проще. Плюс кастомные типы данных (массивы, json и тот же hstore) здорово облегчают жизнь. Psql (консольный клиент) в разы удобнее (если хотя бы немного его освоить).
    zaur: А вот ksavelyev считает, что надо было полностью переходить на MongoDB.

    gem 'postgres_ext'
    gem 'activerecord-postgres-json', github: 'michaelbn/activerecord-postgres-json'
    gem 'activerecord-postgres-hstore'
    

    zaur: Когда переходили с MySQL на PostgreSQL, рельсы были третьей версии. Использовали эти библиотеки для поддержки расширений PostgreSQL.

    gem 'marionette-rails'

    ksavelyev: Добавляет библиотеку Марионетт в Asset Pipelite. Марионетт добавляет к Бэкбону еще один уровень абстракции, вводит понятие модуля, приложения, позволяет удобно работать с визуальными представлениями коллекций. Делает из Бекбона почти, что Ангуляр.
    zaur: от Angular открещивался до последнего.

    gem 'haml_assets'
    gem 'dust_assets'
    gem 'haml_coffee_assets'

    ksavelyev: Добавляет поддержку haml/dust шаблонов, позволяет писать шаблоны с указанным синтаксисом и использовать их внутри Asset Pipeline, в итоге внутри JS приложения мы получаем переменную содержащую скомпилированные и готовые к использованию шаблоны, которые изначально были написаны на haml/dust.

    gem 'turbo-sprockets-rails3'

    ksavelyev: Ускоряет выполнение rake assets:precompile благодаря компиляции и объединения только изменившихся ассетов, содержит дополнительные функции по удалению старых ассетов.

    gem 'terminal-notifier-guard'

    foxweb: Гем-интерфейс к системной утилите вывода всплывающих уведомлений. Во время тестирования в системах Linux и OS X выводится всплывающее сообщение о статусе — успешно, неуспешно. Удобно: тесты прогоняются сами по себе, в это время можно переключиться на другое окно, а всплывающее уведомление напомнит о завершении тестирования.

    gem 'guard'
    gem 'guard-rspec'
    gem 'guard-zeus'

    foxweb: В Rails 4.1 появился Spring для фоновой предзагрузки кода. В Rails 3 для этой же цели приходилось использовать Zeus. Если разработчик меняет какой-то файл — немедленно происходит его перезагрузка Zeus’ом и прогон относящихся к нему тестов RSpec’ом. Guard многим известен как инструмент отслеживания событий, связанных с изменением файлов проекта. guard-rspec как раз запускает тесты при сохранении файлов.
    zaur: Что один, что второй доставляли больше неудобств. Запустить нужный тест проще напрямую руками. А так сплошные оправдания низкой производительности ruby.
    7even: Дело не в “низкой производительности ruby”, а в медленном старте рельсового приложения. Естественно, если бы оно стартовало за пару секунд, эти костыли бы не понадобились.
    foxweb: 470 спеков за 14 секунд — это достаточно быстро.



    gem 'rspec-rails', '~> 2.14'

    foxweb: Кажется, раньше мы использовали TestUnit, но потом с подачи 7even довольно быстро перешли на RSpec. Это подняло процесс тестирования на новый уровень. У нас был полноценный TDD. Потом мы перестали писать тесты, потому что нового кода почти не появлялось, а какой-то код было очень сложно или нецелесообразно (по времени) покрывать тестами.
    zaur: Спорное решение, победило большинство. А этот новый уровень называется “нафиг тесты”.

    gem 'ffaker'

    foxweb: При создании новых сущностей нужно было протестировать и наполнить случайными данными (имя, фамилия, емейл, должность и прочее). В процессе отладки обычному программисту под силу сделать 1-3 записи, потом надоедает, а код так и остаётся недоотлаженным. Собственно, при помощи классов этого гема можно генерировать очень разнообразную и, самое главное, правдоподобную информацию. 7even внёс в него свой вклад добавлением генератора русских имён и фамилий. Таким образом можно было отладить производительность на разных наборах данных до деплоя в production. Применение этого гема не связано с тестами. При написании тестов использовались, например, стандартные рельсовые fixtures.

    gem 'quiet_assets'

    foxweb: Молчаливые ассеты?
    ksavelyev: Именно, молчаливые. Когда рельсовое приложение запускается локально на машине разработчика в дев режиме, веб-сервер отдает статику сам. В продакшне этим занимается Nginx, который ведёт отдельные логи для ассетов. В девелопменте информация о том, что веб сервер отдал пачку ассетов обычно никому не нужна, этот гем блокирует мусорные сообщения о том, что статические файлы были отданы браузеру. В случае необходимости, если такого гема нет, всегда можно фильтровать логи используя grep, например команда tail -f development.log | grep SELECT будет мониторить лог на появление в них SQL запросов.

    gem 'colorize'

    foxweb: При выводе данных в консоль (может быть, логов импорта или каких-то rake-тасков) можно было рубишными методами выводить в консоль цветные строки, что повышало общую читабельность логов.

    gem 'rails-erd'

    foxweb: В какой-то момент число сущностей и связей между ними увеличилось настолько, что стало сложно держать их в голове и передавать знания новым сотрудникам. При помощи этого гема можно было в пару действий сгенерировать диаграммы моделей со связями и кучей разных опций. Диаграмму потом печатали на большом листе A3 и много думали.
    zaur: Потом выбрасывали и рисовали на доске.

    gem 'ruby_parser'

    foxweb: Кто-то экспериментировал с парсингом Ruby. Возможно, для собственного механизма аннотации файлов.
    7even: Не я.

    gem 'capistrano'

    foxweb: С Capistrano всё было хорошо, пока не появилась Mina. Capistrano работал медленно, зато умел делать мультисерверный деплой. Mina быстрая, и ее вывод намного читабельнее. Не успели перевести CMS и фронтенд на Mina, зато спецпроекты и, например, Дом.лента.ру деплоили изначально при помощи Mina. Мультисерверный деплой довольно просто “эмулировался” при помощи простого цикла по списку хостов (кажется, максимум их было 4). То есть, просто 4 деплоя на 4 хоста за один проход.
    zaur: Сейчас для деплоя не используется ни то, ни то. Перешли на схему управления с помощью ansible. Деплой без перерыва в работе сервиса обеспечивает HAProxy.
    7even: Стало раз в 5 медленнее мины, зато zero-downtime.
    foxweb: Скажу тебе по секрету, оно в текущей настройке даже не zero-downtime ;)

    gem 'better_errors'
    gem 'binding_of_caller'


    ksavelyev: better_errors значительно улучшает стандартный информационный экран 500-й ошибки. Появляется браузер стека вызова и интерактивная руби-консолька, позволяющая просматривать состояние объектов.
    foxweb: Не понимаю, как мы раньше отлаживали рельсовые приложения без консоли в браузере.
    7even: Тесты надо писать, а не в браузере ссылочки вручную тыкать.
    ksavelyev: Если бы фронтендеры писали тесты в бекенде, а бекендеры делали бы тенюшки и интегрировали шрифты в страницу, мир однозначно был бы лучше.
    foxweb: Я имел ввиду как раз те случаи, когда в обычной консоли невозможно поймать ошибки вёрстки, например.

    gem 'jazz_hands'

    7even: Набор из pry, awesome_print и нескольких плюшек для pry.
    foxweb: Добавляет в консоль красивую раскраску, подсветку синтаксиса и форматирование вывода в стандартной рельсовой консоли. Оставлено в production, потому что частенько приходилось лазить в консоль на работающих серверах.

    gem 'active_record-annotate'

    7even: Для аннотации моделей изначально использовали annotate_models, но в определенный момент он начал ломаться на новых типах данных в PostgreSQL — и мы решили написать свою реализацию.
    foxweb: ты решил, ты написал.
    7even: Github.

    gem 'rabl'
    gem 'jbuilder'
    gem 'gon'

    zaur: Для сериализации мы начинали использовать Rabl. Ужасно медленное решение. Постепенно стали переходить на JBuilder. А для передачи JSON на страницу на этапе генерации HTML использовали Gon.

    gem 'fast_seeder'

    foxweb: Из того же разряда, что и Ffaker, но умеет использовать в качестве источника данные из CSV-файлов.

    gem 'oj'

    7even: Самый быстрый парсер JSON.
    foxweb: Питер лучше чем Москва. А чем лучше? Чем Москва.

    gem 'slim'

    foxweb: Сначала был Haml, но потом оказалось, что Slim не только легче читается и пишется, но ещё и быстрее рендерится. Для замены Haml на Slim по всему проекту можно использовать, например, haml2slim.

    gem 'russian'

    foxweb: Известный гем для русификации дат и стандартных рельсовых сообщений.

    gem 'whenever', require: false

    foxweb: Кронификатор — библиотека для управления крон-тасками на языке Ruby. Что-то у нас там раз в сутки с новостями такое происходило.

    gem 'redcarpet'

    foxweb: Гем для работы с Markdown, только никто не помнит, где мы его использовали.

    gem 'kaminari'

    foxweb: Пагинатор — разбивает большие списки на страницы (1,2,3...100).

    gem 'truncate_html'

    ksavelyev: Лень-матушка заставляет добавлять в гемфайл гемы из одной функции. В данном случае нам лениво было корректно откусывать кусок HTML регулярным выражением.
    foxweb: Не знал про такое.

    gem 'devise'
    gem 'devise-encryptable'

    zaur: Классика жанра для создания авторизации на сайте.

    gem 'mini_magick'

    foxweb: Гем-интерфейс к ImageMagick.

    gem 'mimemagic'

    foxweb: Гем для определения типа загружаемого контента.

    gem 'foreman'

    foxweb: Простой менеджер процессов для разработчика. Когда работаешь над большим проектом, требуется запускать много маленьких сервисов. Foreman запускает всё нужное одной командой. В нашем примере это Rails, Faye, тесты, сервисы очередей публикации и обработки картинок.
    7even: Запуск тестов через Foreman делал их вывод настолько нечитаемым, что вскоре я выпилил их из Procfile.
    foxweb: Вспомнил, кажется, в общий вывод вместе с тестами попадало вообще всё, включая сообщения Faye и логи приложения.

    gem 'faraday'

    7even: Фарадай — незаменимая библиотека для обращения к внешним сетевым ресурсам. Благодаря системе middleware (аналогичной Rack) можно один раз задать все трансформации как запроса, так и ответа — например, добавить заголовок OAuth-авторизации к запросу перед его отправкой, или распарсить JSON ответа и обернуть его в Hashie::Mash.
    foxweb: “Внешние сетевые ресурсы” — банальные HTTP-запросы на старую версию сайта.
    7even: Окей, к любым сетевым ресурсам. В данном случае старая версия сайта, но фарадай мы использовали в самых разных ситуациях — были и API, и HTML-ки.

    gem 'faraday_middleware'

    7even: В этом геме есть много полезных middleware для фарадая.
    foxweb: Это, например, JSON?
    7even: И, например, mashify.

    gem 'hashie'

    7even: Hashie — собрание расширений к хэшам. Мы использовали Hashie::Mash — вариация хэша, в которой обращение к элементам идет не как обращение к ключу, а как вызов одноименного метода (например, не user[:name], а user.name). К сожалению, в дальнейшем этим гемом стали злоупотреблять и использовать как замену моделям.
    foxweb: Новый формат полюбили верстальщики и назвали его “дот-нотацией”.
    7even: Вот я об этом и говорю.

    gem 'nokogiri'

    foxweb: Известная библиотека для разбора XML/HTML. Активно использовалась при разборе старых архивов. При помощи неё в процессе импорта мы заменяли устаревшие ссылки, теги картинок, вычищали неактуальный код, мусор и много всего прочего. Вроде бы даже закрывали незакрытые тэги.

    gem 'eventmachine'
    gem 'em-synchrony'
    gem 'em-hiredis'
    

    7even: Для обработки видео использовался сторонний сервис E**** *******m, взаимодействие с которым шло через их REST API. На нашей стороне работал демон, который через очередь в редисе получал путь к загруженному редактором видеофайлу, отправлял его на сервер E****, перекладывал в другую Redis-очередь, опрашивал сервер каждые 5 минут — и когда обработка на стороне E**** была завершена, демон выкачивал готовые файлы. Так как все эти процессы должны были проходить параллельно, демон был написан на EventMachine.
    zaur: Звёздочками закрашен не мат.

    gem 'streamio-ffmpeg'

    7even: Эта обертка вокруг ffmpeg позволила получать ширину, высоту, битрейт, длительность и другие метаданные видеофайла.
    foxweb: Эти данные потом использовались для отображения видеоконтента на страницах сайта.

    gem 'ruby-progressbar'

    foxweb: Простая реализация прогресс-бара в консоли. Использовался при импорте данных из старой базы в новый формат. Когда впереди тебя ждёт обработка более 500.000 материалов, прогресс-бар хорошо помогает планировать время и медитировать в процессе. Класс довольно простой. На входе задаются такие параметры, как конечное число цикла, всякие настройки вида и т.д. При выводе сам считает проценты, время, крутит счётчик, двигает полоску, в общем, делает всё, что и обычный прогресс-бар в любой ОС. Сообщить главному редактору, что на импорт старых данных нужно трое суток — бесценно.

    gem 'version'

    foxweb: Гем-безделушка, который увеличивает номер версии в файле VERSION по запросу и ставит соответствующий тэг на коммитах в Git. Поиграли и забыли.

    gem 'environment'

    zaur: Один из велосипедов, для распределения настроек по разным окружениям, коих было больше трёх. Ошибка проектирования, расплата.
    7even: Канул в лету.

    gem 'settings'

    7even: Для хранения настроек приложения в разное время использовались и configatron, и rails_config. Но где-то гем не работал вне контекста Rails — что лишало нас возможности обращаться к настройкам из Faye и демона на эвентмашине — а где-то были другие проблемы; поэтому в итоге была написана своя реализация. В настоящее время она доступна на Github.

    gem 'bluepill'

    7even: Тут все просто: мониторинг сервисов.
    foxweb: Часто нужно было перезапускать упавшие или зависшие сервисы прямо на продакшене. Потом нам надоело и мы поставили Bluepill. Типа Foreman, только для сервера.
    zaur: Ужасный мониторинг сервисов. Но другие были не лучше. Нужно было уметь следить за процессом, который мог изменить PID. Одновременно это делать могли не многие. Менял веб-сервер свой процесс в момент zero-downtime restart. Например во время деплоя, для бесперебойной работы приложения, использовали такую замечательную функциональность Unicorn.

    gem 'diffy'

    foxweb: Офигенно полезный для редакции инструмент. Позволяет смотреть историю редактирования текстов, как в Википедии. Естественно, чтобы было с чем сравнивать, должна сохраняться история версий. Она у нас уже работала, а прикрутить такой инструмент было делом пары часов. Полезный эффект для редакции был огромен.
    zaur: Был у нас случай, когда редактор сделал опечатку. На сайте-то поправили вовремя, а вот в RSS на главную страницу Рамблер.Новостей ушёл, конечно, неправильный вариант.
    ksegoh: И тут парни узнали разницу между Сирией и Ливией.



    zaur: Один из сценариев использования версионирования. Можно проследить, как стажёр пишет заметку. Например, он может последовательно писать по абзацу новости. А может расписать план в несколько предложений, а далее расширить текст. То есть можно увидеть ход мысли человека, или выявить копипастера.

    gem 'roo'

    foxweb: Гем для извлечения данных из таблиц Excel. Пригодился во время Олимпиады, когда редакторы и поставщики контента предоставляли данные по соревнованиям и участникам в табличном виде. Городить веб-интерфейс для работы с таблицами не было ни времени, ни желания.

    gem 'net-scp'

    foxweb: Тоже “олимпийский” гем, предоставлял возможность эти самые таблицы забирать по SSH с файловых серверов.

    gem 'resque'
    gem 'resque-web'
    gem 'resque-pool'

    zaur: Генерация изображений разных размеров производилась в фоне. Одной из причин были неведомые проблемы в работе ImageMagick. При этом некоторое время (а я до сих пор) частично вменяли вину Sidekiq (менеджер конкурентных фоновых процессов). Сменили версии библиотеки, пробовали другие, переустанавливали ОС, меняли сервер. В общем, до сих пор это привидение летает в том доме. Фоновые процессы обрабатывает Resque, конвертирует ImageMagick. В фоне обрабатывались и архивы с изображениями.
    foxweb: Мистика мистикой, но были и рациональные объяснения. Часто я бегал к редакторам, которые под видом JPEG заливали, например, PNG или использовали недопустимые символы в имени файла Что, конечно, не снимает вины с ImageMagick.
    zaur: И с Вас. “Неправильные” символы и в текущей админке доставили удовольствие. Кириллица – ваше проклятие. Попались они, кстати, в HTTP заголовках…



    gem 'ar_after_transaction'

    zaur: Я не знаю, зачем это здесь.
    ksavelyev: Как зачем? Ты и Сева решали проблему выполнения кода после окончания успешной транзакции.

    API-сервис редакционной админки Ведомостей


    gem 'rails-api'

    7even: Так как сервис задумывался исключительно как API, отдающий JSON, было решено облегчить приложение и использовать только необходимый набор функциональности, который дает rails-api.

    gem 'active_model_serializers'

    7even: После ковыряния с rabl и jbuilder оптимальным выбором для сериализации объектов в JSON представлялась разработка из того же rails-api, позволяющая использовать объектный подход.

    gem 'awesome_pry'

    7even: В определенный момент jazz_hands утратил совместимость с новыми версиями Ruby (а, может, и каких-то гемов), к тому же мы все равно не использовали всю его функциональность — поэтому появился такой мини-гем, просто объединяющий в себе pry и awesome_print.

    gem 'ice_nine', require: %w(ice_nine ice_nine/core_ext/object)

    7even: Глубокая заморозка объектов. Руби позволяет изменять значения констант, поэтому для настоящей иммутабельности нужно использовать #freeze; для хэшей и массивов же обычный #freeze только запрещает добавлять и удалять элементы — а чтобы запретить изменение самих элементов, нужно рекурсивно заморозить все объекты. Или подключить этот гем и вызвать #deep_freeze.

    gem 'highline'

    7even: Для самодостаточности приложения и возможности проверить каждый роут без использования фронт-энда был написан небольшой консольный API-клиент, в котором и пригодился highline.

    gem 'rack-cors'

    7even: Поддержка CORS-запросов.

    gem 'database_cleaner'

    7even: Так как тесты используют БД, для очистки всех таблиц перед каждым тестом использовался database_cleaner.
    zaur: Ещё он пригодился, когда писали свои rake task для обнуления базы данных в дев режиме. Такая необходимость возникает из-за того, что pow держит приложение запущенным. А, значит, у базы данных были активные соединения, которые не позволяли просто пересоздать базу.

    gem 'guard-yard'

    7even: Плагин для guard, который запускает генерацию YARD-документации из изменившихся файлов.

    gem 'apiaryio'

    7even: Для документирования API решили использовать apiary.io, а данный гем дает возможность генерировать документацию в HTML и просматривать локально.

    group :development do
      gem 'puma'
    end

    foxweb: Тут Puma, а в production всё-таки Unicorn.

    group :production do
      gem 'unicorn'
    end

    foxweb: Тут Unicorn, а в development всё-таки Puma.

    Фронтенд CMS Ведомостей


    source 'https://rubygems.org'
    source 'https://rails-assets.org'

    ksavelyev: Строка с источником rails-assets.org стала появляться всё чаще и чаще в проектах с богатым фронтендом. Смысл добавления еще одного источника в том, что rails-assets может сделать гем практически из любого github репозитория. Для того, чтобы репозиторий трансформировался в гем нужно, чтобы он был валидным bower-репозиторием. Указав дополнительный источник мы сможем добавлять в гем-файл вызовы типа gem 'rails-assets-BOWER_PACKAGE_NAME'. Все JS/CSS ресурсы этого гема будут доступны в Asset Pipeline и дальше их можно будет использовать в своем проекте так, как будто вы добавили руками необходимые файлы.

    gem 'puma'
    gem 'unicorn', group: :production

    zaur: Ну вы понимаете, что я об этом думаю.



    gem 'compass-rails'

    ksavelyev: Это мой самый любимый гем, сколько времени он мне сэкономил, даже не могу представить. Это такой швейцарский нож для технолога. Основная задача Компаса — упростить и систематизировать работу с различными вендорными префиксами в SASS/SCSS. Компасс добавляет множество полезных функций которые помогают технологу вставлять на страницу кастомные шрифты, превращать каталоги с иконками в спрайты, задавать сложные градиенты в CSS, оперировать бекграундами, тенями, транзишнами не думая о том, как они реализованы в разных браузерах и делать еще много разных интересных и полезных вещей. В общем, освобождает время для творчества.

    gem 'rails-assets-angular'
    gem 'rails-assets-angular-cookies'
    gem 'rails-assets-angular-animate'
    gem 'rails-assets-angular-sanitize'
    gem 'rails-assets-angular-ui-router'
    gem 'rails-assets-angular-promise-tracker'
    gem 'rails-assets-angular-loading-bar'
    gem 'rails-assets-restangular'
    gem 'rails-assets-angular-contenteditable'
    gem 'rails-assets-angular-ui-ace'
    gem 'rails-assets-angular-bootstrap-colorpicker'
    gem 'rails-assets-ng-sortable'

    ksavelyev: На самом деле это не гемы а bower-репозитории, которые трансформировались в гемы благодаря rails-assets, о котором я писал выше.

    Для вас старались:
    • foxweb (@foxweb) — Алексей Курепин (бильд заметки!)
    • 7even (@7even) — Всеволод Ромашов
    • ksavelyev (@ksavelyev) — Константин Савельев
    • ksegoh — Ксения Гохгут
    • zaur (@kavkaz) — Заур Абасмирзоев
    Поделиться публикацией

    Комментарии 84

      +7
      Отличный разбор Гемфайла.
      • НЛО прилетело и опубликовало эту надпись здесь
        0
        Как правило, CMS издательской системы предполагает изменение статусов публикаций. Часто, у издателей есть особые пожелания на этот счет. Соответственно, используются некоторые известные решения.

        Правильно я понимаю, что в вашей CMS очень простая система статусов, вида — Черновик/Опубликовано?
          +1
          В прошлой было несколько статусов, но без state machine.

          Сейчас сделали ещё проще, как вы и сказали, черновик/опубликовано.

          Все дополнительные статусы нужны только для организации редакционного процесса. Как то «не выдана», «выдана редактору», «завершена», «вычитана», «окартинено»… Соответственно есть некоторый набор списков заметок, сгруппированных по определенным признакам, в том числе подобным состояниям. В зависимости от сценария, заметки перетекают из одного потока в другой.
            0
            спасибо. Суть понятна

            И еще пару вопросов, если позволите

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

            2) как вы формируете слаги (ЧПУ)? я так понимаю генерировать их вам не приходится? Не читаю ленту, но по всей видимости там числовые ID в урлах.

            3) Вопрос вне контекста тех. инструментария.
            Если ли трудности при расчете релевантного контента для каждой публикуемой статьи. Я про блоки с названиями вроде: Читайте так же; По этой теме;

            Понимаю, что если процесс расчета релевантного контента содержит какие-то специальные решения — то вряд ли вы расскажете об этом подробно, но если «магических» формул там нет, то было бы интересно узнать в будущих статьях ход решения общих для издательских систем вопросов.

            PS: если вы планируете и дальше освещать вопросы устройства издательских систем, то обещаю быть одним из самых преданных ваших читателей, т.к. именно этим целенаправленно занимаюсь несколько последних лет. За поднятую тему огромное спасибо!
              +2
              Поиск

              Для реализации поска в Ленте использовался внутренний поиск Рамблера, который в свою очередь организован на Sphinx. Раз в N минут к нам приходит запрос от поисковика за обновленными записями для индексации. Чтобы найти материал мы через HTTP обращаемся к поисковику и получаем JSON.

              В Ведомостях сейчас стоит Sphinx, в разрабатываемой версии будет использоваться ElasticSearch. Отправка данных на индексацию будет реализована похожим способом.

              Адрес страницы

              URL каждой заметки делается человеко понятным. В Ленте он составляется как слаг типа материала, дата, слаг данной заметки (пишется руками редактором). Итого получается нечто /news/2012/12/22/foobar/. Каждый элемент адреса редактор может скорректировать.

              В новой версии Ведомостей для составления адреса используются слаг первой рубрики, слаг типа материала, дата, транслитерация заголовка. Как пример: /finance/news/2012/12/22/foo-bar/.

              Если заметка будет опубликована, адрес страницы будет зафиксирован. Есть возможность создать ассоцииованный адрес, и сделать его основным. Это используется, если надо изменить рубрику, дату, или заголовок.

              Релевантные материалы

              Если блок связанных материалов (ссылки по теме) формируется строго руками. То в блоках «Читайте так же» материалы подбираются очень просто: самые свежие по заданному срезу. Например самые свежие в той же рубрике и теге, что и первый рубрика и тег материала. Никакие интеллектуальные системы здесь не нужны – это лишнее.
              • НЛО прилетело и опубликовало эту надпись здесь
                  +1
                  При схожей функциональности это скорее субъективное желание.
            0
            Жизнь показывает, что чем система статусов проще — тем проще и быстрее редакторам управлять сайтом. Другое дело, что у публикаций есть другие признаки, которые не «статус», а, например, наличие картинки или ожидание действия бильд-редактора, на какого редактора эта публикация «назначена», есть ли у неё ссылки, всякие галочки вкл/выкл рекламы/коментов.
              0
              спасибо.

              > наличие картинки или ожидание действия бильд-редактора
              Фактически, это может быть таким же важным признаком публикации, как и сам статус публикации. И потому, особенно интересно какова ваша практика управления состояниями публикации, т.к. это один из самых интересных организационных вопросов (по крайней мере для меня лично).
            +1
            Весело, конечно, и гемы хорошие :) Несколько новых для себя нашел, попробую. Но, жаль, что не везде полностью описываете выбор. И консерватизм смущает :)
              0
              А вас не смущает неиллюзорный шанс получить глюки системы, которые непросто отловить, но которых хватит для порчи репутации проекта?

              Нет задачи обходить стороной нестандартные решения. Практиковаться можно на мелких проектах.
                0
                Больше всего непонятно недоверие к потокам. С хттп-сервером и очередями понятно — если есть ресурсы (память), можно и старые проверенные решения использовать на MRI.
                Вот Eventmachine вместо потоков — усложнение, по-моему. Извините, пожалуйста, если я задачу не понял правильно. По описанию, потоки — в самый раз. И глюков меньше можно получить, чем от EM.

                Просто хотел сказать, что потоки в ruby работают стабильно, у многих проверены в продакшене.
                  0
                  В том контексте EM была как раз кстати. Демон большую часть времени ничего не делал. Ждал когда в очереди появится заявка, скачивал файл. Заявок было немного, а время реакции внешних ресурсов – велико.

                  Когда практика большиства, в вопросе использования потоков, будет распространена – мы тоже до этого дойдём в основных задачах.
              +1
              Очень круто, спасибо. По традиции, пара вопросов=)
              1. Какие впечатления от Devise? У меня получалось, что он экономит немного времени при старте, но съедает кучу потом, когда гнешь его под свое приложение.
              2. Была какая-то причина, по которой не использовались гемы типа simple_form/formtastic?
                0
                1. Нормально, в Ленте нам хватало, расширяться особо не было нужды.

                Но теперь в Ведомостях, сразу начали без него. Нам немного надо, но с какой-то бизнес логикой. Поэтому велосипед.

                Про simple_form сейчас kSavelyev ответит.
                  0
                  Мы пробовали оба гема для общего развития, они нам не пригодились. В админке формы не связаны напрямую с моделями, там one-page приложение на Бекбоне или Ангуляре, поэтому гемы не особо актуальны. На самом сайте же формы простые, 2-3 поля для регистрации или подписки, их можно сделать руками.
                  0
                  потокобезопасности MRI-интерпретатора

                  В каком смысле? Чем он может быть потоконебезопасен? Имелось в виду, что Rails или какой-нибудь middleware не до конца потокобезопасен?
                    0
                    Приложение, чаще всего rails, может подарить глюки, которое практически невозможно воспроизвести. Можно только быть в позиции жертвы.
                    0
                    Почему для api не взяли grape? И вас самих не коробит «фарадай»?
                      0
                      grape пробовали, не очень понравился. В одном новом API-сервисе использовали lotusrb, впечатления положительные.

                      А что не так с faraday?
                    0
                    Впервые недавно столкнулся с puma. Если при phazed-restart pumactl отдаёт код возврата 0, но при этом сам перезапуск воркеров может пройти неудачно, и оставить сервер в состоянии 502. Не понимаю, кто может это любить. Под JRuby есть и более родные сервера.
                      0
                      Прелоад выключали?
                        0
                        Есть там тонкие моменты в настройке. На puma + bluepill я как минимум два проекта в продакшен запускал и они спокойно работают. Сейчас бы сделал по-другому.
                        0
                        terminal-notifier-guard

                        Под linux guard умеет использовать inotify без доп. гемов.
                          +3
                          Зачем вы рекомендуете Compass? Там старые данные, нужно самому помнить, где писать примесь, а где нет. Многих префиксов просто нет.

                          Единсвенный способ работать с префиксами сейчас — это Автопрефиксер, у которого есть гем autoprefixer-rails.

                          У него самые свежие данные по префиксам с Can I Use. Во-вторых, он в пару раз быстрее. В-третьих, он парсит CSS и сам находит префиксы в свойствах, селекторах (:fullscreen, например) и значениях (calc()).

                          Сейчас нет ни одной причины использовать Compass.
                            +1
                            Вы автор этой утилиты?
                              +2
                              Я. Но не только я считаю, что Compass устарел. Google рекомендует именно Автопрефиксер и не упоминает Compass. GitHub хочет уйти к Автопрефиксеру. Яндекс, Google и Mail.ru уже используют Автопрефиксер. Twitter полностью перешёл только на Автопрефиксер (отказались даже от Sass в пользу постпроцессоров типа Rework и PostCSS). Bootstrap и Wordpress тоже собираются с помощью Автопрефиксера, выкинув Compass.
                                0
                                Очень рад за них ;) И за вас тоже.
                                  0
                                  Исправьте тогда, неправильно советовать Compass.
                                    +2
                                    Статья написана и опубликована, исправлять в ней задним числом что-то кроме орфографии и пунктуации не вижу смысла. Могу попросить только Заура добавить в конце блок с вашими рекомендациями.
                          • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              Да, взвалили всё это на администратора.

                              Медленнее стало субъективно. Они ж ничего не замеряли! Там не всё отлажено. Например скачем между LXC vs Docker. Финализируем – расскажем.
                                0
                                Медленнее — потому что Mina компилирует все необходимые команды в один shell-скрипт и выполняет на удаленном сервере за одно соединение; Ansible же (в нашей конфигурации) запускает команды по одной, каждый раз устанавливая новое соединение с сервером (аналогично Capistrano).
                                  0
                                  Ansible же можно настроить не открывать каждый раз новое соединение.
                                    0
                                    Установка controlmaster controlpath не помогла или я что-то не так делаю.
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                  • НЛО прилетело и опубликовало эту надпись здесь
                                +4
                                Отличная подборка, ребят. Спасибо огромное. Красавчики :)
                                  0
                                  Еще вопрос назрел — а как решали проблему индексирования гуглом и яндексом?
                                  Я так понял, что весь контент отдаётся в JSON и дальше ангулар работает с ним и с шаблонами
                                    0
                                    Так работает система управления контентом. Фронтенд работает классически с серверной генерацией HTML.
                                      0
                                      круто! а дублирования кода не возникает, тк шаблоны и вообще страницы разные совсем, верно понимаю?
                                        0
                                        Тут два разных приложения — в одном живут рубишные модели с бизнес логикой, Ангуляр или Бекбон и one-page приложение, в другом — система получения нужного JSON из закрытого API, и система трансформации этого JSON в то, что нужно в шаблоне и работа с конечным пользователем.

                                        Поэтому дублирования нет, страницы абсолютно разные.
                                    +1
                                    Спасибо за статью, открыл для себя кое-что новое. diffy точно возьму на вооружение.

                                    Насчет faye — я использовал для нее обертку private_pub от Ryan Bates, там как раз содержатся некоторые фиксы безопасности. Пришлось немного допилить под свои нужды, но работа меня устраивала.
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                        0
                                        1) Приложений много, они все разные, список гемов абстрактный.
                                        2) GCC никак не тюним. На данный момент это никак не мешает работе приложений.
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                          • НЛО прилетело и опубликовало эту надпись здесь
                                              0
                                              Все всё поняли :)
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                            0
                                            Да. И с помощью nginx можно перестраховаться, если после релода приложение валится. В ситуации с одним сервером это хорошее решение. А если кластер, то лучше балансировать уровнем выше, конечно.
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                          • НЛО прилетело и опубликовало эту надпись здесь
                                              0
                                              А тут просто. При разработке на локальной машине, ребята запускают веб приложения через Pow. Соответственно nginx с его настройками локально нет. В production стараемся делать это средствами nginx.
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                  +1
                                                  Так может подробно и опишите, как упростить локальное развертывание nginx? Например неплохо бы, чтобы у нас был доступен некий app.local который частично повторяет логику production конфигурации.
                                                    0
                                                    Vagrant для этого можно использовать. Пока сам не пробовал, хочу следующий проект с ним начать.
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        +1
                                                        Вагрант, знаю, многие используют, но у нас он не прижился. Как-то слишком на любителя. В основном потому, что команда наша очень компактная, нет недостатка в серверах и нет необходимости поднимать продакшн-окружение локально.
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                              0
                                                              Я так понимаю, Vagrant позиционирует себя как инструмент для разработчиков, а не админов.
                                                              С его помощью можно запустить одну или несколько виртуалок и настроить их с помощью средств для деплоймента (chef/puppet/ansible/руки).

                                                              Мне кажется, это — хороший вариант «как упростить локальное развертывание nginx».

                                                              VBoxManage CLI не смотрел. С ним можно закомитить в гит файл в небольшой, чтобы потом каждый мог запустить у себя ВМ?
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                • НЛО прилетело и опубликовало эту надпись здесь
                                                    0
                                                    Спасибо за полезные комментарии. Передадим их админу.
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      0
                                                      В Ведомостях мы только собираем эту связку, опробованного решения нет.
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        0
                                                        Частенько бывает, что при исправлении ошибок не происходит обновления зависимостей, например, если actionview 4.1.7 зависит от builder, то при переходе на 4.1.8 версия builder у вас не обновится, если исправление каких-то ошибок не связано с исправлением ошибок в builder.
                                                        Откуда у вас такой плохой опыт?
                                                        +2
                                                        Ребята спасибо за классную статью и подачу материала.

                                                        Кто бы мог подумать в далеком 2006, что неизвестный язык руби дойдет до таких высот)
                                                          +1
                                                          В последнее время всё больше вижу что люди стараются использовать puma вместо unicorn на MRI. Сюдя по тестам unicorn значительно быстрее.
                                                            0
                                                            Ребят, это в хаб Rails тоже надо срочно ))) Статья высочайшего класса.
                                                              +1
                                                              На самом деле в rails-api особого смысла нет, тк по быстродействию оно практически не отличается от просто рельсы — что-то порядка пары процентов. Тот же не полюбившийся вам grape с ar дает 20+ процентов прироста, совсем брутальный вариант sinatra/ar/oj и ловкость рук может дать больше 30.

                                                              Puma vs unicorn. Выше пишут, что значительно быстрее, но я не смог увидеть существенной разницы, особенно если за ними рельса ходит в базу и генерит вьюшки, на что уходит 95% времени ответа и разницу можно увидеть только если там синатра отдает что-то из редиса например. Зато пума может дать большой выигрыш по памяти, например 40%, при той же производительности и если у вас с десяток апп серверов и за хостинг с памятью вы платите из своего кармана, то пума начинает нравится намного больше )
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                  0
                                                                  Интересно, тогда напишите плиз, если доведете это до продакшена. Я как-то делал подобное используя nginx с lua в комплекте для разбора рельсовой сессии, но так и не доделал.
                                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                                0
                                                                Ruby кажется вкусным, надо попробовать.
                                                                  0
                                                                  А чем не угодил стандартный метод truncate?
                                                                  gem 'truncate_html'
                                                                    0
                                                                    Меня очень интересует вопрос писали ли вы сырой SQL или обходились возможностями ORM? Особенно учитывая что в жизни проекта был переход с MySQL на PostgreSQL. Этот переход был как «drop-in replacement» (благодаря высокому уровню абстракции ActiveRecord) или всё же пришлось переписать тонну платформо-зависимого кода с одной БД на другую?
                                                                      0
                                                                      Раньше не писали. Возможностей ORM хватало с головой. Позже стало ещё проще – старались делать запросы попроще. Самой большой проблемой при смене базы данных была миграция сами данных. Вроде полно скриптов, которые должны облегчить жизнь, но на практике пришлось повозиться. Кроме того был момент: приостанавливать работу редакции или нет. Решили, что проще для всех, если мы на некоторое время остановим редакцию, нежели будем делать realtime репликацию-миграцию или доливать изменения.

                                                                      Сейчас ситуация хуже. Из-за активного использования в PostgreSQL таких структур данных, как array, hstore, json, приходится писать raw sql, в основном для выражений where. Одна из проблем ActiveRecord. Добавим до кучи жёсткую привязку всегда иметь поле id primary key – с любой нестандартной схемой начинаются танцы. Посматривали на Sequel, но пока не решились.
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          0
                                                                          А эти структуры никак не связаны с масштабированием. Массив служит для упрощения организации ассоциативных связей. hstore удобен для динамических атрибутов объекта. А json для кэшированных структур данных. Валидации на уровне обработчиков в приложении. Разумеется триггеры и хранимые процедуры не используем, всё концентрируется в приложении.

                                                                          А глобально проблемы с масштабированием в такого рода проекта просто нет. Нет такого объёма данных, который не уместится на одном сервере. Нет такого кол-ва запросов, которые нельзя размазать по мастер-слейв.

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

                                                                    Самое читаемое