Admin Panels In Ruby On Rails

Using Devise & ActiveAdmin

What are we going to build?

Before I Start

Why ActiveAdmin?

  • It gives you a lot out the box (e.g. search & model relationships)
  • Easy DSL that is very configurable
  • Really compliments good model design
  • Makes concepts like CRUD & Decorators a bit easier to understand
  • Please don't custom code an admin interface!

Installing

Add to Gemfile:

$ bundle add devise
$ rails generate devise:install
$ bundle add activeadmin
$ rails generate active_admin:install --use_webpacker

Why AdminUser?

I've worked on few applications which used a User model for their main app and their admin panels.

It always felt like a messy experience, where every route could return to much data:

# app/controllers/posts_controller.rb

if current_user.admin? 
  @posts = Post.all
else
  @posts = current_user.posts
end

Having a "You manage the app over here" lead to simpler code.

Adding a resource

$ rails generate

ActiveAdmin:
  active_admin:assets
  active_admin:devise
  active_admin:install
  active_admin:page  active_admin:resource  active_admin:webpacker
$ rails generate active_admin:resource Author
$ rails generate active_admin:resource Post
$ rails generate active_admin:resource Category

Customising index

ActiveAdmin.register Post do
  index do
    selectable_column
    column :id
    column :title
    column :published?
    column :author
    column :created_at
    actions
  end
end

Changing the filters & scopes

ActiveAdmin.register Post do
  filter :title
  filter :author
  filter :created_at
  filter :categories

  scope :published
end

Adding a custom action

ActiveAdmin.register Post do
  member_action :publish, method: :put do
    resource.publish!
    redirect_to resource_path, notice: "Published!"
  end

  action_item :publish, only: :show, if: proc { !resource.published? } do
    link_to 'Publish', [:publish, :admin, resource], method: :put
  end
end

Customising the form

ActiveAdmin.register Post do
  permit_params :title, :body, :author_id, category_ids: []

  form do |f|
    f.inputs :title, :body, :author
    f.inputs "Categories" do
      f.input :categories, as: :check_boxes
    end
    actions
  end
end

Customising Show view

ActiveAdmin.register Post do
  show do
    attributes_table do
      row :title
      row :body
      row :author
      row :created_at
      row :updated_at
      row :published_at
      row :categories
    end
    active_admin_comments
  end
end

Using a decorator

$ bundle add draper
$ rails generate draper:install
$ rails generate decorator Post
# app/decorators/post_decorator.rb
class PostDecorator < ApplicationDecorator
  delegate_all

  def body
    helpers.simple_format(object.body)
  end
end

Using a decorator

ActiveAdmin.register Post do
  decorate_with PostDecorator
end

Performance - Eager Loading

ActiveAdmin.register Post do
  # Reduce N+1s (Use with caution)
  includes :author
end

Performance - Reducing Memory

# config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
  # == Filters
  config.include_default_association_filters = false

  # Or

  config.maximum_association_filter_arity = 12
end

Performance - Reducing Memory

ActiveAdmin.register Post do
  # filter :author
  filter :author_name, as: :string
end

Resources

Questions?

MikeRogers.io
@MikeRogers0 on Twitter

Please remember to Like/Comment/Subscribe!

I really like it

Make sure to use an AdminUser

Because separating your code is good.

Comes with a way to generate paghes

Eager loading is a good way to reduce N+1s, though use with caution, if you eager load to much your memory usage will grow. I normally only eager load "belongs_to" type relationships

Filter with strings, not selects

Filter with strings, not selects

Filter with strings, not selects