DEV Community

Cover image for Почему я программирую на Ruby
Vladislav Kopylov
Vladislav Kopylov

Posted on • Updated on

Почему я программирую на Ruby

Я - backend-разработчик и на 2023 год я программирую около 8 лет. На backend'е платформа не навязывает какой-то один язык для разработки, поэтому за это время я получил большой опыт в программировании, попробовал многие языки, но остановился на Ruby. В данном эссе я хочу сформулировать мысли почему я использую Ruby для решения моих рабочий (и не только) задач, какие у него есть плюсы, минусы, особенности и почему в 2023 он все еще актуальный язык.

1 Причина создания

Ruby причина создания

Начинать рассказ об языке стоит с его истории. Создание языков программирования часто носит утилитарный характер. Например: Си создавался чтобы получить инструмент выше уровня абстракции чем ассемблер. PHP создавался как макросы над C чтобы быстро создать личную веб-страницу. Python создавался как простой и надежный инструмент системного администрирования, чтобы заменить скрипты на bash, Си для ОС Amoeba. А какие-то языки создавались под определенную среду выполнения.

В далекой Японии Юкихиро Мацумото, который имел большой опыт в программировании и знал немало языков, искал инструмент под себя. Искал язык чтобы с комфортом, насколько это возможно, программировать на нем по 8-10 часов. Во всех языках которые были доступны на тот момент были свои минусы и он решил написать свое - язык на котором комфортно, приятно работать. Он хотел создать «настоящий объектно-ориентированный язык». Так и вышла первая версия Ruby в 1995 году. Теперь подробнее про сильные и слабые стороны этой технологии.

2 Сильные стороны технологии

Ruby сильные стороны технологии

2.1 Синтаксис

Ruby Синтаксис

Для реализации цели Матц уделил много внимания возможностью синтаксиса и структуре объектов. Ruby унаследовал объектную модель от Smalltalk 80 и получил от Perl свою скриптовость и продвинутую поддержку регулярок. Получился строгий объектно-ориентированный язык, в котором все является объектом и методом. В нем много возможностей которые предлагает синтаксис. Упор на синтаксический сахар породил такие фишки как вызовы функций без скобок, "блоки кода" (а-ля анонимная функция которую можно передать в любой метод) и метапрограммирование (код который пишет код). И код на этом языке можно прочитать как текст на английском языке.

Но из-за богатого синтаксиса в Ruby порог входа выше чем в похожих языках, например Python. В Ruby нет только простых конструкций, как в Python или Golang. В этих языках если хочется что-то сделать, то есть один способ реализовать это. Человек пришедший после них в Ruby заметит, что синтаксис переусложнен. Например, много методов которые отличаются только названием "алиасы" (filter и select, exit и quit делают тоже самое и нужны только для удобства). Много методов которые делают похожее, но чуть с другим поведением (each и each_with_index). При этом язык позволяет не только перегружать операторы, а также объявить поведение унарных операторов для своих классов - не во многих языках вы найдете похожее. Для удобства разработки Матц также продумал структуру сообщений об ошибках. Если разработчик что-то сделал не так, то получает не километровый backtrace, как в Erlang или Java, и не урезанный как в nodeJS (по умолчанию 10)

Благодаря таким фичам новый язык заинтересовал других людей по всему миру.

2.2 Domain-Specific Language (DSL)

Ruby DLS

Упор на синтаксический сахар, вызовы функций без скобок и "блоки кода" создали в языке возможность написать свой маленький язык под узкую предметную область (DSL). Создавая современное приложение на Ruby программист пишет не на чистом Ruby, а использует использует маленькие специфичные диалекты под узкую задачу, например: для тестирования, для объявление роутов, для написания миграций данных в БД и прочее. Это упрощает написание кода под эти задачи. Дает максимальный уровень абстракции и код выглядит как текст на английском языке. Как результат, создавая программу на Ruby нужно в 3-4 раза меньше кода чем в Java или Python.

Когда я впервые увидел код на руби, то удивился его лаконичностью. Я до этого писал на Си, C++ и PHP и смотрел на Ruby как на детский конструктор. Но именно то, что на нем достаточно написать немного, напомнило мне девиз jQuery "write less, do more" и вдохновило изучить Ruby.

