tenant_realm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ec5080c5a6b4e997fd094811df564e8cb6c7520030a1fb0617917a155dd05223
4
+ data.tar.gz: 159100e048f540997d9e9ffa33ba215c035357573bb297a65afa52bd6547d593
5
+ SHA512:
6
+ metadata.gz: 789611a5e9bc41b794a65e39d4d4cb48a69ad6d37a6d1bde72cb10e6f2bf499278f6715c5f50a7ba26023360408919bf921e33e72b9b994263747582eb0e8dc8
7
+ data.tar.gz: a7dc2cddb27f123a9772bd53d4e4b1f3d39f9f5cad245f83de9329aa9c3bd6acebde917cb42ce84927b12b896396f8837fe715f5f22ede2cbcec365cb2d72bfc
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.2
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ Tenant Realm is a lightweight gem provides some helpers to support working on multi-tenant easily using [Multiple Database with Active Record](https://guides.rubyonrails.org/active_record_multiple_databases.html).
2
+
3
+ # Installation
4
+
5
+ ```sh
6
+ gem 'tenant_realm'
7
+ ```
8
+
9
+ # CLI
10
+
11
+ - init `tenant_realm`
12
+
13
+ ```sh
14
+ rails g tenant_realm
15
+ ```
16
+
17
+ - run migration for all tenants
18
+
19
+ ```sh
20
+ rake tenant_realm:migrate
21
+ ```
22
+
23
+ # Configuration
24
+
25
+ ```rb
26
+ # frozen_string_literal: true
27
+
28
+ Rails.application.config.to_prepare do
29
+ TenantRealm.configure do |config|
30
+ # required
31
+ # identifier is extracted from `identifier_resolver`
32
+ config.fetch_tenant = lambda { |identifier|
33
+ # get tenant using identifier
34
+ }
35
+
36
+ # required
37
+ config.fetch_tenants = lambda {
38
+ # get tenant list
39
+ }
40
+
41
+ # optional
42
+ # default is getting from key :db_config from tenant
43
+ config.dig_db_config = lambda { |tenant|
44
+ tenant[:db_config]
45
+ }
46
+
47
+ # optional
48
+ # set this when you wanna skip the switch database
49
+ config.skip_resolver = lambda { |request|
50
+ request.env['REQUEST_PATH'].match?(/health|favicon.ico/)
51
+ }
52
+
53
+ # required
54
+ # method to extract the identifier of tenant
55
+ config.identifier_resolver = lambda { |request|
56
+ # get identifier from request
57
+ # tenant is retrieved using its domain
58
+ domain = request.referer || request.origin
59
+ domain ? URI(domain).host : nil
60
+ }
61
+
62
+ # optional
63
+ # extract the sharding name for multi_db
64
+ # default will be this proc return value -> tenant's shard_name -> slug -> id
65
+ config.shard_name_from_tenant = lambda { |tenant|
66
+ tenant[:slug]
67
+ }
68
+
69
+ # optional
70
+ # default will be TenantRealm::CurrentTenant
71
+ config.current_tenant = MyCurrentTenant
72
+
73
+ # optional
74
+ config.cache do |cache_config|
75
+ # using cache service
76
+ cache_config.service = :redis
77
+
78
+ # when will the tenant list and tenant are expired
79
+ cache_config.expires_in = 6.days
80
+
81
+ # list keys to cache the tenant
82
+ cache_config.tenant_uniq_cols = %i[slug domain]
83
+
84
+ # in case the column is not simple like above
85
+ # this option will override the one above
86
+ cache_config.tenant_keys_resolver = lambda { |tenant|
87
+ [
88
+ tenant[:slug],
89
+ tenant[:settings][:domain]
90
+ ]
91
+ }
92
+ end
93
+ end
94
+ end
95
+ ```
96
+
97
+ # Customize
98
+
99
+ - Custom current tenant
100
+
101
+ ```rb
102
+ # frozen_string_literal: true
103
+
104
+ class CurrentTenant < TenantRealm::CurrentTenant
105
+ attribute :additional_info
106
+
107
+ def tenant=(tenant)
108
+ super
109
+
110
+ self.additional_info = {
111
+ domain: tenant[:settings][:domain]
112
+ }
113
+ end
114
+ end
115
+ ```
116
+
117
+ # TODO
118
+
119
+ - [ ] support Row-Level Security (RLS)
120
+
121
+ - [ ] support multi-schema
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rubocop/rake_task'
5
+
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: :rubocop
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.config.to_prepare do
4
+ TenantRealm.configure do |config|
5
+ config.fetch_tenant = lambda { |identifier|
6
+ # get tenant using identifier
7
+ }
8
+
9
+ config.fetch_tenants = lambda {
10
+ # get tenant list
11
+ }
12
+
13
+ config.dig_db_config = lambda { |tenant|
14
+ tenant[:db_config]
15
+ }
16
+
17
+ config.skip_resolver = lambda { |request|
18
+ request.env['REQUEST_PATH'].match?(/health|favicon.ico/)
19
+ }
20
+
21
+ config.identifier_resolver = lambda { |request|
22
+ # get identifier from request
23
+ # domain = request.referer || request.origin
24
+ # domain ? URI(domain).host : nil
25
+ }
26
+
27
+ config.shard_name_from_tenant = lambda { |tenant|
28
+ tenant[:slug]
29
+ }
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TenantRealmGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ def generate_tenant_realm
7
+ template(
8
+ 'tenant_realm.tt',
9
+ 'config/initializers/tenant_realm.rb'
10
+ )
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :tenant_realm do
4
+ desc 'Migrate db for all tenants'
5
+ task migrate: :environment do
6
+ tenants = TenantRealm::Tenant.tenants
7
+
8
+ tenants.each do |tenant|
9
+ shard = TenantRealm::Utils.shard_name_from_tenant(tenant:)
10
+
11
+ puts "Migrating #{shard}"
12
+
13
+ db_config = TenantRealm::Utils.dig_db_config(tenant:)
14
+
15
+ if db_config.blank?
16
+ puts "Skip Migrating #{shard}"
17
+
18
+ next
19
+ end
20
+
21
+ db_config = TenantRealm::DbContext.root_db_config.merge(
22
+ database: db_config[:database]
23
+ )
24
+
25
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
26
+ ActiveRecord::Tasks::DatabaseTasks.migrate
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ module Cache
5
+ class BaseCache
6
+ class << self
7
+ def cache_tenants(_tenants)
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def tenants
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def cache_tenant(_tenant)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def tenant(_identifier)
20
+ raise NotImplementedError
21
+ end
22
+
23
+ private
24
+
25
+ def tenant_unique_keys(tenant)
26
+ config = Configuration::Cache
27
+
28
+ if config.tenant_keys_resolver.present?
29
+ Helpers.raise_if_not_proc(config.tenant_keys_resolver, 'cache_config.tenant_keys_resolver')
30
+ Helpers.wrap_array(config.tenant_keys_resolver.call(tenant))
31
+ elsif config.tenant_uniq_cols.present?
32
+ cols = Helpers.wrap_array(config.tenant_uniq_cols)
33
+
34
+ cols.map do |col|
35
+ tenant[col]
36
+ end
37
+ else
38
+ [
39
+ tenant[:slug]
40
+ ]
41
+ end
42
+ end
43
+
44
+ def tenants_key
45
+ 'tenant_realm:tenants'
46
+ end
47
+
48
+ def tenant_key
49
+ 'tenant_realm:tenant'
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis'
4
+ require 'kredis'
5
+
6
+ require_relative 'base_cache'
7
+
8
+ module TenantRealm
9
+ module Cache
10
+ class KredisCache < BaseCache
11
+ class << self
12
+ def cache_tenants(tenants)
13
+ return if tenants.blank?
14
+
15
+ cached_tenants = tenants_kredis
16
+ cached_tenants.value = tenants
17
+ tenants
18
+ end
19
+
20
+ def tenants
21
+ cached_tenants = tenants_kredis
22
+ cached_tenants.value&.map(&:deep_symbolize_keys) || []
23
+ end
24
+
25
+ def cache_tenant(tenant)
26
+ tenant_unique_keys(tenant).each do |key|
27
+ cached_tenant = tenant_kredis(key)
28
+ cached_tenant.value = tenant
29
+ end
30
+
31
+ tenant
32
+ end
33
+
34
+ def tenant(identifier)
35
+ cached_tenant = tenant_kredis(identifier)
36
+ cached_tenant.value&.deep_symbolize_keys
37
+ end
38
+
39
+ private
40
+
41
+ def tenants_kredis
42
+ Kredis.json(tenants_key, expires_in: Configuration::Cache.expires_in)
43
+ end
44
+
45
+ def tenant_kredis(identifier)
46
+ Kredis.json("#{tenant_key}:#{identifier}", expires_in: Configuration::Cache.expires_in)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ class << self
5
+ def configure
6
+ yield Config
7
+ end
8
+
9
+ def configuration
10
+ Config
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ class Config
5
+ class << self
6
+ attr_accessor(
7
+ :fetch_tenant,
8
+ :fetch_tenants,
9
+ :dig_db_config,
10
+ :skip_resolver,
11
+ :current_tenant,
12
+ :identifier_resolver,
13
+ :shard_name_from_tenant
14
+ )
15
+
16
+ def cache
17
+ yield Configuration::Cache
18
+ end
19
+
20
+ def current
21
+ @current ||= current_tenant || CurrentTenant
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ module Configuration
5
+ class Cache
6
+ class << self
7
+ attr_accessor :service,
8
+ :expires_in,
9
+ :tenant_uniq_cols,
10
+ :tenant_keys_resolver
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utils'
4
+ require_relative 'tenant'
5
+
6
+ module TenantRealm
7
+ class DbContext
8
+ # to cache all tenant's connections
9
+ @@shards = {}
10
+
11
+ # to cache all tenant's connections already connected
12
+ @@connected_shards = nil
13
+
14
+ class << self
15
+ def root_db_config
16
+ @@db_config ||= ActiveRecord::Base.connection_db_config.configuration_hash.deep_dup.freeze
17
+ end
18
+
19
+ def init_shards
20
+ config = Utils.load_database_yml
21
+ tenants = Tenant.tenants
22
+
23
+ @@shards = config[Rails.env].keys.each_with_object({}) do |shard, shards|
24
+ shards[shard] = shard
25
+ end
26
+
27
+ tenants.each do |tenant|
28
+ db_config = Utils.dig_db_config(tenant:)
29
+ shard = Utils.shard_name_from_tenant(tenant:)
30
+ next if db_config.blank?
31
+
32
+ add_shard(shard:, db_config:)
33
+ end
34
+
35
+ ActiveRecord::Base.connects_to(shards: connected_shards)
36
+ end
37
+
38
+ def connected_shards
39
+ @@connected_shards ||= build_connected_shards
40
+ end
41
+
42
+ def add_shard(shard:, db_config:)
43
+ return if shard_exist?(shard:) || db_config.blank?
44
+
45
+ shard_name = shard.underscore
46
+ config = Utils.load_database_yml
47
+ tenant_shard_config = root_db_config.merge(database: db_config[:database])
48
+ config[Rails.env][shard_name] = JSON.parse(tenant_shard_config.to_json)
49
+ @@shards[shard_name] = shard_name
50
+
51
+ # sometimes the dynamic tenant's db_config causes connection pool checkout
52
+ # write tenants' db_config to database.yml
53
+ # to fix this problem when restart server
54
+ File.open('config/database.yml', 'w') do |f|
55
+ YAML.dump(config, f)
56
+ end
57
+
58
+ ActiveRecord::Base.configurations.configurations << ActiveRecord::DatabaseConfigurations::HashConfig.new(
59
+ Rails.env,
60
+ shard_name,
61
+ tenant_shard_config
62
+ )
63
+
64
+ sym_shard = shard_name.to_sym
65
+
66
+ connected_shards[sym_shard] = {
67
+ writing: sym_shard,
68
+ reading: sym_shard
69
+ }
70
+ end
71
+
72
+ def shard_exist?(shard:)
73
+ @@shards.key?(shard.underscore)
74
+ end
75
+
76
+ def switch_database(shard:, db_config: nil, &block)
77
+ add_shard(shard:, db_config:)
78
+
79
+ ActiveRecord::Base.connected_to(shard: shard.underscore.to_sym, &block)
80
+ end
81
+
82
+ def flush_connection!
83
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
84
+ ActiveRecord::Base.connection_handler.flush_idle_connections!(:all)
85
+ end
86
+
87
+ def run_migrate(db_config:)
88
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(
89
+ root_db_config.merge(database: db_config[:database])
90
+ ) do
91
+ ActiveRecord::Tasks::DatabaseTasks.migrate
92
+ end
93
+
94
+ false
95
+ rescue StandardError
96
+ flush_connection!
97
+
98
+ true
99
+ end
100
+
101
+ def create_db(shard:, affix: nil)
102
+ database = [shard, affix].compact.join('-').underscore
103
+ db_config = root_db_config.merge(database:).tap do |config|
104
+ config[:host] ||= 'localhost'
105
+ config[:port] ||= 3306
106
+ end
107
+
108
+ ActiveRecord::Base.connection.create_database(database)
109
+
110
+ db_config
111
+ end
112
+
113
+ private
114
+
115
+ def build_connected_shards
116
+ @@shards.keys.each_with_object({}) do |shard, shards|
117
+ sym_shard = shard.to_sym
118
+
119
+ shards[sym_shard] = {
120
+ writing: sym_shard,
121
+ reading: sym_shard
122
+ }
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ class Helpers
5
+ class << self
6
+ def wrap_array(data)
7
+ return [] if data.blank?
8
+
9
+ data.is_a?(Array) ? data : [data]
10
+ end
11
+
12
+ def dev_log(message)
13
+ p message if Rails.env.development?
14
+ end
15
+
16
+ def raise_if_not_proc(source, name)
17
+ raise Error, "#{name} must be a Proc" unless source.is_a?(Proc)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ class Railtie < Rails::Railtie
5
+ rake_tasks do
6
+ Dir[File.expand_path('../tasks/*.rake', __dir__)].each do |file|
7
+ load(file)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'cache/kredis_cache'
4
+
5
+ module TenantRealm
6
+ class Tenant
7
+ @@cache = {
8
+ redis: Cache::KredisCache,
9
+ kredis: Cache::KredisCache
10
+ }
11
+
12
+ class << self
13
+ def tenants
14
+ if cache.present?
15
+ tenants = cache.tenants
16
+ return tenants if tenants.present?
17
+
18
+ tenants = Utils.fetch_tenants
19
+ cache.cache_tenants(tenants)
20
+ tenants
21
+ else
22
+ Utils.fetch_tenants
23
+ end
24
+ end
25
+
26
+ def tenant(identifier)
27
+ if cache.present?
28
+ tenant = cache.tenant(identifier)
29
+ return tenant if tenant.present?
30
+
31
+ tenant = Utils.fetch_tenant(identifier)
32
+ cache.cache_tenant(tenant)
33
+ tenant
34
+ else
35
+ Utils.fetch_tenant
36
+ end
37
+ end
38
+
39
+ def cache_tenants(tenants)
40
+ return Helpers.dev_log('Tenant Realm: Skip cache tenants because cache not configured') if cache.blank?
41
+
42
+ cache.cache_tenants(tenants)
43
+ end
44
+
45
+ def cache_tenant(tenant)
46
+ return Helpers.dev_log('Tenant Realm: Skip cache tenant because cache not configured') if cache.blank?
47
+
48
+ cache.cache_tenant(tenant)
49
+ end
50
+
51
+ private
52
+
53
+ def cache
54
+ @cache ||= @@cache[Configuration::Cache.service]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ class Utils
5
+ class << self
6
+ def load_database_yml
7
+ YAML.load_file('config/database.yml', aliases: true)
8
+ end
9
+
10
+ def fetch_tenants
11
+ Helpers.raise_if_not_proc(Config.fetch_tenants, 'config.fetch_tenants')
12
+
13
+ (Config.fetch_tenants.call.presence || []).map(&:deep_symbolize_keys)
14
+ end
15
+
16
+ def fetch_tenant(identifier)
17
+ Helpers.raise_if_not_proc(Config.fetch_tenant, 'config.fetch_tenant')
18
+
19
+ Config.fetch_tenant.call(identifier)&.deep_symbolize_keys
20
+ end
21
+
22
+ def dig_db_config(tenant:)
23
+ if Config.dig_db_config.is_a?(Proc)
24
+ Config.dig_db_config.call(tenant)
25
+ else
26
+ key = Config.dig_db_config || :db_config
27
+
28
+ tenant[key.to_sym]
29
+ end
30
+ end
31
+
32
+ def shard_name_from_tenant(tenant:)
33
+ shard = if Config.shard_name_from_tenant.is_a?(Proc)
34
+ Config.shard_name_from_tenant.call(tenant)
35
+ else
36
+ key = Config.shard_name_from_tenant || :shard_name
37
+
38
+ tenant[key.to_sym] || tenant[:slug] || tenant[:id]
39
+ end
40
+
41
+ shard.underscore
42
+ end
43
+
44
+ def identifier_resolver(request)
45
+ raise Error, 'config.identifier_resolver must be provided' if Config.identifier_resolver.blank?
46
+
47
+ Helpers.raise_if_not_proc(Config.identifier_resolver, 'config.identifier_resolver')
48
+
49
+ Config.identifier_resolver.call(request)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TenantRealm
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'tenant_realm/config'
4
+ require_relative 'tenant_realm/version'
5
+ require_relative 'tenant_realm/helpers'
6
+ require_relative 'tenant_realm/db_context'
7
+ require_relative 'tenant_realm/class_methods'
8
+ require_relative 'tenant_realm/configuration/cache'
9
+
10
+ module TenantRealm
11
+ require 'tenant_realm/railtie' if defined?(Rails)
12
+
13
+ class Error < StandardError; end
14
+
15
+ class CurrentTenant < ActiveSupport::CurrentAttributes
16
+ attribute :tenant
17
+ end
18
+
19
+ class Railtie < Rails::Railtie
20
+ config.before_configuration do
21
+ Helpers.dev_log('Tenant Realm: Init shard resolver')
22
+
23
+ config.active_record.shard_selector = { lock: false }
24
+ config.active_record.shard_resolver = lambda { |request|
25
+ skip_switch_db = if Config.skip_resolver.is_a?(Proc)
26
+ Config.skip_resolver.call(request)
27
+ else
28
+ false
29
+ end
30
+
31
+ return :primary if skip_switch_db
32
+
33
+ identifier = Utils.identifier_resolver(request)
34
+ tenant = Tenant.tenant(identifier)
35
+ db_config = Utils.dig_db_config(tenant:)
36
+ shard = Utils.shard_name_from_tenant(tenant:)
37
+
38
+ return :primary if db_config.blank?
39
+
40
+ Config.current.tenant = tenant
41
+ DbContext.add_shard(shard:, db_config:)
42
+ ActiveRecord::Base.connects_to(shards: DbContext.connected_shards)
43
+
44
+ shard
45
+ }
46
+ end
47
+
48
+ initializer 'active_record.setup_tenant_realm' do
49
+ Helpers.dev_log('Tenant Realm: Init database shards')
50
+
51
+ ActiveSupport.on_load(:active_record) do
52
+ ActiveRecord::Base.default_shard = :primary
53
+ ActiveRecord::Base.connects_to(
54
+ shards: {
55
+ primary: { writing: :primary, reading: :primary }
56
+ }
57
+ )
58
+
59
+ DbContext.init_shards
60
+ rescue ActiveRecord::ActiveRecordError
61
+ nil
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/tenant_realm/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'tenant_realm'
7
+ spec.version = TenantRealm::VERSION
8
+ spec.authors = ['Alpha']
9
+ spec.email = ['alphanolucifer@gmail.com']
10
+
11
+ spec.summary = 'Ruby on Rails gem to support multi-tenant'
12
+ spec.description = 'Ruby on Rails gem to support multi-tenant'
13
+ spec.homepage = 'https://github.com/zgid123/tenant_realm'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.1.0'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+
19
+ spec.files = Dir.chdir(__dir__) do
20
+ `git ls-files -z`.split("\x0").reject do |f|
21
+ (File.expand_path(f) == __FILE__) ||
22
+ f.start_with?(
23
+ *%w[
24
+ bin/
25
+ test/
26
+ spec/
27
+ features/
28
+ .git
29
+ .circleci
30
+ appveyor
31
+ examples/
32
+ Gemfile
33
+ .rubocop.yml
34
+ .vscode/settings.json
35
+ LICENSE.txt
36
+ lefthook.yml
37
+ ]
38
+ )
39
+ end
40
+ end
41
+
42
+ spec.require_paths = ['lib']
43
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tenant_realm
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Alpha
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-01-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ruby on Rails gem to support multi-tenant
14
+ email:
15
+ - alphanolucifer@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".ruby-version"
21
+ - README.md
22
+ - Rakefile
23
+ - lib/generators/templates/tenant_realm.tt
24
+ - lib/generators/tenant_realm_generator.rb
25
+ - lib/tasks/migrate.rake
26
+ - lib/tenant_realm.rb
27
+ - lib/tenant_realm/cache/base_cache.rb
28
+ - lib/tenant_realm/cache/kredis_cache.rb
29
+ - lib/tenant_realm/class_methods.rb
30
+ - lib/tenant_realm/config.rb
31
+ - lib/tenant_realm/configuration/cache.rb
32
+ - lib/tenant_realm/db_context.rb
33
+ - lib/tenant_realm/helpers.rb
34
+ - lib/tenant_realm/railtie.rb
35
+ - lib/tenant_realm/tenant.rb
36
+ - lib/tenant_realm/utils.rb
37
+ - lib/tenant_realm/version.rb
38
+ - tenant_realm.gemspec
39
+ homepage: https://github.com/zgid123/tenant_realm
40
+ licenses:
41
+ - MIT
42
+ metadata:
43
+ homepage_uri: https://github.com/zgid123/tenant_realm
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.1.0
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.4.13
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Ruby on Rails gem to support multi-tenant
63
+ test_files: []