はじめに
ActiveAdminもCarrierwaveも便利でいいですよね。
でもActiveAdminにはCarrierwave用の処理が入ってないので、app/admin/*rb
に毎回色々書かなきゃいけなくなって大変です。
また、ファイルのアップロードとその上書きアップロードはできるけど、削除はそのままではできないので、これも毎回削除用のチェックボックスを追加しないといけなくて大変です。
せっかくのActiveAdminなので、カラムの種類に応じて勝手に必要な表示とフォームが出力されるようにしたいです。
ということで、とりあえずCarrierwaveのカラムについて勝手に必要な処理をしてくれるようにしました。
他の特殊な型のカラムでも同じように出来るので、今後のためにもメモしておこうかと思います。
環境
rails (4.2.0)
activeadmin (1.0.0.pre2)
carrierwave (0.10.0)
完成サンプル
一覧画面、閲覧画面
- サムネイルがない場合 ・・・ 実ファイルのURLへリンクしたファイル名を表示
- 画像アップロードがされていない(or削除された)場合 ・・・ 空欄
- サムネイルがある場合 ・・・ サムネイル表示
編集画面
- サムネイルがある場合
- サムネイルがない場合
ソースサンプル
まとめてダウンロード
ActiveAdmin+Carrierwave [Gist]
一覧画面、閲覧画面用
#
# https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/view_helpers/display_helper.rb
#
module ActiveAdmin
module ViewHelpers
module DisplayHelper
def pretty_format_with_pretty_other_format(object)
pretty_uploader_format(object) ||
pretty_jsonb_format(object) ||
pretty_format_without_pretty_other_format(object)
end
alias_method_chain :pretty_format, :pretty_other_format
def pretty_uploader_format(object)
if object.class.ancestors.include?(CarrierWave::Uploader::Base)
if object.present?
if object.version_exists?(:thumb) && object.thumb.file.exists?
content_tag(:p,
image_tag(object.url(:thumb)),
{class: 'attachment_wrap attachment_img'}
)
else
link_to object.file.identifier, object.url
end
end
end
end
def pretty_jsonb_format(object)
if object.class.ancestors.include?(Hash)
content_tag(:pre,
JSON.pretty_generate(object).gsub(":", " =>")
)
end
end
end
end
end
Railsでのパッチの方法がまだよく分からないのでそこらへんは適当ですが、
元のpretty_formatメソッドにカラム型毎に処理を追加していけばよさそうでした。
引数のobject
にはModelのAttributeが入ってきてたので、その型なり値なりを見て分岐していけばよさそうです。
Carrierwaveの場合は、
class Animal < ActiveRecord::Base
mount_uploader :image, DefaultUploader
class DefaultUploader < CarrierWave::Uploader::Base
このように定義されていると、imageカラムのときのobject
にはDefaultUploader
のインスタンスが渡ってきます。
デフォルト以外のアップローダーもどんどん定義されていくと思うので、どのアップローダーでも共通に表示されるようにCarrierWave::Uploader::Base
を継承しているクラスかどうかで判断しています。
サムネイル
あとは、サムネイル表示できるようにアップローダーのversion
にthumb
を定義しておけばOKです。
画像以外のアップローダーの場合でも、キャプチャ画像などをthumbに保存するようにしておけば色々捗りそうな予感がしますね。
version :thumb do
process :resize_to_fit => [100, 100]
end
thumbというversionが存在しないアップローダーの場合はファイル名の表示になります。
ちなみに、
参考までに、jsonbカラムの場合の処理も掲載しました。
jsonbカラムの場合はobject
にHash
が渡ってきたので、そのままJSON文字列にして表示しています。
長いJSONが想定される場合は適宜substrするなりして対応すればよいかと思います。
編集画面用
class FileInput < Formtastic::Inputs::FileInput
def image_html_options
{:class => 'attachment_wrap attachment_img'}.merge(options[:image_html] || {})
end
def image_plain_html_options
{:class => 'attachment_wrap attachment_plain'}.merge(options[:image_html] || {})
end
def to_html
input_wrapping do
label_html <<
builder.file_field(method, input_html_options) <<
image_html
end
end
protected
def image_html
return "".html_safe if builder.object.new_record?
case options[:image]
when Symbol
builder.object.send(options[:image])
when Proc
options[:image].call(builder.object)
when String
options[:image].to_s
else
att = builder.object.public_send(method)
if att.present?
if att.version_exists?(:thumb) && att.thumb.file.exists?
builder.template.content_tag :div, nil, image_html_options, true do
builder.template.image_tag(att.url(:thumb) ) +
builder.check_box("remove_#{method.to_s}".to_sym, {}, true, false)
end
else
builder.template.content_tag :div, nil, image_plain_html_options, true do
builder.template.content_tag(:span, att.url() ) +
builder.check_box("remove_#{method.to_s}".to_sym, {}, true, false)
end
end
else
"".html_safe
end
end
end
end
※どこかから拝借したソースをベースにしてますが、URLを紛失してしまったので、探し当てられたら改めて参照を掲載しますm(_ _)m
参照したソースでは、
f.input :image, as: :file, :image => proc { |o| o.image.url(:thumb) }
こんな風に書けば好きな文字を表示できるようにしたZE!HAHAHA! みたいな感じでしたが、
できればapp/admin/*rb
にはなるべく何も書きたくなかったので、何も書かなければデフォルトでサムネイルかファイル名表示するようにしました。
form do |f|
f.input :image, as: :file
f.input :image #imageはCarriewaveのuploaderなのでデフォルトで as: :fileになる
f.inputs #全てのカラムをデフォルトで表示
これらのどの書き方でも自動的にサムネイル表示されます。
全カラムがデフォルトだけでよい場合は、form
のブロックすら不要です。
削除
削除機能自体はCarrierwaveにきちんと搭載されているので、そのお作法に倣えば簡単です。
既にチェックボックスは出力していますが、そのチェックボックスはこのように出力されているはずです。
<input type="file" name="animal[remove_image]"> /* remove_カラム名 */
このremove_カラム名
がtrue(チェックされた状態)で送信されればCarrierwaveがきちんと削除処理してくれます。
このチェックボックスをCarrierwaveに到達させるにはpermissionの指定が必要なので、追加しておきます。
ActiveAdmin.register Animal do
permit_params do
Animal.column_names - ["id","created_at","updated_at"] + ["remove_image"]
end
これで、上の画像のように勝手にチェックボックスが出てきて、チェックすると削除されるところまで実装できちゃいました。
アップローダーを増やしたりmount_uploader
したカラムを増やしたりしても、管理画面にはremove_*
を追記するだけでいいので楽ですね。
※ほんとはここも自動化したいけど、associationとか絡んでくるとpermit_paramsはごちゃごちゃ書くことになりそうで、そこまでサポートできないのでやめました。
注意
今気付きましたが、Formtastic::Inputs::FileInput
を拡張しているので、これをActiveAdmin以外でも使っていると影響を受けちゃいますね。当然ですよね。。
それはそれでいい場合もありますが、例えばメールフォーム等の一回きりの送信しかしない画面であれば、画像削除は不要かもです。
その場合は、それ用の処理を追記するとか、:image
オプションに何もしないブロックを渡すとかしてみてください。
semantic_form_for
なんかを使っている場合はご注意を。
CSS
/**
* FileInput < Formtastic::Inputs::FileInput
*/
.attachment_wrap {
@extend p.inline-hints;
font-style: default;
display: block;
&.attachment_img img {
height: 80px;
}
&.attachment_plain {
}
[name*="[remove_"] {
display: inline-block;
margin-left: 5px;
}
}
サムネイルやチェックボックスのスタイルなどを。
おわり
いやー素晴らしいですね。ActiveAdminもCarrierwaveもRubyもRailsも。