Истоки стека технологий


В определенный момент пришло моё время написать что-то отдаленно похожее на web сайт. Самый обычный сайт: главная страница с отображением на ней трёх табличек из БД и парой форм для заполнения оных контентом. Такие мной были поставлены начальные требовнаия. Думаю, у каждого при написании первого сайта возникает вопрос: какие инструменты для этого использовать? Для меня были довольно принципиальны критерии:

  • использовать Ruby, так как я с ним довольно хорошо знаком (а с php и python — нет)
  • использовать объектную модель насколько это возможно
  • ограничиться только необходимыми средствами разработки: ведь незачем тащить за собой JQuery, когда нужен всего один POST запрос

Соответственно, взгляд упал на Ruby on Rails: это полноценный framework, довольно популярный и, как ни странно, содержит в себе Ruby.

По поводу 'содержит'
Можно сколько угодно говорить, что там "всё" на Ruby. Но самого языка там нет: только отголоски в виде синтаксиса хешей и т.п…
И тот "подправлен" заботливыми рельсами. Я имею ввиду, что весь встречающийся синтаксис мог внезапно оказаться C++. И никто бы не заметил — всё скрывается framework'ом.


И принялся я его изучать, чего и вам желаю. Хороший гайд: помогает понять, что рельсы — это гигантская сборка gem'ов, которые потом ещё и разворачивать придется на passenger. То есть, кроме мороки с bundler'ом или rvm (чего в итоге не избежать), придется ещё и passenger стыковать с Apache или nginx. Меня эти перспективы напугали и, дочитав таки tutorial, я начал искать чем бы RoR заменить, оставив при этом от него только необходимое. Для меня все ограничилось Ruby и ActiveRecord. Первые поиски пути выполнения Ruby кода на Apache показали, что есть mod_ruby: этот и этот.

Что за mod_ruby
Для тех, кто не понял в чем дело: Apache — довольно модульная система и у него есть API, позволяющий обрабатывать запросы как душе угодно.

Вот на это API и пишутся модули, позвоялющие выполнять скрипты PHP, Python и Ruby. Это не CGI, что сулит повышенную производительность. Вот какие модули я имею ввиду.


Однако, первые же опыты привели меня к такой и вот такой ситуации.

Конечно, не бог весть какие проблемы: всё решаемо, но тут я посмотрел на даты последних коммитов, увидел заветное "пол года назад" и отказался от этой идей.

После этого мой выбор пал на FCGI как довольно перспективное продолжение CGI. Почему не CGI? А потому-что. Т.е. для FCGI у Ruby есть gem, который позволяет обрабатывать запросы не в сыром виде CGI, а посредством интерфейса гема. Смотрится удобно, но об этом позже. Ну вроде всё: есть Apache, есть FCGI, Ruby… И, так как у меня есть небольшой backend в виде БД, а работать с тяжелым mysql или аналогами не хотелось, решил я прихватить ActiveRecord из RoR себе в виде файла sqlite3 БД.

Заинтересовало? Добро пожаловать под кат.

Разбор компонент


Составив такой стек, выделил я себе серверок с CentOS и приступил к воплощению идей в жизнь.

Зачем CentOS?
Так уж вышло: попался он под руку. Но, большиство манипуляций будут дейтсвительны и за его пределами. В пределах Linux, конечно.


Apache


Всё начинается с Web сервера. В моём случае всё оказалось довольно просто: yum install epel-release; yum install apache mod_fcgid fcgi-devel mod_ssl gnutls-utils.

Такая портянка пакетов обусловлена нашей жаждой оспользовать gem ruby-fcgi… И, конечно тем, что почти все пакеты в EPEL. По старой админской привычке я пропарсил конфиг Apache… И нашел там кучу совершенно бесполезных модулей, чего и вам советую!

Настройка
Настройка Apache не является главной целью статьи, но, для понимания контекста работ, приведу пример виртуального хоста, на котором все это разворачивалось:

<VirtualHost mysite:443>
#common options
    ServerName mysite:443
#loging
    LogLevel info
    ErrorLog logs/mysite-error_log
    CustomLog logs/mysite-access_log common
#main dir
    DocumentRoot /var/www/html/
    <Directory /var/www/html/>
        Options ExecCGI
        DirectoryIndex index.rb.fcgi
        AllowOverride None
#Access
        Allow from all
#LDAP
        AuthLDAPUrl <>
        AuthLDAPBindDN <>
        AuthLDAPBindPassword <>
