<commit_before>require "active_support"

module Undo
  module Serializer
    class ActiveModel
      def initialize(*args)
        options = args.extract_options!
        @serializer = args.first
        @serializer_source = options.fetch :serializer,
          ->(object) { object.active_model_serializer.new object }
      end

      def serialize(object)
        serializer(object).as_json
      end

      def deserialize(hash)
        hash.each do |object_class, data|
          next unless data.is_a? Hash
          data.stringify_keys!

          id = data.fetch "id"
          object_class = object_class.to_s.camelize.constantize
          object = object_class.where(id: id).first || object_class.new(id: id)

          data.each do |field, value|
            next if "id" == field && object.persisted?
            _, association = field.to_s.split("___")
            if association
              deserialize_association(association, value)
            else
              object.send "#{field}=", value # not public_send!
            end
          end

          object.save!
          return object
        end
      end

      private
      attr_reader :serializer_source

      def serializer(object)
        @serializer ||= serializer_source.call object
      end

      def deserialize_association(association, values)
        Array.wrap(values).each do |value|
          deserialize(association.singularize => value)
        end
      end
    end
  end<commit_msg>wrap in transaction<commit_after>require "active_support"

module Undo
  module Serializer
    class ActiveModel
      def initialize(*args)
        options = args.extract_options!
        @serializer = args.first
        @serializer_source = options.fetch :serializer,
          ->(object) { object.active_model_serializer.new object }
      end

      def serialize(object)
        serializer(object).as_json
      end

      def deserialize(hash)
        object_name, data = hash.first
        return unless data.is_a? Hash
        data.stringify_keys!

        ActiveRecord::Base.transaction do
          initialize_object(object_name, data).tap do |object|
            data.each do |field, value|
              next if "id" == field && object.persisted?

              case field
              when /___(.*)/ then deserialize_association object, $1,    value
              else                deserialize_field       object, field, value
              end
            end

            object.save!
          end
        end

      end

      private
      attr_reader :serializer_source

      def serializer(object)
        @serializer ||= serializer_source.call object
      end

      def deserialize_association(object, association, values)
        Array.wrap(values).each do |value|
          deserialize association.singularize => value
        end
      end

      def deserialize_field(object, field, value)
        object.send "#{field}=", value # not public_send!
      end

      def initialize_object(object_name, data)
        id = data.fetch "id"
        object_class = object_name.to_s.camelize.constantize
        object_class.where(id: id).first || object_class.new(id: id)
      end
    end
  end
{'fraction_non_alphanumeric': 0.07846201938105658, 'fraction_numerical': 0.00031259768677711783, 'mean_word_length': 1.962962962962963, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '4920378', 'n_tokens_mistral': 881, 'n_tokens_neox': 858, 'n_words': 211}