rubyXLでシートをコピーしたりシートを削除したりする(訂正)

rubyXLでシートのコピーを行う方法についてまとめましたが、修正前・修正後ともに納得のいくものではありませんでした。どうしてかというと、データと書式のコピーは行われるのですが、セルの幅や高さについてはコピーされないためです。やはりシートのコピーがベストな回答でしょう。

ということでソースコードを読み込んでみたところ、どうやら下記の方法で上手くいくことがわかり、実際に上手く動作しました。

前提条件

以下の前提条件で動作しました。

  • Ruby 2.1.5
  • rubyXL 3.3.1
  • Excel ブックは 2007 形式で新規に作成(2003以前の形式でもシートを全選択し貼り付ければ上手くいきました。)

具体的なシートのコピー

具体的なシートのコピーは以下のようになります。

  • ワークブックのインスタンスを取得する。
  • コピー対象のシートのインスタンスを取得する。
  • シートの属性として シート名、シートID、所属するブック を設定する。
  • ブックにシートを追加する。

この手順で上手く動作するのですが、上手くいかないでExcelブックをオープン時に ブックの修復ダイアログ が表示されることがありました。どうにも理由はわからないのですが、以下の設定を行っているとエラーが表示されました。

  1. シートにフィルタなどを設定している。
  2. シート関数を設定している。

シート関数は利用できないわけではないのでどうしてかはわかりませんが、シートのコピーを行う場合には参照先が正しくないためエラーとなるのかもしれません。また、フィルタについても同様かもしれません。

また、エラーが出るケースについても

Marshal.load(Marshal.dump(org_sheet))

のように オブジェクトを シリアライズ -> デシリアライズ するとエラーが表示されないこともありました。

ソースコード

ソースコードは以下の通りです。

require 'rubyXL'

# ワークブックの読込
workbook = RubyXL::Parser.parse('excels/beaglesoft_template2.xlsx')

# コピー元シート
org_sheet =  workbook['テストシート名']

add_sheet = Marshal.load(Marshal.dump(org_sheet))

# こちらの方法でも問題ないが、エラーとなるケースが存在したためコメントアウト
# Marshal.dump でシリアライズできない情報が欠落している可能性もある。
# add_sheet = org_sheet

add_sheet.sheet_name = 'コピーシート名'
add_sheet.workbook = workbook

# worksheetの番号を取得する
add_sheet.sheet_id = workbook.worksheets.map(&:sheet_id).max + 1

# テストシートをコピーしてブックに追加
workbook.worksheets << add_sheet

# ブックの内容を列挙する
workbook.worksheets.each do |worksheet|
  p 'シートのクラス名:#{worksheet.class.name}'
  p 'シート名:#{worksheet.sheet_name}'
end

# 元から存在するシートを削除する
delete_sheet_index = workbook.worksheets.index(org_sheet)
workbook.worksheets.delete_at delete_sheet_index

# outputsディレクトリに保存する
file_name = 'beaglesoft_template_#{Time.now.strftime('%Y%m%d%H%M%s')}.xlsx'
workbook.write('outputs/#{file_name}')

ソースコードはこちらから取得できます。

yoichiro-manabe/rubyxl_sample

また、以下の方法で clpone できます。

cd /path/to/clone/path
git clone https://github.com/yoichiro-manabe/rubyxl_sample.git

結論

rubyXLでExcelを操作することは可能ですが、常に対象となるExcelで動作を確認する必要がありそうです。Apache POI と比べるとどちらが安定しているのかは試していませんが、関数などを使用しない、または自分で関数を設定するのであればそれなりに安定的に利用できるように思います。

また、極端に対象となるセルが多いシートについてはパフォーマンスやメモリ使用量の観点から厳しいケースが考えられます。rubyXLはブックオープン時に全シート(XML)を読み込むため、読み込み対象のセルデータが多いほど実行時間がかかります。この辺も課題かもしれません。