{"id":13878770,"url":"https://github.com/hoppergee/multi-tenant-support","last_synced_at":"2025-07-08T10:06:25.124Z","repository":{"id":43739019,"uuid":"410075319","full_name":"hoppergee/multi-tenant-support","owner":"hoppergee","description":"Build a highly secure, no data leak, multi-tenant rails app","archived":false,"fork":false,"pushed_at":"2022-12-06T12:50:25.000Z","size":486,"stargazers_count":47,"open_issues_count":3,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-16T13:08:15.766Z","etag":null,"topics":["multi-tenant","rails","ruby","tenant"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hoppergee.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":null,"patreon":"hoppergee","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2021-09-24T19:09:39.000Z","updated_at":"2024-12-13T03:31:48.000Z","dependencies_parsed_at":"2023-01-24T01:45:38.412Z","dependency_job_id":null,"html_url":"https://github.com/hoppergee/multi-tenant-support","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/hoppergee/multi-tenant-support","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoppergee%2Fmulti-tenant-support","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoppergee%2Fmulti-tenant-support/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoppergee%2Fmulti-tenant-support/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoppergee%2Fmulti-tenant-support/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hoppergee","download_url":"https://codeload.github.com/hoppergee/multi-tenant-support/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoppergee%2Fmulti-tenant-support/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264247845,"owners_count":23579054,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["multi-tenant","rails","ruby","tenant"],"created_at":"2024-08-06T08:01:59.396Z","updated_at":"2025-07-08T10:06:25.096Z","avatar_url":"https://github.com/hoppergee.png","language":"Ruby","readme":"# MultiTenantSupport\n\n[![Test](https://github.com/hoppergee/multi-tenant-support/actions/workflows/main.yaml/badge.svg?branch=main)](https://github.com/hoppergee/multi-tenant-support/actions/workflows/main.yaml) [![Gem Version](https://badge.fury.io/rb/multi-tenant-support.svg)](https://badge.fury.io/rb/multi-tenant-support)\n\n![](https://raw.githubusercontent.com/hoppergee/multi-tenant-support/main/hero.png)\n\nBuild a highly secure, multi-tenant rails app without data leak.\n\nKeep your data secure with multi-tenant-support. Prevent most ActiveRecord CRUD methods to action across tenant, ensuring no one can accidentally or intentionally access other tenants' data. This can be crucial for applications handling sensitive information like financial information, intellectual property, and so forth.\n\n- Prevent most ActiveRecord CRUD methods from acting across tenants.\n- Support Row-level Multitenancy\n- Build on ActiveSupport::CurrentAttributes offered by rails\n- Auto set current tenant through subdomain and domain in controller (overrideable)\n- Support ActiveJob and Sidekiq\n\n\n\nThis gem was inspired much from [acts_as_tenant](https://github.com/ErwinM/acts_as_tenant), [multitenant](https://github.com/wireframe/multitenant), [multitenancy](https://github.com/Flipkart/multitenancy/blob/master/lib/multitenancy/model_extensions.rb), [rails-multitenant](https://github.com/salsify/rails-multitenant), [activerecord-firewall](https://github.com/Shopify/activerecord-firewall), [milia](https://github.com/jekuno/milia).\n\nBut it does more than them, and highly focuses on ActiveRecord data leak protection.\n\n\n\n## What make it differnce on details\n\nIt protects data in every scenario in great detail. Currently, you can't find any multi-tenant gems doing a full data leak protect on ActiveRecord. But this gem does it.\n\n\nOur protection code mainly focus on 5 scenarios:\n\n- Action by tenant\n  - `CurrentTenantSupport.current_tenant` exists\n  - `CurrentTenantSupport.allow_read_across_tenant` is false (default)\n- Action by wrong tenant\n  - `CurrentTenantSupport.current_tenant` does not match `target_record.account`\n  - `CurrentTenantSupport.allow_read_across_tenant` is false (default)\n- Action when missing tenant\n  - `CurrentTenantSupport.current_tenant` is nil\n  - `CurrentTenantSupport.allow_read_across_tenant` is false (default)\n- Action by super admin but readonly\n  - `CurrentTenantSupport.current_tenant` is nil\n  - `CurrentTenantSupport.allow_read_across_tenant` is true\n- Action by super admin but want modify on a specific tenant\n  - `CurrentTenantSupport.current_tenant` is nil\n  - `CurrentTenantSupport.allow_read_across_tenant` is true\n  - Run code in the block of `CurrentTenantSupport.under_tenant`\n\n\nBelow are the behaviour of all ActiveRecord CRUD methods under abvove scenarios:\n\n### Protect on read\n\n\n| Read By  | tenant | missing tenant | super admin | super admin(modify on a specific tenant) |\n| -------- | ------ | -------------- | ----------- | ---------------------------------------- |\n| count    | 🍕      | 🚫              | 🌎           | 🍕                                        |\n| first    | 🍕      | 🚫              | 🌎           | 🍕                                        |\n| last     | 🍕      | 🚫              | 🌎           | 🍕                                        |\n| where    | 🍕      | 🚫              | 🌎           | 🍕                                        |\n| find_by  | 🍕      | 🚫              | 🌎           | 🍕                                        |\n| unscoped | 🍕      | 🚫              | 🌎           | 🍕                                        |\n\n🍕   scoped  \u0026#8203; \u0026#8203; \u0026#8203;  🌎   \u0026#8203;   unscoped    \u0026#8203; \u0026#8203; \u0026#8203;    ✅    \u0026#8203; allow     \u0026#8203; \u0026#8203; \u0026#8203;   🚫  \u0026#8203; disallow   \u0026#8203; \u0026#8203; \u0026#8203;    ⚠️ \u0026#8203;  Not protected\n\n\u003cbr\u003e\n\n### Protect on initialize\n\n| Initialize by | tenant | wrong tenant | missing tenant | super admin | super admin(modify on a specific tenant) |\n| ------------------ | ------ | ------------ | -------------- | ----------- | ---------------------------------------- |\n| new                | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| build              | ✅  \u0026#8203; 🍕   | -           | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| reload  | ✅ | 🚫            | 🚫              | ✅          | ✅ |\n\n🍕   scoped  \u0026#8203; \u0026#8203; \u0026#8203;  🌎   \u0026#8203;   unscoped    \u0026#8203; \u0026#8203; \u0026#8203;    ✅    \u0026#8203; allow     \u0026#8203; \u0026#8203; \u0026#8203;   🚫  \u0026#8203; disallow   \u0026#8203; \u0026#8203; \u0026#8203;    ⚠️ \u0026#8203;  Not protected\n\n\u003cbr\u003e\n\n\n### Protect on create\n\n| create by   | tenant | wrong tenant | missing tenant | super admin | super admin(modify on a specific tenant) |\n| ----------- | ------ | ------------ | -------------- | ----------- | ---------------------------------------- |\n| save        | ✅  \u0026#8203; 🍕   | 🚫            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| save!       | ✅  \u0026#8203; 🍕   | 🚫            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| create      | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| create!     | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| insert      | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| insert!     | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| insert_all  | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| insert_all! | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n\n🍕   scoped  \u0026#8203; \u0026#8203; \u0026#8203;  🌎   \u0026#8203;   unscoped    \u0026#8203; \u0026#8203; \u0026#8203;    ✅    \u0026#8203; allow     \u0026#8203; \u0026#8203; \u0026#8203;   🚫  \u0026#8203; disallow   \u0026#8203; \u0026#8203; \u0026#8203;    ⚠️ \u0026#8203;  Not protected\n\n\u003cbr\u003e\n\n\n### Protect on tenant assign\n\n| Manual assign or update tenant by | tenant | missing tenant | super admin | super admin(modify on a specific tenant) |\n| --------------------------------- | ------ | -------------- | ----------- | ---------------------------------------- |\n| account=                          | 🚫      | 🚫              | 🚫           | 🚫                                        |\n| account_id=                       | 🚫      | 🚫              | 🚫           | 🚫                                        |\n| update(account:)                  | 🚫      | 🚫              | 🚫           | 🚫                                        |\n| update(account_id:)               | 🚫      | 🚫              | 🚫           | 🚫                                        |\n\n🍕   scoped  \u0026#8203; \u0026#8203; \u0026#8203;  🌎   \u0026#8203;   unscoped    \u0026#8203; \u0026#8203; \u0026#8203;    ✅    \u0026#8203; allow     \u0026#8203; \u0026#8203; \u0026#8203;   🚫  \u0026#8203; disallow   \u0026#8203; \u0026#8203; \u0026#8203;    ⚠️ \u0026#8203;  Not protected\n\n\u003cbr\u003e\n\n\n### Protect on update\n\n| Update by        | tenant | wrong tenant | missing tenant | super admin | super admin(modify on a specific tenant) |\n| ---------------- | ------ | ------------ | -------------- | ----------- | ---------------------------------------- |\n| save        | ✅   | 🚫            | 🚫              | 🚫           | ✅                                      |\n| save!       | ✅   | 🚫            | 🚫              | 🚫           | ✅                                      |\n| update           | ✅      | 🚫            | 🚫              | 🚫           | ✅                                        |\n| update_all       | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| update_attribute | ✅      | 🚫            | 🚫              | 🚫           | ✅                                        |\n| update_columns   | ✅      | 🚫            | 🚫              | 🚫           | ✅                                        |\n| update_column    | ✅      | 🚫            | 🚫              | 🚫           | ✅                                        |\n| upsert_all       | ⚠️      | -            | 🚫              | ⚠️           | ⚠️                                        |\n| upsert           | ⚠️      | -            | 🚫              | ⚠️           | ⚠️                                        |\n\n🍕   scoped  \u0026#8203; \u0026#8203; \u0026#8203;  🌎   \u0026#8203;   unscoped    \u0026#8203; \u0026#8203; \u0026#8203;    ✅    \u0026#8203; allow     \u0026#8203; \u0026#8203; \u0026#8203;   🚫  \u0026#8203; disallow   \u0026#8203; \u0026#8203; \u0026#8203;    ⚠️ \u0026#8203;  Not protected\n\n\u003cbr\u003e\n\n\n### Protect on delete\n\n| Delete by   | tenant | wrong tenant | missing tenant | super admin | super admin(modify on a specific tenant) |\n| ----------- | ------ | ------------ | -------------- | ----------- | ---------------------------------------- |\n| destroy     | ✅      | 🚫            | 🚫              | 🚫           | ✅                                        |\n| destroy!    | ✅      | 🚫            | 🚫              | 🚫           | ✅                                        |\n| destroy_all | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| destroy_by  | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| delete_all  | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n| delete_by   | ✅  \u0026#8203; 🍕   | -            | 🚫              | 🚫           | ✅  \u0026#8203; 🍕                                     |\n\n🍕   scoped  \u0026#8203; \u0026#8203; \u0026#8203;  🌎   \u0026#8203;   unscoped    \u0026#8203; \u0026#8203; \u0026#8203;    ✅    \u0026#8203; allow     \u0026#8203; \u0026#8203; \u0026#8203;   🚫  \u0026#8203; disallow   \u0026#8203; \u0026#8203; \u0026#8203;    ⚠️ \u0026#8203;  Not protected\n\n\u003cbr\u003e\n\n\n## Installation\n\n1. Add this line to your application's Gemfile:\n\n    ```ruby\n    gem 'multi-tenant-support'\n    ```\n\n2. And then execute:\n\n    ```\n    bundle install\n    ```\n\n3. Add domain and subdomain to your tenant account table (Skip if your rails app already did this)\n\n    ```\n    rails generate multi_tenant_support:migration YOUR_TENANT_ACCOUNT_TABLE_OR_MODEL_NAME\n\n    # Say your tenant account table is \"accounts\"\n    rails generate multi_tenant_support:migration accounts\n\n    # You can also run it with the tenant account model name\n    # rails generate multi_tenant_support:migration Account\n\n    rails db:migrate\n    ```\n\n4. Create an initializer\n\n    ```\n    rails generate multi_tenant_support:initializer\n    ```\n\n5. Set `tenant_account_class_name` to your tenant account model name in `multi_tenant_support.rb`\n\n    ```ruby\n    - config.tenant_account_class_name = 'REPLACE_ME'\n    + config.tenant_account_class_name = 'Account'\n    ```\n\n6. Set `host` to your app's domain in `multi_tenant_support.rb`\n\n    ```ruby\n    - config.host = 'REPLACE.ME'\n    + config.host = 'your-app-domain.com'\n    ```\n\n7. Setup for ActiveJob or Sidekiq\n\n    If you are using ActiveJob\n\n    ```ruby\n    - # require 'multi_tenant_support/active_job'\n    + require 'multi_tenant_support/active_job'\n    ```\n\n    If you are using sidekiq without ActiveJob\n\n    ```ruby\n    - # require 'multi_tenant_support/sidekiq'\n    + require 'multi_tenant_support/sidekiq'\n    ```\n\n8. Add `belongs_to_tenant` to all models which you want to scope under tenant\n\n    ```ruby\n    class User \u003c ApplicationRecord\n      belongs_to_tenant :account\n    end\n    ```\n\n## Usage\n\n### Get current\n\nGet current tenant through:\n\n```ruby\nMultiTenantSupport.current_tenant\n```\n\n### Switch tenant\n\nYou can switch to another tenant temporary through:\n\n```ruby\nMultiTenantSupport.under_tenant amazon do\n  # Do things under amazon account\nend\n```\n\n### Set current tenant global\n\n```ruby\nMultiTenantSupport.set_tenant_account(account)\n```\n\n### Temp set current tenant to nil\n\n```ruby\nMultiTenantSupport.without_current_tenant do\n  # ...\nend\n```\n\n### 3 protection states\n\n1. `MultiTenantSupport.full_protected?`\n2. `MultiTenantSupport.allow_read_across_tenant?`\n3. `MultiTenantSupport.unprotected?`\n\n#### Full protection(default)\n\nThe default state is full protection. This gem disallow modify record across tenant by default.\n\nIf `MultiTenantSupport.current_tenant` exist, you can only modify those records under this tenant, otherwise, you will get some errors like:\n\n- `MultiTenantSupport::MissingTenantError`\n- `MultiTenantSupport::ImmutableTenantError`\n- `MultiTenantSupport::NilTenantError`\n- `MultiTenantSupport::InvalidTenantAccess`\n- `ActiveRecord::RecordNotFound`\n\nIf `MultiTenantSupport.current_tenant` is missing, you cannot modify or create any tenanted records.\n\nIf you switched to other state, you can switch back through:\n\n```ruby\nMultiTenantSupport.turn_on_full_protection\n\n# Or\nMultiTenantSupport.turn_on_full_protection do\n  # ...\nend\n```\n\n#### Allow read across tenant for super admin\n\nYou can turn on the permission to read records across tenant through:\n\n```ruby\nMultiTenantSupport.allow_read_across_tenant\n\n# Or\nMultiTenantSupport.allow_read_across_tenant do\n  # ...\nend\n```\n\nYou can put it in a before action in SuperAdmin's controllers\n\n#### Turn off protection\n\nSometimes, as a super admin, we need to execute certain maintenance operations over all tenant records. You can do this through:\n\n```ruby\nMultiTenantSupport.turn_off_protection\n\n# Or\nMultiTenantSupport.turn_off_protection do\n  # ...\nend\n```\n\n### Set current tenant acccount in controller by default\n\nThis gem has set a before action `set_current_tenant_account` on ActionController. It search tenant by subdomain or domain. Do remember to `skip_before_action :set_current_tenant_account` in super admin controllers.\n\nFeel free to override it, if the finder behaviour is not what you want.\n\n### Override current tenant finder method if domain/subdomain is not the way you want\n\nYou can override `find_current_tenant_account` in any controller with your own tenant finding strategy. Just make sure this method return the tenat account record or nil.\n\nFor example, say you only want to find tenant with domain not subdomain. It's very simple:\n\n```ruby\nclass ApplicationController \u003c ActionController::Base\n  private\n\n  def find_current_tenant_account\n    Account.find_by(domain: request.domain)\n  end\nend\n```\n\nThen your tenant finding strategy has changed from domain/subdomain to domain only.\n\n### upsert_all\n\nCurrently, we don't have a good way to protect this method. So please use `upser_all` carefully.\n\n### Unscoped\n\nThis gem has override `unscoped` to prevent the default tenant scope be scoped out. But if you really want to scope out the default tenant scope, you can use `unscope_tenant`.\n\n### Console\n\nConsole does not allow read across tenant by default. But you have several ways to change that:\n\n1. Set `allow_read_across_tenant_by_default` in the initialize file\n\n    ```ruby\n    console do |config|\n      config.allow_read_across_tenant_by_default = true\n    end\n    ```\n2. Set the environment variable `ALLOW_READ_ACROSS_TENANT` when call consoel command\n\n    ```bash\n    ALLOW_READ_ACROSS_TENANT=1 rails console\n    ```\n3. Manual change it in console\n\n    ```ruby\n    $ rails c\n    $ irb(main):001:0\u003e MultiTenantSupport.allow_read_across_tenant\n    ```\n\n## Testing\n### Minitest (Rails default)\n\n```ruby\n# test/test_helper.rb\nrequire 'multi_tenant_support/minitet'\n```\n### RSpec (with Capybara)\n\n```ruby\n# spec/rails_helper.rb or spec/spec_helper.rb\nrequire 'multi_tenant_support/rspec'\n```\n\nAbove code will make sure the `MultiTenantSupport.current_tenant` won't accidentally be reset during integration and system tests. For example:\n\nWith above testing requre code\n\n```ruby\n# Integration test\ntest \"a integration test\" do\n  host! \"apple.example.com\"\n\n  assert_no_changes \"MultiTenantSupport.current_tenant\" do\n    get users_path\n  end\nend\n\n# System test\ntest \"a system test\" do\n  Capybara.app_host = \"http://apple.example.com\"\n\n  assert_no_changes \"MultiTenantSupport.current_tenant\" do\n    visit users_path\n  end\nend\n```\n\n## Code Example\n\n### Database Schema\n\n```ruby\ncreate_table \"accounts\", force: :cascade do |t|\n  t.bigint \"domain\"\n  t.bigint \"subdomain\"\nend\n\ncreate_table \"users\", force: :cascade do |t|\n  t.bigint \"account_id\"\nend\n```\n\n#### Initializer\n\n```ruby\n# config/initializers/multi_tenant_support.rb\n\nMultiTenantSupport.configure do\n  model do |config|\n    config.tenant_account_class_name = 'Account'\n    config.tenant_account_primary_key = :id\n  end\n\n  controller do |config|\n    config.current_tenant_account_method = :current_tenant_account\n  end\n\n  app do |config|\n    config.excluded_subdomains = ['www']\n    config.host = 'example.com'\n  end\n\n  console do |config|\n    config.allow_read_across_tenant_by_default = false\n  end\nend\n```\n\n#### Model\n\n```ruby\nclass Account \u003c AppplicationRecord\n  has_many :users\nend\n\nclass User \u003c ApplicationRecord\n  belongs_to_tenant :account\nend\n```\n\n#### Controler\n\n```ruby\nclass UsersController \u003c ApplicationController\n  def show\n    @user = User.find(params[:id]) # This result is already scope under current_tenant_account\n    @you_can_get_account = current_tenant_account\n  end\nend\n```\n\n## ActiveRecord proteced methods\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth colspan=\"8\"\u003eActiveRecord proteced methods\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ecount\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003esave\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eaccount=\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eupsert\u003c/td\u003e\n      \u003ctd\u003e⚠️ (Partial)\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003efirst\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003esave!\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eaccount_id=\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003edestroy\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003elast\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003ecreate\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eupdate\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003edestroy!\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ewhere\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003ecreate!\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eupdate_all\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003edestroy_all\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003efind_by\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003einsert\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eupdate_attribute\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003edestroy_by\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ereload\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003einsert!\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eupdate_columns\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003edelete_all\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003enew\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003einsert_all\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eupdate_column\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003edelete_by\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd\u003ebuild\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003einsert_all!\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n      \u003ctd\u003eupsert_all\u003c/td\u003e\n      \u003ctd\u003e⚠️ (Partial)\u003c/td\u003e\n      \u003ctd\u003eunscoped\u003c/td\u003e\n      \u003ctd\u003e🔒\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/hoppergee/multi_tenant_support.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","funding_links":["https://patreon.com/hoppergee"],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoppergee%2Fmulti-tenant-support","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhoppergee%2Fmulti-tenant-support","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoppergee%2Fmulti-tenant-support/lists"}