RubyでDomino(Notes)

<2014/9/18>

RubyGemsからさくっとインストールできるNotesgripに作り直しました。

はじめに

ウチのオフィスでは、情報蓄積/共有にDomino(Lotus Notes)を使っていて、データベース(DB)もたくさんある。

時々、Notes-DBの情報をExcelに書き出したり、以前作ったDBのデータを新規に作り直したDBに移す作業があったりする。 基本的にはNotes-DBのデータにアクセスするときはLotus ScriptというVBAベースのスクリプトをNotes-DB上のエージェントや ボタンに組み込んで処理する。

しかし、悲しいかなLotusScriptはVisualBasicをベースにしたスクリプト言語であり、使っていると実にストレスがたまる。 Notes自体は大好きで、これ無しには仕事ができない体になっているのだが、Lotus Scriptはどうしても好きになれない。

まぁ、DBに文書を保存するときに走らせるスクリプトや、一般ユーザーがDB上で操作するボタンなど常にDB操作とともに 動作するしくみではLotus Scriptを使うのはしょうがないとしても、たまにしか使わないような特殊なツールまでLotus Scriptを 使うのはできればさけたい。

幸い、DominoもCOMを使って制御できる。それならWIN32OLEを使ってRubyから制御して、楽しくお仕事をしよう。

Dominoのクラス構成

DominoもExcelと同じように、ほとんどの要素はクラスとして定義されていて、その要素を制御するためのメソッドや、各種属性を 参照したり変更するためのプロパティが用意されている。

主なDominoのクラスを以下に示す。

ノーツの主なクラス構成

NotesSession

Dominoの実行環境というちょっと抽象的なものを示すクラス。COMでDominoを制御するときは、まず、NotesSessionオブジェクトを生成し、 ここから芋づる式に他の要素を示すオブジェクトを生成していく。

NotesDatabase

Dominoのデータベースを示す。

NotesView

データベース内のビューを示す。ビューとは文書の一覧のこと。「作成者別」や「日付順」、「システム別」などいろんな観点で文書を 並べ替えて表示するための要素。

NotesDocument

データベース内の文書を示す。

NotesDocumentCollection

1つ以上の文書の集合。ビューなどからある条件で検索した結果を示す。NotesDocumentの配列のようなもの。

NotesItem

文書内のフィールドを示す。フィールドとは、各文書内で文字列や値や時刻などを保持するデータ領域のこと。ビューにはフィールドの 値を表示することができる。

RubyからDominoを制御する利点

DominoはR5.02からCOMによる制御ができるようになった*1。COMというのは、 Windowsが提供するオブジェクト間の通信機構のこと。

RubyもWIN32OLEという拡張ライブラリを使うとCOMを利用できる。RubyからDominoを制御する利点は、ずばり、「プログラムが楽しく組める」ということだ。 長年、NotesでいろんなDBを作ってきてLotusScriptも使っているが、LotusScriptによるプログラミングそのものにはいつもストレスを感じる。

Rubyが持つ強力な文字列処理とArrayやHashといった強力なデータ構造が自由に使えるのは魅力的だ。Rubyの文字列やArrayに 慣れてしまうと、LotusScriptの文字列や配列はどうしようもないくらい低機能だ。

LotusScriptでも自前のクラスを定義できるんだろうが、実は一度もクラスを自分で定義したことはない。関数や定数でさえ、あれほど面倒なのだから、 クラスの定義なんて死ぬほどまどろっこしいに違いない。Rubyではクラス定義はあたりまえのようにできる。

逆に欠点は、

RubyからDominoを使うサンプル(生のWIN32OLE)

以下の例は、Dominoのヘルプ文書から、文書のタイトルの一覧を表示するサンプルだ。

  01: require 'win32ole'
  02:
  03: ns = WIN32OLE.new('Notes.NotesSession')
  04: db = ns.GetDatabase("", "help/help5_client.nsf")
  05: view = db.GetView("目次")
  06: doc = view.GetFirstDocument
  07: while doc
  08:   form_name = doc.GetItemValue('Form')
  09:   subject = doc.GetItemValue('Subject')
  10:   puts "Form:#{form_name}, Subject:#{subject}"
  11:   doc = view.GetNextDocument(doc)
  12: end
  

これをみると、ほとんどLotusScriptと同じだとわかるだろう。Dominoが用意しているメソッドがそのまま使えるのはCOMとWIN32OLEのおかげだ。

ちなみに、LotusScriptだとこうなる。

  01: Sub Click(Source As Button)
  02:   Dim ns As New NotesSession
  03:   Set db = ns.GetDatabase("", "help/help5_client.nsf")
  04:   Set view = db.GetView("目次")
  05:   Set doc = view.GetFirstDocument
  06:   While Not(doc Is Nothing)
  07:     Print "Form: "+doc.Form(0) + ", Subject:" + doc.Subject(0)
  08:     Set doc = view.GetNextDocument(doc)
  09:   Wend
  10: End Sub
  

ちなみに、私がどのへんでストレスを感じるかというと、

Dominoの各オブジェクトをラップしよう

WIN32OLEを使えば、DominoのオブジェクトをRubyから扱える。しかし、どんなオブジェクトもWIN32OLEクラスの オブジェクトにしか見えないため、ちょっとあつかいづらい場面が出てくる。

デバッグしようと思って、p doc と書いても、みんなWIN32OLEとしか表示されないのはちょっと寂しい。やっぱり、NotesDocument オブジェクトであれば"NotesDocument"と表示してほしい。

例えば、NotesViewオブジェクトであれば、eachメソッドでイテレータが欲しいし、NotesDocumentクラスであれば、[]メソッドで 文書中のフィールド(NotesItem)にアクセスしたい。

このように機能を拡張できれば、RubyからDominoを制御するのはより楽しくなる。

そこで、RubydeExcelと同じようにDominoの各クラスをラップするクラスを用意し、 別のオブジェクトを得る主なメソッドはオーバーライドしてしまって、生のWIN32OLEではなく、用意したラップクラスの オブジェクト(内部にWIN32OLEオブジェクトを内包)を返すようにすれば、以下のような記述が可能になる。

  ns = Notes::NotesSession.new
  db = ns.database("","test.nsf")
  p db # -> NotesDatabase
  
  view = db.view("ALL")
  view.each {|doc|
    p doc['Subject'].text # "Subject"フィールドの内容を表示
  }
  

ちなみに、上の例でviewはDominoのNotesViewクラスをラップしたNotes::NotesViewクラスのオブジェクトで、内部にDominoのviewオブジェクトを指す WIN32OLEオブジェクトを内包している。

RubydeDominoライブラリ

RubydeDominoは、RubyからDominoを制御するための拡張ライブラリである。

ダウンロード

インストール方法

RubydeDominoのドキュメント

RubydeDominoの使い方。

LotusScriptユーザーのためのRubyレクチャー

RubydeDominoのサンプルをいくつか載せながら、Rubyスクリプトの書き方を説明していく。

ライセンス

ライセンスはRubyのライセンスに従います。

Copyright (C) 2003,2004 Oka Yasushi <yac@tech-notes.dyndns.org>

You may redistribute it and/or modify it under the same license terms as Ruby.

免責事項

本プログラムは無保証です。作者は、プログラム自身のバグ、あるいは、本プログラムの実行など から発生するいかなる損害に対しても責任を持ちません。

変更履歴

2004/7/7

Ver 1.00 Initial Release


*1R4.6でもできてたが、いまいち動作が不安定だった。