ActiveAdmin gem is a popular tool for building admin interfaces in Ruby on Rails apps. In this tutorial, I will describe a couple of less obvious tips and performance optimization techniques.
Active Admin should probably never be used for client-facing parts of the interface because it’s a bit clunky. But it can hardly be matched for an internal admin user interface development speed and simplicity.
For a great intro about how to start using ActiveAdmin with modern Rails check out this article and the official docs.
Here comes the first tip:
Add query persistence to filters
Filters are one of my favorite features of Active Admin. They let you mix various search conditions with a simple UI. One issue with default implementation is that the search query is not persistent. Every time you click away to a different page, you need to do a new search from scratch.
Filters UX can be improved by adding the following files:
app/config/initializers/aa_filters_persistance.rb
Also, add the following code to app/assets/javascripts/active_admin.js
Next just add this code at the bottom of config/initializers/active_admin.rb
With that in place, your filter queries will be persisted in session on a per page basis, making navigating the admin panel more pleasant.
Normalize blank attributes
Active Admin uses Formtastic under the hood. There is a known issue with blank values populating your models after submitting a form with empty fields.
If your model does not validate a presence of data, you might end up with several attributes set to an empty string ""
because that’s what is sent from an empty form field.
There is a simple way to define declarative API for keeping your attributes in a correct state. It uses Active Record callbacks under the hood, but the despite all the hate they get, I think normalizing model attributes can be a valid use case for them. I can recommend this blog post for an interesting write up on potential callback use cases.
To avoid blank ghost attributes you need to add the following module:
config/initializers/normalize_blank_values.rb
and then include and setup it in your model:
app/models/user.rb
Unless you explicitly bypass callbacks, you should not see empty string instead of nil
again.
Watch out for slow filters
Talking about filters, their default implementation in Active Admin can slow your Rails app to a crawl. The problem is that Active Admin creates a select filter for all the has_many
relation on a model. So if a user has_many
posts, /admin/users
view will display select box, rendering data of ALL the posts present in the database.
This issue can easily be overlooked when starting to work on an app, and your dataset is still small. Only after a while, you might begin to notice delays, memory issues or even server timeouts.
A simple way to significantly reduce memory usage is to pluck the necessary attributes from the collection, to avoid instantiating the full-blown Active Record objects:
in app/admin/users.rb
This tip should be applied to all the collection
values in your Active Admin Formtastic forms and views.
In case your collections have more than couple thousand objects you should consider adding autocomplete powered by JSON endpoint, but that’s outside the scope of this tutorial.
Use custom form views
Rendering custom form views is a powerful way to customize Active Admin interface. Declaring collection, member or batch actions, that render a custom view is quite simple but not mentioned in the official docs. I will cover a batch action example because its most complex.
You need to start by declaring a batch action that renders a view and another one that receives params submitted from the form:
app/admin/users.rb
and adding the view file itself:
app/views/admin/users/bulk_set_email.html.erb
Finally, you need to add a form object that contains the code required to perform the action. Form objects are a good way to avoid cluttering controller DSL with business logic, check out this blog post for more in depth info about them. I’ve recently started following a convention where form object accepts arguments needed to render a form in an initializer. It also has a submit
method doing the actual work done and returning true or false based on whether action succeed.
app/forms/admin/users/bulk_set_email_form.rb
Check out this GIF to see a custom Active Admin form in action.
You could even embed a dynamic React component inside Active Admin using this technique, but that’s a story for another blogpost.
Summary
I’ve worked on a couple of commercial projects where using Active Admin made a lot of sense business-wise. I am aware that many serious developers would frown upon seeing it in their Gemfiles, but I have a feeling this library is not going away any time soon. You can check out this repository to see all the described tips applied to a barebones Rails app.