AxlsxでExcelファイルをcell単位でprotectionをした話

Axlsxでエクセルと戯れる日々です。
2シート目とかにマスタデータを保持しておいて、1シート目でプルダウンでマスタデータを選ばせたりしています。
変更できないように2シート目はシート全体を保護しております。

今日はAxlsxでセルの保護をしたお話です。

これググっても全然出てきませんでした。

結論から言うとこれでできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'axlsx'
require "securerandom"

package = Axlsx::Package.new
sheet = package.workbook.add_worksheet(name: 'lists')
sheet.sheet_protection.password = SecureRandom.uuid
locked = package.workbook.styles.add_style :locked => true
unlocked = package.workbook.styles.add_style :locked => false

sheet.add_row(['品名', '単価', '数量', '計'], style: unlocked )
sheet.add_row(['にんじん',    80, 1,      '=B2*C2'], style: unlocked)

sheet.rows[0].cells[0].style = locked # A1をロック(lock cell => A1)

package.serialize('test.xlsx')

この答えの出し方が最終的にRubyのことはRubyに聞くという方式で解決したのですが、
この解決の仕方がRubyエンジニアっぽいなと思ったのでどうやってこの結論に辿り着いたかダラダラ書こうかと思います。

まずセルの保護をしたいという要件がありました。
コピペエンジニアの端くれとしてまずはググります。

行単位のロックのお話は見つけましたが、セルの保護ではいい結果がありません。
ruby on rails 3 - How do I protect header rows but allow to enter new rows using AXLSX? - Stack Overflow

テストは下手なドキュメントよりも役にたつと誰かが言っていたので(実際の使い方のサンプルが見れるので)Axlsxのテストを見てみます。
bundle open axlsxでaxlsxのインストールされたディレクトリを開いて、grep protectとかやってみます。
いくつかヒットしたのでソースを見てみますがなんのこっちゃわかりません感じです。

とりあえずgrepでひっかかった
Axlsx::CellProtection.new
をキーワードにグーグルで検索してみますが有用な情報は皆無です。

一旦途方にくれます。コーヒーとか飲みます。

おもむろに再度irbを立ち上げます。
とりあえず怪しいものがないかコンソールから確認していってみることにします

1
2
3
4
5
require 'axlsx'
package = Axlsx::Package.new
sheet = package.workbook.add_worksheet(name: 'lists')

sheet.add_row(['品名', '単価', '数量', '計'])

と打ちます。

cellに保護をかけたいのだからcellオブジェクトにプロパティがあるんじゃないかとあたりをつけます。

sheet.cellとうちます。そんなんねーよと返ってきます。
sheet.cells と打ちます。なんかコンソールに文字がたくさんでます。

A1のセルを取得してみます。
sheet.cells[0,0]とうちます。空の配列が返ってきます。
sheet.cells[0]とうちます。なんか一杯でてきます。
sheet.cells[0].classとうちます。 # = > Axlsx::Cell
お、なんかいいの返ってきました。
sheet.cells[0].methods.grep(/protect/)とかうってみます。 [:protected _methods]しか返ってきません。
一回煮詰まります。 sheet.cells[0].valueとかここで色々他のことをやってみますが、なかなかうまくいきません。

そもそもセル単位で値とか設定できるんかと思いつきます。
ググります。見つけます。

ruby on rails - Modify specific cell value using Axlsx gem given the column number and row numer - Stack Overflow

特定のセルを取得するのは以下の方法でした。
sheet.rows[0].cells[0]

sheet.rows[0].cells[0].methodsでメソッド一回全部のメソッドを出してみます。

sheet.rows[0].cells[0].pos # = > [0,0]
とか
sheet.rows[0].cells[0].reference # = > $A $1
とか面白そうなメソッドがあります。

その中にstyle=というメソッドを見つけました。
これってひょっとして行単位のロックであったロックの書式を設定できるのではないかと推測します。

そして出きたのが冒頭のコードです。

sheet.rows[0].cells[0].style = locked
だけでは不十分で
sheet.sheet _protection.password
でパスワードを設定しないといけないとか、保護をかけて必要な所をlockしたりunlockしたりしないといけないとか色々とはまりながらもなんとか答えを出せました。
今回は運も味方してくれたかなぁという気もします。

困ったときはインタラクティブにどんなメソッドがあるかどんな値が設定されているか確認しながら
試しながらやることで問題が解決できるというのは素晴らしいと思いました。

Comments