静的にCSVファイルを取り扱うクラス

前回、動的なコードでCSVファイルを取り扱ってみたので、今回はそれ以外の方法で試してみた。やってみたのが、CSVファイルを2次元配列として読み込んで、取り扱う方法。

2次元配列のデータとして取り扱う場合

コントローラー
app/controllers/csvs_controller.rb
class CsvsController < ApplicationController
  def list
    @csvs = FasterCSV.read("test/fixtures/people1.txt")
    @columns = @csvs.shift
  end

  def show
    @csvs = FasterCSV.read("test/fixtures/people1.txt")
    @columns = @csvs.shift
    @csv = @csvs[params[:id].to_i - 1]
  end
ビュー
app/views/csvs/list.rhtml
<h1>Listing csvs</h1>
<table>
  <tr>
  <% for column in @columns %>
    <th><%= column %></th>
  <% end %>
  </tr>
  
<% for csv in @csvs %>
  <tr>
  <% for column in @columns %>
    <td><%=h csv[@columns.index(column)] %></td>
  <% end %>
    <td><%= link_to 'Show', :action => 'show', :id => csv[@columns.index("id")] %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => csv[@columns.index("id")] %></td>
    <td><%= link_to 'Destroy', { :action => 'destroy', :id => csv[@columns.index("id")] },
                    :confirm => 'Are you sure?', :post => true %></td>
  </tr>
<% end %>
</table>
ビュー
app/views/csvs/show.rhtml
<% for column in @columns %>
<p>
  <b><%= column %>:</b> <%=h @csv[@columns.index(column)] %>
</p>
<% end %>

<%= link_to 'Edit', :action => 'edit', :id => @csv[@columns.index("id")] %> |
<%= link_to 'Back', :action => 'list' %>
  • CSVファイルの内容を全件表示するだけなら、取り扱うクラスを作るまでもなく、配列として読み込んで処理する方が手っ取り早い。
  • しかし、並べ替えや検索などの機能を追加するなら、専用のクラスを作っておいた方がいいと思った。

CSVファイルを、一つの配列として取り扱うクラスを追加した場合

モデル
app/models/data_array.rb
class DataArray
  def initialize(file_name)
    @csvs = FasterCSV.read(file_name)
    @columns = @csvs.shift
  end
  
  def read_all
    @csvs
  end
  
  def columns
    @columns
  end
  
  def index(column)
    @columns.index(column)
  end
end
コントローラー
app/controllers/csvs_controller.rb
class CsvsController < ApplicationController
  def list
    @data = DataArray.new("test/fixtures/people1.txt")
    @csvs = @data.read_all
  end

  def show
    @data = DataArray.new("test/fixtures/people1.txt")
    @csvs = @data.read_all
    @csv = @csvs[params[:id].to_i - 1]
  end
ビュー
app/views/csvs/list.rhtml
<h1>Listing csvs</h1>
<table>
  <tr>
  <% for column in @data.columns %>
    <th><%= column %></th>
  <% end %>
  </tr>
  
<% for csv in @csvs %>
  <tr>
  <% for column in @data.columns %>
    <td><%=h csv[@data.index(column)] %></td>
  <% end %>
    <td><%= link_to 'Show', :action => 'show', :id => csv[@data.index("id")] %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => csv[@data.index("id")] %></td>
    <td><%= link_to 'Destroy', { :action => 'destroy', :id => csv[@data.index("id")] }, 
                    :confirm => 'Are you sure?', :post => true %></td>
  </tr>
<% end %>
</table>
ビュー
app/views/csvs/show.rhtml
<% for column in @data.columns %>
<p>
  <b><%= column %>:</b> <%=h @csv[@data.index(column)] %>
</p>
<% end %>

<%= link_to 'Edit', :action => 'edit', :id => @csv[@data.index("id")] %> |
<%= link_to 'Back', :action => 'list' %>

感じたこと

動的コード 静的コード
オブジェクトの単位 CSVファイル1行を1オブジェクト CSVファイル全体を1つのオブジェクト
特定のデータを取り出す場合 csv.id csv[@data.index("id")]

慣れない自分にとっては、静的コードの方がシンプルで手っ取り早く感じる。しかし、1行を1オブジェクトにしておいた方が、このあと機能追加していく時には、小回りが利きそうだ。コードを見て何をやっているかも、感覚的に分かり易い。それには、列タイトルに対応するメソッドを、動的に追加することが必要になる。そんな時、動的なクラスを作りたくなるのかもしれない。

  • 動的コード、静的コード、どちらにもfindメソッドを追加するべきと思った。(検索や並べ替えは、きっと必要になる。)
  • やっているうちに、自分が欲しいのはRailsActiveRecordの機能そのものだと気付いた。
  • それなら、CSVファイルをデータベースとしてインポートすれば、いつものRails感覚で取り扱うことが出来るのでは...。