activerecord-multi-tenant 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,3 @@
1
+ module MultiTenant
2
+ VERSION = '0.2.0'
3
+ 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: []