Module: ActiveRecord::Tenanted::TenantCommon

Extended by:
ActiveSupport::Concern
Defined in:
lib/active_record/tenanted/tenant.rb

Overview

instance methods common to both Tenant and Subtenant

Instance Method Summary collapse

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



21
22
23
# File 'lib/active_record/tenanted/tenant.rb', line 21

def ==(other)
  super && tenant == other.tenant
end

#attributes_for_databaseObject

The tenant is an ordinary attribute in serialized snapshots of the record, the same way Job and GlobalId serialize the tenant. Rails's only callers of this method are the serializers (Marshal 7.1 format and MessagePack); it does not feed the persistence write path.



115
116
117
# File 'lib/active_record/tenanted/tenant.rb', line 115

def attributes_for_database
  super.merge!("tenant" => tenant)
end

#cache_keyObject



17
18
19
# File 'lib/active_record/tenanted/tenant.rb', line 17

def cache_key
  tenant ? "#{tenant}/#{super}" : super
end

#deleteObject



56
57
58
59
60
# File 'lib/active_record/tenanted/tenant.rb', line 56

def delete
  ensure_tenant_context_safety

  super
end

#encode_with(coder) ⇒ Object



119
120
121
122
# File 'lib/active_record/tenanted/tenant.rb', line 119

def encode_with(coder)
  super
  coder["tenant"] = tenant
end

#ensure_belongs_to_tenant_safetyObject

A belongs_to writes its foreign key onto this record, so nothing else notices when the target came from another tenant's database.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/active_record/tenanted/tenant.rb', line 84

def ensure_belongs_to_tenant_safety
  self.class.reflect_on_all_associations(:belongs_to).each do |reflection|
    # Avoid creating the Association object for a belongs_to that was never touched
    next unless association_cached?(reflection.name)

    target = association(reflection.name).target

    if target && target.class.tenanted? && target.tenant != tenant
      raise WrongTenantError,
            "#{self.class} model belongs to tenant #{tenant.inspect}, but its " \
            "#{reflection.name} association belongs to tenant #{target.tenant.inspect}"
    end
  end
end

#ensure_tenant_context_safetyObject



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/active_record/tenanted/tenant.rb', line 99

def ensure_tenant_context_safety
  self_tenant = self.tenant
  current_tenant = self.class.current_tenant

  if current_tenant.nil?
    raise NoTenantError, "Cannot connect to a tenanted database while untenanted (#{self.class})"
  elsif self_tenant != current_tenant
    raise WrongTenantError,
          "#{self.class} model belongs to tenant #{self_tenant.inspect}, " \
          "but current tenant is #{current_tenant.inspect}"
  end
end

#from_json(json, include_root = include_root_in_json) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/active_record/tenanted/tenant.rb', line 133

def from_json(json, include_root = include_root_in_json)
  hash = ActiveSupport::JSON.decode(json)
  hash = hash.values.first if include_root

  @tenant = hash.delete("tenant") if hash.key?("tenant")

  self.attributes = hash
  self
end

#hashObject



26
27
28
# File 'lib/active_record/tenanted/tenant.rb', line 26

def hash
  [ super, tenant ].hash
end

#increment!Object

#decrement! delegates here



76
77
78
79
80
# File 'lib/active_record/tenanted/tenant.rb', line 76

def increment!(...)
  ensure_tenant_context_safety

  super
end

#init_with(coder, &block) ⇒ Object



124
125
126
127
128
129
130
131
# File 'lib/active_record/tenanted/tenant.rb', line 124

def init_with(coder, &block)
  super

  # Psych::Coder does not implement #key?, but exposes the underlying hash as #map
  @tenant = coder.map["tenant"] if coder.map.key?("tenant")

  self
end

#inspectObject



30
31
32
33
34
# File 'lib/active_record/tenanted/tenant.rb', line 30

def inspect
  return super unless tenant

  super.sub(/\A#<\S+ /, "\\0tenant: #{tenant.inspect}, ")
end

#marshal_load(state) ⇒ Object



143
144
145
146
147
148
149
150
# File 'lib/active_record/tenanted/tenant.rb', line 143

def marshal_load(state)
  has_tenant = state[0].key?("tenant")
  tenant_name = state[0].delete("tenant")

  super

  @tenant = tenant_name if has_tenant
end

#reloadObject



44
45
46
47
48
# File 'lib/active_record/tenanted/tenant.rb', line 44

def reload(...)
  ensure_tenant_context_safety

  super
end

#to_global_id(options = {}) ⇒ Object Also known as: to_gid



36
37
38
# File 'lib/active_record/tenanted/tenant.rb', line 36

def to_global_id(options = {})
  super(options.merge(tenant: tenant))
end

#to_signed_global_id(options = {}) ⇒ Object Also known as: to_sgid



40
41
42
# File 'lib/active_record/tenanted/tenant.rb', line 40

def to_signed_global_id(options = {})
  super(options.merge(tenant: tenant))
end

#touchObject



69
70
71
72
73
# File 'lib/active_record/tenanted/tenant.rb', line 69

def touch(...)
  ensure_tenant_context_safety

  super
end

#update_columnsObject

#update_column delegates here



63
64
65
66
67
# File 'lib/active_record/tenanted/tenant.rb', line 63

def update_columns(...)
  ensure_tenant_context_safety

  super
end

#valid?Boolean

Returns:

  • (Boolean)


50
51
52
53
54
# File 'lib/active_record/tenanted/tenant.rb', line 50

def valid?(...)
  ensure_tenant_context_safety

  super
end