実行中のモデルからマイグレーションの利用

テーブルの追加、削除については、ActiveRecord::Migrationを利用して処理することにした。データベースへの接続やSQLを意識せず、Rubyのコードとして書けるので、こっちの方が楽。変更後のコードは、以下のようになった。

  • create_csv_table、drop_csv_tableの修正。
  • execute_sqlは、不要なので削除。
  • csv_columnsは、結果を配列で返すよう変更。
モデル
app/models/csv.rb
class Csv < ActiveRecord::Base
  validates_uniqueness_of :file_name
  def validate
    errors.add(:file_name, "アップロードするファイルを指定してください。") if @file.nil? || @file.size == 0
  end
  
  # Tempfileオブジェクトをインスタンス変数に保存する。
  # new()や、update_attributes()の時、呼び出される。
  def tempfile=(file)
    return if file.nil? || file.size == 0
    @file = file
    self.file_name = file.original_filename
    self.file_size = file.size
  end
  
  # テーブルを追加する。
  def create_csv_table
    ActiveRecord::Migration.create_table table_name do |t|
      csv_columns.each do |f|
        t.column f, :string
      end
    end
  end
  
  def table_name
    File.basename(self.file_name, ".*")
  end
  
  def csv_columns
    # 「"」を取り除いて、文字コードをUTF-8に変換して、改行で区切った配列を返す。
    str   = @file.read.gsub(/"+/, '')
    str   = NKF.nkf('-w', str)
    lines = str.split("\n")
    # 配列の先頭を取り出して、カンマで区切った配列にして、余分な空白を削除する。
    columns = lines.shift.split(",").collect{|n| n.strip}
    
    # id列が存在しなければ追加する。
    if columns.index("id").nil?
      id = 0
      lines.collect! {|line| id +=1; "#{id},#{line}"}
    end
    
    # インポート用のファイルを保存する。
    File.delete("public/temp.csv") rescue nil
    File.open("public/temp.csv", "wb") do |f|
      f.write(lines.join("\n"))
    end
    
    # 最初のidフィールドは、読み飛ばす。
    columns.shift if columns.first == 'id'
    columns
  end
  
  # csvデータをインポートする。
  # インポートを高速化するため、sqlite3のインポート命令を、直接コマンド実行する。
  def import_csv
    env = ENV['RAILS_ENV'] || 'development'
    system("sqlite3", "-separator", ",", "db/#{env}.sqlite3", ".import public/temp.csv #{table_name}")
  end
  
  # テーブルを削除する。
  def drop_csv_table
    ActiveRecord::Migration.drop_table(table_name)
  end
end