activerecord-multi-tenant 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/activerecord-multi-tenant.rb +6 -0
- data/lib/activerecord-multi-tenant/copy_from_client.rb +33 -0
- data/lib/activerecord-multi-tenant/default_scope.rb +12 -0
- data/lib/activerecord-multi-tenant/migrations.rb +36 -0
- data/lib/activerecord-multi-tenant/multi_tenant.rb +72 -0
- data/lib/activerecord-multi-tenant/referential_integrity.rb +18 -0
- data/lib/activerecord-multi-tenant/version.rb +3 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 47a1f8686348899edcbff39802df23196a67adae
|
4
|
+
data.tar.gz: aaf2bcf30117e8de2b7925400215e7f7990a997e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60b61705874dfd045cc5dfea6ce5b2ce87e3be49fdf90c46ee97534c85be757a0b481c96a50842bff09917fb2823e01c342c8b57b8255f37f1f5ec9faa791bbd
|
7
|
+
data.tar.gz: 6bb2620d436d1cdd15d3c9daf40bf54eb9115cb76bcf29181c0befa5b258774fa5fdf1d9765b2b09a3db9f2c24b5dcbfc6fdb934731f33f50568f9bfb11fbf45
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'activerecord-multi-tenant/copy_from_client'
|
2
|
+
require 'activerecord-multi-tenant/default_scope'
|
3
|
+
require 'activerecord-multi-tenant/migrations'
|
4
|
+
require 'activerecord-multi-tenant/multi_tenant'
|
5
|
+
require 'activerecord-multi-tenant/referential_integrity'
|
6
|
+
require 'activerecord-multi-tenant/version'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MultiTenant
|
2
|
+
class CopyFromClientHelper
|
3
|
+
attr_reader :count
|
4
|
+
|
5
|
+
def initialize(conn, column_types)
|
6
|
+
@count = 0
|
7
|
+
@conn = conn
|
8
|
+
@column_types = column_types
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(row)
|
12
|
+
row = row.map.with_index { |val, idx| @column_types[idx].type_cast_for_database(val) }
|
13
|
+
@conn.put_copy_data(row)
|
14
|
+
@count += 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module CopyFromClient
|
19
|
+
def self.copy_from_client(columns, &block)
|
20
|
+
conn = connection.raw_connection
|
21
|
+
column_types = columns.map { |c| columns_hash[c.to_s] }
|
22
|
+
helper = MultiTenant::CopyFromClientHelper.new(conn, column_types)
|
23
|
+
conn.copy_data %{COPY #{quoted_table_name}("#{columns.join('","')}") FROM STDIN}, PG::TextEncoder::CopyRow.new do
|
24
|
+
block.call helper
|
25
|
+
end
|
26
|
+
helper.count
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if defined?(ActiveRecord::Base)
|
32
|
+
ActiveRecord::Base.send(:include, MultiTenant::CopyFromClient)
|
33
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class ActiveRecord::Base
|
2
|
+
class << self
|
3
|
+
alias :unscoped_orig :unscoped
|
4
|
+
def unscoped
|
5
|
+
if respond_to?(:scoped_by_tenant?) && MultiTenant.current_tenant_id
|
6
|
+
unscoped_orig.where(arel_table[MultiTenant.partition_key].eq(MultiTenant.current_tenant_id))
|
7
|
+
else
|
8
|
+
unscoped_orig
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MultiTenant
|
2
|
+
module MigrationExtensions
|
3
|
+
def create_distributed_table(table_name, partition_key)
|
4
|
+
execute "SELECT create_distributed_table($$#{table_name}$$, $$#{partition_key}$$)"
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute_on_all_nodes(sql)
|
8
|
+
execute sql
|
9
|
+
execute "SELECT citus_run_on_all_workers($$#{sql}$$)"
|
10
|
+
end
|
11
|
+
|
12
|
+
def enable_extension_on_all_nodes(extension)
|
13
|
+
execute_on_all_nodes "CREATE EXTENSION IF NOT EXISTS \"#{extension}\""
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if defined?(ActiveRecord::Migration)
|
19
|
+
ActiveRecord::Migration.send(:include, MultiTenant::MigrationExtensions)
|
20
|
+
end
|
21
|
+
|
22
|
+
module ActiveRecord
|
23
|
+
module ConnectionAdapters # :nodoc:
|
24
|
+
module SchemaStatements
|
25
|
+
alias :orig_create_table :create_table
|
26
|
+
def create_table(table_name, options = {}, &block)
|
27
|
+
ret = orig_create_table(table_name, options.except(:partition_key), &block)
|
28
|
+
if options[:partition_key] && options[:partition_key].to_s != 'id'
|
29
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{table_name}_pkey"
|
30
|
+
execute "ALTER TABLE #{table_name} ADD PRIMARY KEY(id, #{options[:partition_key]})"
|
31
|
+
end
|
32
|
+
ret
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Note that the actual details here are subject to change, and you should avoid
|
2
|
+
# calling acts_as_tenant methods directly as a user of this library
|
3
|
+
require 'acts_as_tenant'
|
4
|
+
|
5
|
+
module MultiTenant
|
6
|
+
def self.current_tenant
|
7
|
+
ActsAsTenant.current_tenant
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.current_tenant=(tenant)
|
11
|
+
ActsAsTenant.current_tenant = tenant
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.current_tenant_id
|
15
|
+
ActsAsTenant.current_tenant.try(:id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.with(tenant, &block)
|
19
|
+
ActsAsTenant.with_tenant(tenant, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.partition_key
|
23
|
+
ActsAsTenant.fkey
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.with_id(tenant_id, &block)
|
27
|
+
MultiTenant.with(TenantIdWrapper.new(id: tenant_id), &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
class TenantIdWrapper
|
31
|
+
attr_reader :id
|
32
|
+
|
33
|
+
def initialize(id:)
|
34
|
+
@id = id
|
35
|
+
end
|
36
|
+
|
37
|
+
def new_record?; true; end
|
38
|
+
def touch; nil; end
|
39
|
+
end
|
40
|
+
|
41
|
+
module ModelExtensions
|
42
|
+
def self.included(base)
|
43
|
+
base.extend(ClassMethods)
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
def multi_tenant(tenant, options = {})
|
48
|
+
# Provide fallback primary key setting to ease integration with the typical Rails app
|
49
|
+
self.primary_key = 'id' if primary_key.nil?
|
50
|
+
|
51
|
+
# Typically we don't need to run on the tenant model itself
|
52
|
+
if to_s.underscore.to_sym != tenant
|
53
|
+
belongs_to(tenant)
|
54
|
+
acts_as_tenant(tenant, options)
|
55
|
+
|
56
|
+
around_save -> (record, block) { persisted? ? MultiTenant.with_id(record.public_send(tenant.to_s + '_id')) { block.call } : block.call }
|
57
|
+
around_update -> (record, block) { MultiTenant.with_id(record.public_send(tenant.to_s + '_id')) { block.call } }
|
58
|
+
around_destroy -> (record, block) { MultiTenant.with_id(record.public_send(tenant.to_s + '_id')) { block.call } }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Workaround for https://github.com/citusdata/citus/issues/687
|
62
|
+
if to_s.underscore.to_sym == tenant
|
63
|
+
before_create -> { self.id ||= self.class.connection.select_value("SELECT nextval('" + [self.class.table_name, self.class.primary_key, 'seq'].join('_') + "'::regclass)") }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if defined?(ActiveRecord::Base)
|
71
|
+
ActiveRecord::Base.send(:include, MultiTenant::ModelExtensions)
|
72
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Disable Rails trigger enable/disable mechanism used for test cases, since
|
2
|
+
# DISABLE TRIGGER is not supported on distributed tables.
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module ConnectionAdapters
|
6
|
+
module PostgreSQL
|
7
|
+
module ReferentialIntegrity
|
8
|
+
def supports_disable_referential_integrity?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
def disable_referential_integrity
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-multi-tenant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Citus Data
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: acts_as_tenant
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: database_cleaner
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.3.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pg
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: ''
|
84
|
+
email: engage@citusdata.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- lib/activerecord-multi-tenant.rb
|
90
|
+
- lib/activerecord-multi-tenant/copy_from_client.rb
|
91
|
+
- lib/activerecord-multi-tenant/default_scope.rb
|
92
|
+
- lib/activerecord-multi-tenant/migrations.rb
|
93
|
+
- lib/activerecord-multi-tenant/multi_tenant.rb
|
94
|
+
- lib/activerecord-multi-tenant/referential_integrity.rb
|
95
|
+
- lib/activerecord-multi-tenant/version.rb
|
96
|
+
homepage: https://github.com/citusdata/activerecord-multi-tenant
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.5.1
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: ActiveRecord/Rails integration for multi-tenant databases, in particular
|
120
|
+
the Citus extension for PostgreSQL
|
121
|
+
test_files: []
|