bulk_dependency_eraser 1.0.3 → 1.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
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: cbea15215c816eca6790c1d7c3cbf4f4f68534fc3ff18cc38e8c26dc7e94513d
         | 
| 4 | 
            +
              data.tar.gz: 532a711c1290ea4fea4200acc75d9916eb18a2451207b89d1163372d7372028d
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e5639d23e7fbad812f11db05ad4b4dc3e88cd8823b3623ec38c892ed144ca6002b2e6bf56a826cca62aa2e87312fc676ac0668d1d6301a66496f91a62c7896fe
         | 
| 7 | 
            +
              data.tar.gz: 20f81c4ddae6e875de07849088493f21095a567ccf2e1e39bfe21bbe1486dbfaa76ad06bc53f247724ca0f5e79a7f7af699e480345c2b5a800f617c06bb83844
         | 
| @@ -209,19 +209,24 @@ module BulkDependencyEraser | |
| 209 209 | 
             
                  nullify_association_names    = nullify_associations.map(&:name)
         | 
| 210 210 | 
             
                  restricted_association_names = restricted_associations.map(&:name)
         | 
| 211 211 |  | 
| 212 | 
            -
                  if  | 
| 213 | 
            -
             | 
| 214 | 
            -
             | 
| 215 | 
            -
                    puts " Restricted Associations: #{restricted_association_names}"
         | 
| 216 | 
            -
                  end
         | 
| 217 | 
            -
             | 
| 218 | 
            -
                  # Iterate through the assoc names, if there are any :through assocs, then remap 
         | 
| 212 | 
            +
                  # Iterate through the assoc names, if there are any :through assocs, then rename the association
         | 
| 213 | 
            +
                  # - Rails interpretation of any dependencies of a :through association is to apply it to
         | 
| 214 | 
            +
                  #   the leaf association at the end of the :through chain(s)
         | 
| 219 215 | 
             
                  destroy_association_names = destroy_association_names.collect do |assoc_name|
         | 
| 220 216 | 
             
                    find_root_association_from_through_assocs(klass, assoc_name)
         | 
| 221 217 | 
             
                  end
         | 
| 222 218 | 
             
                  nullify_association_names = nullify_association_names.collect do |assoc_name|
         | 
| 223 219 | 
             
                    find_root_association_from_through_assocs(klass, assoc_name)
         | 
| 224 220 | 
             
                  end
         | 
| 221 | 
            +
                  restricted_association_names = restricted_association_names.collect do |assoc_name|
         | 
| 222 | 
            +
                    find_root_association_from_through_assocs(klass, assoc_name)
         | 
| 223 | 
            +
                  end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                  if opts_c.verbose
         | 
| 226 | 
            +
                    puts "Destroyable Associations: #{destroy_association_names}"
         | 
| 227 | 
            +
                    puts "Nullifiable Associations: #{nullify_association_names}"
         | 
| 228 | 
            +
                    puts " Restricted Associations: #{restricted_association_names}"
         | 
| 229 | 
            +
                  end
         | 
| 225 230 |  | 
| 226 231 | 
             
                  destroy_association_names.each do |destroy_association_name|
         | 
| 227 232 | 
             
                    association_parser(klass, query, query_ids, destroy_association_name, :delete)
         | 
| @@ -255,7 +260,7 @@ module BulkDependencyEraser | |
| 255 260 | 
             
                  when 'ActiveRecord::Reflection::HasManyReflection'
         | 
| 256 261 | 
             
                    association_parser_has_many(parent_class, query, query_ids, association_name, type)
         | 
| 257 262 | 
             
                  when 'ActiveRecord::Reflection::HasOneReflection'
         | 
