controlshift/prague-server

View on GitHub
app/actions/queries/export.rb

Summary

Maintainability
A
55 mins
Test Coverage
require 'csv'

module Queries
  class Export < ApplicationAction
    def record_set
      @record_set ||= ActiveRecord::Base.connection.execute(sql)
    end

    def first_row
      @first_row ||= ActiveRecord::Base.connection.execute(sql + " LIMIT 1").to_a.first
    end

    def total_rows
      @total_rows ||= ActiveRecord::Base.connection.execute("select count(*) from (#{sql}) tmp").first['count'].to_i
    end

    def find_in_batches(options = {})
      batch_size = options[:batch_size] || 10000
      primary_key_offset = options[:primary_key_offset] || 0

      records = records_for_batch(batch_size, primary_key_offset)

      while records.any?
        primary_key_offset = records.last['id']

        yield records

        break if records.size < batch_size

        if primary_key_offset
          records = records_for_batch(batch_size, primary_key_offset)
        else
          raise "Primary key not included in the custom select clause"
        end
      end
    end

    def records_for_batch(batch_size, primary_key_offset)
      ActiveRecord::Base.connection.execute(sql_for_batch(batch_size, primary_key_offset)).to_a
    end

    def sql_for_batch(batch_size, primary_key_offset)
      ActiveRecord::Base.send(:sanitize_sql_array,
        ["#{sql} AND #{klass.table_name}.id > :primary_key_offset
          ORDER BY #{klass.table_name}.id LIMIT :batch_size",
          primary_key_offset: primary_key_offset, batch_size: batch_size
        ])
    end


    def as_csv_stream
      Enumerator.new do |response_blob|
        if first_row.present?

          # insert the first header row
          response_blob << CSV.generate do |csv|
            csv << header_row
          end

          find_in_batches do |batch|
            response_blob << CSV.generate do |csv|
              batch.each do |row|
                csv << generate_row(row)
              end
            end
          end
        end
      end
    end

    def header_row
      first_row.keys
    end

    def generate_row(row)
      columns = row.keys
      columns.collect do |c|
        row[c]
      end
    end
  end
end