Rails管理画面gem の新星！administrate を使おう - その3 有名Gemと連携

この記事は Rails管理画面gem の新星！administrate を使おう その３です。
関連記事はこちら

- [Rails管理画面gem の新星！administrate を使おう - その0 紹介 - Qiita](http://qiita.com/mm36/items/253b32f152aa94e93fda)
- [Rails管理画面gem の新星！administrate を使おう - その1 導入 - Qiita](http://qiita.com/mm36/items/c36c7a95e4e836dbffef)
- [Rails管理画面gem の新星！administrate を使おう - その2 基本カスタマイズ - Qiita](http://qiita.com/mm36/items/b21ffa65b97246fd8396)
- この記事
- [TBD] Rails管理画面gem の新星！administrate を使おう - その4 もっとカスタマイズ！

- - -

## 他のgemと連携

おなじみの有名gemと連携してみます。
（ここでは有名gemは利用可能な状態で記載していきます）

連携お相手のgemのことがちゃんとわかってればサクッとできる。
（わかってなかったので苦戦・・）

- 認証 : devise
- タグ付け：act_as_taggable_on
- 画像アップロード：paperclip
- ページネーション：kaminari

## 認証 : devise

Administrateには認証系の機能は一切ありません。
アプリケーションでお使いの認証機能をこちらにも使ってみます。
一番の有名どころであるDeviseとの連携は簡単！

### 準備

特になし。
Deviseは使える状態になってるものとします。

### 管理画面側修正

``` rails generate administrate:install``` で生成された ```application_controller.rb``` をちょこっと修正します。

``` app/controllers/admin/application_controller.rb

module Admin
  class ApplicationController < Administrate::ApplicationController
    before_filter :authenticate_admin

    def authenticate_admin
      # TODO Add authentication logic here. ここここ！！
      authenticate_user!
    end

    # Override this value to specify the number of elements to display at a time
    # on index pages. Defaults to 20.
    # def records_per_page
    #   params[:per_page] || 20
    # end
  end
end
```

```authenticate_user!``` は devise でおなじみのアレですね。


## タグ付け：act_as_taggable_on

[Administrate demoの顧客管理アプリケーション](https://administrate-prototype.herokuapp.com/admin)を例に拡張してみます。

### 準備

Product にタグをつけてみます。

``` app/models/product.rb
class Product < ActiveRecord::Base
  validates_presence_of :name, :price, :image_url

  acts_as_taggable
  acts_as_taggable_on :category
end
```

### 管理画面側修正

Dashboardから操作できるようにします。

#### イマイチなやり方

```category_list``` をStringで登録できるようにしてみます。

```app/dashboards/product_dashboard.rb
  ATTRIBUTE_TYPES = {
    # ...
    category_list: Field::String,
  }

  COLLECTION_ATTRIBUTES = {
    # ...
    :category_list,
  }
  
  SHOW_PAGE_ATTRIBUTES = {
    # ...
    :category_list,
  }

  FORM_ATTRIBUTES = {
    # ...
    :category_list,
  }
```

acts_as_taggable_on の登録の仕方から、 category_list にカンマ区切りで登録すると、無事タグとして登録できることがわかります。。

↓カテゴリの所、, も入力するとちゃんと3個のタグが付くの図

新規作成の場合。

![image](https://qiita-image-store.s3.amazonaws.com/0/5691/93bd7e71-6cec-4e1a-203a-828f37a31146.png)

登録できて、タグも3つ作られている。
よしよし・・・いや・・待て、全然よくない！

#### 問題点

すでに作成されたProduct のupdateをしようとすると、
カテゴリにカンマなくなってる！
(editで戻ってきた時にすでにcategory_list登録済みの場合）

![image](https://qiita-image-store.s3.amazonaws.com/0/5691/6608e958-f09d-c9e7-79d7-ae3a6b0b8283.png)

このまま登録してしまうと、```game gift family``` という残念な1個のタグになってしまいます；；
これはいかん・・なんとか form で表示するときにカンマ付きに戻しておきたい。


#### 修正

カスタムフィールドを追加してみます。

```
> rails generate administrate:field string_tag 
```

以下のファイルが生成

- app/fields/string_tag_field.rb
- app/views/fields/string_tag_field/_show.html.erb
- app/views/fields/string_tag_field/_index.html.erb
- app/views/fields/string_tag_field/_form.html.erb

微修正。。。

``` app/fields/string_tag_field.rb
  def joined
    data.join(',')
  end
```

``` app/views/fields/string_tag_field/_form.html.erb
<div class="field-unit__label">
  <%= f.label field.attribute %>
</div>
<div class="field-unit__field">
  <%= text_field_tag "#{f.object_name}[#{field.attribute}]", field.joined %>
</div>
```

これでデータが登録済みの場合は ```join(',')``` してくれた文字列が表示されるように！

もうちょっとタグてきな見た目にするjsやcssと連携する方法までは力尽きて調べてません。。
（管理画面なので・・入力者多少頑張れ・・とか逃げてみる）


## 画像アップロード：Paperclip

### その前に・・

[本家のデモサイト](https://administrate-prototype.herokuapp.com/admin/products) では、プロダクトに画像が表示される状態になっています。
どうやって実現してるのか、ちょっと気になって調べてみました。

- Product モデルに image_url を string で保持しているだけ
- 画像を実際にアプリにアップロードせず、URLだけを保持
- Administrate の標準タグでついてくる ```Field::Image``` を使ってる

#### gem の中を見てどうやって実現してるのかチェック！

```Field::Image``` の定義を見てみましょう！

もし RubyMine を使っていれば、今使ってる gem のコードを覗くのはとっても簡単。

- Project エクスプローラーで External Library を選ぶ
- administrateを選択
- 見てみたい field の定義を選ぶ ```administrate/views/fields/image```

![image](https://qiita-image-store.s3.amazonaws.com/0/5691/945d7a2a-3dba-d529-df92-bd49e29dac64.png)

コードを見てみると、index, showでは ```image_tag``` を使って画像として描画、 form では ```text_field``` を使って文字入力を受け付ける状態でした。

index の中で良い感じに小さい画像で表示されるところが気になる・・・
parcial viewには違いがないので、CSSでスタイル付けしてそうな感じ。。

先ほどの gem のスタイル系のところを漁ってみます。

```administrate/app/assets/stylesheets/administrate/components/_table.css
.collection-data {
  @include padding(null $base-spacing);
}

.table__row {
  background-color: $white;
  border-left: 2px solid transparent;
  transition: background-color $base-duration $base-timing;

  &:hover {
    background-color: $base-background-color;
    border-left-color: $blue;
    cursor: pointer;
  }

  img {
    max-height: 2em;
  }
}

.table__action--destroy {
  color: $light-red;
}
```

```.table__row``` 内に出てくる ```img``` の高さが設定されてます。
表示上だけ小さくしてるわけですね。

### gemの選択

これでも良いのですが、やはりアプリないで画像も管理したいところ。
画像系の有名Gemで、Carrierwave か Paperclip か・・
と一度使ったことがある Carrierwave で行こうかと思ったけど、Github searchから、同じく thoughtbot の gem である Paperclip の連携情報を発見したので、Paperclip 採用！

[Add example of file upload · Issue #195 · thoughtbot/administrate](https://github.com/thoughtbot/administrate/issues/195)

↑をフォローしていけば難なく導入できますが、一応やり方を・・

### 準備

Product モデルの ```image_url:string``` をやめて、Paperclipと連携。
（Paperclip連携は [RailsでPaperclipを使ってファイルをアップロードする - Rails Webook](http://ruby-rails.hatenadiary.com/entry/20140716/1405443484) などを参考に）

デモのProduct(image_urlアリ）から修正を加えていくので、マイグレーションします。

```db/migrates/20160228013716_add_paperclip_to_product.rb
class AddPaperclipToProduct < ActiveRecord::Migration
  def self.up
    remove_column :products, :image_url
    change_table :products do |t|
      t.attachment :image
    end
  end

  def self.down
    remove_attachment :products, :image
    add_column :products, :image_url, :string
  end
end
```

Product モデルに ```has_attached_file``` と ```validates_attachment_content_type``` を付けて、Paperclipと連携できるようにします。

```app/models/product.rb
class Product < ActiveRecord::Base
  ...

  has_attached_file :image, styles: { medium: "300x300", thumbnail: "100x100" }
  validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/ 
end
```

### custom field

それでは、 Paperclip 用のカスタムフィールドを作ってみましょう！

```
> ./bin/rails g administrate:field paperclip
```
以下のファイルが生成

- app/fields/paperclip_field.rb
- app/views/fields/paperclip_field/_show.html.erb
- app/views/fields/paperclip_field/_index.html.erb
- app/views/fields/paperclip_field/_form.html.erb

#### 修正箇所

##### 画像のURL、サムネイル画像のURLを返すメソッドを追加。

```app/fields/paperclip_field.rb
require "administrate/fields/base"

class PaperclipField < Administrate::Field::Base
  def url
    data.url
  end

  def thumbnail
    data.url(:thumbnail)
  end

  def to_s
    data
  end
end
```

##### showページでは縮小なしのオリジナル画像を表示

```image_tag``` を使う

```app/views/fields/paperclip_field/_show.html.erb
<%= image_tag field.url %>
```

##### indexページでは縮小したサムネイル画像を表示

```image_tag``` を使う

```app/views/fields/paperclip_field/_index.html.erb
<%= image_tag field.thumbnail %>
```

##### formページでは、ファイルを選択してアップロードできるように

```file_field``` を使う

```app/views/fields/paperclip_field/_form.html.erb
<div class="field-unit__label">
  <%= f.label field.attribute %>
</div>
<div class="field-unit__field">
  <%= f.file_field field.attribute %>
</div>
```

### 管理画面側修正

#### Dashboard

Paperclip が付与してくれたフィールドを追加します。

```app/dashboards/product_dashboard.rb
  ATTRIBUTE_TYPES = {
    image: PaperclipField,
  }
```

```COLLECTION_ATTRIBUTES``` , ```SHOW_PAGE_ATTRIBUTES```, ```FORM_ATTRIBUTES``` にも ```:image``` の呼び出しを追加して、ダッシュボード上に表示される様にしてみます。

今度は index ページでは thumbnail 画像が読み込まれるので、読み込み速度的にも良さそうです。

## ページネーション：kaminari

Administrateはデフォルトでいい感じにページネーションしてくれるのですが、 kaminari の default style以外を指定してる場合、レイアウトが崩れる場合があるようです。

namespaceを分ければうまいことアプリ本体側のページネーションスタイルと両立できるようになるようです。

### 前提例

アプリには kaminari bootstrap styleを使ってる。

```
> ./bin/rails g kaminari:views bootstrap3 
```

生成ファイル

- app/views/kaminari/_first_page.html.haml
- app/views/kaminari/_gap.html.haml
- app/views/kaminari/_last_page.html.haml
- app/views/kaminari/_next_page.html.haml
- app/views/kaminari/_page.html.haml
- app/views/kaminari/_paginator.html.haml
- app/views/kaminari/_prev_page.html.haml


### 準備

ページネーションするくらい沢山ダミーデータを生成します。（FactoryGirl使用）

#### ファクトリでダミーデータの準備

```spec/factories/customers.rb
FactoryGirl.define do
  factory :customer do
    sequence :name, "テストn_1"
    sequence(:email){|n| "tester-#{n}@example.com" }
  end
end
```

#### シード呼び出しの準備

```db/seeds.rb
require 'factory_girl'

Customer.destroy_all

FactoryGirl.create_list(:customer, 50)
```

#### ダミーデータ生成

```
> ./bin/rake db:seed
```

### スタイル崩れ

![image](https://qiita-image-store.s3.amazonaws.com/0/5691/8cd872a1-b2ec-2262-09dc-5f9d154b0e92.png)

こんな感じでページネーション部分が縦に乗っかってます；；

どうする・・
またもや administrate の issues を掘ります。

[Fixed pagination styles if kaminari already used in Rails-app by mgrachev · Pull Request #203 · thoughtbot/administrate](https://github.com/thoughtbot/administrate/pull/203)

これ。近そう！

### 管理画面側修正

administrate の 全体 index に関する変更なので、ビューページのカスタマイズを実行します。

```
> rails generate administrate:views:index
```

生成ファイル

- app/views/admin/application/index.html.erb
- app/views/admin/application/_collection.html.erb

最下部のページネーション部分に ```views_prefix``` を追加。

```app/views/admin/application/index.html.erb
<%= paginate resources, views_prefix: 'admin' %>
```

#### kaminari default theme for administrate

```
> ./bin/rails g kaminari:views default
```

ここで生成されるファイルを、 ```app/views/admin/kaminari``` 以下に保存。

すると、無事スタイル崩れなしでページネーションが表示される。

![image](https://qiita-image-store.s3.amazonaws.com/0/5691/2ecb53a4-07f2-f83a-3776-a71d220bd735.png)