#Authorization
        AuthType Basic
        AuthBasicProvider ldap
        AuthName "Input your domain login name and password"
        Require ldap-attribute <>
        Require ldap-attribute <>
    </Directory>
#Scripts timeouts
FcgidIOTimeout 300
#SSL
    SSLEngine on
    SSLProtocol TLSv1
    SSLCertificateFile /var/www/html/certs/ca_cert.pem
    SSLCertificateKeyFile /var/www/html/certs/ca_key.pem
</VirtualHost>

<> помечены опущенные параметры

Само собой, имя mysite прописано в DNS сервере, mod_ssl установлен.


mod_fcgid


Как вы должны были заметить, вместе с Apache мы ставим mod_fcgid, который и есть интерфейс для встраивания скриптов для обработки запросов.

При установке этот модуль создал конфиг в /etc/httpd/conf.d/fcgid, содержащий:

LoadModule fcgid_module modules/mod_fcgid.so
AddHandler fcgid-script fcg fcgi fpl
FcgidIPCDir /var/run/mod_fcgid
FcgidProcessTableFile /var/run/mod_fcgid/fcgid_shm

Интерес здесь представляет только строка AddHandler fcgid-script fcg fcgi fpl, которую неплохо бы из соображений безпасности заменить на AddHandler rb.fcgi.

Так мы ограничиваем исполнение FCGID только скриптов с расширением rb.fcgi.

Ruby


Само собой, надо ставить Ruby. Но не в коем случае не из репозитория: многие печали буду. А конкретнее: придется прописывать полные пути до gem'ов при их подключении.

А потом перепрописывать при обновлении. Так что ставим RVM:

sudo su -
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable --ruby=2.2.1
source /etc/profile.d/rvm.sh 

Почему установка глобальная?
Если проект действительно серъезный, то ни в коем случае так не делайте. Необходимо создать пользователя для сайта и делать всё в его окружении. Но не об этом речь.


После выполнения этого в системе появится rvm и ruby… и ещё кое что.

FCGI gem


Дело дошло до настройки FCGI. Если как обычно, то gem install fcgi. Только в этом случае gem установится в текущий gemset в rvm. А для того, чтобы в скриптах, которые будет выполнять Apache, gem'ы были видны, надо устанавливать их в глобальный gemset. То есть, rvm gemset use global; gem install fcgi. И далее, если я говорю "устанавливаем gem", то имеется в виду нечто подобное.

И снова глобальная установка
Если у вас таки отдельный пользователь и в конфиге VirtualHost сделан Suexec, то можно создать гемсет для него, но не факто что такой фокус удастся: я не представляю как Apache делает Suexec и какой вид окружения пробрасывается.


Буквально это нам даёт право писать require "sqlite3" в FCGI скриптах для Apache. Однако, интерпретатор в скриптах по-прежнему придется указывать #!/usr/local/rvm/rubies/ruby-2.2.1/bin/ruby — мне этого не удалось избежать.

А как же SetEnv?
Он то конечно есть, но от проблемы он не спасает: всеравно статический путь в конфигурациях.


Теперь, потерев руки, можно делать файл /var/www/html/index.rb.fcgi и писать в него чтото типа:

#!/usr/local/rvm/rubies/ruby-2.2.1/bin/ruby
require "fcgi"

FCGI.each { |request|
    case request.env["REQUEST_METHOD"]
        when 'GET'
            request.out.print "Content-Type: text/html\nStatus: 200 ОК\n\n<!DOCTYPE html><html><h3 align=center style=\"color: green\"><strong>Success</strong></h3></html>"
        else
            request.out.print "Content-Type: text/html\nStatus: 403 Forbidden\n\n<!DOCTYPE html><html><h3 align=center style=\"color: red\"><strong>Access denied</strong></h3></html>"
    end
    request.finish
}

Довольно простой скрипт: выдает страничку с 'Success' на GET запрос и страничку с 'Access denied' на любой другой.

Т.е. fcgi gem предоставляет нам класс FCGI, который, будучи использованный в FCGI скриптах, может принимать запросы самостоятельно.
Мной это изучено по документации проекта.

Мы получаем при запросе объект request, который имеет:

  • env поле: содержит всю информацию о запросе: тип запроса, параметы запроса, тип брузера, пользователь и многое другое. Все это можно (и нужно) использовать при обработке запроса;
  • in поле представляет собой тело запроса. То есть, насколько мне стало ясно, env — это представление заголовка, а in — буквально тело HTTP запроса;
  • out поле служит для записи в него всего, что надо отдать в ответ на запрос: полная FCGI-свобода действий, что видно по отправке статуса 200 и 403 выше.