2.3 Web development

Ruby web development

Из Ruby получился язык общего назначения. На нем можно писать скрипты для администрирования систем, программировать электронику и прочее. Но наибольшее развитие он сделал как веб-технология благодаря фреймворку RubyOnRails.

В 2005 году David Heinemeier Hansson - опытный веб-разработчик, выкладывает на YouTube видео как он создает блог за 15 минут (видео). Эта презентация фреймворка RubyOnRails была революцией для своего времени. Rails резко нарастила популярность на контрасте со скоростью разработки на других стеках в то время. В этом же году опытный джавист Брюс Тейт выпускает книгу Beyond Java о минусах Java и о том, что Ruby и Rails - новое слово в космонавтике. Сам пик популярности Ruby в вебе приходился с 2007 по 2013 год. Этот пик породил огромную экосистему готовых решений, библиотек, для веб-программирования и не только.

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

До сих пор RubyOnRails - fullstack фреймворк. На нем можно быстро поднять какой-нибудь веб-сервис с нуля. По-умолчанию в нем есть инструменты и для написания интерфейса. Говорят, что программирование на Ruby в среднем на 30-40% быстрее чем на других языках. Плюс многие разработчики могут писать на нем бек и фронт (простенький и не очень), что сокращает издержки для бизнеса. Причем скорость разработки не идет в ущерб качеству т.к. освободившиеся время можно потратить на рефакторинг, который постоянно хотят разработчики, а бизнес нет; или на обновления библиотек, обдумывание архитектуры или поиск лучших абстракций. Да и бизнес реалии таковы, что у разработчиков почти всегда не хватает времени или четкости требований, чтобы писать код качественно, строго придерживаться паттернов, делая всё как в учебниках.

Поэтому Ruby оказался идеальным, быстрым и очень эффективным решением для проверки бизнес идей. И сейчас, несмотря на статус-кво что Ruby медленный, люди забывают о его сильной стороне — скорости самой разработки. А при условии что чтобы бизнес выжил он должен быстро приспосабливаться к внешним изменениям, это позволяет быстро адаптировать ваше приложение под новые задачи или изменения ТЗ. Поэтому немалая часть стартапов Y-combinator запускается при помощи RubyOnRails.

2.4 Стандартизация

Ruby Стандартизация

Основной костяк экосистемы Ruby сформировался вокруг одного фреймворка. Это породило единый пул инструментов. Стандартизировано написание тестов для приложения, поэтому редкий рубист не пишет тесты. Стандартизирована библиотека для веб интерфейса - это Rack. Благодаря нему на уровне IT инфраструктуры, серверу приложения будет все равно что за ним приложение на Rails или на другом Ruby web-фреймворке. Стандартизирован и код-стайл. Поэтому новый разработчик на новом проекте не увидит для себя сюрпризов (по код-стайлу аналогичная ситуация в Python и Go, а в некоторых языка надо выбирать из N вариантов). Многие сторонние библиотеки уже адаптированы под рельсу, это сокращает время на интеграцию кода в приложение. Не будет ситуаций что при добавления сторонней библиотеки приходится разрешать конфликт зависимостей или тратить время на то чтобы объяснить библиотеки как работать внутри этого фреймворка. Такая ситуация привела к тому, что можно нанять нового Ruby разработчика и он достаточно быстро разберется в приложении. Он уже знает структуру папок, особенность работы и загрузки файлов и прочее. Из минусов, некоторым становится скучно, что все приложения одинаковые, но у нас есть и другие фреймворки (менее популярные).

2.5 База знаний по тому как писать приложения

Ruby База знаний по тому как писать приложения

За годы своей популярности и использования, в комьюнити накопилась обширная база по тому как писать веб-приложения. Комьюнити набрало экспертизу как писать монолиты, которые в будущем вырастут и как дальше их поддерживать. Такой опыт действительно есть т.к. такие огромные приложения как Cookpad, GitHub, GitLab, Shopify, BaseCamp, Stripe написаны на Ruby.

