Sharing code between ActiveAdmin resources22 February 2015
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.
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
# 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:
- Ways to load code
- Rails autoloading — how it works, and when it doesn’t
- 5 Reasons to Avoid Bundler.require
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