011 / Rails

ActiveAdmin - Disable JSON/XML export

This 2012 workaround solved a real product and data-exposure problem, but it is also a useful example of when a monkey patch should eventually disappear.

Sometimes you may want to limit ActiveAdmin’s export functionality. In the application that prompted this post, CSV was useful to administrators while JSON and XML were unnecessary surface area.

At the time, I added the following override in a Rails initializer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module ActiveAdmin
  module Views
    class PaginatedCollection
      def build_pagination_with_formats(options)
        div id: "index_footer" do
          build_pagination
          div(
            page_entries_info(options).html_safe,
            class: "pagination_information"
          )
          build_download_format_links([:csv]) unless @download_links == false
        end
      end
    end
  end
end

After adding the initializer, the Rails process needed to be restarted. The allowed formats could then be changed by adjusting:

1
build_download_format_links([:csv])

What the patch actually changed

This was not implementing export logic. It was changing the index footer so that ActiveAdmin only presented links for an explicit list of formats.

That distinction matters. Hiding a UI link is a usability decision, not an authorisation boundary. If exporting data is sensitive, the corresponding controller action and format must also be protected by the application’s authorisation policy. A user who can construct the URL should not gain access merely because the button is absent.

Why monkey patches age badly

The override reopened an internal ActiveAdmin class and replaced part of its rendering behaviour. It was pragmatic, but it coupled the application to private implementation details:

  • an upstream rename could break application start-up;
  • a changed method signature could fail only when the index rendered;
  • upstream fixes to the original method would not automatically reach the replacement;
  • the reason for the patch could become invisible to future maintainers.

When a patch is unavoidable, I now treat it like temporary infrastructure: document the upstream version, link to the missing capability, add a focused test and define the condition that allows its removal.

The supported approach today

Current ActiveAdmin releases document download_links directly on the index page. Restricting one resource to CSV can be expressed without replacing a view class:

1
2
3
ActiveAdmin.register Post do
  index download_links: [:csv]
end

The links can also be disabled, made conditional, or configured for the whole application. The examples are maintained in ActiveAdmin’s current index-page guide.

A global configuration might look like:

1
2
3
ActiveAdmin.setup do |config|
  config.download_links = [:csv]
end

That is a much healthier extension point: the intent is obvious, the framework owns the implementation and upgrades can validate the option through its own test suite.

The lesson beyond ActiveAdmin

The useful part of this old snippet is not the monkey patch itself. It is the sequence of decisions behind it:

  1. Remove formats the user does not need.
  2. Enforce data access independently from presentation.
  3. Prefer a supported configuration surface.
  4. Isolate and test a patch when no supported surface exists.
  5. Delete the patch as soon as the framework provides one.

A short workaround can be entirely reasonable. The long-term risk begins when temporary code loses its history and quietly becomes architecture.