activerecord-multi-tenant 2.0.0 → 2.1.1
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/activerecord-multi-tenant/migrations.rb +69 -8
- data/lib/activerecord-multi-tenant/model_extensions.rb +15 -1
- data/lib/activerecord-multi-tenant/multi_tenant.rb +17 -0
- data/lib/activerecord-multi-tenant/query_rewriter.rb +1 -1
- data/lib/activerecord-multi-tenant/sidekiq.rb +16 -0
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/lib/activerecord-multi-tenant.rb +0 -1
- data/spec/activerecord-multi-tenant/associations_spec.rb +21 -0
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +25 -11
- data/spec/activerecord-multi-tenant/record_modifications_spec.rb +19 -0
- data/spec/schema.rb +0 -1
- metadata +3 -3
- data/lib/activerecord-multi-tenant/persistence_extension.rb +0 -13
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e198970d8d5a4db04c8e203e28021668c765fdde78f9bb9a7cd0e6c577bfbe0e
         | 
| 4 | 
            +
              data.tar.gz: 6abb30aac520063dbef7ddc5689714ef1dd9eb691aa382498b30c8bbcf03844b
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 5010dbbd2a8036ad5a3adae79a49567a279a318931ffe832663dd2430f455d1895e26cca6d96456484b894cc241cca21fbab31aa301f9ab4ef0538a4266b4005
         | 
| 7 | 
            +
              data.tar.gz: e94b7ee0f619b0763879efefbae3a69792e00e8b9f2a2661a82666d4c129decea132f20b44999177bdc0162dec0e9b3cac4f402d70a76fb14518e283f4441d6c
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,13 @@ | |
| 1 1 | 
             
            # Changelog
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 2.1.0      2022-10-20
         | 
