Please be careful the cake is a lie

Sharing code between ActiveAdmin resources

Recently I’ve been working with ActiveAdmin a lot, since we use it as an application framework to build an internal admin-like tool. It serves us very well most of the time but the level of magic it introduces is considerable.

The other day I had to implement the same functionality for two resources1 and as every well-groomed Rails developer would want it, I wanted it DRY. Like super dry.

I created a new ActiveSupport::Concern and dropped it in app/admin/concerns and of course it didn’t work. I tried the obvious way to include it but it turns out that ActiveAdmin resources are a special kind of animals.

One does not simply include a module in an ActiveAdmin resource

The ActiveAdmin.register block runs in the context of an instance of ActiveAdmin::ResourceDSL and somehow the included block threw a MultipleIncludedBlocks error. I couldn’t figure out why, but if include is not working we just try extend and bingo! it works like a charm.

I ended up with something like this:

# in app/admin/concerns/confirmable.rb
module Confirmable
  def self.extended(base)
    base.instance_eval do
      action_item :confirm do
        link_to 'Confirm', url_for(action: :confirm)
      end

      # omitting other gory details for brevity
    end
  end
end

And then

# in app/admin/post.rb
ActiveAdmin.register Post do
  extend Confirmable
end

This worked pretty well on my local machine but then I pushed it to origin and the CI build blew up. It could not load the Confirmable module. I was baffled because according to the source code ActiveAdmin simply loads everything in the app/admin folder. I thought maybe there is more to code loading in ruby that I know of. So I ran off and read some pretty good articles about the topic:

These were very good but ultimately none of them helped. I moved the confirmable.rb file around but nothing seemed to help2. And the most infuriating part was that I could not reproduce it on my local machine. It didn’t matter which environment I used, which gems were installed.

As my last resort I turned to the GitHub issues page of ActiveAdmin: there must have been somebody else who wanted to do the same thing. And yes! finally something usable. It turns out that you should not put your modules into app/admin, so I ended up creating an app/admin_shared folder. It’s not particularly nice but at least it works.

UPDATE: It was brought to my attention that include does actually work, but it is another piece of ActiveAdmin magic. You cannot use an ActiveSupport::Concern, you have to roll your own self.included hook.

  1. If you are not familiar with ActiveAdmin, a resource is basically an autogenerated controller for a model.

  2. Interestingly the where to put things question always comes back to haunt you when you work with Rails.