module Mongoid::Multitenancy::Document::ClassMethods

Constants

MULTITENANCY_OPTIONS

List of authorized options

Attributes

tenant_field[RW]
tenant_options[RW]

Public Instance Methods

delete_all(conditions = nil) click to toggle source

Redefine 'delete_all' to take in account the default scope

# File lib/mongoid/multitenancy/document.rb, line 98
def delete_all(conditions = nil)
  scope = scoped
  scope = scopewhere(conditions) if conditions
  scope.delete
end
index(spec, options = nil) click to toggle source

Redefine 'index' to include the tenant field in first position

Calls superclass method
# File lib/mongoid/multitenancy/document.rb, line 89
def index(spec, options = nil)
  if tenant_options[:full_indexes]
    spec = { tenant_field => 1 }.merge(spec)
  end

  super(spec, options)
end
tenant(association = :account, options = {}) click to toggle source

Defines the tenant field for the document.

@example Define a tenant.

tenant :client, optional: false, immutable: true, full_indexes: true

@param [ Symbol ] name The name of the relation. @param [ Hash ] options The relation options.

All the belongs_to options are allowed plus the following ones:

@option options [ Boolean ] :full_indexes If true the tenant field

will be added for each index.

@option options [ Boolean ] :immutable If true changing the tenant

wil raise an Exception.

@option options [ Boolean ] :optional If true allow the document

to be shared among all the tenants.

@option options [ Boolean ] :index If true build an index for

the tenant field itself.

@option options [ Boolean ] :scopes If true create scopes :shared

and :unshared.

@return [ Field ] The generated field

# File lib/mongoid/multitenancy/document.rb, line 32
def tenant(association = :account, options = {})
  options = { full_indexes: true, immutable: true, scopes: true }.merge!(options)
  assoc_options, multitenant_options = build_options(options)

  # Setup the association between the class and the tenant class
  belongs_to association, assoc_options

  # Get the tenant model and its foreign key
  self.tenant_field = reflect_on_association(association).foreign_key.to_sym
  self.tenant_options = multitenant_options

  # Validates the tenant field
  validates_tenancy_of tenant_field, multitenant_options

  define_scopes if multitenant_options[:scopes]
  define_initializer association
  define_inherited association, options
  define_index if multitenant_options[:index]
end
validates_tenancy_of(*args) click to toggle source

Validates whether or not a tenant field is correct.

@example Define the tenant validator

class Person
  include Mongoid::Document
  include Mongoid::Multitenancy::Document
  field :title
  tenant :client

  validates_tenant_of :client
end

@param [ Array ] *args The arguments to pass to the validator.

# File lib/mongoid/multitenancy/document.rb, line 84
def validates_tenancy_of(*args)
  validates_with(TenancyValidator, _merge_attributes(args))
end
validates_tenant_uniqueness_of(*args) click to toggle source

Validates whether or not a field is unique against the documents in the database.

@example

class Person
  include Mongoid::Document
  include Mongoid::Multitenancy::Document
  field :title

  validates_tenant_uniqueness_of :title
end

@param [ Array ] *args The arguments to pass to the validator.

# File lib/mongoid/multitenancy/document.rb, line 66
def validates_tenant_uniqueness_of(*args)
  validates_with(TenantUniquenessValidator, _merge_attributes(args))
end

Private Instance Methods

build_options(options) click to toggle source

@private

# File lib/mongoid/multitenancy/document.rb, line 107
def build_options(options)
  assoc_options = {}
  multitenant_options = {}

  options.each do |k, v|
    if MULTITENANCY_OPTIONS.include?(k)
      multitenant_options[k] = v
      assoc_options[k] = v if k == :optional
    else
      assoc_options[k] = v
    end
  end

  [assoc_options, multitenant_options]
end
define_index() click to toggle source

@private

Create the index

# File lib/mongoid/multitenancy/document.rb, line 170
def define_index
  index({ tenant_field => 1 }, background: true)
end
define_inherited(association, options) click to toggle source

@private

Define the inherited method

Calls superclass method
# File lib/mongoid/multitenancy/document.rb, line 138
def define_inherited(association, options)
  define_singleton_method(:inherited) do |child|
    child.tenant association, options.merge(scopes: false)
    super(child)
  end
end
define_initializer(association) click to toggle source

@private

Define the after_initialize

# File lib/mongoid/multitenancy/document.rb, line 126
def define_initializer(association)
  # Apply the default value when the default scope is complex (optional tenant)
  after_initialize lambda {
    if Multitenancy.current_tenant && new_record?
      send "#{association}=".to_sym, Multitenancy.current_tenant
    end
  }
end
define_scopes() click to toggle source

@private

Define the scopes

# File lib/mongoid/multitenancy/document.rb, line 148
def define_scopes
  # Set the default_scope to scope to current tenant
  default_scope lambda {
    if Multitenancy.current_tenant
      tenant_id = Multitenancy.current_tenant.id
      if tenant_options[:optional]
        where(tenant_field.in => [tenant_id, nil])
      else
        where(tenant_field => tenant_id)
      end
    else
      all
    end
  }

  scope :shared, -> { where(tenant_field => nil) }
  scope :unshared, -> { where(tenant_field => Multitenancy.current_tenant.id) }
end