Сообщество Rails продвигает концепцию majestic monolith. Чтобы не страдать от разросшегося приложения, собрана методология как писать монолиты, как прочерчивать архитектурные границы внутри монолита. Чтобы потом не переписывать их на микросервисы и позднее дополнительно не страдать от того, что у нас получился распределенный монолит. Или от того, что мы пришли в мир распределенных приложений и надо писать схемы описываются как микросервисы общаются между собой и мониторить пути запросов. Сами рельсы предлагаются основные концепции по стыковочным точками и сервисным объектом. Про остальные решение можно узнать посетив встречи коммьюнити, конференции или почитав блоги опытных разработчиков.

Также вопросы безопасности не обошли эту технологию стороной. Ruby хорош и для приложений которые собирают личные данные (финансовые платформы, маркетплейсы и прочее). В экосистему внедрен Security Development Lifecycle, для контроля уязвимостей и проверки чистоты лицензий уже есть готовые решения. В Rails 7 есть поддержка шифрования данных БД из коробки. По этой причине финтех-продукты запускаются и на Ruby тоже. Со всем этим Ruby уступает только Java, и языкам семейства Си.

2.6 Инструмент решения бизнес задач

Ruby Инструмент решения бизнес задач

Когда пишешь код на Ruby ты не часто задумываешься о вещах, о которых думаю коллеги использующие другие языки: об объявлении типов, об использовании и очистки памяти и прочего. Обычно тебе не надо заботиться о памяти и об других узких вещах и тратить время чтобы объяснить компьютеру что с ними делать. Плюс рутинные вещи (роутинг, изменения структуры БД, тесты и прочее) не программируются, а описываются при помощи DSL. Поэтому из Ruby получился удобный язык чтобы писать бизнес-логику и внедрять новые фичи. Это как раз это то чем в настоящее время занимаются большинство разработчиков. 8 разработчиков из 10 занимаются бизнес-автоматизацией - решают или упрощают бизнес-задачи при помощи кода. RubyOnRails диктует разработчику как делать и диктует структуру, а разработчик следуя ей решает именно бизнес задачу, пишет код именно под нее, а не "борется" с фреймворком.

Но тут надо добавить что если пишешь что-то нетипичное, то будешь "бороться" с фреймворком или с легаси, как на других языках. Благо в RubyOnRails эти "рамки" достаточно широкие так что выходишь за них редко и уже имея немалый опыт программирования.

3 Слабые стороны технологии

Ruby слабые стороны технологии

Теперь про слабые стороны технологии или за что его критикуют.

3.1 Скорость работы приложения

Редкий разработчик не слышал что Ruby медленный, но по факту, многие языки "медленные". Скорость исполнения компьютерной программы зависит от особенностей работы с памятью и является одной из особенностей всех технологий где забота о памяти делегируется виртуальной машине. Подробнее об этом можно посмотреть доклад Григория Петрова Почему Ruby медленный? на основе которого я писал небольшую статью.

Для многих разработчиков все языки которые мы называем "интерпретируемые" (или скриптовыми) - медленные т.к. интерпретатор читает исходный код программы строчка за строчкой и выполняет ее. Но по состоянию на 2023 год современные "интерпретируемые" языки уже такими не являются. Тот же Ruby, Python компилируются в байткод и исполняются в виртуальной машине (VM). В Ruby с версии 1.9 (2011 год) интерпретатор прекомпилирует Ruby в байткод перед его выполнением. С Ruby 2.6 (2018) появляется Just-In-Time compiler (JIT), который компилирует часто используемые инструкции в бинарный код (почти как в nodeJS). Какие компиляторы могут ускорить математические вычисления в десятки раз, но не могут ускорить сложный проект из-за особенностей использования памяти в самом языке. Зато при создании таких языков у нас не ограничивается синтаксис, об удобстве которого так заботился Матц, и есть возможности к расширяемости.

