5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
# File 'lib/activerecord-multi-tenant/model_extensions.rb', line 5
def multi_tenant(tenant_name, options = {})
if to_s.underscore.to_sym == tenant_name || (!table_name.nil? && table_name.singularize.to_sym == tenant_name)
unless MultiTenant.with_write_only_mode_enabled?
before_create -> do
if self.class.columns_hash[self.class.primary_key].type == :uuid
self.id ||= SecureRandom.uuid
else
self.id ||= self.class.connection.select_value("SELECT nextval('#{self.class.table_name}_#{self.class.primary_key}_seq'::regclass)")
end
end
end
else
class << self
def scoped_by_tenant?
true
end
def partition_key
@partition_key ||= ancestors.detect{ |k| k.instance_variable_get(:@partition_key) }
.try(:instance_variable_get, :@partition_key)
end
def primary_key
return @primary_key if @primary_key
primary_object_keys = Array.wrap(connection.schema_cache.primary_keys(table_name)) - [partition_key]
if primary_object_keys.size == 1
@primary_key = primary_object_keys.first
elsif connection.schema_cache.columns_hash(table_name).include? DEFAULT_ID_FIELD
@primary_key = DEFAULT_ID_FIELD
else
@primary_key = nil
end
end
def inherited(subclass)
super
MultiTenant.register_multi_tenant_model(subclass)
end
end
MultiTenant.register_multi_tenant_model(self)
@partition_key = options[:partition_key] || MultiTenant.partition_key(tenant_name)
partition_key = @partition_key
if MultiTenant.tenant_klass_defined?(tenant_name)
belongs_to tenant_name, **options.slice(:class_name, :inverse_of, :optional).merge(foreign_key: options[:partition_key])
end
after_initialize Proc.new { |record|
if MultiTenant.current_tenant_id &&
(!record.attribute_present?(partition_key) || record.public_send(partition_key.to_sym).nil?)
record.public_send("#{partition_key}=".to_sym, MultiTenant.current_tenant_id)
end
}
to_include = Module.new do
define_method "#{partition_key}=" do |tenant_id|
write_attribute("#{partition_key}", tenant_id)
was = send("#{partition_key}_was")
was_nil_or_skipped = was.nil? || was.class == Object
raise MultiTenant::TenantIsImmutable if send("#{partition_key}_changed?") && persisted? && !was_nil_or_skipped
tenant_id
end
if MultiTenant.tenant_klass_defined?(tenant_name)
define_method "#{tenant_name}=" do |model|
super(model)
raise MultiTenant::TenantIsImmutable if send("#{partition_key}_changed?") && persisted? && !send("#{partition_key}_was").nil?
model
end
define_method "#{tenant_name}" do
if !association(tenant_name.to_sym).loaded? && !MultiTenant.current_tenant_is_id? && MultiTenant.current_tenant_id && public_send(partition_key) == MultiTenant.current_tenant_id
return MultiTenant.current_tenant
else
super()
end
end
end
end
include to_include
around_save -> (record, block) {
if persisted? && MultiTenant.current_tenant_id.nil?
MultiTenant.with(record.public_send(partition_key)) { block.call }
else
block.call
end
}
around_update -> (record, block) {
if MultiTenant.current_tenant_id.nil?
MultiTenant.with(record.public_send(partition_key)) { block.call }
else
block.call
end
}
around_destroy -> (record, block) {
if MultiTenant.current_tenant_id.nil?
MultiTenant.with(record.public_send(partition_key)) { block.call }
else
block.call
end
}
end
end
|