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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gem_release.yml +5 -0
  3. data/.github/stale.yml +1 -0
  4. data/.github/workflows/lint.yml +25 -0
  5. data/.github/workflows/test.yml +71 -0
  6. data/.github_changelog_generator +2 -0
  7. data/.gitignore +21 -0
  8. data/.rspec +2 -0
  9. data/.rubocop.yml +24 -0
  10. data/CHANGELOG.md +3 -0
  11. data/Gemfile +51 -0
  12. data/LICENSE +26 -0
  13. data/README.md +99 -0
  14. data/Rakefile +7 -0
  15. data/app/decorators/solidus_acts_as_tenant/store_credit_decorator.rb +12 -0
  16. data/app/decorators/solidus_acts_as_tenant/variant_decorator.rb +31 -0
  17. data/app/models/spree/products_taxon.rb +9 -0
  18. data/app/models/spree/reimbursement_credit.rb +9 -0
  19. data/app/models/spree/tenant.rb +11 -0
  20. data/bin/console +17 -0
  21. data/bin/rails +7 -0
  22. data/bin/rails-engine +13 -0
  23. data/bin/rails-sandbox +16 -0
  24. data/bin/rake +7 -0
  25. data/bin/sandbox +77 -0
  26. data/bin/setup +8 -0
  27. data/config/locales/en.yml +4 -0
  28. data/config/routes.rb +5 -0
  29. data/db/migrate/20241204043609_add_tenant_id_to_solidus_tables.rb +103 -0
  30. data/db/migrate/20241204055845_update_spree_unique_indexes_with_tenant_scope.rb +21 -0
  31. data/db/migrate/20241204105318_create_spree_tenants.rb +16 -0
  32. data/lib/generators/solidus_acts_as_tenant/install/install_generator.rb +36 -0
  33. data/lib/generators/solidus_acts_as_tenant/install/templates/initializer.rb +15 -0
  34. data/lib/generators/solidus_acts_as_tenant/install/templates/tenant_aware_models.yml +98 -0
  35. data/lib/solidus_acts_as_tenant/configuration.rb +32 -0
  36. data/lib/solidus_acts_as_tenant/engine.rb +23 -0
  37. data/lib/solidus_acts_as_tenant/factories/preference_factory.rb +7 -0
  38. data/lib/solidus_acts_as_tenant/factories/tenant_factory.rb +9 -0
  39. data/lib/solidus_acts_as_tenant/tenant_aware.rb +51 -0
  40. data/lib/solidus_acts_as_tenant/testing_support/factories.rb +5 -0
  41. data/lib/solidus_acts_as_tenant/utils/tenant_selector.rb +74 -0
  42. data/lib/solidus_acts_as_tenant/version.rb +5 -0
  43. data/lib/solidus_acts_as_tenant.rb +8 -0
  44. data/solidus_acts_as_tenant.gemspec +38 -0
  45. data/spec/lib/solidus_acts_as_tenant/configuration_spec.rb +43 -0
  46. data/spec/lib/solidus_acts_as_tenant/solidus_acts_as_tenant_spec.rb +27 -0
  47. data/spec/lib/solidus_acts_as_tenant/tenant_aware_spec.rb +48 -0
  48. data/spec/models/spree/store_credit_spec.rb +15 -0
  49. data/spec/models/spree/variant_spec.rb +37 -0
  50. data/spec/spec_helper.rb +34 -0
  51. data/spec/support/config.rb +23 -0
  52. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :preference, class: 'Spree::Preference' do
5
+ sequence(:key) { |i| "Key#{i}" }
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :tenant, class: ::SolidusActsAsTenant.config.class_name do
5
+ sequence(:name) { |i| "Demo #{i}" }
6
+ sequence(:subdomain) { |i| "demo-#{i}" }
7
+ sequence(:domain) { |i| "demo-#{i}.com" }
8
+ end
9
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{File.dirname(__FILE__)}/../factories/**"].each do |f|
4
+ require File.expand_path(f)
5
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusActsAsTenant
4
+ VERSION = '0.0.5'
5
+ 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