Также в основе Ruby лежит механизм Global Interpreter Lock (GIL) из-за которого тормозит выполнение программы т.к. надо останавливать выполнение кода и очистить память. Это надо принять т.к. язык создавался когда на компьютерах были одноядерные процессоры. Конечно язык уже изменился и в данных момент это уже Global Virtual Machine Lock (Global VM Lock), но проблема осталась. Благо развитие технологии не стоит на месте, поэтому в местах приложения где Global VM Lock является проблемой можно использовать такие возможности языка как Fiber, Ractor и прочее. Но все же, если хочется написать быструю "числодробилку" (скрип для перемножения матриц, ML или расчет рендеринга), то лучше взять языки где забота о памяти делегирована разработчику. Поэтому Ruby не нашел себя в ML, но в вебе большая часть времени тратиться на I/O (приложение ждет ответа от БД или от http-соединения), там где это критично можно использовать Thread. Например в Rails 7 уже есть параллельное выполнение запросов в БД load_async.

В 2019 году компания Basecamp (основной разработчик RubyOnRails) выпустила статью, что только 15% операционного бюджета тратится на исполнения Ruby кода. Если хорошо написать и сконфигурировать приложение, то тормозов особо не почувствуешь. Даже если бы они потратили усилия и сделали язык в 2 раза быстрее, то это не сильно уменьшит затраты компании. Так что ускорение исполнения кода не в ТОП-приоритетах.

Не для каждого приложения скорость исполнения кода критична. Вы заметите пробуксовку если ваше приложения начнет обрабатывать сотню запросов в секунду. Редкий разработчик получается такую нагрузку на начальных этапах создания приложения/продукта. Если MVP на старте имеет под сотню запросов - это очень хорошо. Но и с ней RubyOnRails может справится т.к. имеются возможности масштабируемости через увеличение процессов и потоков.

С точки зрения языка, есть сферы где скорость исполнения решает: парсинг XML, работа с шифрованием и прочим. Для таких сфер есть выход т.к. Ruby позволяет встроить расширения на C++. Популярные библиотеки для работы с такими задачами написаны как раз на C++, а на Ruby реализована только оболочка/интерфейс. Точь-в-точ такая же ситуация в Python в сфере ML. Сам код Python не занимается числодроблением или перемножением матриц, а занимается только управлением потоком данных. Там все математические операции совершаются расширениями на C++, а комьюнити Python потратило много времени чтобы ситуация стала такой.

3.2 Остальные сферы использования

Исторически сложилось, что основная сфера использования Ruby - это веб-приложения. Если нужен ML или машинное зрения, то экосистема Python намного богаче. Если нужно подсчитать аналитику, то есть R или Python. Но это не значит что какие-то простенькие данные не получится посчитать на Ruby. Еще есть сферы в которых монополия одной технологии: фронтенд, мобильная разработка и прочее, в них так исторически сложилось. Но по факту Ruby используется не только в вебе. Если интересно, то посмотрите как его используют в родной для него Японии. Там находится огромное ruby-комьюнити и исторически сложилось что Ruby - это не только рельсы. Сфера его использования более широкая, японцы применяют его как обычный скриптовый язык. Они используют его в программировании контроллеров (есть mruby), как скриптовый язык для других систем, в написание поисковых движков и пр. Поэтому, если хотите послушать доклады про Ruby, а не про Rails - добро пожаловать на RubyKaigi.

Еще Ruby активно используется в инфраструктурных задачах. Под macOS на нем написана популярная утилита установки приложений и пакетов HomeBrew из-за которой Ruby появился по-умолчанию в macOS. Плюс в вакансиях сисадминов в требованиях можно увидеть опыт с Ruby. Под Ruby есть решения для других платформ, например: для gamedev и для frontend. Они не очень популярны, но при желании поиграть с ними можно.

3.3 Аннотация типов

Такие языки как Javascript и Python к этому уже дошли, а что с Ruby? Для документации аннотации типов есть yard, но это аннотации только на уровне документации. Для написании и проверки типов есть инструмент Sorbet от Stripe. В Ruby с версии 3.0 появилась нативная аннотация типов (RBS), но они пишутся в отдельных файлах, как в Cи и TypeScript. Типы вынесены в отдельные файлы т.к. создатель языка уверен, что разработчики должны ориентироваться на бизнес-фичах, а не на написании типов. Он уверен что через пару лет языки программирования отойдут от ручного написания типов в сторону инструментов автоматическая генерация сигнатур и работа по этому направлению уже идет - TypeProf.

3.4 Входной порог

