Excelデータのヘッダー行をheadersというhashに格納して、ActiceRecordにデータを保存しようとしたらうまくいかず、解決したもののモヤモヤするのでここにメモしておく。

Excelのデータをまずrooで読んで、それをActiveRecordでSQLite3に入れようとしたら怒られた。具体的にはDBのフィールド名をExcelのヘッダーを格納したhashデータでやろうと思ったらシンタックスエラーになった。

require "sqlite3"
require "active_record"
require "roo"
require "pry"

class Hoge < ActiveRecord::Base
  establish_connection :adapter  => 'sqlite3',
                       :database => './sample.db'
end

FILE = "sample.xlsx"
doc = Roo::Excelx.new(FILE)
doc.default_sheet = doc.sheets.first

headers = {}
(doc.first_column..doc.last_column).each do |col|
  headers[col] = doc.cell(doc.first_row, col)
end

((doc.first_row + 3)..doc.last_row).each do |row|
  indb = Hoge.new
  headers.keys.each do |col|
    value = doc.cell(row, col)
    indb.send(headers[col]) = value
  end
  indb.save
end

p Hoge.last

エラーの内容がこちら。

app.rb:24: syntax error, unexpected '=', expecting keyword_end
    indb.send(headers[col]) = value

「え?」と思って色々調べると、なんか超訳でsendは文字列をメソッド名的に使える便利メソッドだと勝手に解釈していたのだけど、どうやら違うみたいだ。こりゃいっかい本格的に本を買って勉強したくなる。オライリーとかでRuby2.1.1に対応した最新のが出て欲しい。

で、解決したコードがコチラ。

require "sqlite3"
require "active_record"
require "roo"
require "pry"

class Hoge < ActiveRecord::Base
  establish_connection :adapter  => 'sqlite3',
                       :database => './sample.db'
end

FILE = "sample.xlsx"
doc = Roo::Excelx.new(FILE)
doc.default_sheet = doc.sheets.first

headers = {}
(doc.first_column..doc.last_column).each do |col|
  headers[col] = doc.cell(doc.first_row, col)
end

((doc.first_row + 3)..doc.last_row).each do |row|
  indb = Hoge.new
  headers.keys.each do |col|
    value = doc.cell(row, col)
    indb[headers[col]] = value
  end
  indb.save
end

p Hoge.last

いつもはこう ar.name だが シンボルでも ar[:name] 文字列でも ar[‘name’] send にシンボルでも ar.send(:name) send に文字列でも ar.send(‘name’)

という情報を見て、カラムの指定を[]にしたらアッサリ望み通りの動作をした。他の書き方はうまく動かなかった。この辺の動作に関しては真面目に勉強しておかないとダメだ。

####参考

Activerecord のカラムへアクセスする