Hi! This is Rob, and I work on Kayac for the server-side engineering team. We’re going to add audio and image previews to our Active Admin views so we can see previews of all our media content!
Project repo: https://github.com/Rob117/active-admin-uploads
Why
I was recently working on a project where I needed a portable solution for enabling image and audio previews in active admin. The admins needed to be able to click on any given piece of audio to hear it, or an image preview to see it full size. They also needed this information available in the show, index, and edit views. This is the code that I distilled to perform that function. The full walkthrough is at the bottom.
Note: This code is not very Safari friendly, so I strongly recommend Chrome or Firefox for working with active admin (and also in general).
The Code
Module EnhancedUploader
# We grab the form object, then try to get the specific object we want to
# input to replace. If that object exists, we show it as a hint.
def input_audio(f, property)
f.input property, hint: audio_content(f.object, property)
end
def input_image(f, property)
f.input property, hint: image_content(f.object, property)
end
# We simply grab an object and check to see if our property exists on it.
# If it does, display the object
def audio_content(object, property)
if object.persisted? && object.respond_to?(property)
audio_tag object.send(property), controls: true
else
content_tag(:span, 'No Current Audio')
end
end
def image_content(object, property)
if object.persisted? && object.respond_to?(property)
image_tag object.send(property), height: 150
else
content_tag(:span, 'No Current Image')
end
end
end
Inputs Explanation
First, we’ll start with the input form options:
def input_audio(f, property)
f.input property, hint: audio_content(f.object, property)
end
def input_image(f, property)
f.input property, hint: image_content(f.object, property)
end
So, in order to understand what this code does, you have to understand what a hint is in active admin. When you edit something in active admin, it allows you to give some context as to what that object might be. You typically specify that with the hint: param. For example, you could write some text next to a field called ‘credit card’ on an input form that says ‘application only accepts visa.’, and it would show that next to the input field.
Let us assume we have a user profile picture that we want to display a preview of when we edit it on a form. We also have a audio sample of the user saying ‘hello’ that we would like to be playable on the form. The code for the input image and audio would look something like this:
# f is a form
form do |f|
extend EnhancedUploader
input_image f, :profile_picture
input_audio f, :hello_audio
end
Please check the section “The Use” for a minimal, guided, working example.
Audio and Image Content methods
Next, we’ll explain what the following audio and image rendering code does:
def audio_content(object, property)
if object.persisted? && object.respond_to?(property)
audio_tag object.send(property), controls: true
else
content_tag(:span, 'No Current Audio')
end
end
def image_content(object, property)
if object.persisted? && object.respond_to?(property)
image_tag object.send(property), height: 150
else
content_tag(:span, 'No Current Image')
end
end
This code is much easier to explain. In each method, we accept an object. First, we check that it is persisted (saved to the DB), because if it isn’t there’s nothing to show or render - we cannot render data that we do not have.
Next, we check to make sure that the object can actually respond to the method that we want. It wouldn’t be a good idea to try to render something that the object doesn’t have! For example, before we try to display a profile_photo, we should make sure that the model actually has a profile_photo field that we can show.
Lastly, we simply render an html5-compliant image link or audio player with controls. The code in the show section would look something like this:
show do
extend EnhancedUploader
attributes_table do
row :profile_photo do |row_object|
image_content row_object, :profile_photo
end
row :hello_audio do |row_object|
audio_content row_object, :hello_audio
end
end
end
Walkthrough
Laying Foundation
In your terminal
rails new demo rails g model User name:string location:string rails db:create db:migrate
Now we add active admin and devise.
In your gemfile, add the lines
gem 'activeadmin' gem 'devise'
In the console:
bundle rails generate devise:install rails g active_admin:install rails db:migrate db:seed rails s
Go to localhost:3000/admin, login with
username: admin@example.com
password: password
create file at app/admin/user.rb, and paste in this content:
ActiveAdmin.register User do
permit_params :name, :location
# Show the essential data in the index
index do
selectable_column # we can select columns for batch actions
column :id
column :name
column :location
column :created_at
column :updated_at
actions
end
# When you click on the show for any individual item, this data is rendered
show do
attributes_table do # display the following attributes
row :id
row :name
row :location
row :created_at
row :updated_at
end
end
# When you click on edit, this form is rendered
form do |f|
f.semantic_errors
f.inputs do
f.input :name
f.input :location
end
f.actions
end
end
Go to http://localhost:3000/admin/users and confirm that we can create, delete, and edit our basic users.
Adding CarrierWave
Time to add an image uploader and an audio uploader. First, let’s include our library that will manage data display for us. Move the following file to lib/enhanced_uploader
https://github.com/Rob117/active-admin-uploads/blob/master/lib/enhanced_uploader.rb
Add the following to your gemfile:
gem 'carrierwave', '~> 1.0'
In the terminal, type:
bundle
make two uploaders, one for profile_photo and one for hello_audio:
rails g uploader profile_photo rails g uploader hello_audio
Add both uploaders to the user model (terminal):
rails g migration add_profile_photo_to_users profile_photo:string rails g migration add_hello_audio_to_users hello_audio:string rails db:migrate
Open app/models/user.rb file and mount uploaders
class User < ApplicationRecord mount_uploader :profile_photo, ProfilePhotoUploader mount_uploader :hello_audio, HelloAudioUploader end
Replace app/admin/user.rb with the following (we aren’t showing the enhanced uploader quite yet):
ActiveAdmin.register User do
permit_params :name, :location, :profile_photo, :hello_audio
# Show the essential data in the index
index do
selectable_column
column :id
column :name
column :location
column :profile_photo
column :hello_audio
column :created_at
column :updated_at
actions
end
# When you click on the show for any individual item, this data is rendered
show do
attributes_table do
row :id
row :name
row :location
row :profile_photo
row :hello_audio
row :created_at
row :updated_at
end
end
# When you click on edit, this form is rendered
form do |f|
f.semantic_errors
f.inputs do
f.input :name
f.input :location
f.inputs :profile_photo
f.inputs :hello_audio
end
f.actions
end
end
We just added the fields. Restart your server and access your users page. Create a user if you need to. Notice that on index, show, and edit that the fields for profile_photo and hello_audio are blank. This is to be expected - we haven’t uploaded anything yet!
Upload a small image and a small audio clip.
Notice that after we’ve uploaded it you can only view the path names and not anything inside the file.
To include our ‘library’, we only need make one small change, in config/application.rb:
require_relative 'boot'
require 'rails/all'
Bundler.require(*Rails.groups)
module Blogly
class Application < Rails::Application
config.autoload_paths += %W(#{config.root}/lib) # Add this line!
end
end
Now, simply change your admin/user.rb file to this final code:
ActiveAdmin.register User do
permit_params :name, :location, :profile_photo, :hello_audio
# Show the essential data in the index
index do
extend EnhancedUploader # include uploader
selectable_column
column :id
column :name
column :location
column :profile_photo do |row_object|
image_content row_object, :profile_photo
end
column :hello_audio do |row_object|
audio_content row_object, :hello_audio
end
column :created_at
column :updated_at
actions
end
# When you click on the show for any individual item, this data is rendered
show do
extend EnhancedUploader # include uploader
attributes_table do
row :id
row :name
row :location
row :profile_photo do |item|
image_content item, :profile_photo
end
row :hello_audio do |item|
audio_content item, :hello_audio
end
row :created_at
row :updated_at
end
end
# When you click on edit, this form is rendered
form do |f|
extend EnhancedUploader # include uploader
f.semantic_errors
f.inputs do
f.input :name
f.input :location
input_image f, :profile_photo
input_audio f, :hello_audio
end
f.actions
end
end
Refresh your server, and head to the users page. Now you should see previews of the audio and image that you uploaded on each index, show, and edit page! Success!
Extra - Careers!
Do you speak Japanese to a high conversational level (~N2) and want to work with us? Click the link below to send us a message!