Rails 4.1 で Dragonfly を使う


[PaperClip](http://qiita.com/akishin/items/8bc2196500781ded2c0d "Rails 4.1 で PaperClip を使う")  や [CarrierWave](http://qiita.com/akishin/items/46b2e239cf4190171da8 "Rails 4.1 で CarrierWave を使う") と同じファイルアップロード系プラグインの Dragonfly を試してみた。

## 公式

markevans/dragonfly
https://github.com/markevans/dragonfly

Dragonfly
http://markevans.github.io/dragonfly/

Using with Rails
http://markevans.github.io/dragonfly/rails/

## 試した環境

```shell-session
% cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.1 LTS"
```

Ruby 周りは以下。

```shell-session
% ./bin/rake about 
About your application's environment
Ruby version              2.1.3-p242 (x86_64-linux)
RubyGems version          2.2.2
Rack version              1.5
Rails version             4.1.8
JavaScript Runtime        Node.js (V8)
Active Record version     4.1.8
Action Pack version       4.1.8
Action View version       4.1.8
Action Mailer version     4.1.8
Active Support version    4.1.8
```

## 使ってみる

適当なプロジェクトを作成。

```shell-session
% rails new uploader --skip-bundle && cd uploader && bundle install --path=vendor/bundle
```

Gemfile に以下を追記。

```ruby:Gemfile
gem 'dragonfly'
```

追記したら再度 `bundle install` を実行。
dragonfly 1.0.7 がインストールされた。

インストール完了したら以下を実行。

```shell-session
% ./bin/rails g dragonfly
```

このジェネレータを実行する事で以下のファイルが生成される。

```ruby:config/initializers/dragonfly.rb
require 'dragonfly'

# Configure
Dragonfly.app.configure do
  plugin :imagemagick

  secret "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

  url_format "/media/:job/:name"

  datastore :file,
    root_path: Rails.root.join('public/system/dragonfly', Rails.env),
    server_root: Rails.root.join('public')
end

# Logger
Dragonfly.logger = Rails.logger

# Mount as middleware
Rails.application.middleware.use Dragonfly::Middleware

# Add model functionality
if defined?(ActiveRecord::Base)
  ActiveRecord::Base.extend Dragonfly::Model
  ActiveRecord::Base.extend Dragonfly::Model::Validations
end
```

適当な scaffold を生成。

```shell-session
% ./bin/rails g scaffold user name:string password:string
```

生成された User モデルに `dragonfly_accessor :avatar` を追記。

```ruby:app/models/user.rb
class User < ActiveRecord::Base
  dragonfly_accessor :avatar
end
```

アップロードファイル用カラムの為のマイグレーションファイルを作成。

```shell-session
% ./bin/rails g migration add_avatar_uid_to_users avatar:string
```

生成されたファイルを以下のように修正。
`dragonfly_accessor` で指定した名前 + `_uid` というカラムを追加する。

```ruby:db/migrate/2014XXXXXXXXXX_add_avatar_uid_to_users.rb
class AddAvatarUidToUsers < ActiveRecord::Migration
  def change
    add_column :users, :avatar_uid, :string
  end
end
```

マイグレーション実行。

```shell-session
% ./bin/rake db:create db:migrate
```

以下のようなテーブルが作成された。

```sql
CREATE TABLE "users" (
    "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
    ,"name" VARCHAR (255)
    ,"password" VARCHAR (255)
    ,"created_at" datetime
    ,"updated_at" datetime
    ,"avatar_uid" VARCHAR (255)
);
```

View の `form_for` 内の適当な位置に `file_field` を追加する。
Rails4 なので `:multipart => true` は省略。

```erb:app/views/users/_form.html.erb 
<div class="field">
  <%= f.label :avatar %><br>
  <%= f.file_field :avatar %>
</div>
```

Controller で Strong Parameters が追加したパラメータを通すように `permit` に追加。
但し、追加するのはカラム名である `avatar_uid` ではなく `avatar` の方。

```ruby:app/controllers/users_controller.rb
def user_params
  params.require(:user).permit(:name, :password, :avatar)
end
```

これで `rails server` で起動してブラウザからアクセスするとファイルアップロード出来るようになっている。
アップロードしたファイルは以下のようなディレクトリ構造で保存されている。

```shell-session
public
└── system
    └── dragonfly
        └── development
            └── 2014
                └── 11
                    └── 30
                        ├── 7c7l2khd6a_example_image1.jpg
                        └── 7c7l2khd6a_example_image1.jpg.meta.yml
```

ファイル名のプリフィックスとしてランダムな文字列が付与されている模様。
example_image1.jpg.meta.yml の内容は以下のようなもの。

```
---
name: example_image1.jpg
model_class: User
model_attachment: avatar
```

アップロードしたファイルのパスが DB のカラムに保存される模様。

```
sqlite> select * from users;
1|test|test|2014-11-30 05:37:02.392274|2014-11-30 05:37:02.392274|2014/11/30/7c7l2khd6a_example_image1.jpg
```

Model 経由でファイルにアクセスする際には avatar_uid ではなく、avatar メソッドでアクセスすることで Dragonfly が提供するオブジェクト経由でアップロードした画像に対する様々な操作が行なえる。

但し、画像の情報を取得するのに ImageMagick の identify コマンドを使用しているようなのでインストールしておく必要がある。

```ruby
> u = User.find(1)
> u.avatar.class
=> #<Class:0x007f721470db30>
> u.avatar
=> <Dragonfly Attachment uid="2014/11/30/7c7l2khd6a_example_image1.jpg", app=:default>
> u.avatar.height
DRAGONFLY: shell command: 'identify' '-ping' '-format' '%m %w %h' '/home/akishin/src/rails/uploader/public/system/dragonfly/development/2014/11/30/7c7l2khd6a_example_image1.jpg'
=> 768
> u.avatar.width
=> 1024
> u.avatar.format
=> "jpeg"
> u.avatar.aspect_ratio
=> 1.3333333333333333
> u.avatar.size
=> 412202
irb(main):013:0> u.avatar.url
=> "/media/W1siZiIsIjIwMTQvMTEvMzAvN2M3bDJraGQ2YV8wMy5qcGciXV0?sha=49e3d8c3fe3ecdb4"
```

ドキュメントを見ると他にもアップロードした画像ファイルに対して様々な操作が行えるようになっている。
ただ、特に画像専用というわけではなく、普通にテキストファイルなどのその他のファイルをアップロードすることも問題なく出来る。

## 関連

Rails 4.1 で Paperclip を使う - Qiita http://qiita.com/akishin/items/8bc2196500781ded2c0d

Rails 4.1 で CarrierWave を使う - Qiita http://qiita.com/akishin/items/46b2e239cf4190171da8

## 参考

ASCIIcasts - “Episode 253 - CarrierWaveでファイルのアップロード”
http://ja.asciicasts.com/episodes/253-carrierwave-file-uploads

Rails 超お手軽な画像アップローダー CarrierWave の使い方 | Workabroad.jp
http://www.workabroad.jp/tech/1118

carrierwave + rails 4.1 画像アップローダー - 酒と泪とRubyとRailsと
http://morizyun.github.io/blog/carrierwave-image-uploader-rails/

carrierwave 使い方 - Qiita
http://qiita.com/dahugani/items/39b888aa64f482ff434a

AWS - Rails+Carrierwave+fog+Heroku+S3でハマったメモ - Qiita
http://qiita.com/kimihito_/items/3ff9c0c4407447ee3ab3