in и out — обычные объекты типа IO, что какбы намекает.

Одна из задач выполнена: мы получаем чистый Ruby для разработки сайтов. Можно использовать все, что только есть в интернетах, для генерации html.

ActiveRecord gem


Вот тут, внезапно, пронадобилось использовать БД как backend. И, раз уж мы на Ruby, воспользуемся Rails фишкой в виде ActiveRecord.
Не скрою, я пользовался этой статьёй, но у меня есть что добавить. А, дабы не путать вас в ссылках на неё, опишу всё по порядку.

Устанавливаем gem: gem install activerecord sqlite3 — для работы с sqlite3 адаптером БД.

Подключение БД


Для работы с БД необходимо 'создать соединение' с БД. Это выглядит както так:

require 'active_record'
require 'sqlite3'
ActiveRecord::Base.establish_connection(
  :adapter => 'sqlite3',
  :database => "/var/www/html/db/mysite.db"
)

Ничего сверхъестественного и ничего нового они не превносят — просто указание на расположение БД и её формат. Эти строки надо добавить при инициализации любого fcgi скрипта для подключения в его окружение БД.

Миграции


Миграции (в терминологии Rails) — это метод автоматизаци разворачивания схемы БД при разработке, тестировании, внедрении,… По сути, это скрипты, которые запускаются для создания другими людьми БД, используемой сайтом.

Пример такого скрипта:

#!/usr/local/rvm/rubies/ruby-2.2.1/bin/ruby
require 'active_record'
require 'sqlite3'
ActiveRecord::Base.establish_connection(
  :adapter => 'sqlite3',
  :database => "/var/www/html/db/mysite.db"
)
class CreateUsers < ActiveRecord::Migration
  def up
    create_table :users do |t|
      t.string :name
      t.string :email
      t.string :group
      t.timestamps null: false
    end
  end
end
CreateUsers.migrate(:up)

Этот скрипт создает таблицу users, содержащую 5 полей — id, name, email, group и timestamp.

Существует довольно много действий, осуществимых с БД путем миграций, но не будем заострять на этом внимание. Все умеют читать мануалы. А для нас главное понять, что для создания и модификации БД необходимо написать ряд скриптов, а не носить везде с собой файл sqlite со схемой. Эти файлы никуда не включаются — это отдельная часть проекта и при работе сайта она не используется.

Валидации


Казалось бы, БД есть — почему бы не использовать её. Но не все так просто: неплохо бы определиться с тем, что наша БД может содержать, а что категорически нет.

Это подразуммевает, что у нас на руках есть схема таблиц БД с обозначенными полями, их типами, допустимыми значениями и связями между ними.

Что-то код не работает
Я привожу только часть кода как пример: опускаю некоторый функционал, так как реализация чего-либо не является целью статьи.
Цель — вывести ActiveRecord на чистую воду.


Здесь и начинается то, что упускает большинство руководств по использванию ActiveRecord. Не буду томить, скрипт валидаций:

