Taming Administrate — A gentle monkey patch

2023-12-22 3 min

Administrate is a framework made for Rails that allows to build admin dashboards. One of the most interesting aspects of Administrate is the customization features. We’re able to customize the dashboards, the views, add attribute partials.

Administrate also allows us to extract the templates so that we can adapt the code to our needs. This is actually good when the changes are related to the style or structure of the page. But I have seen many times extracting views only for changes related to attribute manipulation such as normalizing data or just filtering which attributes are displayed.

There is no need to extract and customize the view for data manipulation. We can recur to a gentle monkey patch.


Administrate uses the following classes to display data according to the page template type.

When thinking about monkey patching what usually comes to mind is opening the class and changing the code. In my case, I just want to remove some attributes from the show page, so it will look like this.

Administrate::Page::Show.class_eval do
  def initialize(dashboard, resource, rejected_attributes)
    super(dashboard)
    @resource = resource
    @rejected_attributes = rejected_attributes
  end	

  def attributes
    dashboard.show_page_attributes.map do |attr_name|
      attribute_field(dashboard, resource, attr_name, :show)
    end.reject do |attr|
      @rejected_attributes.include?(attr.name)
    end
  end
end

There are many reasons why this is not a good approach, but I won’t list them all here. Let’s just say that we don’t know how these changes will affect the rest of the library.

💡 If you’re interested in learning more, Eileen Uchitelle has a great article against monkey patching.

This particular example will end up with the “wrong number of arguments” error when opening the corresponding model show page.

This time the catch was quick and easy enough to know what the problem is and make a fix by adding a default value to that 3rd argument in the constructor signature.

def initialize(dashboard, resource, rejected_attributes = [])

So instead of using brute force, a cleaner (and wiser) method would be to extend the Administrate::Page::Show class more or less as follows.

module Administrate
  module Page
    class CustomShow < Administrate::Page::Show
      def initialize(dashboard, resource, rejected_attributes = [])
        super(dashboard, resource)
        @rejected_attributes = rejected_attributes
      end

      def attributes
        super.reject do |attr|
          @rejected_attributes.include?(attr.name)
        end
      end
    end
  end
end

Notice the call to super? Now we are making use of the power of OOP: inheritance. There is no need to modify the internals of administrate.

This code can be saved at lib/administrate/page/custom_show.rb. I’m using the lib folder because I want to keep framework and application changes separate.

Now this new class can be used in the model controller as shown below without the need to extract the view or resort to a monkey patch.

module Admin
  class WhateverController < Admin::ApplicationController
    def show
      page = Administrate::Page::CustomShow.new(
        dashboard,
        requested_resource,
        hidden_attributes
      )

      render(locals: { page: page })
    end

    private

    def hidden_attributes
      return [] if super_role?

      ['foo', 'bar']
    end
  end
end

Voilà! Much more elegant and maintainable.

This is how to make a gentle monkey patch for administrate. Of course, this is a specific example, but I hope you get the gist.