В Ruby входной порог выше чем у других языков, поэтому часто Ruby - не первый язык программирования. У синтаксиса богатые возможности, но именно они могут запутать новичка. Поэтому новичкам советуют начать с Python или Go т.к. там все конструкции простые, а после первого опыта человек смотрит какие еще языки есть, что ему не нравится в текущем инструменте и какие возможности дают другие технологии. Собственно, многие Ruby программисты уже имели опыт программирования на других языках прежде чем они пришли к Ruby. Благо линтер Rubocop задает единый стандарт написания и поможет объяснить новичку как писать код правильнее и покажет ему лучшие практики.

4 Особенности найма

Ruby особенности найма

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

С другими языками такое не пройдет. Ищите вы Python или PHP разработчика и уже на этапе составления описании вакансии мы отрезаем часть кандидатов потому что они работали с другим фреймворком. Возьмем Java или PHP разработчика - на собесе надо понять на чем он может писать. Возьмем питониста - надо понять его специальность - backend, автотесты, ML, Data Science, ETL. Поэтому одного разработчика на Python может быть недостаточно для запуска приложения. Большинство RubyOnRails разработчиков - это fullstack специалисты, которые в одиночку могу запустить web-приложение. Кто-то может написать простенький фронт, кто-то следит за технологиями и писал HTML поверх вебсокетов. Но на рынке их меньше чем nodeJS или Python разработчиков и обычно просят они больше.

5 Комьюнити

Ruby комьюнити

Хочется добавить что за любой технологией стоят люди, а Ruby славится своим комьюнити. Может быть оно не такое большое как в других языках т.к. разработчиков меньше (но история знает примеры когда костяк экосистемы написал всего один человек), но оно не последнее по влиянию на open source. Благодаря Ruby-комьюнити появились такие проекты как GitHub т.к. сам принцип обмениваться кодом и идеями лежит в основе этого комьюнити. Еще у RubyOnRails больше всего контрибьюторов по сравнению с другими технологиями. Такой же любовью к open source наверно может похвастаться только фронтенд комьюнити.

Само комьюнити продолжает идею Матца что Ruby создавался в первую очередь для комфорта иначе как объяснить что в RubyOnRails есть такие методы у массива как second, third, second_to_last, third_to_last и внезапно forty_two. Плюс, Ruby разработчики, когда уходят в другие языки часто привносят туда идеи, которые они увидели в мире Ruby, например: dry-python и pyenv. Экосистема Ruby была вдохновением для других языков например: RubyOnRails вдохновила PHP разработчиков на Laravel; Rspec вдохновила JS разработчиков на написании библиотек mocha, jasmine и Chai.

При этом Ruby комьюнити повезло что у них в 2009 году получилось написать один каноничный удобный и качественный установщик зависимостей bundler Единый детерминированный установщик зависимостей с lock-файлом, с поддержкой multi-ENVs и с возможностью установки зависимостей сразу с GitHub. Чем не могут похвастаться экосистемы Go и NodeJS. Конечно и у него были свои проблемы в первые годы, например проблемы со скоростью, но и их удалось преодолеть. Bundler стал прообразом таких инструментов как yarn в JS, cargo в Rust и composer в PHP.

Раньше экосистемой Ruby вдохновлялись, но сейчас тренд развернулся на то, что Ruby стал брать что-то от других. У нас есть pattern matching, как в Elixir и Erlang. Это инструмент позволяет тратить меньше сил на описание control flow. Есть трайспайлер RubyNext (как в babel в JS) - инструмент для использования новых возможностей языка на предыдущих версиях Ruby. Он помогает собрать обратную связь о нововведениях и повысить качество и скорость внедрения новых фич в языке. Но, до сих пор где-то вдохновляются RubyOnRails даже в решениях для фронта. Например sockpuppet для Django - это имплементация stimulus-reflex от Rails.

Комьюнити продолжает расширяться и собирать большие конференции по всему свету RubyKaigi (Японию), RubyRussia (Россия), Euruko (Европа), RailsConf (США), RubyConf (США) и другие.

6 Актуальность технологии

Ruby актуальность технологии

