Excel大好きな人達
多くの案件ではExcel好きなプロパーとかExcel好きなプロパーとかExcel好きなプロパーが跋扈しており、確固たる理由はないがなんとなく「Excelライクで」とか言われることが非常に多いと感じています。
Excelライクと言わないまでも、Excelをアップロードできたりダウンロードできたりと言った要件は、非常にカジュアルに生じています。
「DXの意味わかっているの?」と言いたくなるのですが、これが原状なのでしょうがありません。
Railsライブラリの限界
ただ、Railsエンジニアなら一度は経験したことがあると思うのですが、RailsにはExcelをうまく扱えるライブラリがあまりありません。
ライブラリ | 機能 | 注記 |
---|---|---|
Axlsx | 新規作成から書き出しだけ | 新規作成のみで、テンプレートファイルからの新規作成もできない。複雑なワークシートを作ろうと思うと、コードが恐ろしく読みにくくなる |
Roo | 読み込みだけ | 読み込み専用 |
RubyXL | 読み書き可能 | 数少ない読み書き可能なライブラリだが、parse が恐ろしく遅いのであまり使い物にならない |
Pythonにはopenpyxlがある
一方、Pythonは世界中のデータサイエンティストが使っていることもあって、openpyxlという非常に使いやすいライブラリが存在して、ものすごく手軽に扱うことが可能になっています。
今回は、Rubyからopenpyxlを呼び出してみました。
コンテナにpython及びライブラリを入れる
今回はDockerを使っている前提で、Rubyの動いているコンテナにpythonをインストールします。システム管理やaws-cli
を動かすためにすでに入っているケースも多いと思います。
FROM ruby:2.7.4-alpine
RUN mkdir /app
ENV APP_ROOT /app
WORKDIR $APP_ROOT
RUN apk add --update --no-cache \
...
python3 python3-dev py3-pip && \
pip install openpyxl
...
コンテナを使っていない場合はvenvを使ったほうがいいと思いますが、本稿では触れません。
簡単なPythonスクリプトを作成する
ここでは、標準入力から{"A1": "Hello, World!"}
のようなJSONを受け取り、テンプレートのExcelファイルに書き込んだうえ、そのExcelファイルを標準出力に書き出すスクリプトを作成しました。
メインのロジックをあまりRailsの外におきたくなかったので、極力シンプルにしています。
import os
import sys
import json
import openpyxl
import tempfile
cell_values = json.loads(sys.stdin.read())
wb = openpyxl.load_workbook("spec/fixtures/template.xlsx")
ws = wb.worksheets[0]
for cell, value in cell_values.items():
ws[cell] = value
tmp = tempfile.NamedTemporaryFile(delete=False)
wb.save(tmp.name)
tmp.close()
with open(tmp.name, 'rb') as f:
sys.stdout.buffer.write(f.read())
os.unlink(tmp.name)
Ruby(Rails)からpopen
でpythonを呼び出す
excel = IO.popen('python3 python/to_excel.py', 'r+') do |io|
io.puts cell_values.to_json
io.close_write
io.read
end
excelにはExcelファイルのバイナリが入ります。ActiveStorageに入れるなり、send_data
するなりして料理しましょう。