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!