Active Admin Enhanced

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!

www.kayac.com

Related Artices (Japanese)

techblog.kayac.com

techblog.kayac.com

不健康な人間が楽して健康を手にするためのTIPS

こんにちは。技術部の小池です。

今回は不健康な人間がいかにして楽して健康を手に入れるか、というゆるゆるエントリになります。

私の健康について

先日健康診断をビクビクしながら受診したところ、視力以外はほぼ問題ないレベルの健康状態でした。悪玉コレステロール値が2016年度のF判定からB判定になり、酒飲みのカルマである尿酸値と肝機能の各項目もA判定です。

私はここ3〜4年の間で酷いときは20代半ばにも関わらず尿酸値、肝機能もC判定やD判定を喰らってました。なんとなくやばいな、と思いつつ食生活を改める、酒の量を減らすといった改善はできませんでした。酒は心の栄養とはよく言ったもので、食事量や飲酒量を制限するとストレスに襲われ逆に健康を損なってしまいます。以前禁酒を試みたことがありますが、なんとなく夜眠れなくなったり気分が沈みがちになったりと散々でした。

そんな私がいかにして楽して健康を手に入れたのか、その軌跡を紹介します。なお本内容は私の個人的主観に基づくもので、内容の正しさは一切保証しません。あらかじめご了承ください。

栄養補助食品に頼る

サプリメントによって不足している栄養を補います。貧血気味の方が鉄分のサプリメントを摂取するなど、比較的身近な手法だと思います。色んなサプリメントがありますが、私はもっぱらアメリカ製サプリメントを使用しています。アメリカはサプリメント先進国なので日本のものより品質がよく、値段も安いです。

私が普段愛用しているサプリメントを紹介します。iHerb.comというサイトです。Amazonでも購入できますがiHerb.comだと栄養成分の内訳が見れる上に値段が安いことが多いのでの私はiHerb.comを使っています。怪しいサイトでもなくちゃんと届きますよ!

Life Extension, Two-Per-Dayカプセル, 120錠

ビタミンを摂取できます。これを飲み始めてから口内炎ができなくなったスゴイやつです。名前の通り1日2錠飲まなくても1錠で十分です。1錠でも余ったビタミンが緑色の尿となって排出されてきます。

自分がビタミンをどの程度摂取すればいいのか気になる方は、厚生労働省が公開している 日本人の食事摂取基準(2015 年版)の概要 を読んでみると良いでしょう。

たとえばビタミンCで見てみると、私は18~29歳男性なので上記資料によれば1日あたりの推定平均必要量が85mg、推奨量が100mgとなっています。Two-Per-Dayは2錠で800mg摂取できると書いてあるので大幅オーバーですね。ビタミンCは水溶性なので余った分が尿として排出されているという状態です。

アメリカ人基準で作られているためか粒が大粒のため人によっては飲みにくいのと、放牧的な香りがきついのがネックです。在庫が不安定なので在庫があるときに買っておくのもポイント。

Now Foods, タウリン、500 mg、100カプセル

文字通りタウリンを摂取できます。タウリンは肝臓の働きを助ける効果があるので個人的には必須アイテムです。これを飲み始めてから肝機能がみるみる回復し、安定してA判定になるようになったスゴイやつです。私は2014年から飲み始めた結果、以下のように改善していきました。

年度 γ-GTP
2013 52
2014 53
2015 41
2016 46
2017 32

飲み始めて1年後の2015年には改善が見られます(2016年に一度増えてますが)。γ-GTPは50までが基準値なので、2015年からは基準値内を保っています。GPTなども同様に改善されています。健康ですね!

タウリンがたくさん入ってる某栄養ドリンクと比べても圧倒的に値段が安いです。試しにタウリン1000mgあたりの値段をメーカー希望小売価格で大雑把に比較してみました。

商品 タウリン1000mgあたりの値段
Now Foods社タウリン 18.16円
某栄養ドリンク 158円

驚きの値段の差ですね。某栄養ドリンクと違い余分なカフェインや糖分も入ってない純粋なタウリンそのものなのもグッド。

Now Foods, シリマリン(Silymarin), オオアザミエキス, 300 mg, 100粒(ベジタリアンカプセル)

こちらも肝機能を助けてくれます。酒を飲んだ日は帰宅したらこれを飲んでます。ちょっとくらい酒を飲みすぎてもこれを飲めば翌日に残らない、私に安定した勤怠をもたらしてくれるスゴイやつです。平日でもギリギリまで酒を飲みたい人におすすめです。私はたまに飲み忘れて翌日死んでます。

社内でじわじわとiHerbユーザが増えてきているのでこのままじわじわとハーブの輪を広げていきたい思いです。社内だとオメガ3脂肪酸のサプリメントを摂取している人がいました。

位置ゲーをプレイする

位置ゲーは昨年リリースしたPokémon GOで一気にメジャーになったような感じがありますね。私もPokémon GOをプレイしています。Pokémon GOを開発したNiantic社の前作Ingressもプレイしており、かれこれ数年間位置ゲーをプレイしていることになります。iPhoneのヘルスケアアプリによると1日平均13,635歩を歩いているようです。

私はPokémon GOのテスターに当選していて去年の春ぐらいからプレイしていたので大体Pokémon GOで歩き回った結果の歩数ですね。悪玉コレステロール値は何故下がったのか自分でも疑問でしたが多分運動量が増えたからだと思います。地味に体重も4kg減ってました。

Pokémon GOプレイ時は自分がオープンワールドのMMORPGのキャラクターになった気分でいます。ゲームのキャラクターは疲れないので僕も疲れません。レアポケモンの沸き待ちで公園に篭っている姿は狩場でネームドモンスターの沸き待ちをしている姿と一緒だなあと感じています。ゲームをプレイしながら健康になれるっていいですね!たーのしー!

水を飲む

定期的な水分補給です。尿酸値が高い方は医師の方からよく言われているのではないでしょうか。プログラミングをしてるとすぐ数時間とか経ってしまうので気がついたときに水を飲むようにしています。

私の場合は一時間座りっぱなしでいるとApple Watchのアクティビティアプリが警告を出してくれるので、そのタイミングで立ち上がるついでにコップ1杯の水を飲むようにしています。

ウォーターサーバや自動販売機まで歩くことで気分転換にもなり、体もほぐれます。また寝る前にもコップ1杯の水を飲んでます。

おわりに

ここまで書いていておいてなんですが、何が楽かは人によってそれぞれだと思います。

TMTOWTDI (There’s More Than One Way To Do It.)

やり方はひとつじゃない!というPerlの思想です。

私は食事量や飲酒量を変えることが凄まじい苦行だったのでこのやり方に落ち着きましたが、人それぞれ自分に合った健康法を探すのが良いのではないでしょうか。

カヤックでは一緒にハーブの輪を広げるエンジニアも募集しています!

関連記事

techblog.kayac.com