Experiments with Active Admin
Last Updated on: July 22, 2020
Given the huge popularity of Active Admin, and its nice looking UI, it came first on our list. But in order for it to suffice, there were certain things that it needed to be able to do. Specifically:
1. A lot of our models have state machine.
2. We would want the state machine events to turn up on the index page (besides the “View”, “Edit”, “Delete” options.)
For eg: If an “Invitation” model has events like “invite”, “resend_invite” as events, then we would want them to appear as links.
3. We wanted to keep this DRY and not repeat the code for state machines everywhere. So we needed someway to keep the piece that adds state machine events central and DRY (Note: It is easy to get the state machine events for every model, and thus the code to add links would remain the same for every model)
Active Scaffold had been pretty kind to us and was allowing us to do all the above.
Now ActiveAdmin too has a pretty neat DSL, so you can basically do something like this:
link_to "invite", .. +
link_to "resend_invite", .. +
Our initial thought was to just register a method with ActiveAdmine::ResourceDSL and then call that.
So in lib/hook_state_machine_event.rb
1: module ActiveAdmine
2: module HookStateMachineEvents
3: def hook_state_machine_events
4: index do
5: // the code that pull the state machine events and adds action links.
and in config/initialize/active_admin_extension.rb:
1: ActiveAdmine::ResourceDSL.send(:include, ActiveAdmine::HookStateMachineEvents)
and then in app/controller/admin/invitation.rb:
1: index do
2: column :email
3: column :first_name
This is when things got interesting!
ActiveAdmin ONLY takes the first block passed to the index method and ignores any other. So basically, you can not have a different index block for each model, and then hook the central thing in just one place, as there can be only ONE index block per model.
What that means is that the block that we wanted to get executed in lib/hook_state_machine_event.rb in would never be called.
This technique had worked pretty well for us with Active Scaffold as it’s config objects were arrays that you could easily add to from outside the actual controller code, but Active Admin needed a different treatment.
Since we can not append to the block from outside, we figured that we needed to define our own DSL and register that with Active Admin. And then in our DSL, we need to add the common code, append that to the block that is being passed from each controller and then pass this new appended block to Active Admin’s DSL.
Step 1: Registering our own DSL with Active Admin:
: ActiveAdmin::ResourceDSL.send(:include, ActiveAdmin::StateMachineEvents)
Step 2: Define your own DSL in that module lib/active_admin/state_machine_events.rb
1: require 'active_support/concern'
2: module ActiveAdmin
3: module StateMachineSecuredEvents
4: extend ActiveSupport::Concern
5: included do
6: def custom_index &block
7: common_proc = generate_common_proc()
8: c = Proc.new do
9: instance_exec &block
10: instance_exec &common_proc
12: index &c
In the above code, line 6 defines our custom DSL, that Active Admin now recognizes. Line 7 gets the common proc that we would need (the generate_common_proc is a function call that has the business logic for generating the block needed).
Line 8 – 11 adds our common proc with whatever is being passed to the custom_index and line 12 finally calls Active Admin’s default index with the modified block.
The way, this would be used in a controller would be something like this:
1: ActiveAdmin.register Invitation, as: "Invitation" do
2: custom_index do
4: column :email
5: column :last_name
8: filter :email
9: filter :last_name
While not very clean, and definitely brittle than the DSLs that we had for Active Scaffold it got our goal accomplished!
Stay tuned with Systango for more such insightful posts!