class Host < ActiveRecord::Base
    belongs_to :user, :inverse_of => :hosts, :validate => true

    validates :address, :presence => true, :uniqueness => true
    validates :user_id, :presence => true
    validate :address_and_user_should_exists,

    def address_and_user_should_exists
        if Address.find_by_id(address_id) == nil
            errors.add(:address_id, "should points at exist address")
        end
        if User.find_by_id(user_id) == nil
            errors.add(:user_id, "should points at exist user")
        end
    end

    validates :name, length: { minimum: 2, maximum: 20 }, presence: true, :format => { :with => /\S(\S|-)*\S[^\z]/i }, :uniqueness => true
    validates :purpose, length: { minimum: 4, maximum: 100 }, presence: true, :format => { :with => /[^\$\^\&\`]+/i }
    validates :description, length: { maximum: 700 }, presence: false, :format => { :with => /[^\$\^\&\`]*/i }

end

class User < ActiveRecord::Base
    has_many :hosts, :inverse_of => :user, :dependent => :destroy

    def self.valid_groups
        ["test", "probe" "etc"]
    end
    validates :name, length: { minimum: 2, maximum: 30 }, presence: true, :format => { :with => /\S(\S| )*\S[^\z]/i }, :uniqueness => true
    validates :email, length: { minimum: 5, maximum: 40 }, presence: true, :format => { :with => /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i }
    validates :group, length: { minimum: 2, maximum: 30 }, presence: true, :format => { :with => /(\w|-)+/i },
        :inclusion => { :in => valid_groups, :message => "%{value} is not a valid group. Select one from drop-down hint."}
end

require и подключение БД опущены

Он предназначен для определения допустимых значений, записываемых в БД. В приведенном скрипте:

  • описаны две таблицы: users и hosts (согласно нотации Activerecord, классу User соответствует таблица users и т.п.). Они представлены классами, наследующими ActiveRecord:Base.
  • описаны отношения между полями этих таблиц в виде директив has_many и belongs_to
  • описаны сами валидаторы значений полей — это все методы класса (self.) и все вызовы `validate`

Начнем разбор всего, что написано в валидаторе. Строка class User < ActiveRecord::Base означает, что в окружении у нас теперь есть класс User, который представляет собой (связан с) таблицей users БД, которую мы где-то выше подключили. Так, вызов User.find_by_id позволяет производить поиск записей по id в таблице. И ничего больше. То есть, никакой магии нет — просто возвращается запись, если она есть.

Далее в обоих классах идёт строка, описывающая связи между таблицами.

Например, has_many :hosts, :inverse_of => :user, :dependent => :destroy означает, что:

  • для записи этой таблицы можно ожидать, что она имеет ноль или несколько связанных записей в таблице hosts
  • является логической парой связи user, опиcанной в User
  • при удалении записи из этой таблицы будут удалены связанные с ней записи

Здесь и кроется основная тайна ActiveRecord: связи AR не являются связями в БД. В БД их попросту нет.

То есть, при использовании связи has_one не следует ожидать, что AR будет отслеживать соблюдение отношения 1:1 при добавлении записей: это на вашей совести.

А ActiveRecord только гарантирует, что объявление связи позволит находить связанные записи.

И, наконец, идёт список валидаторов полей:

    validates :user_id, :presence => true
    validate :address_and_user_should_exists,
    def address_and_user_should_exists
        if Address.find_by_id(address_id) == nil
            errors.add(:address_id, "should points at exist address")
        end
        if User.find_by_id(user_id) == nil
            errors.add(:user_id, "should points at exist user")
        end
    end
    validates :name, length: { minimum: 2, maximum: 20 }, presence: true, :format => { :with => /\S(\S|-)*\S[^\z]/i }, :uniqueness => true

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

Там можно узнать как пользоваться валидаторами, в том числе и в своих коварных целях.

К чему я веду: ActiveRecord упрощает жизнь тем, что не приходится иметь дело с sql синтаксисом или чем-то подобным. Однако, вам придется реализовать всю логику работы схемы БД самостоятельно.

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

Сухой остаток


После написания всех упомянутых выше вещей мы получаем:

  • Доступ к записям БД посредством вызова User.find_by(:name => "Vasya"), User.first.name, User.all.each {...}
  • Возможность создания и удаления записей (с выполнением наших валидаций): User.create(:name => "Oleg", ...), User.first.destroy
  • Возможность получения связанных записей: User.first.hosts (причем, если связь 1:1, то было бы User.first.host). Поконкретнее про связи здесь.

Вместо заключения


На выходе мы получили исполнение Ruby скриптов по запросам Apache с вкраплениями ActiveRecord. В такой стек хорошо вписывается MVC из RubyOnRails путем использования валидаций как модели, Ruby + FCGI как контроллера и Ruby генераторов html как представлений. Надо ли приводить гайд на структуру проектов на основе вышеописанного покажет время.

Спасибо за внимание.

ЗЫ: Раз уж у многих матёрых web разработчиков пошла на эту статью реакция, то вот дисклеймер: описывается как работают ruby приложения в web. Без Rack. Без Rails. Ruby + AR -> FCGI -> Apache. Только то, что необходимо. Если вам мало уровней абстракции вашего кода от http, html и sql, то можно добавить сколько угодно слоёв. Это упрощает жизнь, но не стоит забывать про их существование.

Комментарии (31)


  1. Metus
    29.02.2016 13:44
    +2

    А чем плохи unicorn, puma и прочие?


    1. zirf
      29.02.2016 15:07

      помогает понять, что рельсы — это гигантская сборка gem'ов, которые потом ещё и разворачивать придется на passenger

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

      $ rails new i_am_lazy
      $ cd i_am_lazy
      $ rails server


      1. Metus
        29.02.2016 15:34

        Переформулирую вопрос — чем rack-сервер хуже fsgi?
        Чем rack-сервер хуже даёт понять, что рельсы — это ....?


        1. deman_killer
          29.02.2016 15:40
          -2

          Не могу ответить точно. Но rack-сервер не предназначен для production, не так ли?


          1. fuCtor
            29.02.2016 15:50
            +1

            rack сервер наоборот предназначен для продакшена. У вас может быть X узлов, на которых крутится сервер приложение, и один узел с балансировщиком (nginx), вот это как раз будет более боевая конфигурация чем Apache + FCGI.


            1. deman_killer
              29.02.2016 16:02
              -2

              Это все хорошо, только причем тут RoR. Скорее всего, X*Rack + nginx будет шустрее, чем fcgi+Apache… И всеравно зависит от ситуации. Только Rack можно притащить с собой и без Rails.
              Вопрос только целесообразности.
              Судя по этому Rack работает через CGI, FCGI и т.п. То есть всеравно получается Rack -> (CGI, FCGI) -> (Apache, nginx)


          1. Metus
            29.02.2016 15:53

            Почему не предназначен? puma, unicorn — это и есть серверы, работающие через rack-интерфейс. И их используют в production.
            Для production не предназначен webrick, но это только один из многих — и все они взаимозаменяемы.

            Они легко присоединяются к тому же nginx через сокет и мы получаем явное разделение: сервер-приложения и веб-сервер.


            1. deman_killer
              29.02.2016 16:21
              -2

              Каюсь, rack перепутал с webrick. Но, опять же, rack работает через стандартные интерфейсы web серверов.


      1. deman_killer
        29.02.2016 15:36
        -1

        Если не углубляться в "как", то рельсы прекрасно себя показывают. Речь не о том, что RoR сложно использовать, а о том, что не всегда оправдано нести с собой их целиком. Довольно много при поисках "что такое ActiveRecord" я находил проблем, связанных с непониманием принципов его работы: натыкаются на несоответствие реальности и ожиданий. Тут и приходится копать: а каким путем происходит "подтягивание" функционала рельсами. А если все работает прозрачно, то и ошибок меньше.


        1. sl_bug
          01.03.2016 00:31
          +2

          Если рельсы не оправданы, то есть sinatra. А зачем ваши мучения не ясно


      1. estum
        29.02.2016 15:51

        Собственно, рельсы и любой другой фреймворк тут вообще не причем, все что нужно — это rack, app-сервер (puma, unicorn или passenger) и nginx. Даже писать ничего особо не надо: пара строк в конфигах и готово.

        Просто автор, видимо, не знает про rack и решил изобрести — даже не велосипед, нет — а колесо.


        1. zirf
          29.02.2016 16:35

          Ну я автора собственно и процитировал, дабы намекнуть что в общем все автоматизировано уже. Сервера приложений отличаются своими фичами, и фактор привычности, конечно. Web сервер по большому счету тоже все равно какой.


        1. avdept
          29.02.2016 16:43

          Квадратное причем.


    1. deman_killer
      29.02.2016 15:42
      -2

      FCGI — стандрат, поддерживаемый apache. Unicorn, passenger,… — отдельное ПО.
      Работа с FCGI показалась мне прозрачнее.


  1. fuCtor
    29.02.2016 15:47

    Зачем такие сложности??
    Для подключения gem-ов как минимум есть bundler. Для запуска есть rack-сервера, которые завернут куда надо, некоторые даже явно конфигурировать не надо, просто добавить в зависимости.

    использовать Ruby, так как я с ним довольно хорошо знаком

    и в тоже время

    if Address.find_by_id(address_id) == nil

    Не однозначный конечно текст.


    1. deman_killer
      29.02.2016 16:07
      -2

      Про bundler согласен. Только надо попробовать что с ним будет, когда Apache из своего окружения вызовет скрипт… можно туда зацепить bundler или нет.
      Про код: а лучше писать неявно?


      1. Metus
        29.02.2016 16:11

        Предполагаю, имелось ввиду что-то вроде этого:

        if Address.find(address_id).nil?


        1. fuCtor
          29.02.2016 16:37

          Хотя бы да, но есть еще варианты

          unless Address.find_by_id(address_id)
            errors.add(:address_id, "should points at exist address")
          end

          или более наглядно

          unless Address.exists?(address_id)
            errors.add(:address_id, "should points at exist address")
          end


          1. Gris
            29.02.2016 23:22

            Тогда уж еще проще

            errors.add(:address_id, "should points at exist address") unless Address.exists?(address_id)

            или валидацию написать

            class Address < ActiveRecord::Base
                has_many :hosts, inverse_of: :address
            end
            
            class Host < ActiveRecord::Base
                belongs_to :address
                validates :address, presence: true, uniqueness: true
            end

            тоже должно работать, без необходимости в кастомном валидаторе.


            1. deman_killer
              29.02.2016 23:55
              -1

              has_many и belongs_to — не валидаторы. В этом и вся соль — они только определяют пачку вспомогательных методов.


              1. sl_bug
                01.03.2016 00:32

                не только. зависит от параметров.


                1. deman_killer
                  02.03.2016 16:28

                  Ок. Тогда по старой схеме: с меня это, а минус с вас.


            1. printercu
              01.03.2016 11:14

              Уже некоторое время можно required: true вместо presence валидатора:

              belongs_to :address, inverse_of: :hosts, required: true


        1. KriMs
          29.02.2016 16:38
          +1

          Metus, Не сработает, сразу после find() вы получите ActiveRecord::RecordNotFound если записи нет.
          у fuCtor корректный вариант


  1. avdept
    29.02.2016 16:36
    +5

    Автор посмотрел как создать блог за 15 и решил сделать свой сайт?

    использовать Ruby, так как я с ним довольно хорошо знаком (а с php и python — нет)

    увы вы с руби знакомы судя по всему не лучше чем с php\python, т.к. по коду это уровень человека который пишет на руби 2-3 месяца

    ограничиться только необходимыми средствами разработки: ведь незачем тащить за собой JQuery, когда нужен всего один POST запрос

    а то что рельсы тащат за собой кучу active-*** гемов, и еще кучу зависимостей — это ничего? Или то что jquery-ujs по дефолту идет в рельсовом гемфайле?

    То есть, при использовании связи has_one не следует ожидать, что AR будет отслеживать соблюдение отношения 1:1 при добавлении записей: это на вашей совести.

    Как раз таки AR проверяет что бы вы yе добавили еще одну запись при наличии оной.

    Про apache и прочие танцы с бубном — выдает в вас пхпшника все же.


    1. deman_killer
      29.02.2016 20:17
      -1

      увы вы с руби знакомы судя по всему не лучше чем с php\python, т.к. по коду это уровень человека который пишет на руби 2-3 месяца

      Может и так. Пруф?

      а то что рельсы тащат за собой кучу active-*** гемов, и еще кучу зависимостей — это ничего? Или то что jquery-ujs по дефолту идет в рельсовом гемфайле?

      А я о чем? Как раз и использовал AR без рельс. Честно, вы читали что я написал?

      Как раз таки AR проверяет что бы вы yе добавили еще одну запись при наличии оной

      А вы попробуйте. Не проверяет он без валидатора ничего. has_ дает только методы доступа.


  1. fzn7
    29.02.2016 16:53
    +1

    Мать чесная… А Sinatra чем не понравился?


    1. deman_killer
      29.02.2016 20:23
      -2

      Хм… Ничем — стоит присмотреться.
      Но он, опять же, использует Rake. Гляньте посты товарища fuCtor на эту тему выше.


      1. printercu
        29.02.2016 21:01
        +3

        rack, rake… всё смешалось.
        зачем это вообще тут, для кого


  1. j_wayne
    01.03.2016 10:41
    +1

    Много раз разворачивали nginx + passenger + bundler + rvm для продакшена разных проектов. Не понимаю, чего тут можно бояться? До этого и Apache бывало использовали (если уж это железобетонное требование)


    1. zirf
      01.03.2016 15:12

      Вообще, с настройкой парится можно дома по выходным. А так масса вариантов помучиться 1 раз. 2 контейнера Docker, webserver+webapp и db. Сами приложения на хосте или в отдельном контейнере, как и базы. С небольшим джентльменским наборчиком выпуск приложения превращается в довольно простое мероприятие, в отличие. Или готовые PaaS использовать. Самое главное — четко разделить работу ИТ-инженера и разработчика. Это не должен делать один человек. А тут описание любительских мучений. Просто блины — комом это нормально. Задача комментаторов не только себя показывать, но и автору намекнуть что хотелось бы видеть.