blog.kotamiyake.me

為せば成る、為さねば成らぬ何事も

今回はRails 5.2から導入されるActive Storageを試してみたいと思います。

個人で開発しているRails 5.1のアプリに導入してみることにしました。

開発自体もまだ始めたばかりで、5.2がbetaになったこともあり、Rails自体をアップグレードしてActive Storageを利用してみることにします。

Railsのアップグレード

ではさっそくRailsをアップグレードしてみます。

Gemfileに現在の最新バージョンである5.2.0.beta2を指定してbundle update railsします。

gem 'rails', '~> 5.2.0.beta2'

Railsアップグレードと同時にActive Storageがインストールされているのが確認できます。

# snip...

Fetching actionmailer 5.2.0.beta2 (was 5.1.4)
Installing actionmailer 5.2.0.beta2 (was 5.1.4)
Fetching activemodel 5.2.0.beta2 (was 5.1.4)
Installing activemodel 5.2.0.beta2 (was 5.1.4)
Fetching arel 9.0.0 (was 8.0.0)
Installing arel 9.0.0 (was 8.0.0)
Fetching activerecord 5.2.0.beta2 (was 5.1.4)
Installing activerecord 5.2.0.beta2 (was 5.1.4)
Fetching activestorage 5.2.0.beta2
Installing activestorage 5.2.0.beta2

# snip...

READMEに従いコマンドを実行します。

rails active_storage:install

そしてエラーが発生します…

rails aborted!
Don't know how to build task 'active_storage:install' (see --tasks)
/Users/kotamiyake/code/private/projects/xxx/bin/rails:9:in `require'
/Users/kotamiyake/code/private/projects/xxx/bin/rails:9:in `<top (required)>'
/Users/kotamiyake/code/private/projects/xxx/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'
(See full trace by running task with --trace)

gemをアップグレードしたのですが、アプリ自体のアップグレードをやっていませんでした。

rails app:update

とりあえずroutes.rbと、deviseの導入で修正していたdevelopment.rb以外はアップグレード後のコードを取り込む形にしました。

しかしpumaを起動したところでさらにエラーが発生。

bin/rails s -p 3005
/Users/kotamiyake/code/private/projects/seamless/config/boot.rb:4:in `require': cannot load such file -- bootsnap/setup (LoadError)
        from /Users/kotamiyake/code/private/projects/seamless/config/boot.rb:4:in `<top (required)>'
        from bin/rails:3:in `require_relative'
        from bin/rails:3:in `<main>'

5.2ではbootsnapというRuby, Railsの高速化のためのgemがデフォルトで導入されるのですが、そちらをインストールしていませんでした。

bootsnapについては、Bootsnapについて という記事で詳しく説明されているので参考にして下さい。

gem 'bootsnap', require: false

無事pumaが起動しました。

では気を取り直して再度Active Storageを導入を開始したいと思います。

rails active_storage:install
rails aborted!
Don't know how to build task 'active_storage:install' (see --tasks)
bin/rails:4:in `<main>'
(See full trace by running task with --trace)

あれ…?どうやらconfig/application.rbでActive Storageを読み込む部分がコメントアウトされていたためRakeタスクが見つからなかったようです。

require "active_storage/engine"

これで無事インストールが完了しました。

下記のようなマイグレーションファイルが生成されました。

# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
  def change
    create_table :active_storage_blobs do |t|
      t.string   :key,        null: false
      t.string   :filename,   null: false
      t.string   :content_type
      t.text     :metadata
      t.bigint   :byte_size,  null: false
      t.string   :checksum,   null: false
      t.datetime :created_at, null: false

      t.index [ :key ], unique: true
    end

    create_table :active_storage_attachments do |t|
      t.string     :name,     null: false
      t.references :record,   null: false, polymorphic: true, index: false
      t.references :blob,     null: false

      t.datetime :created_at, null: false

      t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
    end
  end
end
rails db:migrate

Active Storageを使ってファイルをアップロードしてみる

それではさっそくファイルアップロードを試してみます。

今回は開発環境で試すのでクラウド環境へのアップロードではなくローカルへファイルを保存することにします。

Active Storage Overview — Ruby on Rails Guides を参考に進めます。

まずはActive Storageの設定ファイルであるconfig/storage.ymlを作成します。

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

また使用するストレージの種類を設定します。Local以外にもAmazon S3、Google Cloud Storage、 Microsoft Azure Storageなどが利用できるようです。

今回はlocalを設定します。

config.active_storage.service = :local

ファイルを添付するモデルに定義を追加します。

class User < ApplicationRecord
  has_one_attached :avatar
end

StrongParameterに上記の属性を追加します。deviseを利用しているので定義の内容は違いますが、やっていることは基本的に同じです。

devise_parameter_sanitizer.permit(:account_update, keys: [:name, :avatar, accounts_attributes: [:name]])

最後にフォームにフィールドを追加して準備完了です。

  <div class="field">
    <%= f.label :avatar %><br />
    <%= f.file_field :avatar %>
  </div>

これでcarrierwaveやpaperclipのようにファイルアップロードが可能になりました。

アップロードした画像を表示する

今度はアップロードしたファイルを表示してみます。表示にはurl_forを使うようです。

    <% if f.object.avatar.attached? %>
      <div>
        <%= image_tag url_for(f.object.avatar) %>
      </div>
    <% end %>

サイズが大きかったりした場合はresizeすることができます。

resizeするにはmini_magickが必要なのでインストールします。

gem 'mini_magick'

これでファイルをresizeして表示できるようになりました。

<%= image_tag f.object.avatar.variant(resize: "100x100") %>

まとめ

今回は駆け足でActive Storageの使い方を紹介しました。まだまだ新しいライブラリなので色々と地雷がありそうですが、使い続ける中で発生した問題はTipsなどは随時紹介していきたいと思います。