汎用的なCSV出力機能を作る#1

## 背景

サービスを運営していく上で、ユーザのリストが欲しい、エクセル形式で欲しいみたいなことが多々ある。
今回はなるべく管理が入らず、後々はUIから項目をカスタマイズできるCSV出力機能の実装を目指します。

## いざ、実装してみる

### コントローラを生成

自分の場合、管理画面に追加する想定なので`dashboard/`となってます。

```
rails g controller dashboard/csv_export index create
```

### index を作る

```rb
class Dashboard::CsvExportController < Dashboard::BaseController
  def index
    @models = all_model_array
  end

  def create
  end

  private

  def all_model_array
    tables = ActiveRecord::Base.connection.tables
    tables -= ["ar_internal_metadata", "schema_migrations"]
    tables.map! {|table| table.classify }
  end
end

```

モデル一覧を取得してます。

`index.html.slim`

```
.page-header
  h1 CSV出力

table.table.table-striped
  tr
    th モデル名
    th 出力

  - @models.each do |model|
    tr
      td = model
      td = link_to 'CSV出力', '/', class: 'btn btn-primary'

```

bootstrapを使っていれば、こんな感じなるはず

![スクリーンショット 2017-11-02 14.40.28.png](https://qiita-image-store.s3.amazonaws.com/0/157657/9edeb9f7-1ff2-8a49-6bf0-9c5f9cc006a0.png)


### createアクションを作る

さて、メインのCSV出力を実装します。

こちらの記事を参考させていただきました。
https://tisnote.com/ruby-on-rails-csv/

```rb

require 'csv'

class Dashboard::CsvExportController < Dashboard::BaseController
  def index
    @models = all_model_array
  end

  def create
    model = params[:model]
    csv_data = genarate_csv_for_model(model)
    send_data(csv_data, filename: "#{model}.csv")
  end

  private

  def all_model_array
    tables = ActiveRecord::Base.connection.tables
    tables -= ["ar_internal_metadata", "schema_migrations"]
    tables.map! {|table| table.classify }
  end

  def genarate_csv_for_model(model)
    csv_date = CSV.generate do |csv|
      csv_column_names = eval "#{model}.column_names"
      csv << csv_column_names
      models = eval "#{model}.all"
      models.each do |product|
        csv_column_values = []
        csv_column_names.each do |column|
          csv_column_values << product.send(column)
        end
        csv << csv_column_values
      end
    end
    csv_date
  end
end
```

`index.html.slim`

```
.page-header
  h1 CSV出力

table.table.table-striped
  tr
    th モデル名
    th 出力

  - @models.each do |model|
    tr
      td = model
      td = link_to 'CSV出力', dashboard_csv_export_create_path(format: :csv, model: model), class: 'btn btn-primary'

```

リンク先を修正すれば、機能自体は実装完了です。

### リファクタリング

コントローラにロジックを書くのは、好きくないので今回はサービスクラスに移転してあげました。
しかし、メソッド、変数の命名が下手くそです....

### ロジックをサービスクラスに移動

```rb
require 'csv'

class CsvService

  def initialize(column_names=[], records=[], file_name="example")
    @column_names = column_names
    @records = records
    @file_name = file_name
  end

  def self.initialize_by_model(model_name)
    column_names = eval "#{model_name}.column_names"
    records = eval "#{model_name}.all"
    CsvService.new(column_names, records, model_name)
  end

  def generate_csv
    CSV.generate do |csv|
      csv << @column_names
      @records.each do |record|
        column_values = []
        @column_names.map { |column| column_values << record.send(column) }
        csv << column_values
      end
    end
  end
end
```

```rb
class Dashboard::CsvExportController < Dashboard::BaseController
  def index
    @models = all_model_array
  end

  def create
    @model_name = params[:model]
    @csv_service = CsvService.initialize_by_model(@model_name)
    @csv_data = @csv_service.generate_csv
    send_data(@csv_data, filename: "#{@model_name}.csv")
  end

  private

  def all_model_array
    tables = ActiveRecord::Base.connection.tables
    tables -= %w(ar_internal_metadata schema_migrations)
    tables.map! { |table| table.classify }
  end
end
```

一旦こんな感じ

これから、項目の選択とか絞り込みみたいなことに挑戦していこうと思います。