{"id":13879362,"url":"https://github.com/PerfectMemory/mongoid-multitenancy","last_synced_at":"2025-07-16T15:32:12.462Z","repository":{"id":5944735,"uuid":"7165418","full_name":"PerfectMemory/mongoid-multitenancy","owner":"PerfectMemory","description":"Add Multitenancy to your models for Mongoid 6+ (6, 7, 8, 9)","archived":false,"fork":false,"pushed_at":"2024-09-20T13:56:33.000Z","size":131,"stargazers_count":53,"open_issues_count":0,"forks_count":30,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-11-22T12:02:32.561Z","etag":null,"topics":[],"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/PerfectMemory.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.TXT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-12-14T12:53:10.000Z","updated_at":"2024-09-20T13:53:19.000Z","dependencies_parsed_at":"2024-11-08T10:40:41.302Z","dependency_job_id":"581adad5-cb89-45c1-b84d-e01125de81b0","html_url":"https://github.com/PerfectMemory/mongoid-multitenancy","commit_stats":{"total_commits":93,"total_committers":5,"mean_commits":18.6,"dds":"0.053763440860215006","last_synced_commit":"cc88c52a547063db777fd2da8bbb8ee907b5b87e"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectMemory%2Fmongoid-multitenancy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectMemory%2Fmongoid-multitenancy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectMemory%2Fmongoid-multitenancy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerfectMemory%2Fmongoid-multitenancy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PerfectMemory","download_url":"https://codeload.github.com/PerfectMemory/mongoid-multitenancy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226143895,"owners_count":17580245,"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":[],"created_at":"2024-08-06T08:02:18.419Z","updated_at":"2024-11-24T08:31:18.201Z","avatar_url":"https://github.com/PerfectMemory.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# mongoid-multitenancy [![Build Status](https://api.travis-ci.org/PerfectMemory/mongoid-multitenancy.png?branch=master)](https://travis-ci.org/PerfectMemory/mongoid-multitenancy) [![Coverage Status](https://coveralls.io/repos/github/PerfectMemory/mongoid-multitenancy/badge.svg?branch=master)](https://coveralls.io/github/PerfectMemory/mongoid-multitenancy?branch=master) [![Code Climate](https://codeclimate.com/github/PerfectMemory/mongoid-multitenancy.png)](https://codeclimate.com/github/PerfectMemory/mongoid-multitenancy)\n\nmongoid-multitenancy adds the ability to scope [Mongoid](https://github.com/mongoid/mongoid) models to a tenant in a **shared database strategy**. Tenants are represented by a tenant model, such as `Client`. mongoid-multitenancy will help you set the current tenant on each request and ensures that all 'tenant models' are always properly scoped to the current tenant: when viewing, searching and creating.\n\nIt is directly inspired by the [acts_as_tenant gem](https://github.com/ErwinM/acts_as_tenant) for Active Record.\n\nIn addition, mongoid-multitenancy:\n\n* allows you to set the current tenant\n* allows shared items between the tenants\n* allows you to define an immutable tenant field once it is persisted\n* is thread safe\n* redefines some mongoid functions like `index`, `validates_with` and `delete_all` to take in account the multitenancy.\n\nCompatibility\n===============\n\nmongoid-multitenancy 2 is compatible with mongoid 6/7/8/9. For mongoid 4/5 compatiblity, use mongoid-multitenancy 1.2.\n\nInstallation\n===============\n\nAdd this line to your application's Gemfile:\n\n    gem 'mongoid-multitenancy', '~\u003e 2.0'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install mongoid-multitenancy\n\nUsage\n===============\n\nThere are two steps to add multi-tenancy to your app with mongoid-multitenancy:\n\n1. setting the current tenant and\n2. scoping your models.\n\nSetting the current tenant\n--------------------------\nThere are two ways to set the current tenant: (1) by setting the current tenant manually, or (2) by setting the current tenant for a block.\n\n**Setting the current tenant in a controller, manually**\n\n```ruby\nMongoid::Multitenancy.current_tenant = client_instance\n```\n\nSetting the current_tenant yourself requires you to use a before_filter to set the Mongoid::Multitenancy.current_tenant variable.\n\n**Setting the current tenant for a block**\n\n```ruby\nMongoid::Multitenancy.with_tenant(client_instance) do\n  # Current tenant is set for all code in this block\nend\n```\n\nThis approach is useful when running background processes for a specified tenant. For example, by putting this in your worker's run method,\nany code in this block will be scoped to the current tenant. All methods that set the current tenant are thread safe.\n\n**Note:** If the current tenant is not set by one of these methods, mongoid-multitenancy will apply a global scope to your models, not related to any tenant. So make sure you use one of the two methods to tell mongoid-multitenancy about the current tenant.\n\nScoping your models\n-------------------\n```ruby\nclass Client\n  include Mongoid::Document\n\n  field :name, :type =\u003e String\n  validates_uniqueness_of :name\nend\n\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant(:tenant)\n\n  field :title, :type =\u003e String\nend\n```\n\nAdding `tenant` to your model declaration will scope that model to the current tenant **BUT ONLY if a current tenant has been set**.\nThe association passed to the `tenant` function must be valid.\n\n`tenant` accepts several options:\n\n| Option  | Default | Description |\n| ------------- | ------------- | ------------- |\n| :optional  | false  | set to true when the tenant is optional |\n| :immutable  | true  | set to true when the tenant field is immutable |\n| :full_indexes  | true  | set to true to add the tenant field automatically to all the indexes |\n| :index  | false  | set to true to define an index for the tenant field |\n| :scopes  | true  | set to true to define scopes :shared and :unshared |\n| :class_name, etc. |   | all the other options will be passed to the mongoid relation (belongs_to) |\n\nSome examples to illustrate this behavior:\n\n```ruby\n # This manually sets the current tenant for testing purposes. In your app this is handled by the gem.\nMongoid::Multitenancy.current_tenant = Client.find_by(:name =\u003e 'Perfect Memory') # =\u003e \u003c#Client _id:50ca04b86c82bfc125000025, :name: \"Perfect Memory\"\u003e\n\n # All searches are scoped by the tenant, the following searches will only return objects belonging to the current client.\nArticle.all # =\u003e all articles where tenant_id =\u003e 50ca04b86c82bfc125000025\n\n # New objects are scoped to the current tenant\narticle = Article.new(:title =\u003e 'New blog')\narticle.save # =\u003e \u003c#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: 50ca04b86c82bfc125000025\u003e\n\n # It can make the tenant field immutable once it is persisted to avoid inconsistency\narticle.persisted? # =\u003e true\narticle.tenant = another_client\narticle.valid? # =\u003e false\n```\n\n**Optional tenant**\n\nWhen setting an optional tenant, for example to allow shared instances between all the tenants, the default scope will return both the tenant and the free-tenant items. That means that using `Article.delete_all` or `Article.destroy_all` will **remove the shared items too**.\n\nNote: if a current tenant is set and you want to mark the item shared, you must explicitly set the tenant relation to nil after the initialization.\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant(:tenant, optional: true)\n\n  field :title, :type =\u003e String\nend\n\nMongoid::Multitenancy.with_tenant(client_instance) do\n  Article.all # =\u003e all articles where tenant_id.in [50ca04b86c82bfc125000025, nil]\n  article = Article.new(:title =\u003e 'New article')\n  article.save # =\u003e \u003c#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: 50ca04b86c82bfc125000025\u003e\n\n  # tenant needs to be set manually to nil\n  article = Article.new(:title =\u003e 'New article', :tenant =\u003e nil)\n  article.save # =\u003e \u003c#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: 50ca04b86c82bfc125000025\u003e\n  article.tenant = nil\n  article.save =\u003e \u003c#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: nil\u003e\nend\n```\n\nRails\n-------------------\n\nIf you are using Rails, you may want to set the current tenant at each request.\n\n**Manually set the current tenant in ApplicationController using the host request**\n\n```ruby\nclass ApplicationController \u003c ActionController::Base\n  before_filter :set_current_client\n\n  def set_current_client\n    current_client = Client.find_by_host(request.host)\n    Mongoid::Multitenancy.current_tenant = current_client\n  end\nend\n```\n\nSetting the current_tenant yourself requires you to use a before_filter to set the Mongoid::Multitenancy.current_tenant variable.\n\nMongoid Uniqueness validators\n-------------------\n\nmongoid-multitenancy brings a TenantUniqueness validator that will, depending on the tenant options, check that your uniqueness\nconstraints are respected:\n\n* When used with a *mandatory* tenant, the uniqueness constraint is scoped to the current client.\n\nIn the following case, 2 articles can have the same slug if they belongs to 2 different clients.\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant :tenant\n\n  field :slug\n\n  validates_tenant_uniqueness_of :slug\nend\n```\n\n* When used with an *optional* tenant, the uniqueness constraint by default is not scoped if the item is shared, but is\n  scoped to the client new item otherwise. Note that by default in that case a private item cannot have a value if a shared item\n  already uses it. You can change that behaviour by setting the option `exclude_shared` to `true`.\n\nIn the following case, 2 private articles can have the same slug if they belongs to 2 different clients. But if a shared\narticle has the slug \"slugA\", no client will be able to use that slug again, like a standard `validates_uniqueness_of` does.\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant :tenant, optional: true\n\n  field :slug\n\n  validates_tenant_uniqueness_of :slug\nend\n```\n\nIn the following case, 2 private articles can have the same slug if they belongs to 2 different clients even if a shared\narticle already uses that same slug, like a `validates_uniqueness_of scope: :tenant` does.\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant :tenant, optional: true\n\n  field :slug\n\n  validates_tenant_uniqueness_of :slug, exclude_shared: true\nend\n```\n\nTenantUniqueness validator also allow to specify additional `conditions` to limit the uniqueness of the constraint.\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant :tenant, optional: true\n\n  field :title\n  field :slug\n\n  validates_tenant_uniqueness_of :slug, exclude_shared: true, conditions: -\u003e { ne(title: nil) }\nend\n```\n\nMongoid indexes\n-------------------\n\nmongoid-multitenancy automatically adds the tenant foreign key in all your mongoid indexes to avoid to redefine all your validators. If you prefer to define the indexes manually, you can use the option `full_indexes: false` on the tenant or `full_index: true/false` on the indexes.\n\nTo create a single index on the tenant field, you can use the option `index: true` like any `belongs_to` declaration (false by default)\n\nOn the example below, only one index will be created:\n\n* { 'tenant_id' =\u003e 1, 'title' =\u003e 1 }\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant :tenant, full_indexes: true\n\n  field :title\n\n  index({ :title =\u003e 1 })\nend\n```\n\nOn the example below, 3 indexes will be created:\n\n* { 'tenant_id' =\u003e 1 }\n* { 'tenant_id' =\u003e 1, 'title' =\u003e 1 }\n* { 'name' =\u003e 1 }\n\n```ruby\nclass Article\n  include Mongoid::Document\n  include Mongoid::Multitenancy::Document\n\n  tenant :tenant, index: true\n\n  field :title\n  field :name\n\n  index({ :title =\u003e 1 })\n  index({ :name =\u003e 1 }, { full_index: false })\nend\n```\n\nAuthor \u0026 Credits\n----------------\nmongoid-multitenancy is written by [Aymeric Brisse](https://github.com/abrisse/), from [Perfect Memory](http://www.perfect-memory.com).\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Added some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPerfectMemory%2Fmongoid-multitenancy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FPerfectMemory%2Fmongoid-multitenancy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPerfectMemory%2Fmongoid-multitenancy/lists"}