ActiveAdmin is great framework for managing website administration interfaces. It allows us to list records of a table, filter them, sort them and also support exporting them in CSV, XML or JSON. By default, records that are being displayed are exported. For example, say we have a products table where each product is distinguished by their type. We can apply a filter to list all electronics products. Clicking on CSV link below the list will export all electronics products only.

But sometimes we need to download specific records coming from a custom query or scope using the default csv link. For example, its not possible to filter out electronics and fashion products both. In this case, you would need query or a scope that can filter the records. To export the records, we need to override default index action.

In admin/product.rb -

controller do
  def index
    index! do |format|
      @products = Product.custom_query # or scope

      format.html
      format.csv   { export_csv @products }
      format.json  { render json: @products }
      format.xml   { render xml: @products }
    end
  end
end

Next, we need to add export_csv method which we used above when request format is csv. We can add this in Admin::ProductsController buts its good to add it at a place where it can be used by other admin controllers as well. ActiveAdmin gem has a module Streaming which has method export_csv (same method name but without arguments). We will override this method. Also, note the last line of export_csv method creates a block using self which is an object of controller that accepts the request of downloading CSV. To override the existing export functionality, we need to pass a custom controller (I have named it as DummyController) object that holds the records that are generated by custom query/scope.

In config/initializers/active_admin.rb -

class ActiveAdmin::ResourceController
  module Streaming
    class DummyController
      def initialize(collection:)
        @collection = collection
      end
      attr_reader :collection
    end

    def export_csv(collection)
      controller = DummyController.new(collection: collection)
      headers['Content-Disposition'] = %{attachment; filename="#{csv_filename}"}
      stream_resource &active_admin_config.csv_builder.method(:build).to_proc.curry[controller]
    end
  end
end

We are not done yet. When we try to download CSV using above code it will give an error -

NoMethodError (undefined method 'find_collection' for #<ActiveAdmin::ResourceController::Streaming::DummyController>)

Before we add find_collection method to DummyController to fix above error, lets check out build method here under CSVBuilder class which is called from the block we created above in export_csv method (active_admin_config.csv_builder.method(:build)). The build method iterates over the collection and prepares the CSV. If we look closer in that method, there are three methods that are called from controller object - find_collection, view_context and apply_decorator. We need to add all three the methods in DummyController.

class ActiveAdmin::ResourceController
  module Streaming
    class DummyController
      def initialize(collection:)
        @collection = collection
      end
      attr_reader :collection

      def find_collection(*)
        collection
      end

      def apply_decorator(resource)
        resource
      end

      def view_context
        @view_context ||= ViewContext.new
      end

      class ViewContext
        include MethodOrProcHelper
      end
    end

    def export_csv(collection)
      controller = DummyController.new(collection: collection)
      headers['Content-Disposition'] = %{attachment; filename="#{csv_filename}"}
      stream_resource &active_admin_config.csv_builder.method(:build).to_proc.curry[controller]
    end
  end
end

Now you can download records from a custom query or scope in CSV format.

Note: This code snippet has been tested in Rails 5.0 app having active_admin gem locked at version 1.0.0.pre4. This might or might not work in earlier or newer versions of active_admin gem.