diff --git a/README.md b/README.md index 4a12227..6c3c80b 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,16 @@ end ``` This is useful in shared routes such as admin panels or internal dashboards when `require_tenant` option is enabled throughout the app. +### Allowing tenant updating for a block ### + +```ruby +ActsAsTenant.with_mutable_tenant do + # Tenant updating is enabled for all code in this block +end +``` + +This will allow you to change the tenant of a model. This feature is useful for admin screens, where it is ok to allow certain users to change the tenant on existing models in specific cases. + ### Require tenant to be set always ### If you want to require the tenant to be set at all times, you can configure acts_as_tenant to raise an error when a query is made without a tenant available. See below under configuration options. diff --git a/lib/acts_as_tenant.rb b/lib/acts_as_tenant.rb index 9752822..1249bd8 100644 --- a/lib/acts_as_tenant.rb +++ b/lib/acts_as_tenant.rb @@ -12,6 +12,7 @@ module ActsAsTenant @@configuration = nil @@tenant_klass = nil @@models_with_global_records = [] + @@mutable_tenant = false class << self attr_writer :default_tenant @@ -87,6 +88,14 @@ def self.default_tenant @default_tenant unless unscoped end + def self.mutable_tenant!(toggle) + @@mutable_tenant = toggle + end + + def self.mutable_tenant? + @@mutable_tenant + end + def self.with_tenant(tenant, &block) if block.nil? raise ArgumentError, "block required" @@ -120,6 +129,13 @@ def self.without_tenant(&block) self.unscoped = old_unscoped end + def self.with_mutable_tenant(&block) + ActsAsTenant.mutable_tenant!(true) + without_tenant(&block) + ensure + ActsAsTenant.mutable_tenant!(false) + end + def self.should_require_tenant? if configuration.require_tenant.respond_to?(:call) !!configuration.require_tenant.call diff --git a/lib/acts_as_tenant/model_extensions.rb b/lib/acts_as_tenant/model_extensions.rb index 1e6f5a1..ffcc64f 100644 --- a/lib/acts_as_tenant/model_extensions.rb +++ b/lib/acts_as_tenant/model_extensions.rb @@ -5,6 +5,7 @@ module ModelExtensions class_methods do def acts_as_tenant(tenant = :account, **options) ActsAsTenant.set_tenant_klass(tenant) + ActsAsTenant.mutable_tenant!(false) ActsAsTenant.add_global_record_model(self) if options[:has_global_records] diff --git a/spec/models/model_extensions_spec.rb b/spec/models/model_extensions_spec.rb index 97928a3..97dd3ba 100644 --- a/spec/models/model_extensions_spec.rb +++ b/spec/models/model_extensions_spec.rb @@ -407,6 +407,37 @@ end end + describe "::with_mutable_tenant" do + it "should return the value of the block" do + value = ActsAsTenant.with_mutable_tenant { "something" } + expect(value).to eq "something" + end + + it "should raise an error when no block is provided" do + expect { ActsAsTenant.with_mutable_tenant }.to raise_error(ArgumentError, /block required/) + end + + it "should set tenant back to immutable after the block" do + ActsAsTenant.with_mutable_tenant do + "something" + end + expect(ActsAsTenant.mutable_tenant?).to eq false + end + + describe "mutability" do + before do + @account = Account.create!(name: "foo") + @project = @account.projects.create!(name: "bar") + end + + it "should allow tenant_id to change inside the block" do + new_account_id = @account.id + 1 + expect { ActsAsTenant.with_mutable_tenant { @project.account_id = new_account_id } }.to_not raise_error(ActsAsTenant::Errors::TenantIsImmutable) + expect(@project.account_id).to eq new_account_id + end + end + end + # Tenant required context "tenant required" do before do