solidus_acts_as_tenant 0.0.5
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/.gem_release.yml +5 -0
- data/.github/stale.yml +1 -0
- data/.github/workflows/lint.yml +25 -0
- data/.github/workflows/test.yml +71 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +24 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +51 -0
- data/LICENSE +26 -0
- data/README.md +99 -0
- data/Rakefile +7 -0
- data/app/decorators/solidus_acts_as_tenant/store_credit_decorator.rb +12 -0
- data/app/decorators/solidus_acts_as_tenant/variant_decorator.rb +31 -0
- data/app/models/spree/products_taxon.rb +9 -0
- data/app/models/spree/reimbursement_credit.rb +9 -0
- data/app/models/spree/tenant.rb +11 -0
- data/bin/console +17 -0
- data/bin/rails +7 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/sandbox +77 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +4 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20241204043609_add_tenant_id_to_solidus_tables.rb +103 -0
- data/db/migrate/20241204055845_update_spree_unique_indexes_with_tenant_scope.rb +21 -0
- data/db/migrate/20241204105318_create_spree_tenants.rb +16 -0
- data/lib/generators/solidus_acts_as_tenant/install/install_generator.rb +36 -0
- data/lib/generators/solidus_acts_as_tenant/install/templates/initializer.rb +15 -0
- data/lib/generators/solidus_acts_as_tenant/install/templates/tenant_aware_models.yml +98 -0
- data/lib/solidus_acts_as_tenant/configuration.rb +32 -0
- data/lib/solidus_acts_as_tenant/engine.rb +23 -0
- data/lib/solidus_acts_as_tenant/factories/preference_factory.rb +7 -0
- data/lib/solidus_acts_as_tenant/factories/tenant_factory.rb +9 -0
- data/lib/solidus_acts_as_tenant/tenant_aware.rb +51 -0
- data/lib/solidus_acts_as_tenant/testing_support/factories.rb +5 -0
- data/lib/solidus_acts_as_tenant/utils/tenant_selector.rb +74 -0
- data/lib/solidus_acts_as_tenant/version.rb +5 -0
- data/lib/solidus_acts_as_tenant.rb +8 -0
- data/solidus_acts_as_tenant.gemspec +38 -0
- data/spec/lib/solidus_acts_as_tenant/configuration_spec.rb +43 -0
- data/spec/lib/solidus_acts_as_tenant/solidus_acts_as_tenant_spec.rb +27 -0
- data/spec/lib/solidus_acts_as_tenant/tenant_aware_spec.rb +48 -0
- data/spec/models/spree/store_credit_spec.rb +15 -0
- data/spec/models/spree/variant_spec.rb +37 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/config.rb +23 -0
- metadata +179 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
class AddTenantIdToSolidusTables < ActiveRecord::Migration[7.0]
|
2
|
+
# If using a different tenant model, you will need to change the ::SolidusActsAsTenant.config.tenant_column_name
|
3
|
+
# that is added to the tables.
|
4
|
+
|
5
|
+
# These are tables that don't need a tenancy but may be desired:
|
6
|
+
# spree_countries (also remove "iso" uniqueness index)
|
7
|
+
# spree_states
|
8
|
+
# spree_permission_sets
|
9
|
+
# spree_roles (also remove "name" uniqueness index)
|
10
|
+
# spree_role_permissions
|
11
|
+
|
12
|
+
TABLES = %w[
|
13
|
+
spree_addresses
|
14
|
+
spree_adjustments
|
15
|
+
spree_credit_cards
|
16
|
+
spree_payments
|
17
|
+
spree_refunds
|
18
|
+
spree_roles_users
|
19
|
+
spree_shipping_method_categories
|
20
|
+
spree_store_credit_types
|
21
|
+
spree_store_credits
|
22
|
+
spree_calculators
|
23
|
+
spree_users
|
24
|
+
spree_adjustment_reasons
|
25
|
+
spree_assets
|
26
|
+
spree_cartons
|
27
|
+
spree_customer_returns
|
28
|
+
spree_inventory_units
|
29
|
+
spree_line_item_actions
|
30
|
+
spree_line_items
|
31
|
+
spree_log_entries
|
32
|
+
spree_option_type_prototypes
|
33
|
+
spree_option_types
|
34
|
+
spree_option_values
|
35
|
+
spree_option_values_variants
|
36
|
+
spree_orders
|
37
|
+
spree_payment_capture_events
|
38
|
+
spree_payment_methods
|
39
|
+
spree_preferences
|
40
|
+
spree_prices
|
41
|
+
spree_product_option_types
|
42
|
+
spree_product_properties
|
43
|
+
spree_products
|
44
|
+
spree_products_taxons
|
45
|
+
spree_properties
|
46
|
+
spree_property_prototypes
|
47
|
+
spree_prototype_taxons
|
48
|
+
spree_prototypes
|
49
|
+
spree_refund_reasons
|
50
|
+
spree_reimbursement_credits
|
51
|
+
spree_reimbursement_types
|
52
|
+
spree_reimbursements
|
53
|
+
spree_return_authorizations
|
54
|
+
spree_return_items
|
55
|
+
spree_return_reasons
|
56
|
+
spree_shipments
|
57
|
+
spree_shipping_categories
|
58
|
+
spree_shipping_method_stock_locations
|
59
|
+
spree_shipping_method_zones
|
60
|
+
spree_shipping_methods
|
61
|
+
spree_shipping_rate_taxes
|
62
|
+
spree_shipping_rates
|
63
|
+
spree_state_changes
|
64
|
+
spree_stock_items
|
65
|
+
spree_stock_locations
|
66
|
+
spree_stock_movements
|
67
|
+
spree_store_credit_categories
|
68
|
+
spree_store_credit_events
|
69
|
+
spree_store_payment_methods
|
70
|
+
spree_stores
|
71
|
+
spree_store_shipping_methods
|
72
|
+
spree_store_credit_reasons
|
73
|
+
spree_tax_categories
|
74
|
+
spree_tax_rates
|
75
|
+
spree_taxonomies
|
76
|
+
spree_taxons
|
77
|
+
spree_tax_rate_tax_categories
|
78
|
+
spree_unit_cancels
|
79
|
+
spree_user_addresses
|
80
|
+
spree_user_stock_locations
|
81
|
+
spree_variant_property_rule_conditions
|
82
|
+
spree_variant_property_rule_values
|
83
|
+
spree_variant_property_rules
|
84
|
+
spree_variants
|
85
|
+
spree_wallet_payment_sources
|
86
|
+
spree_zone_members
|
87
|
+
spree_zones
|
88
|
+
].push(::Spree.user_class.table_name).uniq.freeze
|
89
|
+
|
90
|
+
def up
|
91
|
+
TABLES.each do |table|
|
92
|
+
add_column table, ::SolidusActsAsTenant.config.tenant_column_name, :bigint
|
93
|
+
add_index table, ::SolidusActsAsTenant.config.tenant_column_name
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def down
|
98
|
+
TABLES.each do |table|
|
99
|
+
remove_index table
|
100
|
+
remove_column table, ::SolidusActsAsTenant.config.tenant_column_name
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class UpdateSpreeUniqueIndexesWithTenantScope < ActiveRecord::Migration[7.0]
|
2
|
+
# If using a different tenant model, you will need to change the :::SolidusActsAsTenant.config.tenant_column_name,
|
3
|
+
# in the following indexes.
|
4
|
+
def up
|
5
|
+
remove_index :spree_preferences, name: :index_spree_preferences_on_key
|
6
|
+
remove_index :spree_products, name: :index_spree_products_on_slug
|
7
|
+
|
8
|
+
add_index :spree_preferences, [::SolidusActsAsTenant.config.tenant_column_name, :key], unique: true,
|
9
|
+
name: :index_spree_preferences_on_tenant_and_key
|
10
|
+
add_index :spree_products, [::SolidusActsAsTenant.config.tenant_column_name, :slug], unique: true,
|
11
|
+
name: :index_spree_products_on_tenant_and_slug
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
remove_index :spree_preferences, name: :index_spree_preferences_on_tenant_and_key
|
16
|
+
remove_index :spree_products, name: :index_spree_products_on_tenant_and_slug
|
17
|
+
|
18
|
+
add_index :spree_preferences, :key, unique: true, name: :index_spree_preferences_on_key
|
19
|
+
add_index :spree_products, :slug, unique: true, name: :index_spree_products_on_slug
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateSpreeTenants < ActiveRecord::Migration[7.0]
|
2
|
+
# If using a different tenant model, you will need to create a migration for it and remove this one.
|
3
|
+
def change
|
4
|
+
create_table :spree_tenants do |t|
|
5
|
+
t.string :name
|
6
|
+
t.string :subdomain # Only used if setting tenant by subdomain
|
7
|
+
t.string :domain # Only used if setting tenant by domain
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :spree_tenants, :name, unique: true
|
13
|
+
add_index :spree_tenants, :subdomain, unique: true
|
14
|
+
add_index :spree_tenants, :domain, unique: true
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusActsAsTenant
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
8
|
+
|
9
|
+
def self.exit_on_failure?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def copy_initializer
|
14
|
+
template 'initializer.rb', 'config/initializers/solidus_acts_as_tenant.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy_config
|
18
|
+
template 'tenant_aware_models.yml', 'config/tenant_aware_models.yml'
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_migrations
|
22
|
+
run 'bin/rails railties:install:migrations FROM=solidus_acts_as_tenant'
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_migrations
|
26
|
+
puts 'Remember to inspect and adapt the migration files before migrating!' # rubocop:disable Rails/Output
|
27
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) # rubocop:disable Layout/LineLength
|
28
|
+
if run_migrations
|
29
|
+
run 'bin/rails db:migrate'
|
30
|
+
else
|
31
|
+
puts 'Skipping bin/rails db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
SolidusActsAsTenant.configure do |config|
|
4
|
+
# This configuration is used to dynamically prepend existing models with the acts_as_tenant concern.
|
5
|
+
# See the tenant_aware_classes.yml file for inormation on how to configure this file.
|
6
|
+
config.tenant_aware_models =
|
7
|
+
YAML.safe_load(ERB.new(File.read(Rails.root.join('config/tenant_aware_models.yml'))).result)
|
8
|
+
|
9
|
+
config.tenant_column_name = :tenant_id
|
10
|
+
# Tenant model class name
|
11
|
+
config.class_name = 'Spree::Tenant'
|
12
|
+
|
13
|
+
# These options are passed to the acts_as_tenant class method in the tenant_aware_classes.yml file.
|
14
|
+
config.acts_as_tenant_args = [:tenant, { class_name: 'Spree::Tenant' }]
|
15
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Any model that is already defined and has an a tenant column can be added to this list.
|
2
|
+
# It is not restricted to the Solidus models so it can be used for any model that is inherited from a gem.
|
3
|
+
|
4
|
+
# It is importnant to also note that any unique indexes on the database will need to replaced with a unique index on scoped to the tenant column.
|
5
|
+
|
6
|
+
# Format:
|
7
|
+
# - [Class name, [uniqueness attributes to update with tenant scope]]
|
8
|
+
# The uniqueness attributes array are optional for cases where there is already a uniqueness validation on the model that are not scoped to the tenant column.
|
9
|
+
|
10
|
+
# Notes regarding Solidus models:
|
11
|
+
# These are are classes that are created in this extention because they are not defined in Solidus:
|
12
|
+
# [Spree::ProductTaxon, []],
|
13
|
+
# [Spree::ReimbursementCredit, []],
|
14
|
+
|
15
|
+
# These are classes that don't need a tenancy but may be desired:
|
16
|
+
# [Spree::Country, []],
|
17
|
+
# [Spree::State, []],
|
18
|
+
# [Spree::PermissionSet, []],
|
19
|
+
# [Spree::Role, [:name]],
|
20
|
+
# [Spree::RolePermission, []],
|
21
|
+
|
22
|
+
# These are not needed at all as they are internal to Solidus (and may cause issues):
|
23
|
+
# [Spree::OrderMutex, []],
|
24
|
+
|
25
|
+
# The user model conflicts with devise, so it is not included here.
|
26
|
+
# But it may be included if the TenantNotSet error is bypassed using the config added on this fork's branch:
|
27
|
+
# https://github.com/nebulab/acts_as_tenant/tree/Pass-the-ar-relation-for-context-to-should_require_tenant
|
28
|
+
# [<%= ::Spree.user_class.to_s %>, []]
|
29
|
+
|
30
|
+
---
|
31
|
+
- [Spree::Address, []]
|
32
|
+
- [Spree::AdjustmentReason, [name, code]]
|
33
|
+
- [Spree::Adjustment, []]
|
34
|
+
- [Spree::Asset, []]
|
35
|
+
- [Spree::Calculator, []]
|
36
|
+
- [Spree::Carton, []]
|
37
|
+
- [Spree::CreditCard, []]
|
38
|
+
- [Spree::CustomerReturn, []]
|
39
|
+
- [Spree::InventoryUnit, []]
|
40
|
+
- [Spree::LineItemAction, []]
|
41
|
+
- [Spree::LineItem, []]
|
42
|
+
- [Spree::LogEntry, []]
|
43
|
+
- [Spree::OptionType, [name]]
|
44
|
+
- [Spree::OptionValue, [name]]
|
45
|
+
- [Spree::OptionValuesVariant, []]
|
46
|
+
- [Spree::Order, []]
|
47
|
+
- [Spree::PaymentCaptureEvent, []]
|
48
|
+
- [Spree::PaymentMethod, []]
|
49
|
+
- [Spree::Payment, []]
|
50
|
+
- [Spree::Preference, [key]]
|
51
|
+
- [Spree::Price, []]
|
52
|
+
- [Spree::ProductOptionType, []]
|
53
|
+
- [Spree::ProductProperty, []]
|
54
|
+
- [Spree::Product, [slug]]
|
55
|
+
- [Spree::Property, []]
|
56
|
+
- [Spree::RefundReason, [name]]
|
57
|
+
- [Spree::Refund, []]
|
58
|
+
- [Spree::ReimbursementType, [name]]
|
59
|
+
- [Spree::Reimbursement, []]
|
60
|
+
- [Spree::ReturnAuthorization, []]
|
61
|
+
- [Spree::ReturnItem, []]
|
62
|
+
- [Spree::ReturnReason, []]
|
63
|
+
- [Spree::RoleUser, []]
|
64
|
+
- [Spree::Shipment, []]
|
65
|
+
- [Spree::ShippingCategory, []]
|
66
|
+
- [Spree::ShippingMethodCategory, []]
|
67
|
+
- [Spree::ShippingMethodStockLocation, []]
|
68
|
+
- [Spree::ShippingMethodZone, []]
|
69
|
+
- [Spree::ShippingMethod, []]
|
70
|
+
- [Spree::ShippingRateTax, []]
|
71
|
+
- [Spree::StateChange, []]
|
72
|
+
- [Spree::ShippingRate, []]
|
73
|
+
- [Spree::StockItem, []]
|
74
|
+
- [Spree::StockLocation, [code]]
|
75
|
+
- [Spree::StockMovement, []]
|
76
|
+
- [Spree::StoreCreditCategory, []]
|
77
|
+
- [Spree::StoreCreditEvent, []]
|
78
|
+
- [Spree::StoreCreditType, []]
|
79
|
+
- [Spree::StoreCredit, []]
|
80
|
+
- [Spree::StorePaymentMethod, []]
|
81
|
+
- [Spree::Store, [code]]
|
82
|
+
- [Spree::StoreShippingMethod, []]
|
83
|
+
- [Spree::StoreCreditReason, [name]]
|
84
|
+
- [Spree::TaxCategory, [name]]
|
85
|
+
- [Spree::Taxonomy, [name]]
|
86
|
+
- [Spree::Taxon, []]
|
87
|
+
- [Spree::TaxRate, []]
|
88
|
+
- [Spree::TaxRateTaxCategory, []]
|
89
|
+
- [Spree::UnitCancel, []]
|
90
|
+
- [Spree::UserAddress, []]
|
91
|
+
- [Spree::UserStockLocation, []]
|
92
|
+
- [Spree::VariantPropertyRuleCondition, []]
|
93
|
+
- [Spree::VariantPropertyRuleValue, []]
|
94
|
+
- [Spree::VariantPropertyRule, []]
|
95
|
+
- [Spree::Variant, [sku]]
|
96
|
+
- [Spree::WalletPaymentSource, []]
|
97
|
+
- [Spree::ZoneMember, []]
|
98
|
+
- [Spree::Zone, [name]]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusActsAsTenant
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :tenant_aware_models
|
6
|
+
attr_writer :tenant_column_name, :acts_as_tenant_args, :class_name
|
7
|
+
|
8
|
+
def tenant_column_name
|
9
|
+
@tenant_column_name ||= :tenant_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def acts_as_tenant_args
|
13
|
+
@acts_as_tenant_args ||= [:tenant, { class_name: 'Spree::Tenant' }]
|
14
|
+
end
|
15
|
+
|
16
|
+
def class_name
|
17
|
+
@class_name ||= 'Spree::Tenant'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def configuration
|
23
|
+
@configuration ||= Configuration.new
|
24
|
+
end
|
25
|
+
|
26
|
+
alias config configuration
|
27
|
+
|
28
|
+
def configure
|
29
|
+
yield configuration
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'solidus_core'
|
4
|
+
require 'solidus_support'
|
5
|
+
|
6
|
+
module SolidusActsAsTenant
|
7
|
+
class Engine < Rails::Engine
|
8
|
+
include SolidusSupport::EngineExtensions
|
9
|
+
|
10
|
+
isolate_namespace ::Spree
|
11
|
+
|
12
|
+
engine_name 'solidus_acts_as_tenant'
|
13
|
+
|
14
|
+
# use rspec for tests
|
15
|
+
config.generators do |g|
|
16
|
+
g.test_framework :rspec
|
17
|
+
end
|
18
|
+
|
19
|
+
config.to_prepare do
|
20
|
+
SolidusActsAsTenant::TenantAware.setup_tenant_aware_models
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusActsAsTenant
|
4
|
+
# TenantAware automatically adds multi-tenant support to models
|
5
|
+
# by adding tenant scoping and updating uniqueness validations to be tenant-aware.
|
6
|
+
module TenantAware
|
7
|
+
class << self
|
8
|
+
def setup_tenant_aware_models
|
9
|
+
tenant_aware_models = SolidusActsAsTenant.config.tenant_aware_models || []
|
10
|
+
|
11
|
+
tenant_aware_models.each do |klass, validator_attributes|
|
12
|
+
Module.new do
|
13
|
+
@validator_attributes = validator_attributes
|
14
|
+
define_singleton_method(:prepended) do |base|
|
15
|
+
options = ::SolidusActsAsTenant.config.acts_as_tenant_args
|
16
|
+
base.acts_as_tenant(*options[0..-2], **options.last)
|
17
|
+
|
18
|
+
@validator_attributes&.each do |attribute|
|
19
|
+
TenantAware.update_uniqueness_validation(base, attribute)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
klass.constantize.prepend(self)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_uniqueness_validation(base, attribute)
|
29
|
+
attribute = attribute.to_sym
|
30
|
+
validator = base._validators[attribute].find { _1.kind == :uniqueness }
|
31
|
+
raise "No uniqueness validator found for #{attribute} on #{base}" unless validator
|
32
|
+
|
33
|
+
new_options = validator.options.dup
|
34
|
+
new_options[:scope] = Array(new_options[:scope]).push(::SolidusActsAsTenant.config.tenant_column_name)
|
35
|
+
|
36
|
+
remove_existing_validation(base, attribute)
|
37
|
+
base.validates_uniqueness_of attribute, **new_options
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_existing_validation(base, attribute)
|
41
|
+
base._validators[attribute].reject! { _1.kind == :uniqueness }
|
42
|
+
base._validate_callbacks.each do |callback|
|
43
|
+
next unless callback.filter.try(:attributes)&.include?(attribute) &&
|
44
|
+
callback.filter.kind == :uniqueness
|
45
|
+
|
46
|
+
callback.filter.attributes.delete(attribute)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusActsAsTenant
|
4
|
+
module Utils
|
5
|
+
class TenantSelector
|
6
|
+
MESSAGES = {
|
7
|
+
available: "Available tenants: %<tenants>s",
|
8
|
+
prompt: "Select tenant: ",
|
9
|
+
not_found: "Tenant not found in list '%<tenants>s'",
|
10
|
+
current: "You are now Tenant '%<name>s'"
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def ask
|
14
|
+
return if tenants.empty?
|
15
|
+
|
16
|
+
puts format(MESSAGES[:available], tenants: tenants) # rubocop:disable Rails/Output
|
17
|
+
print MESSAGES[:prompt] # rubocop:disable Rails/Output
|
18
|
+
|
19
|
+
tenant_name = ENV["DEFAULT_TENANT"] || gets.strip
|
20
|
+
switch_tenant!(tenant_name) unless tenant_name.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def tenants
|
24
|
+
@tenants ||= fetch_tenants
|
25
|
+
end
|
26
|
+
|
27
|
+
def switch_tenant!(tenant_name)
|
28
|
+
if exists?(tenant_name)
|
29
|
+
switch(tenant_name)
|
30
|
+
elsif numeric_tenant?(tenant_name)
|
31
|
+
switch(tenants[tenant_name.to_i])
|
32
|
+
else
|
33
|
+
puts format(MESSAGES[:not_found], tenants: tenants) # rubocop:disable Rails/Output
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
puts format(MESSAGES[:current], name: current) # rubocop:disable Rails/Output
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def current
|
42
|
+
ActsAsTenant.current_tenant&.name
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def fetch_tenants
|
48
|
+
[*tenant_names].uniq
|
49
|
+
.each_with_index
|
50
|
+
.to_h { |name, index| [index, name] }
|
51
|
+
end
|
52
|
+
|
53
|
+
def tenant_names
|
54
|
+
@tenant_names ||= ::SolidusActsAsTenant.config.class_name.constantize.pluck(:name).sort
|
55
|
+
end
|
56
|
+
|
57
|
+
def switch(tenant_name)
|
58
|
+
return false unless tenants.value?(tenant_name)
|
59
|
+
|
60
|
+
tenant = ::SolidusActsAsTenant.config.class_name.constantize.find_by(name: tenant_name)
|
61
|
+
ActsAsTenant.current_tenant = tenant
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def exists?(tenant_name)
|
66
|
+
tenant_names.include?(tenant_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def numeric_tenant?(tenant_name)
|
70
|
+
tenant_name =~ /^\d+$/ && tenants.key?(tenant_name.to_i)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'acts_as_tenant'
|
4
|
+
require 'solidus_acts_as_tenant/configuration'
|
5
|
+
require 'solidus_acts_as_tenant/tenant_aware'
|
6
|
+
require 'solidus_acts_as_tenant/utils/tenant_selector'
|
7
|
+
require 'solidus_acts_as_tenant/version'
|
8
|
+
require 'solidus_acts_as_tenant/engine'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/solidus_acts_as_tenant/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'solidus_acts_as_tenant'
|
7
|
+
spec.version = SolidusActsAsTenant::VERSION
|
8
|
+
spec.authors = ['Ikraam Ghoor']
|
9
|
+
spec.email = 'consult.ikraam@gmail.com'
|
10
|
+
|
11
|
+
spec.summary = 'Adds tenant functionality to solidus using the row level acts_as_tenant gem'
|
12
|
+
spec.description = 'Adds tenant functionality to solidus using the row level acts_as_tenant gem'
|
13
|
+
spec.homepage = 'https://github.com/nebulab/solidus_acts_as_tenant#readme'
|
14
|
+
spec.license = 'BSD-3-Clause'
|
15
|
+
|
16
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/nebulab/solidus_acts_as_tenant'
|
18
|
+
spec.metadata['changelog_uri'] = 'https://github.com/nebulab/solidus_acts_as_tenant/blob/main/CHANGELOG.md'
|
19
|
+
|
20
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5', '< 4')
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
|
25
|
+
|
26
|
+
spec.files = files.grep_v(%r{^(test|spec|features)/})
|
27
|
+
spec.test_files = files.grep(%r{^(test|spec|features)/})
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_dependency 'acts_as_tenant', '~> 1.0.1'
|
33
|
+
spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 5']
|
34
|
+
spec.add_dependency 'solidus_support', '>= 0.14.1'
|
35
|
+
|
36
|
+
spec.add_development_dependency 'pry'
|
37
|
+
spec.add_development_dependency 'solidus_dev_support', '~> 2.11'
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe SolidusActsAsTenant::Configuration do
|
4
|
+
let(:configuration) { described_class.new }
|
5
|
+
|
6
|
+
describe 'default values' do
|
7
|
+
it 'has default tenant_column_name' do
|
8
|
+
expect(configuration.tenant_column_name).to eq(:tenant_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'has default acts_as_tenant_args' do
|
12
|
+
expect(configuration.acts_as_tenant_args).to eq([:tenant, { class_name: 'Spree::Tenant' }])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has default class_name' do
|
16
|
+
expect(configuration.class_name).to eq('Spree::Tenant')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'custom configuration' do
|
21
|
+
it 'allows setting tenant_aware_models' do
|
22
|
+
models = ['Spree::Product', 'Spree::Order']
|
23
|
+
configuration.tenant_aware_models = models
|
24
|
+
expect(configuration.tenant_aware_models).to eq(models)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'allows setting tenant_column_name' do
|
28
|
+
configuration.tenant_column_name = :custom_tenant_id
|
29
|
+
expect(configuration.tenant_column_name).to eq(:custom_tenant_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'allows setting acts_as_tenant_args' do
|
33
|
+
custom_args = [:custom_tenant, { class_name: 'Custom::Tenant' }]
|
34
|
+
configuration.acts_as_tenant_args = custom_args
|
35
|
+
expect(configuration.acts_as_tenant_args).to eq(custom_args)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'allows setting class_name' do
|
39
|
+
configuration.class_name = 'Custom::Tenant'
|
40
|
+
expect(configuration.class_name).to eq('Custom::Tenant')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe SolidusActsAsTenant do
|
4
|
+
describe '.configuration' do
|
5
|
+
it 'returns the same configuration instance' do
|
6
|
+
expect(described_class.configuration).to eq(described_class.configuration)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'aliases config to configuration' do
|
10
|
+
expect(described_class.config).to eq(described_class.configuration)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.configure' do
|
15
|
+
it 'yields the configuration instance' do
|
16
|
+
expect { |b| described_class.configure(&b) }.to yield_with_args(SolidusActsAsTenant::Configuration)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'allows configuring through a block' do
|
20
|
+
described_class.configure do |config|
|
21
|
+
config.tenant_column_name = :custom_tenant_id
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(described_class.configuration.tenant_column_name).to eq(:custom_tenant_id)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
describe SolidusActsAsTenant::TenantAware do
|
2
|
+
describe '#setup_tenant_aware_models' do
|
3
|
+
let!(:tenant) { ::SolidusActsAsTenant.config.class_name.constantize.find_or_create_by!(name: 'Test') }
|
4
|
+
let!(:tenant2) { create(:tenant, name: 'Test2') }
|
5
|
+
|
6
|
+
SolidusActsAsTenant.config.tenant_aware_models.each do |klass, validator_attributes|
|
7
|
+
describe "for #{klass}" do
|
8
|
+
it 'has a tenant accessor and the current tenant is already set', set_tenant: true do
|
9
|
+
expect(klass.constantize.new.tenant).to eq tenant
|
10
|
+
end
|
11
|
+
|
12
|
+
validator_attributes.each do |attribute|
|
13
|
+
it "validates uniqueness of #{attribute}", set_tenant: true do
|
14
|
+
entry = create(klass.demodulize.underscore.downcase.to_sym, attribute => 'test@test.com')
|
15
|
+
duplicate = entry.dup
|
16
|
+
|
17
|
+
if klass == 'Spree::Product'
|
18
|
+
duplicate.master = entry.master
|
19
|
+
duplicate.slug = entry.slug
|
20
|
+
end
|
21
|
+
|
22
|
+
duplicate.valid?
|
23
|
+
|
24
|
+
expect(duplicate.errors[attribute]).to include('has already been taken')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'allows duplicate values across different tenants' do
|
28
|
+
::ActsAsTenant.current_tenant = tenant
|
29
|
+
entry = create(klass.demodulize.underscore.downcase.to_sym, attribute => 'test@test.com',
|
30
|
+
tenant: tenant)
|
31
|
+
duplicate = entry.dup
|
32
|
+
|
33
|
+
if klass == 'Spree::Product'
|
34
|
+
duplicate.master = entry.master
|
35
|
+
duplicate.slug = entry.slug
|
36
|
+
end
|
37
|
+
|
38
|
+
::ActsAsTenant.current_tenant = tenant2
|
39
|
+
duplicate.tenant = tenant2
|
40
|
+
duplicate.valid?
|
41
|
+
|
42
|
+
expect(duplicate.errors[attribute]).to be_empty
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|