| 258 | 
            -
                    association_parser_has_many(parent_class, query, query_ids, association_name, type | 
| 263 | 
            +
                    association_parser_has_many(parent_class, query, query_ids, association_name, type)
         | 
| 259 264 | 
             
                  when 'ActiveRecord::Reflection::BelongsToReflection'
         | 
| 260 265 | 
             
                    if type == :nullify
         | 
| 261 266 | 
             
                      report_error("#{parent_class.name}'s association '#{association_name}' - dependent 'nullify' invalid for 'belongs_to'")
         | 
| @@ -342,10 +347,11 @@ module BulkDependencyEraser | |
| 342 347 | 
             
                    assoc_query = assoc_query.where(specified_foreign_key.to_sym => query_ids)
         | 
| 343 348 | 
             
                  end
         | 
| 344 349 |  | 
| 345 | 
            -
                  #  | 
| 346 | 
            -
                  if  | 
| 347 | 
            -
             | 
| 348 | 
            -
                   | 
| 350 | 
            +
                  # Works in theory for ONE record's has_one, but we're dealing with potentially many
         | 
| 351 | 
            +
                  # # Apply limit if has_one assocation (subset of has_many)
         | 
| 352 | 
            +
                  # if opts[:limit]
         | 
| 353 | 
            +
                  #   assoc_query = assoc_query.limit(opts[:limit])
         | 
| 354 | 
            +
                  # end
         | 
| 349 355 |  | 
| 350 356 | 
             
                  if type == :delete
         | 
| 351 357 | 
             
                    # Recursively call 'deletion_query_parser' on association query, to delete any if the assoc's dependencies
         | 
| @@ -2,7 +2,11 @@ module BulkDependencyEraser | |
| 2 2 | 
             
              class Nullifier < Base
         | 
| 3 3 | 
             
                DEFAULT_OPTS = {
         | 
| 4 4 | 
             
                  verbose: false,
         | 
| 5 | 
            -
                  db_nullify_wrapper: self::DEFAULT_DB_WRAPPER
         | 
| 5 | 
            +
                  db_nullify_wrapper: self::DEFAULT_DB_WRAPPER,
         | 
| 6 | 
            +
                  # Set to true if you want 'ActiveRecord::InvalidForeignKey' errors raised during nullifications
         | 
| 7 | 
            +
                  # - I can't think of a use-case where a nullification would generate an invalid key error
         | 
| 8 | 
            +
                  # - Not hurting anything to leave it in, but might remove it in the future.
         | 
| 9 | 
            +
                  enable_invalid_foreign_key_detection: false
         | 
| 6 10 | 
             
                }.freeze
         | 
| 7 11 |  | 
| 8 12 | 
             
                DEFAULT_DB_WRAPPER = ->(block) do
         | 
| @@ -22,6 +26,53 @@ module BulkDependencyEraser | |
| 22 26 | 
             
                def initialize class_names_columns_and_ids:, opts: {}
         | 
| 23 27 | 
             
                  @class_names_columns_and_ids = class_names_columns_and_ids
         | 
| 24 28 | 
             
                  super(opts:)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  if opts_c.verbose
         | 
| 31 | 
            +
                    puts "Combining nullification column groups (if groupable)"
         | 
| 32 | 
            +
                    puts "Before Combination: #{@class_names_columns_and_ids}"
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  @class_names_columns_and_ids = combine_matching_columns(@class_names_columns_and_ids)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  if opts_c.verbose
         | 
| 38 | 
            +
                    puts "After Combination: #{@class_names_columns_and_ids}"
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                # Combine columns if the IDs are the same
         | 
| 43 | 
            +
                # - will do one SQL call instead of several
         | 
| 44 | 
            +
                def combine_matching_columns(nullification_hash)
         | 
| 45 | 
            +
                  return {} if nullification_hash.none?
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  merged_hash = {}
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  nullification_hash.each do |klass_name, columns_and_ids|
         | 
| 50 | 
            +
                    merged_hash[klass_name] = {}
         | 
| 51 | 
            +
                    columns_and_ids.each do |key, array|
         | 
| 52 | 
            +
                      sorted_array = array.sort
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      # Find any existing key in merged_hash that has the same sorted array
         | 
| 55 | 
            +
                      matching_key = merged_hash[klass_name].keys.find { |k| merged_hash[klass_name][k].sort == sorted_array }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                      if matching_key
         | 
| 58 | 
            +
                        # Concatenate the matching keys and update the hash
         | 
| 59 | 
            +
                        new_key = key.is_a?(Array) ? key : [key]
         | 
| 60 | 
            +
                        if matching_key.is_a?(Array)
         | 
| 61 | 
            +
                          new_key += matching_key
         | 
| 62 | 
            +
                        else
         | 
| 63 | 
            +
                          new_key << matching_key
         | 
| 64 | 
            +
                        end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                        merged_hash[klass_name][new_key] = sorted_array
         | 
| 67 | 
            +
                        merged_hash[klass_name].delete(matching_key)
         | 
| 68 | 
            +
                      else
         | 
| 69 | 
            +
                        # Otherwise, just add the current key-value pair
         | 
| 70 | 
            +
                        merged_hash[klass_name][key] = sorted_array
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  merged_hash
         | 
| 25 76 | 
             
                end
         | 
| 26 77 |  | 
| 27 78 | 
             
                def execute
         | 
| @@ -34,15 +85,19 @@ module BulkDependencyEraser | |
| 34 85 | 
             
                        klass = class_name.constantize
         | 
| 35 86 |  | 
| 36 87 | 
             
                        columns_and_ids = class_names_columns_and_ids[class_name]
         | 
| 37 | 
            -
             | 
| 38 88 | 
             
                        columns_and_ids.each do |column, ids|
         | 
| 39 89 | 
             
                          current_column = column
         | 
| 40 | 
            -
             | 
| 41 | 
            -
                           | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 90 | 
            +
             | 
| 91 | 
            +
                          if opts_c.enable_invalid_foreign_key_detection
         | 
| 92 | 
            +
                            # nullify with referential integrity
         | 
| 93 | 
            +
                            nullify_by_klass_column_and_ids(klass, column, ids)
         | 
| 94 | 
            +
                          else
         | 
| 95 | 
            +
                            # nullify without referential integrity
         | 
| 96 | 
            +
                            # Disable any ActiveRecord::InvalidForeignKey raised errors.
         | 
| 97 | 
            +
                            # - src: https://stackoverflow.com/questions/41005849/rails-migrations-temporarily-ignore-foreign-key-constraint
         | 
| 98 | 
            +
                            #        https://apidock.com/rails/ActiveRecord/ConnectionAdapters/AbstractAdapter/disable_referential_integrity
         | 
| 99 | 
            +
                            ActiveRecord::Base.connection.disable_referential_integrity do
         | 
| 100 | 
            +
                              nullify_by_klass_column_and_ids(klass, column, ids)
         | 
| 46 101 | 
             
                            end
         | 
| 47 102 | 
             
                          end
         | 
| 48 103 | 
             
                        end
         | 
| @@ -60,6 +115,23 @@ module BulkDependencyEraser | |
| 60 115 |  | 
| 61 116 | 
             
                attr_reader :class_names_columns_and_ids
         | 
| 62 117 |  | 
| 118 | 
            +
                def nullify_by_klass_column_and_ids klass, columns, ids
         | 
| 119 | 
            +
                  nullify_columns = {}
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  # supporting nullification of groups of columns simultaneously
         | 
| 122 | 
            +
                  if columns.is_a?(Array)
         | 
| 123 | 
            +
                    columns.each do |column|
         | 
| 124 | 
            +
                      nullify_columns[column] = nil
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
                  else
         | 
| 127 | 
            +
                    nullify_columns[columns] = nil
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  nullify_in_db do
         | 
| 131 | 
            +
                    klass.unscoped.where(id: ids).update_all(nullify_columns)
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 63 135 | 
             
                def nullify_in_db(&block)
         | 
| 64 136 | 
             
                  puts "Nullifying from DB..." if opts_c.verbose
         | 
| 65 137 | 
             
                  opts_c.db_nullify_wrapper.call(block)
         |