Rails — многопользовательское приложение с настраиваемой структурой

Я организую мультитенантное приложение с одной кодовой базой/приложением, используя поддомены для обнаружения арендатора, а затем запускаю SET SCHEMA на postgres, чтобы делать забавные вещи.

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

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

Я исследовал несколько вариантов, включая использование include/extends (mixins). Проблема в том, что в продакшене методы остаются в объектах (понятно). Я пробовал использовать жемчужину миксологии, чтобы обойти это, но она работает не совсем так, как я планировал, и немного более инвазивна, чем мне хотелось бы, я также не понимаю, как связать ее с моделями ( в контроллерах я только что попробовал смешать/размешать через фильтры до/после).

Если у кого-то есть какие-либо идеи о том, как лучше всего подойти/решить эту проблему, я был бы очень признателен за ваши отзывы. FWIW это Rails3


person Mike Scappa    schedule 02.05.2011    source источник
comment
Соответствующий вопрос здесь. Любопытно узнать, удалось ли вам это с тех пор.   -  person Denis de Bernardy    schedule 10.07.2011


Ответы (2)


Если вы говорите о переключении баз данных на лету между запросами, то я думаю, что вас ждет мир боли, по крайней мере, когда дело доходит до использования ActiveRecord. Это сильно испортит систему кэширования, поскольку она запоминает вещи на основе идентификатора, а не идентификатора + схемы, что может привести к перекрестному загрязнению.

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

class Site < ActiveRecord::Base
  # Represents a site or installation of the application
end

class User < ActiveRecord::Base
  belongs_to :site
end

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

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

person tadman    schedule 02.05.2011
comment
Тадман, спасибо за ответ. На самом деле проблема не в базе данных и не в перекрестном заражении. Я не буду переключать базы данных на лету, я буду использовать функциональность схемы postgres и выбирать схему с фильтром вокруг, что должно быть хорошо. И поскольку я не многопоточен, все должно быть в порядке и без перекрестного загрязнения. На самом деле вы можете увидеть доказательство этого у Гая Наора из Morph Labs на act_as_conference 2009: bestechvideos.com/2009/03/26/. Моя проблема больше связана с настройкой функциональности. - person Mike Scappa; 02.05.2011
comment
По разным причинам, включая, но не ограничиваясь безопасностью, область видимости site_id в общих таблицах невозможна. Я также думаю, что это более подвержено ошибкам при перекрестном загрязнении, чем схемы postgres. - person Mike Scappa; 02.05.2011
comment
Самый надежный способ — иметь отдельные развертывания с разными файлами database.yml для каждого экземпляра, что, если управлять им через внешний интерфейс с балансировкой нагрузки, такой как Passenger, не так плохо, как кажется. Может быть, я больше олдскульный в этом смысле. Я посмотрю это видео. - person tadman; 03.05.2011
comment
Проблема не в данных. Кроме того, нежелательно разделять данные по разным базам данных. Это потребовало бы от меня управления экземплярами на уровне клиента, что было бы PITA для правильного масштабирования (некоторые клиенты горячие, некоторые нет и т. д.). Что касается данных, наиболее эффективным подходом в моей ситуации являются схемы pg. Как я уже указывал, моя проблема конкретно связана с настройкой для каждого клиента. Это означает изменение контроллеров, моделей и представлений (переопределение MVC по умолчанию на уровне арендатора без создания полностью отдельного приложения). - person Mike Scappa; 03.05.2011
comment
Думаю, мне так же любопытно, как и вам, узнать ответ на этот вопрос. - person tadman; 03.05.2011

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

Если вы нашли лучший ответ на этот вопрос, пожалуйста, обновите его.

person pdenya    schedule 15.06.2011
comment
На самом деле, это очень похоже на подход, который я выбрал. Код нужно доработать, но вроде работает. Обычно я определяю новые методы для класса в базе данных (включая код метода) или переопределяю метод в базе данных. Во время выполнения он проверяет, существует ли измененное действие или новое действие с учетом контекста, и запускает код eval. В случае переопределенных действий я создаю псевдоним для старого действия, а затем отменяю его, когда запрос завершается --- что должно нормально работать в средах с одним потоком. - person Mike Scappa; 15.06.2011