| 4 | 
            +
            * Fix query building for models with mismatched partition_keys [#150](https://github.com/citusdata/activerecord-multi-tenant/pull/150)
         | 
| 5 | 
            +
            * Identify tenant even if class name is nonstandard [#152](https://github.com/citusdata/activerecord-multi-tenant/pull/152)
         | 
| 6 | 
            +
            * Add current_tenant_id to WHERE clauses when calling methods on activerecord instance or its associations [#154](https://github.com/citusdata/activerecord-multi-tenant/pull/154)
         | 
| 7 | 
            +
            * Make create_distributed_table, create_reference_table reversible & add ruby wrapper for rebalance_table_shards [#155](https://github.com/citusdata/activerecord-multi-tenant/pull/155)
         | 
| 8 | 
            +
            * Support create_distributed_table, create_reference_table in schema.rb [#156](https://github.com/citusdata/activerecord-multi-tenant/pull/156)
         | 
| 9 | 
            +
            * Add client and server sidekiq middleware to sidekiq middleware chain [#158](https://github.com/citusdata/activerecord-multi-tenant/pull/158)
         | 
| 10 | 
            +
             | 
| 3 11 | 
             
            ## 2.0.0      2022-05-19
         | 
| 4 12 |  | 
| 5 13 | 
             
            * Replace RequestStore with CurrentAttributes [#139](https://github.com/citusdata/activerecord-multi-tenant/pull/139)
         | 
| @@ -2,12 +2,40 @@ module MultiTenant | |
| 2 2 | 
             
              module MigrationExtensions
         | 
| 3 3 | 
             
                def create_distributed_table(table_name, partition_key)
         | 
| 4 4 | 
             
                  return unless citus_version.present?
         | 
| 5 | 
            -
             | 
| 5 | 
            +
             | 
| 6 | 
            +
                  reversible do |dir|
         | 
| 7 | 
            +
                    dir.up do
         | 
| 8 | 
            +
                      execute "SELECT create_distributed_table($$#{table_name}$$, $$#{partition_key}$$)"
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
                    dir.down do
         | 
| 11 | 
            +
                      undistribute_table(table_name)
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 6 14 | 
             
                end
         | 
| 7 15 |  | 
| 8 16 | 
             
                def create_reference_table(table_name)
         | 
| 9 17 | 
             
                  return unless citus_version.present?
         | 
| 10 | 
            -
             | 
| 18 | 
            +
             | 
| 19 | 
            +
                  reversible do |dir|
         | 
| 20 | 
            +
                    dir.up do
         | 
| 21 | 
            +
                      execute "SELECT create_reference_table($$#{table_name}$$)"
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                    dir.down do
         | 
| 24 | 
            +
                      undistribute_table(table_name)
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def undistribute_table(table_name)
         | 
| 30 | 
            +
                  return unless citus_version.present?
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  execute "SELECT undistribute_table($$#{table_name}$$))"
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def rebalance_table_shards
         | 
| 36 | 
            +
                  return unless citus_version.present?
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  execute 'SELECT rebalance_table_shards()'
         | 
| 11 39 | 
             
                end
         | 
| 12 40 |  | 
| 13 41 | 
             
                def execute_on_all_nodes(sql)
         | 
| @@ -28,21 +56,19 @@ module MultiTenant | |
| 28 56 | 
             
                end
         | 
| 29 57 |  | 
| 30 58 | 
             
                def citus_version
         | 
| 31 | 
            -
                  execute("SELECT extversion FROM pg_extension WHERE extname = 'citus'").getvalue(0,0).try(:split, '-').try(:first)
         | 
| 59 | 
            +
                  execute("SELECT extversion FROM pg_extension WHERE extname = 'citus'").getvalue(0, 0).try(:split, '-').try(:first)
         | 
| 32 60 | 
             
                rescue ArgumentError => e
         | 
| 33 | 
            -
                  raise unless e.message ==  | 
| 61 | 
            +
                  raise unless e.message == 'invalid tuple number 0'
         | 
| 34 62 | 
             
                end
         | 
| 35 63 | 
             
              end
         | 
| 36 64 | 
             
            end
         | 
| 37 65 |  | 
| 38 | 
            -
            if defined?(ActiveRecord::Migration)
         | 
| 39 | 
            -
              ActiveRecord::Migration.send(:include, MultiTenant::MigrationExtensions)
         | 
| 40 | 
            -
            end
         | 
| 66 | 
            +
            ActiveRecord::Migration.include MultiTenant::MigrationExtensions if defined?(ActiveRecord::Migration)
         | 
| 41 67 |  | 
| 42 68 | 
             
            module ActiveRecord
         | 
| 43 69 | 
             
              module ConnectionAdapters # :nodoc:
         | 
| 44 70 | 
             
                module SchemaStatements
         | 
| 45 | 
            -
                  alias  | 
| 71 | 
            +
                  alias orig_create_table create_table
         | 
| 46 72 | 
             
                  def create_table(table_name, options = {}, &block)
         | 
| 47 73 | 
             
                    ret = orig_create_table(table_name, **options.except(:partition_key), &block)
         | 
| 48 74 | 
             
                    if options[:partition_key] && options[:partition_key].to_s != 'id'
         | 
| @@ -54,3 +80,38 @@ module ActiveRecord | |
| 54 80 | 
             
                end
         | 
| 55 81 | 
             
              end
         | 
| 56 82 | 
             
            end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            module ActiveRecord
         | 
| 85 | 
            +
              class SchemaDumper
         | 
| 86 | 
            +
                private
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                alias initialize_without_citus initialize
         | 
| 89 | 
            +
                def initialize(connection, options = {})
         | 
| 90 | 
            +
                  initialize_without_citus(connection, options)
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  @distribution_columns =
         | 
| 93 | 
            +
                    if ActiveRecord::Migration.citus_version.present?
         | 
| 94 | 
            +
                      @connection.execute('SELECT logicalrelid::regclass AS table_name, column_to_column_name(logicalrelid, partkey) AS dist_col_name FROM pg_dist_partition').to_h do |v|
         | 
| 95 | 
            +
                        [v['table_name'], v['dist_col_name']]
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
                    else
         | 
| 98 | 
            +
                      {}
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                # Support for create_distributed_table & create_reference_table
         | 
| 103 | 
            +
                alias table_without_citus table
         | 
| 104 | 
            +
                def table(table, stream)
         | 
| 105 | 
            +
                  table_without_citus(table, stream)
         | 
| 106 | 
            +
                  table_name = remove_prefix_and_suffix(table)
         | 
| 107 | 
            +
                  distribution_column = @distribution_columns[table_name]
         | 
| 108 | 
            +
                  if distribution_column
         | 
| 109 | 
            +
                    stream.puts "  create_distributed_table(#{table_name.inspect}, #{distribution_column.inspect})"
         | 
| 110 | 
            +
                    stream.puts
         | 
| 111 | 
            +
                  elsif @distribution_columns.key?(table_name)
         | 
| 112 | 
            +
                    stream.puts "  create_reference_table(#{table_name.inspect})"
         | 
| 113 | 
            +
                    stream.puts
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
            end
         | 
| @@ -3,7 +3,7 @@ module MultiTenant | |
| 3 3 | 
             
                DEFAULT_ID_FIELD = 'id'.freeze
         | 
| 4 4 |  | 
| 5 5 | 
             
                def multi_tenant(tenant_name, options = {})
         | 
| 6 | 
            -
                  if to_s.underscore.to_sym == tenant_name
         | 
| 6 | 
            +
                  if to_s.underscore.to_sym == tenant_name || (!table_name.nil? && table_name.singularize.to_sym == tenant_name)
         | 
| 7 7 | 
             
                    unless MultiTenant.with_write_only_mode_enabled?
         | 
| 8 8 | 
             
                      # This is the tenant model itself. Workaround for https://github.com/citusdata/citus/issues/687
         | 
| 9 9 | 
             
                      before_create -> do
         | 
| @@ -129,6 +129,20 @@ end | |
| 129 129 |  | 
| 130 130 | 
             
            ActiveSupport.on_load(:active_record) do |base|
         | 
| 131 131 | 
             
              base.extend MultiTenant::ModelExtensionsClassMethods
         | 
| 132 | 
            +
             | 
| 133 | 
            +
              # Ensure we have current_tenant_id in where clause when a cached ActiveRecord instance is being reloaded, or update_columns without callbacks is called
         | 
| 134 | 
            +
              MultiTenant.wrap_methods(ActiveRecord::Base, 'self', :delete, :reload, :update_columns)
         | 
| 135 | 
            +
             | 
| 136 | 
            +
              # Any queuries fired for fetching a singular association have the correct current_tenant_id in WHERE clause
         | 
| 137 | 
            +
              # reload is called anytime any record's association is accessed
         | 
| 138 | 
            +
              MultiTenant.wrap_methods(ActiveRecord::Associations::Association, 'owner', :reload)
         | 
| 139 | 
            +
             | 
| 140 | 
            +
              # For collection associations, we need to wrap multiple methods in returned proxy so that any queries have the correct current_tenant_id in WHERE clause
         | 
| 141 | 
            +
              ActiveRecord::Associations::CollectionProxy.alias_method :equals_mt, :== # Hack to prevent syntax error due to invalid method name
         | 
| 142 | 
            +
              ActiveRecord::Associations::CollectionProxy.alias_method :append_mt, :<< # Hack to prevent syntax error due to invalid method name
         | 
| 143 | 
            +
              MultiTenant.wrap_methods(ActiveRecord::Associations::CollectionProxy, '@association.owner', :find, :last, :take, :build, :create, :create!, :replace, :delete_all, :destroy_all, :delete, :destroy, :calculate, :pluck, :size, :empty?, :include?, :equals_mt, :records, :append_mt, :find_nth_with_limit, :find_nth_from_last, :null_scope?, :find_from_target?, :exec_queries)
         | 
| 144 | 
            +
              ActiveRecord::Associations::CollectionProxy.alias_method :==, :equals_mt
         | 
| 145 | 
            +
              ActiveRecord::Associations::CollectionProxy.alias_method :<<, :append_mt
         | 
| 132 146 | 
             
            end
         | 
| 133 147 |  | 
| 134 148 | 
             
            class ActiveRecord::Associations::Association
         | 
| @@ -109,6 +109,23 @@ module MultiTenant | |
| 109 109 | 
             
                end
         | 
| 110 110 | 
             
              end
         | 
| 111 111 |  | 
| 112 | 
            +
              # Wrap calls to any of `method_names` on an instance Class `klass` with MultiTenant.with when `'owner'` (evaluated in context of the klass instance) is a ActiveRecord model instance that is multi-tenant
         | 
| 113 | 
            +
              def self.wrap_methods(klass, owner, *method_names)
         | 
| 114 | 
            +
                method_names.each do |method_name|
         | 
| 115 | 
            +
                  original_method_name = :"_mt_original_#{method_name}"
         | 
| 116 | 
            +
                  klass.class_eval <<-CODE, __FILE__, __LINE__ + 1
         | 
| 117 | 
            +
                    alias_method :#{original_method_name}, :#{method_name}
         | 
| 118 | 
            +
                    def #{method_name}(*args, &block)
         | 
| 119 | 
            +
                      if MultiTenant.multi_tenant_model_for_table(#{owner}.class.table_name).present? && #{owner}.persisted? && MultiTenant.current_tenant_id.nil?
         | 
| 120 | 
            +
                        MultiTenant.with(#{owner}.public_send(#{owner}.class.partition_key)) { #{original_method_name}(*args) }
         | 
| 121 | 
            +
                      else
         | 
| 122 | 
            +
                        #{original_method_name}(*args)
         | 
| 123 | 
            +
                      end
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
                  CODE
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
             | 
| 112 129 | 
             
              # Preserve backward compatibility for people using .with_id
         | 
| 113 130 | 
             
              singleton_class.send(:alias_method, :with_id, :with)
         | 
| 114 131 |  | 
| @@ -305,7 +305,7 @@ module ActiveRecord | |
| 305 305 | 
             
                            model_right = MultiTenant.multi_tenant_model_for_table(relation_left.table_name)
         | 
| 306 306 | 
             
                            model_left = MultiTenant.multi_tenant_model_for_table(relation_right.table_name)
         | 
| 307 307 | 
             
                            if model_right && model_left
         | 
| 308 | 
            -
                              join_enforcement_clause = MultiTenant::TenantJoinEnforcementClause.new( | 
| 308 | 
            +
                              join_enforcement_clause = MultiTenant::TenantJoinEnforcementClause.new(relation_right[model_right.partition_key], relation_left)
         | 
| 309 309 | 
             
                              node_join.right.expr = node_join.right.expr.and(join_enforcement_clause)
         | 
| 310 310 | 
             
                            end
         | 
| 311 311 | 
             
                          end
         | 
| @@ -33,6 +33,22 @@ module Sidekiq::Middleware::MultiTenant | |
| 33 33 | 
             
              end
         | 
| 34 34 | 
             
            end
         | 
| 35 35 |  | 
| 36 | 
            +
            Sidekiq.configure_server do |config|
         | 
| 37 | 
            +
              config.server_middleware do |chain|
         | 
| 38 | 
            +
                chain.add Sidekiq::Middleware::MultiTenant::Server
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
              config.client_middleware do |chain|
         | 
| 41 | 
            +
                chain.add Sidekiq::Middleware::MultiTenant::Client
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            Sidekiq.configure_client do |config|
         | 
| 46 | 
            +
              config.client_middleware do |chain|
         | 
| 47 | 
            +
                chain.add Sidekiq::Middleware::MultiTenant::Client
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
            end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
             | 
| 36 52 | 
             
            module Sidekiq
         | 
| 37 53 | 
             
              class Client
         | 
| 38 54 | 
             
                def push_bulk_with_tenants(items)
         | 
| @@ -10,4 +10,3 @@ require_relative 'activerecord-multi-tenant/query_rewriter' | |
| 10 10 | 
             
            require_relative 'activerecord-multi-tenant/query_monitor'
         | 
| 11 11 | 
             
            require_relative 'activerecord-multi-tenant/version'
         | 
| 12 12 | 
             
            require_relative 'activerecord-multi-tenant/with_lock'
         | 
| 13 | 
            -
            require_relative 'activerecord-multi-tenant/persistence_extension'
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe MultiTenant, 'Association methods' do
         | 
| 4 | 
            +
              let(:account1) { Account.create! name: 'test1' }
         | 
| 5 | 
            +
              let(:account2) { Account.create! name: 'test2' }
         | 
| 6 | 
            +
              let(:project1) { Project.create! name: 'something1', account: account1 }
         | 
| 7 | 
            +
              let(:project2) { Project.create! name: 'something2', account: account2, id: project1.id }
         | 
| 8 | 
            +
              let(:task1) { Task.create! name: 'task1', project: project1, account: account1 }
         | 
| 9 | 
            +
              let(:task2) { Task.create! name: 'task2', project: project2, account: account2, id: task1.id }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              context 'include the tenant_id in queries and' do
         | 
| 12 | 
            +
                it 'creates a task with correct account_id' do
         | 
| 13 | 
            +
                  expect(project2.tasks.create(name: 'task3').account_id).to eq(account2.id)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
                it 'return correct account_id' do
         | 
| 16 | 
            +
                  expect(task1.project.account_id).to_not eq(task2.project.account_id) # belongs_to
         | 
| 17 | 
            +
                  expect(project2.tasks.count).to eq(1)
         | 
| 18 | 
            +
                  expect(project2.tasks.first.account_id).to eq(account2.id) # has_many
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -70,6 +70,24 @@ describe MultiTenant do | |
| 70 70 | 
             
                it { expect(@partition_key_not_model_task.non_model_id).to be 77 }
         | 
| 71 71 | 
             
              end
         | 
| 72 72 |  | 
| 73 | 
            +
             | 
| 74 | 
            +
              describe 'Tenant model with a nonstandard class name' do
         | 
| 75 | 
            +
                let(:account_klass) do
         | 
| 76 | 
            +
                  Class.new(ActiveRecord::Base) do
         | 
| 77 | 
            +
                    self.table_name = 'account'
         | 
| 78 | 
            +
                    def self.name
         | 
| 79 | 
            +
                      'UserAccount'
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    multi_tenant(:account)
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
                it "does not register the tenant model" do
         | 
| 86 | 
            +
                  expect(MultiTenant).not_to receive(:register_multi_tenant_model)
         | 
| 87 | 
            +
                  account_klass
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
             | 
| 73 91 | 
             
              describe 'Changes table_name after multi_tenant called' do
         | 
| 74 92 | 
             
                before do
         | 
| 75 93 | 
             
                  account_klass.has_many(:posts, anonymous_class: post_klass)
         | 
| @@ -186,11 +204,7 @@ describe MultiTenant do | |
| 186 204 | 
             
                end
         | 
| 187 205 |  | 
| 188 206 | 
             
                it 'handles belongs_to with optional: true' do
         | 
| 189 | 
            -
                   | 
| 190 | 
            -
                    sub_task
         | 
| 191 | 
            -
                  end
         | 
| 192 | 
            -
             | 
| 193 | 
            -
                  record = sub_task.optional_sub_tasks.create!
         | 
| 207 | 
            +
                  record = OptionalSubTask.create(sub_task_id: sub_task.id)
         | 
| 194 208 | 
             
                  expect(record.reload.sub_task).to eq(sub_task)
         | 
| 195 209 | 
             
                  expect(record.account_id).to eq(nil)
         | 
| 196 210 | 
             
                end
         | 
| @@ -400,10 +414,10 @@ describe MultiTenant do | |
| 400 414 |  | 
| 401 415 | 
             
              it "applies the team_id conditions in the where clause" do
         | 
| 402 416 | 
             
                option1 = <<-sql.strip
         | 
| 403 | 
            -
                  SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND " | 
| 417 | 
            +
                  SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "tasks"."account_id" = "sub_tasks"."account_id" WHERE "tasks"."project_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."account_id" = 1
         | 
| 404 418 | 
             
                sql
         | 
| 405 419 | 
             
                option2 = <<-sql.strip
         | 
| 406 | 
            -
                  SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND " | 
| 420 | 
            +
                  SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "tasks"."account_id" = "sub_tasks"."account_id" WHERE "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = 1 AND "tasks"."account_id" = 1
         | 
| 407 421 | 
             
                sql
         | 
| 408 422 |  | 
| 409 423 | 
             
                account1 = Account.create! name: 'Account 1'
         | 
| @@ -418,7 +432,7 @@ describe MultiTenant do | |
| 418 432 |  | 
| 419 433 | 
             
                MultiTenant.without do
         | 
| 420 434 | 
             
                  expected_sql = <<-sql
         | 
| 421 | 
            -
                                    SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND " | 
| 435 | 
            +
                                    SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "tasks"."account_id" = "sub_tasks"."account_id" WHERE "tasks"."project_id" = 1
         | 
| 422 436 | 
             
                                 sql
         | 
| 423 437 |  | 
| 424 438 | 
             
                  project = Project.first
         | 
| @@ -456,7 +470,7 @@ describe MultiTenant do | |
| 456 470 | 
             
                  expect(project.categories).to include(category1)
         | 
| 457 471 |  | 
| 458 472 | 
             
                  expected_sql = <<-sql
         | 
| 459 | 
            -
                                    SELECT "projects".* FROM "projects" INNER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND " | 
| 473 | 
            +
                                    SELECT "projects".* FROM "projects" INNER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = "project_categories"."account_id" INNER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
         | 
| 460 474 | 
             
                                sql
         | 
| 461 475 |  | 
| 462 476 | 
             
                  expect(Project.where(account_id: 1).joins(:categories).to_sql).to eq(expected_sql.strip)
         | 
| @@ -490,7 +504,7 @@ describe MultiTenant do | |
| 490 504 |  | 
| 491 505 | 
             
                MultiTenant.without do
         | 
| 492 506 | 
             
                  expected_sql = <<-sql
         | 
| 493 | 
            -
                                 SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND " | 
| 507 | 
            +
                                 SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = "project_categories"."account_id" LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
         | 
| 494 508 | 
             
                                 sql
         | 
| 495 509 |  | 
| 496 510 | 
             
                  expect(Project.where(account_id: 1).eager_load(:categories).to_sql).to eq(expected_sql.strip)
         | 
| @@ -522,7 +536,7 @@ describe MultiTenant do | |
| 522 536 |  | 
| 523 537 | 
             
                MultiTenant.without do
         | 
| 524 538 | 
             
                  expected_sql = <<-sql
         | 
| 525 | 
            -
                                 SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND " | 
| 539 | 
            +
                                 SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "tasks"."account_id" = "projects"."account_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
         | 
| 526 540 | 
             
                                 sql
         | 
| 527 541 |  | 
| 528 542 | 
             
                  expect(Task.where(account_id: 1).joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(expected_sql.strip)
         | 
| @@ -54,6 +54,25 @@ describe MultiTenant, 'Record modifications' do | |
| 54 54 | 
             
                end
         | 
| 55 55 | 
             
              end
         | 
| 56 56 |  | 
| 57 | 
            +
              it 'should not update other objects with same id when calling object.update_columns' do
         | 
| 58 | 
            +
                # When two records with same id but different account_id are updated, it should only update the current one
         | 
| 59 | 
            +
                expect(project.account).to eq(account)
         | 
| 60 | 
            +
                expect(project2.account).to eq(account2)
         | 
| 61 | 
            +
                expect(project.id).to eq(project2.id)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                MultiTenant.without do
         | 
| 64 | 
            +
                  project2.update_columns(name: 'newthing2')
         | 
| 65 | 
            +
                  expect(project.reload.name).to eq('something')
         | 
| 66 | 
            +
                  expect(project2.reload.name).to eq('newthing2')
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              it 'should return the same object when calling object.reload' do
         | 
| 71 | 
            +
                # When two records with same id but different account_id are updated, it should not return the other object
         | 
| 72 | 
            +
                expect(project.reload.account_id).to eq(account.id)
         | 
| 73 | 
            +
                expect(project2.reload.account_id).to eq(account2.id)
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 57 76 | 
             
              it 'test delete for reference tables' do
         | 
| 58 77 | 
             
                category1 = Category.create! name: 'Category 1'
         | 
| 59 78 | 
             
                expect(Category.count).to eq(1)
         | 
    
        data/spec/schema.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: activerecord-multi-tenant
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.1.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Citus Data
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022- | 
| 11 | 
            +
            date: 2022-10-20 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rails
         | 
| @@ -172,12 +172,12 @@ files: | |
| 172 172 | 
             
            - lib/activerecord-multi-tenant/migrations.rb
         | 
| 173 173 | 
             
            - lib/activerecord-multi-tenant/model_extensions.rb
         | 
| 174 174 | 
             
            - lib/activerecord-multi-tenant/multi_tenant.rb
         | 
| 175 | 
            -
            - lib/activerecord-multi-tenant/persistence_extension.rb
         | 
| 176 175 | 
             
            - lib/activerecord-multi-tenant/query_monitor.rb
         | 
| 177 176 | 
             
            - lib/activerecord-multi-tenant/query_rewriter.rb
         | 
| 178 177 | 
             
            - lib/activerecord-multi-tenant/sidekiq.rb
         | 
| 179 178 | 
             
            - lib/activerecord-multi-tenant/version.rb
         | 
| 180 179 | 
             
            - lib/activerecord-multi-tenant/with_lock.rb
         | 
| 180 | 
            +
            - spec/activerecord-multi-tenant/associations_spec.rb
         | 
| 181 181 | 
             
            - spec/activerecord-multi-tenant/controller_extensions_spec.rb
         | 
| 182 182 | 
             
            - spec/activerecord-multi-tenant/fast_truncate_spec.rb
         | 
| 183 183 | 
             
            - spec/activerecord-multi-tenant/model_extensions_spec.rb
         | 
| @@ -1,13 +0,0 @@ | |
| 1 | 
            -
            module ActiveRecord
         | 
| 2 | 
            -
              module Persistence
         | 
| 3 | 
            -
                alias :delete_orig :delete
         | 
| 4 | 
            -
             | 
| 5 | 
            -
                def delete
         | 
| 6 | 
            -
                  if MultiTenant.multi_tenant_model_for_table(self.class.table_name).present? && persisted? && MultiTenant.current_tenant_id.nil?
         | 
| 7 | 
            -
                    MultiTenant.with(self.public_send(self.class.partition_key)) { delete_orig }
         | 
| 8 | 
            -
                  else
         | 
| 9 | 
            -
                    delete_orig
         | 
| 10 | 
            -
                  end
         | 
| 11 | 
            -
                end
         | 
| 12 | 
            -
              end
         | 
| 13 | 
            -
            end
         |