<commit_before>require 'transproc'
require 'transproc/coercions'
require 'transproc/hash'
require 'transproc/array'

require 'rom/processor'

module ROM
  class Processor
    class Transproc < Processor
      attr_reader :header, :model, :mapping, :tuple_proc

      EMPTY_FN = -> tuple { tuple }.freeze

      class Composer
        attr_reader :fns, :default

        def initialize(default = nil)
          @fns = []
          @default = default
        end

        def <<(other)
          fns.concat(Array(other).compact)
        end

        def to_fn
          fns.compact.reduce(:+) || default
        end
      end

      def self.build(header)
        new(header).to_transproc
      end

      def initialize(header)
        @header = header
        @model = header.model
        @mapping = header.mapping
        initialize_tuple_proc
      end

      def to_transproc
        compose(EMPTY_FN) do |ops|
          ops << header.select(&:preprocess?).map { |attr| visit(attr, true) }
          ops << t(:map_array!, tuple_proc) if tuple_proc
        end
      end

      def compose(default = nil, &block)
        composer = Composer.new(default)
        yield(composer)
        composer.to_fn
      end

      private

      def visit(attribute, preprocess = false)
        type = attribute.class.name.split('::').last.downcase
        send("visit_#{type}", attribute, preprocess)
      end

      def visit_attribute(attribute, preprocess = false)
        if attribute.typed?
          t(:map_key!, attribute.name, t(:"to_#{attribute.type}"))
        end
      end

      def visit_hash(attribute, preprocess = false)
        with_tuple_proc(attribute) do |tuple_proc|
          t(:map_key!, attribute.name, tuple_proc)
        end
      end

      def visit_array(attribute, preprocess = false)
        with_tuple_proc(attribute) do |tuple_proc|
          t(:map_key!, attribute.name, t(:map_array!, tuple_proc))
        end
      end

      def visit_wrap(attribute, preprocess = false)
        name = attribute.name
        keys = attribute.header.tuple_keys

        compose do |ops|
          ops << t(:nest, name, keys)
          ops << visit_hash(attribute)
        end
      end

      def visit_group(attribute, preprocess = false)
        if preprocess
          name = attribute.name
          header = attribute.header
          keys = header.tuple_keys
          other = header.select(&:preprocess?)

          compose do |ops|
            ops << t(:group, name, keys)

            ops << other.map { |attr|
              t(:map_array!, t(:map_key!, name, visit_group(attr, true)))
            }
          end
        else
          visit_array(attribute)
        end
      end

      def initialize_tuple_proc
        @tuple_proc = compose do |ops|
          ops << t(:map_hash!, mapping) if header.aliased?
          ops << header.map { |attr| visit(attr) }
          ops << t(-> tuple { model.new(tuple) }) if model
        end
      end

      def with_tuple_proc(attribute)
        tuple_proc = new(attribute.header).tuple_proc
        yield(tuple_proc) if tuple_proc
      end

      def t(*args)
        Transproc(*args)
      end

      def new(*args)
        self.class.new(*args)
      end

    end
  end
end
<commit_msg>Use nest! in transproc processor<commit_after>require 'transproc'
require 'transproc/coercions'
require 'transproc/hash'
require 'transproc/array'

require 'rom/processor'

module ROM
  class Processor
    class Transproc < Processor
      attr_reader :header, :model, :mapping, :tuple_proc

      EMPTY_FN = -> tuple { tuple }.freeze

      class Composer
        attr_reader :fns, :default

        def initialize(default = nil)
          @fns = []
          @default = default
        end

        def <<(other)
          fns.concat(Array(other).compact)
        end

        def to_fn
          fns.compact.reduce(:+) || default
        end
      end

      def self.build(header)
        new(header).to_transproc
      end

      def initialize(header)
        @header = header
        @model = header.model
        @mapping = header.mapping
        initialize_tuple_proc
      end

      def to_transproc
        compose(EMPTY_FN) do |ops|
          ops << header.select(&:preprocess?).map { |attr| visit(attr, true) }
          ops << t(:map_array!, tuple_proc) if tuple_proc
        end
      end

      def compose(default = nil, &block)
        composer = Composer.new(default)
        yield(composer)
        composer.to_fn
      end

      private

      def visit(attribute, preprocess = false)
        type = attribute.class.name.split('::').last.downcase
        send("visit_#{type}", attribute, preprocess)
      end

      def visit_attribute(attribute, preprocess = false)
        if attribute.typed?
          t(:map_key!, attribute.name, t(:"to_#{attribute.type}"))
        end
      end

      def visit_hash(attribute, preprocess = false)
        with_tuple_proc(attribute) do |tuple_proc|
          t(:map_key!, attribute.name, tuple_proc)
        end
      end

      def visit_array(attribute, preprocess = false)
        with_tuple_proc(attribute) do |tuple_proc|
          t(:map_key!, attribute.name, t(:map_array!, tuple_proc))
        end
      end

      def visit_wrap(attribute, preprocess = false)
        name = attribute.name
        keys = attribute.header.tuple_keys

        compose do |ops|
          ops << t(:nest!, name, keys)
          ops << visit_hash(attribute)
        end
      end

      def visit_group(attribute, preprocess = false)
        if preprocess
          name = attribute.name
          header = attribute.header
          keys = header.tuple_keys
          other = header.select(&:preprocess?)

          compose do |ops|
            ops << t(:group, name, keys)

            ops << other.map { |attr|
              t(:map_array!, t(:map_key!, name, visit_group(attr, true)))
            }
          end
        else
          visit_array(attribute)
        end
      end

      def initialize_tuple_proc
        @tuple_proc = compose do |ops|
          ops << t(:map_hash!, mapping) if header.aliased?
          ops << header.map { |attr| visit(attr) }
          ops << t(-> tuple { model.new(tuple) }) if model
        end
      end

      def with_tuple_proc(attribute)
        tuple_proc = new(attribute.header).tuple_proc
        yield(tuple_proc) if tuple_proc
      end

      def t(*args)
        Transproc(*args)
      end

      def new(*args)
        self.class.new(*args)
      end

    end
  end
end

{'fraction_non_alphanumeric': 0.0998162890385793, 'fraction_numerical': 0.0, 'mean_word_length': 1.910022271714922, 'pattern_counts': {'":': 0, '<': 45, '<?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': '4097613', 'n_tokens_mistral': 1950, 'n_tokens_neox': 1876, 'n_words': 464}