Язык и фреймворк были очень популярные в 2013-2015. Но c 2016 года появились миф что "Ruby is dead". В этих криках нет смысла т.к. любая технология проходит один и то же hype cycle. Каждая технология проходит пик завышенных ожиданий и люди считают что она устарела, хотя на самом деле после этого она выходит на плато продуктивности и это именно тот этап когда она достаточно зрелая для использовании в продакшене. Java же не умерла с 2005 года и до сих пор актуальна в продакшене.

По существу редкая технология может умереть, особенно в open source т.к. за годы на ней пишутся очень много всего, что нужно поддерживать и дальше развивать. Иначе как объяснить что по состоянию на 2023 год в рейтинге TIOBE Fortran на 16 месте? Плюс саму актуальность технологий рассчитать сложно т.к. у нас есть только косвенные способы это сделать. Кол-во вопросов на stackoverflow? Это показывает кол-во начинающих разработчиков которые учатся. Звездочки или кол-во проектов на github? Но github - это склад строительных материалов, в некоторых комьюнити не принято ставить звездочки. В серьезном энтерпрайзе не часто библиотеки публикуются публично. Вакансии на hh.ru? Это показатель нехватки специалистов в этой области на данный момент. Даже сам TIOBE рассчитывается по упоминаниям в 25 поисковых движках. Поэтому посчитать актуальность на самом деле сложно.

Подытожим

Ruby Почему я программирую на Ruby

Как видно, Ruby изначально создавался как максимально удобный язык для разработки и он до сих пор он не менял своего ключевого приоритета. У него богатая стабильная экосистема с разнообразными решениями под широкий спектр задач. Он был и остается языком общего назначения и в web-разработке он до сих пор прекрасен как fullstack инструмент. А уж как писать web-приложения в комьюнити знают. У языка есть так называемые "слабые" места, но их можно преодолеть когда знаешь как и почему возникли эти особенности. Комьюнити продолжает развивать язык и инструменты вокруг него, смотрит и берет фишечки из других языков, что делает Ruby актуальным инструментом. Именно поэтому он идеально подходит под мои текущие нужды после стольких лет использования.

Дополнительно, соглашусь со словами Григория Петрова, что язык Ruby идеально подходит человеку если:

  • Он понимает, что ему нравится программировать и он хочет чтобы сам процесс написания кода доставлял максимальное удовольствие.
  • Его область интересов - помогать бизнесу. Не gamedev, не frontend, не код для физических устройств, не код для научных расчетов, не сайт-визитки. Ruby и его экосистема хороша чтобы решать бизнес задачи при помощи веб-приложения, скриптов интеграции и прочего.

Если у вас именно такие потребности, то сделать ставку на Ruby - хорошее решение!

I ❤️ Ruby

Top comments (7)

Collapse
 
mikhailushka profile image
Михаил

Отличный материал!

Collapse
 
ilias_gimranov profile image
Ilias Gimranov

Крутая статья - много интересного! И круто, что есть много ссылок на другие материалы.

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
kagayakashi profile image
Vyacheslav Odinokov • Edited

Крутая статья, жаль что начать работать на нём сложно мне, как человеку из Европы.
На текущей работе менять php 7.4 который надо переписывать на 8.3 не хотят.
Найти другую работу уже на Ruby/Ror, где не просто магазин делают не получается.
Я нормальных вакансий не вижу.
Либо они не умеют их заполнять и пишут всякие умные фразочки, которые я не понимаю.
Либо я плохо ищу.

Collapse
 
sergeev profile image
Vasily Sergeev

В 2016 году я пытался войти в ruby даже удалось уехать с региона в Москву и работать в шикарном Москва Сити, но не зашло до этого был php и C++, возможно не хватило экспертизы. С 2020 активно изучал python и ML, ушел в яндекс где до сих пор создаю нейросети и ML модели. Не сказал бы что жалею что не стал связывать свою жизнь с ruby но всему свое время... Если что-то не получаеться то возможно это не для вас! Всем удачи и следуйте своему сердцу а не трендам в ИТ!
З.Ы Из всех анкет а их 4, это Python/Ruby/C#/PHP, больше всего предложений идет от ruby! примерно 2-3 в неделю! И это показатель актуальности и малого количества разработчиков, на другие анекты такого спроса нет, ищи нас сам!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.