<commit_before>module Merger
  class Merge
    attr_reader :keep, :duplicates, :options
  
    def initialize(*records)
      @options = records.extract_options!
      records = records.flatten.uniq
      @keep = options[:keep] || records.sort_by(&:id).first
      @duplicates = records - [@keep]
    end
  
    def ignored_associations
      return @ignored if @ignored
      @ignored = Array(options[:skip_association])
      keep.class.reflect_on_all_associations.each do |association|
        @ignored << association.through_reflection.name if association.through_reflection
      end
      @ignored
    end

    def associations!
      keep.class.reflect_on_all_associations.each do |association|
        duplicates.each do |record|
          next if ignored_associations.include?(association.name)
          case association.macro
          when :has_many, :has_and_belongs_to_many
            name = "#{association.name.to_s.singularize}_ids"
            keep.send("#{name}=", keep.send(name) | record.send(name))
          when :belongs_to, :has_one
            keep.send("#{association.name}=", record.send(association.name)) if keep.send("#{association.name}").nil?
          end
        end
      end
    end
    
    def merge!
      associations!
      duplicates.each(&:destroy)
    end
  
  end
end<commit_msg>wrap merge! in a transaction<commit_after>module Merger
  class Merge
    attr_reader :keep, :duplicates, :options
  
    def initialize(*records)
      @options = records.extract_options!
      records = records.flatten.uniq
      @keep = options[:keep] || records.sort_by(&:id).first
      @duplicates = records - [@keep]
    end
  
    def ignored_associations
      return @ignored if @ignored
      @ignored = Array(options[:skip_association])
      keep.class.reflect_on_all_associations.each do |association|
        @ignored << association.through_reflection.name if association.through_reflection
      end
      @ignored
    end

    def associations!
      keep.class.reflect_on_all_associations.each do |association|
        duplicates.each do |record|
          next if ignored_associations.include?(association.name)
          case association.macro
          when :has_many, :has_and_belongs_to_many
            name = "#{association.name.to_s.singularize}_ids"
            keep.send("#{name}=", keep.send(name) | record.send(name))
          when :belongs_to, :has_one
            keep.send("#{association.name}=", record.send(association.name)) if keep.send("#{association.name}").nil?
          end
        end
      end
    end
    
    def merge!
      keep.class.transaction do
        associations!
        duplicates.each(&:destroy)
      end
    end
  
  end
end
{'fraction_non_alphanumeric': 0.10291405385466618, 'fraction_numerical': 0.0, 'mean_word_length': 2.606382978723404, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5961155', 'n_tokens_mistral': 821, 'n_tokens_neox': 820, 'n_words': 162}