[IM]ContributorとSpecial Thanksの定義

ReadmeなどにINTER-MediatorのContributorとSpecial Thanksの項目があって、ともかく英語で書いてありますが、この区分をいちおうきちんと定義しておきます。もちろん、状況に応じてルールの増減はあります。

Contributor

  • コア部分に関連するソースコードをコミットあるいは提供した
  • レポジトリにまとまったサンプルを提供した
  • 開発進行において重要な決定を下し、それを遂行した
  • Webサイトを主体的に編集した。あるいはWebサイトにまとまったページを作成した
  • 貢献順。たとえば、コードの行数など。判断はとりあえず新居が行う

Special Thanks

  • バグレポートを具体的にコミュニティに公開した
  • イベントやサイト運用などのコミュニティ活動を支えた
  • 順番は姓のアルファベット順、Contributorになればこちらのリストからは落とす
  • 通常は氏名のみ、つまり組織名は書かないが、希望があれば書きますよ

こんなところかと思いますが、ご意見ありますでしょうか?

[IM]日付時刻関数の実装

INTER-Mediatorに日付時刻関数を実装しようとしています。というか、少し実装しました。とりあえずの目標はマニュアルに書いた関数のサポートです。クライアントサイドで動かすので、JavaScriptの仕組みとうまく連動させないといけません。しかし、そのままのスペックはちょっとどうかと思い、このような仕組みを考えました。

日付や時刻は原則として整数あるいは小数点数を取るにしても、連続した数値になります。この点は問題ないのですが、JavaScriptなら1ミリ秒が「1」、Excelだと1日が「1」というように、そのルールを知らないといけませんし、処理系によっては1秒が1の場合もあります。覚えておけばいいといえばいいのですが、決まっているから書かないという状況になると、つどつど調べないといけません。これは不便です。

この1単位の問題に加えて、データベースでは、DATE、TIME、DATETIMEという3つの型を併用します。もちろん、1ms=1にまとめるメリットはあるかと思いますが、あえて、INTER-Mediatorの中では、2つのスタンダードを作ることにしました。

  • 1日=1、つまり、DATE型フィールドに対応(date関数で生成=日付データ)
  • 1秒=1、細かい点はさておいて、TIME、DATETIMEに対応(datetime関数で生成=日時データ)

とします。日付の計算は日単位、日付時刻の計算は秒単位とするわけです。つまり、

  • date(‘2014-10-8’) – date(‘2014-10-6’) → 2
  • datetime(‘2014-10-8 09:00:00’) – datetime(‘2014-10-6 21:00:00’) → 129,600(36時間)

とすることで、データベースの型と、日付時刻関数の結果の対応付けを考えました。

ちなみに、JavaScriptではなぜgetMonth関数だけが0〜11になって実際の月の数値を得るには+1しないといけないのかとか、見方を変えればgetDate関数が0スタートになっているべきなのではなど、疑問は多々わきます。日付計算のしやすさなどの理由はあるかもしれませんが、ここがけっこうJavaScriptのはまりどころだったりします。

そこで、日付時刻の要素取得(つまり月や日を数値として取り出す)関数と、特定の要素に対する計算を行う関数を用意すれば、結果的には「見える通りの数値」として扱えるのではないかと考えました。たとえば、月を得る関数がmonth()だとして、11月なら11という数字が得られ、加えて3ヶ月後などの日付を求めるaddmonth(d, x)があれば用途は足りると考えたわけです。しかし、日付と日時というダブルスタンダードを持ち込むデメリットがここで発生します。整数化された数値を見て、どちらの型なのかがわかりません。結果的に、

  • 日付データの月を返す:monthd(x)
  • 日時データの月を返す:monthdt(x)
  • 日付データに指定した月数を加えた値を返す:addmonthd(x)
  • 日時データに指定した月数を加えた値を返す:addmonthdt(x)

という手法にならざるを得ないと考えました。関数が増えるだけ面倒もあるかもしれませんが、要素取得や計算の関数の末尾が「d」なのか「dt」なのかという点を注意すればいいので、さほどややこしいルールとも考えられません。

しかし、このままだと、month関数やaddmonth関数の存在が気になります。「なし」でもいいのですが、名前的にはもったいないです。考えたのは、なんらかの設定で、日付計算するか、日時計算するのかということを、定義ファイル上のオプション指定で決められるようにするという手法です。たとえば、デフォルとは日付としても、何かの指定をすれば日時になるという具合です。キーワードなどはまだ考えていません。つまりこういう関数があるということです。

  • 月を返す(単位は設定依存):month(x)
  • 指定した月数を加えた値を返す(単位は設定依存):addmonth(x)

 

関数の数は増えますが、把握できる内容ではないかと思います。

タイムゾーンもまともに考えれば頭が痛いですが、日時の解釈の問題と考え、そしてデータベース内ではタイムゾーンは一定にする、あるいはしたいぞと思うことが普通なので、日付あるいは日時データの書式化の問題かとも考えていますが、ここはまだじっくり考えていません。

以上のような方針で実装を考えていますが、どうでしょうか?ちなみに、今現在レポジトリにあるものは、date関数とdatetime関数は実装されています。

JavaScriptの日付のパースはあまりに微妙

JavaScriptの日付や時刻の処理ははまりどころが多いのでも有名ですが、こんなはまりポイントを発見したので、まとめておきます。JavaScriptのDateクラスを使えば日付時刻を記録できるオブジェクトです。getTime()メソッドで1970年1月1日からのミリ秒経過時間(基準からの経過時間)を得て計算するのが一般的なパターンです。文字列で与えられた日付や時刻があったとき、Dateクラスのスタティックメソッドのparseにより、基準からの経過時間が得られます。MDNのドキュメントには、RFC2822 or ISO 8601に従った文字列ならOKとなっています。前者は「Oct 5, 2014」、後者は「2014-10-05」といった文字列です。

では、「2014-10-5」でも一瞬いいのじゃないかなと思って調べてみました。実は、この形式はISO 8601のルール上では間違いです。月と日は常に2桁である必要があります。まず、Chromeです。最初の2行を見れば、一見すると、2014-10-05でも、2014-10-5でもいいように見えます。しかし、Date.parse()の数値を見ると、右側の実行結果の2行目になりますが、ちょっとだけ違う数値になっています。3月3日について、月日の1ないしは2桁の全ての組み合わせてやってみたのが、引き続く4行です。最初の2行に戻ると、9時間の差があります。

shot8846

つまり、Chromeでは、ISO 8601に従った「2014-10-05」は、日本時間の10月5日のAM 9:00であり、そうでないものは標準時での10/5 0:00になるとうことですね。つまり、日本時間にすると9時間前なので、ちょっとだけ小さな数字になります。

Firefoxを見てみましょう。なんと、「2014-10-5」などのISO 8601のルールをはずれたものは、エラーとなります。正しいといえば正しい。

shot8847

Chromeで次のような式を計算したら、-1になったんですよね。0になることを期待しますが、そうではなかった。それで気づきました。それに、Firefoxでは結果がでないということもありますね。

Date.parse("2014-10-5") / (60*60*24*1000) - Date.parse("2014-10-05") /(60*60*24*1000)

ほんとにはまりどころ満載です。

やはり値段相応だった激安スマホ

2年ほど前に携帯電話を持つことをやめて、iPod touch + Wi-Fiルータで過ごしてきたのですが、iPhone 6が欲しくなり、今はSIMロックフリーのiPhone 6を使っています。しかし、それに先立つ8月に、Wi-Fiルータの調子が悪くなり、iPhone 6は発表されてないものの9月にリリースされるのは見え見えな時期だけにどうするか…。で、しかも、どうしても電話連絡必要な仕事が直後に何日かあり、迷ったのですが、freetelのprioriと、ビッグカメラのIIJmio。ルータではDTIのServerMan SIMだったのですが、何かの都合でうまく動かないと困るので、ちょっと出費がかさむけど、別途SIMを買ったのです。prioriは1万円ほどで、他のスマホと比べると激安です。ともかく、iPhone 6を買うまでの間、このラインナップで行こうとしたわけです。

このprioriはすでに発売されている機種のリモデルみたいだったので、それなりに動くのかと期待をしました。ブログ等で書かれている記事がほとんど見つからず、あまり売れている感じがしないので、若干不安だったのですが、まあ、人柱の1本にもなるかと思って思い切って買いました。いろいろ不便はあるのですが、自分としてはデザリングさえ動けば、iPod touchが外出時に使えるのでそれでよかったということもあります。

最初にはまったのは、メモリの少なさです。そのためにmicro SDが付いているのだということに買ってから気づきました。数個程度しかメイン領域にはアプリケーションが入りません。いろいろ試行錯誤して、何も入れないことにしました。iPod touchで使うことにしました。まあ、LINEくらい…と思ったのだけど、LINEは同じアカウントを複数のモバイルデバイスに入れられないので、結局、prioriは単にWi-Fiルータとして使うことにしたのです。まあ、これは仕方ない。

しかし、使っていて、なんか不安定なこともあるのと、携帯電池で充電しようとしても充電がされていないことに気づきました。電池との相性とかケーブルとかと思ったけど、原因はすぐにわかりました。USBケーブルを少しだけ動かしただけでも、切れている状態になることがわかりました。コネクタはあまりロックする感じがなく甘い感じなのですが、ぐっと最後の最後まで突っ込むと、充電中となります(下の図左)。しかしながら、少しだけコネクタを動かすと、充電中にはなりません(下の図右)。右側のコネクタがすこーしだけ浮いているのがわかりますか? 画面の横幅は5.0cmです。(写真の上下を分けるような線が入っていますが、上半分のコントラストをいじりました。合成ではありません)

IMG_1015

コネクタの部分だけを拡大してみましょう。こんな、1mm以下の「抜け」で、電源が供給されなくなってしまいます。携帯電池を繋いだ時には確かに充電マークになっているのだけど、カバンに入れて少しケーブルが引っ張られるだけで、切断状態になっていたようです。

IMG_1016

充電するには専用のUSBケーブルが必要です。そのケーブルも少し太い感じで反動があるのです。なので、ケーブルをぐっと突き刺して、そのままテーブルの上にポンと置くと、ケーブルの反発力で微妙にコネクタに力が加わり、切断状態になってしまいます。特急列車の指定席で窓際に置いて充電していたのですが、振動ですぐにコネクタに力が加わり切断状態になりました。

これは、「抜けやすい」という状況ではなく、「ほとんど繋がらない」という状況です。コネクタの部分、なんとかならなかったのかと思います。充電するには、机の上にそーっと置いておく必要があります。外で電源がなくなったときには、2回ほどやりましたが、priori自体を手持ちするしかありません。カバンに入れると、即座に切断状態になります。

同社より同じデバイスと思われる機種のものが以前から売られていて、春頃に一度販売終了となり、prioriという名前で再発売された感じの機種だと思うのですが、作っている側、売っている人たち、この問題に気づかなかったのでしょうか? 売られていたのだから使ってきた人たちは不便がなかったのでしょうか? この機種に賭けた訳ではないので、まあ、1ヶ月ちょっと不便に使わせてもらいましたが、まさに値段相応というのはこういうのかというのが正直な感想です。

[IM]コンテキストの共有化とPusherの利用

INTER-Mediatorでは、「コンテキスト」は、データベースに対するデータの出入り口的なイメージのものであり、検索条件などでの意味づけされたデータソースを意味します。その「共有化」とは、同一エンティティが複数のページ上のオブジェクトに展開されているとき、1つのエンティティを変更すると、その結果が他のオブジェクトにも反映される仕組みと定義します。Ver.4.4までに、単一ページ内のコンテキストの共有化が実現しています。つまり、あるページ上に、同一フィールドとバインドした要素があるとすると、一方を変更すると、もう一方は自動的に更新します。この動作を実現するためのプログラミングは必要なく、バインドの設定(ターゲット指定の付与)だけで可能です。

Ver.4.5に向けて、コンテキストの共有化をマルチクライアントで実現する仕組みを開発しており、概ね動くところまできました。つまり、同一のページを複数のクライアントで参照しているとき、誰かがデータを変更すると、その結果は他のユーザのページにも反映されるという動作が典型的です。従って、1つのフィールドを単一の要素にバインドしている場合でも、マルチユーザつまり複数のブラウザで同一のエンティティをバインドしているという点で「共有化」されていると言えるわけです。

コンテキストの共有化を実現するために、ページファイル上でのターゲット指定や、定義ファイルでのコンテキスト定義以外に何をしなければならないかをこの文書にまとめておきます。単一ページ内のコンテキストの共有化は特別な仕掛けは不要です。しかしながら、マルチクライアントでのコンテキストの共有化では、WebRTCを利用したPusherというサービスを利用することにしました。試用程度なら無償ですが、実運用には有償となってしまうものの、開発の効率化のために利用することにしました。

Pusherアカウントの取得とアプリケーション登録

Pusherのサイトでアカウントを取得します。Pusherでは「App」という単位で管理ができるので、たとえばINTER-Mediatorで作る1つのソリューションを、1つのPusherのAppとして登録するという方法もありますし、複数のソリューションで共有してもいいかもしれません。いずれにしても、アカウントを作成し、New Appというボタンなどで新たに1つのAppを作成します。ページ上に表示されるapp_id、key、secretの3つの情報がこの後に必要となります。

Pusherのサーバプログラムのインストール

PusherのサーバモジュールはPHP版を利用します。こちらのレポジトリをダウンロードし、そこから得られるlibディレクトリにあるPusher.phpという1つのファイルだけをサーバにインストールします。他は使用しません。ファイルはPHPの設定ファイル(php.iniが代表的)で、include_pathの設定で参照できるディレクトリにあればかまいません。もっとも安直な方法は、INTER-Mediatorフォルダに入れて、サーバにコピーしておくことです。もし、設定が以下のようなものであれば、例えば/usr/lib/phpディレクトリにPusher.phpをコピーしておけば良いでしょう。

include_path = ".:/usr/lib/php/pear:/usr/lib/php"

ページファイルへの追加

Pusherのクライアントソフトウエアを、ページファイルで組み込む必要があります。たとえば、以下のように、ヘッダ部で定義ファイル(include_MySQL.php)の読み込みの前に読み込みます。この方法だと、Pusherのサイトから直接取り出すので、ファイルを自分のサーバにコピーする必要はありません。ソースはこの通りコピペで大丈夫ですが、Pusherのバージョンが変わった時などはそれに合わせてください。

<html>
<head>
    :
    <script src="http://js.pusher.com/2.2/pusher.min.js" type="text/javascript"></script>
    <script type="text/javascript" src="include_MySQL.php"></script>
</head>

定義ファイルあるいはparams.phpへの追加

Pusherで定義したAppに関する指定は、定義ファイルのオプション部あるいはparams.phpで指定をします。原則的にはどちらか一方で定義をしてください。両方指定すると、定義ファイルの方が優先されます。定義ファイルでは、pusherをキーにした配列を定義し、さらにPusherのAppで示された3つの値を配列の各要素の値とします。以下は、定義ファイルでの定義例です。

IM_Entry(
    array( 
              /* コンテキストの定義 */ 
       ),
    array(
        :
        'pusher' => array(
            'app_id' => '1234',
            'key' => '9876543210',
            'secret' => '9876543210',
        ),
    ),
    array('db-class' => 'PDO'),
    false
);

params.phpファイルに記述するときには、以下のように、$pusherParameters変数に同様な配列として定義をします。

$pusherParameters = array(
 'app_id' => '1234',
 'key' => '9876543210',
 'secret' => '9876543210',
);

上記のいずれかがあると、マルチクライアントのコンテキストの共有化がオンになります。定義ファイルあるいはparams.phpの指定の有無だけで、共有化の利用/不使用が決まります。指定がないと一切何も行いません。指定があるのに、Pusherのサーバあるいはクライアントソフトウエアが利用できない状態になると、なんらかのエラーが発生します。

現状での制約

レコードの追加においては、そのコンテキストの検索条件を加味して、検索条件に合わないレコードの追加は行いません。しかしながら、別のクライアントで作成したレコードが当初はコンテキストに合わないものの、フィールドの値を変更してコンテキストの検索条件に合うようになっても、現状ではそのレコードが見えるようにはなりません。

さらに、コンテキストのソート条件は現状では加味されておらず、一連の表示リストのサイトに常に追加されます。

[IM]コンテキストに対応したモデルの実装

以前に「コンテキストの内部実装」という記事を書きましたが、コンテキストというか、正確には、コンテキストに対応したモデルの実装について、書いておきます。内部のソースを読んでいない人にはなんのことかさっぱり分からないと思います。

この仕組みを実装しているのは、INTER-Mediator-Context.jsファイルにあるIMLibContextクラスです。そこにあるいくつかのプロパティのデータ構造は、コードを読む場合や改造では絶対に必要な知識なので、一度まとめておこうと思っていました。

以下、「recKey」は、レコードを特定する文字列で、「キーフィールド=その値」という文字列です。たとえば、”id=3″のような文字列に相当します。そして、以下の「key」はすべてフィールド名の文字列が入ります。「portal」はFileMaker Serverを使う場合に、定義ファイルで’portal’=>trueにしている場合の、関連レコードに対応する外部キーの値です。FMS以外ではportal引数は渡されません。以下、portalが有る場合とない場合のデータ形式を記述します。

まず、IMLibContextクラスのプロパティstoreには、実際のフィールドのデータを記録します。store自体がオブジェクトで、以下のようにアクセスすることで、フィールドの値Valueにアクセス可能です。

this.store[recKey][key]→Value
this.store[recKey][key][portal]→Value

次に、プロパティbindingです。これは、データベースのフィールドが、どの要素に結びついているのかを記録するオブジェクトです。データベースから、バインドしているノードを求めるために用意しています。右辺は、オブジェクトの配列になっています。IdValueはバインドしたノードのid属性値です。Targetは、そこでのターゲット指定です。なお、Target指定は “” の場合もありますが、そのときにはTargetの値は “_im_no_target” という文字列にしています。

this.binding[recKey][field]→[{id: IdValue, target: Target}, ...]
this.binding[recKey][field][portal]→[{id: IdValue, target:Target}, ...]

次はcontextInfoプロパティです。こちらは、バインドしたノードから、データベースのどのレコードの、どのフィールドなのかを求めるためのものです。要するに逆テーブルです。thisと、.contextは同じものですが、thisが使えない状況を考慮してプロパティを作ってあります。

this.contextInfo[nodeId][target].context→コンテキスト自身
this.contextInfo[nodeId][target].record→recKeyの値
this.contextInfo[nodeId][target].field→keyの値
this.contextInfo[nodeId][target].portal→portalの値

これらの3つが主要なデータ構造となります。

FileMaker 13で、XMLおよびXSLTによる出力

久しぶりにタイトルのような事をやりました。この記事のハイライトは、変数でXSLTファイルの位置を指定できるようになったものの、エラーが出るという事象に遭遇しますが、その対処方法が分かったということです。

XML出力は、FileMaker 5くらいからありますし、出力するXMLデータにXSLTを適用して、テキストやらHTMLやらを生成することができるので、ちまちまと文字列処理を書かなくてもいいので便利です。XSLTの習得が大変という話もありますが、今時この種の情報は「基本知識」でしょうから、勉強すればいいのです。私自身は幸いにも、今はなくなってしまったXSLTでのWeb公開の時代に、かなり勉強していたし実稼働させたソリューションもあったので、自分的にはXSLTはOKです。この記事では、XMLやXSLTあるいはそれらを利用するFileMakerの機能については詳細には説明しませんので、必要な方は予習をしてください。

現実にXML/XSLTを使うとき、当然ながらXMLはテーブルを出力するときのフォーマットなので、テーブルから生成されることになります。一方、XSLTはどうでしょうか? 確実は方法は、Webサーバ等に配置して、URLで指定することですが、データベースファイルと別の管理対象ができて不便です。そこで、XSLTのテキスト自体をテーブルのフィールドにテキストとして保存しておくことで、データベースと一体化して管理ができて便利です。ただし、そのXSLTのテキストを利用するときにはファイルにしなければなりません。現実にはスクリプトを組むとして、1フィールド、1レコードだけのタブ区切りで出力すれば、余計な “” などは含まれません。ただし、その1フィールドに、改行が入っていると、コード番号12のものに置き換えられます。後から変更するのは面倒なので、XSLTのテキストから改行を除去する計算式を設定した計算フィールドをエクスポートすればいいのです。もちろん、XSLT内では改行の有無で動作が変わらないにしなければなりません。

さて、そこで、このXSLTファイルをどこに保存するのかということですが、WindowsでもMacで稼働できるようするには、テンポラリフォルダがいいのではないでしょうかということで、次のようなスクリプトで変数にパスを入れることにしました。順当な方法かと思います。

スクリーンショット 2014-06-26 10.47.20

この変数$templateJの結果を利用して、XSLTのテキストをエクスポートします。前に説明したように、スクリプトで、1レコードのみを対象レコードとして、改行を省いたテキストのフィールドだけをタブ区切りで出力します。出力ファイルに、変数$templateJを指定します。

ここで、XSLTによる変換をスクリプトのどこかで組み込むとします。スクリプトステップの「レコードのエクスポート」で、「出力ファイルの指定」を行い、OKボタンを押すと、ファイルタイプが「XML」の場合はさらに次のダイアログボックスが表示されます。ここで「XSLスタイルシートを使用」を選択し、ファイルを指定するわけですが、「ファイル」だとダイアログボックスが出て来て、特定のファイルの指定が必要です。Get ( テンポラリパス ) は一定のパスではない模様ですし、Win/Macでパスは違います。なので、「計算」を選択して、前の変数$templateJを指定して、実際に動かしてみます。ボタンと計算式の文字列が重なっているところにFileMaker社のこの機能への姿勢が見て取れます。

スクリーンショット 2014-06-26 10.46.40

すると、XMLのエクスポートをするときに、次のようなエラーが出ます。SAX(XMLのパーサ)のエラーがそのまま見えていて意味が分かりにくいですが、ようするにファイルが見つからないということです。Windowsでは、実際に見つからなかったパスも見えています。

スクリーンショット 2014-06-26 10.49.33

変数$templateJで出力し、その直後に変数$templateJでファイル参照して見つからない。ようするにバグなのですが、バグで使えません、以上ということでは前に進みません。バグは直らない可能性もあるからです。

そこで、変数$templateJの内容を、MacとWindowsで調べ見ました。Macだと、「/Macintosh HD/var/tmp/….」みたいになっています。Windowsだと、「/C:/Users/msyk/….」となっています。まず、Macは、明らかにパスの最初のコンポーネントを取り去れば、存在可能なUNIXパスになります。Windowsは…ともかく、まず、XSLTのテキストをエクスポートした後、以下のような「変数を設定」スクリプトステップを追加して、最初のコンポーネントを省いてみました。

スクリーンショット 2014-06-26 10.46.58

Macでは、予想通り、それでエラーなく動きました。一方、Windowsは別の対策をしないといけないかと思ったのですが、なんと、ちゃんと動きました。UNIXみたいなパスの与え方でどうやら動くようです。

XSLTのTipsを少し書いておきましょう。関連レコードのフィールドは、TOさえあれば出力できると思っていたら、ポータルに展開しないと出力されません。ポータルがない場合は、ちゃんと警告メッセージを出して、最初のレコードの値だけが出力されると言われます。ということで、ポータル作成はさぼれません。

関連レコードのデータは、たとえば2つのフィールドがあるとして、関連レコードが3つあると、次のように出力します。このノードの上位ノードのタグはROWになります。

<COL><DATA>1</DATA><DATA>2</DATA><DATA>3</DATA></COL>
<COL><DATA>abc</DATA><DATA>def</DATA><DATA>ghi</DATA></COL>

上記の2つのフィールドの番号を1、2とします。ROWタグがカレントノードのとき、上記のデータを展開するのにfor-eachを使うのですが、同じポータルにある関連レコードがいくつかるとき、for-eachをそのうち1つのフィールドに対して行います。次のような感じです。ネームスペースの略号は、xsl、fmrとしています。

<xsl:for-each select="fmr:COL[1]/fmr:DATA">
    <div>
        <xsl:value-of select="." />
        <xsl:value-of select="../../fmr:COL[2]/fmr:DATA[position()]" />
    </div>
</xsl:for-each>

最初のfor-eachは普通にDATAタグが複数あるので、それを1つ1つ取り出します。3行目の「.」によって、1つ目のフィールドの現在のDATAのテキストを取り出すので、「1」「2」「3」という値が順次取り出されます。2つ目のフィールドは、4行目にあるように、XPATHの記述で、カレントノードからROWまで「../..」でさかのぼり、そこから、COL、DATA集合へと下ります。そして「position()」というのは関数です。for-eachでループしているときに、何番目なのかを示す値を返します。これで、「abc」「def」「ghi」という文字列が順次得られます。

最後に大サービスで(笑)、FileMakerのXML出力向け(ただし、FMRXMLRESULTスキーマ)の汎用的なテンプレートを書いておきましょう。コピペして、あとはデータの順序に合わせてFMPXMLRESULTやROWタグ要素に対応するテンプレートにHTML等で出力テンプレートを書くだけです。

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    xmlns:fmr="http://www.filemaker.com/fmpxmlresult"
    exclude-result-prefixes="xhtml fmr">
<xsl:output encoding="UTF-8" method="xml" indent="yes"
   omit-xml-declaration="yes"/>

<xsl:template match="fmr:ERRORCODE | fmr:PRODUCT | fmr:METADATA">
</xsl:template>

<xsl:template match="fmr:FMPXMLRESULT">
    <html><body><table>
       <tr><th>Name</th><th>Activity</th></tr>
       <xsl:apply-templates />
    </table></body></html>
</xsl:template>

<xsl:template match="fmr:ROW">
    <tr>
        <td><xsl:value-of select="fmr:COL[3]/fmr:DATA[position()]" /></td>
        <td>
            <table>
                <xsl:for-each select="fmr:COL[1]/fmr:DATA">
                   <tr>
                       <td><xsl:value-of select="." /></td>
                       <td><xsl:value-of select="../../fmr:COL[2]/fmr:DATA[position()]" /></td>
                   </tr>
                </xsl:for-each>
             </table>
         </td>
    </tr>
</xsl:template>

FileMaker Server 13トラブル対策メモ

Mac OS X Mavericks+Server v3上で稼働させているFileMaker Server 13が、再起動後ちゃんと動かなくなりました。トラブル対策をして動くようになったので、「今度また再起動したとき」に慌てないように、メモっておきます。

そもそも、あるデータベースに対して、スケジュールでスクリプトを動かそうとしたのでした。そうしたら、正しいアカウントで認証しても実行できるスクリプトがないと言われました。そんなはずはありません。他のパネルを見てみると、公開しているデータベースの一覧には何も出ていないのに、FileMaker Proからはデータベースを開くことができます。つまり、一覧がおかしいということでしょうか。ここで、Admin-Console再起動をするか、サーバの再起動をするか、非常に迷ったのですが、いろいろあってサーバの再起動をしました。正確には、30分待っても落ちなかったのでディスクアクセスをしていないタイミングでリセットしました。

その後、起動はして、FileMaker Proからのデータベース接続はできましたが、Admin-Consoleは開かず(ログインパネルすら出ない)、そしてWeb公開も失敗しています。Webサーバのデーモンhttpdが1つも上がっていない。

まず、やってみたのは、以下のコマンドの入力です。Admin-Consoleにログイン可能な管理アカウントは、ユーザ名がadmin、パスワードはpassword(もちろん架空のもの)です。まず、ここまでで、Admin-Consoleは接続できるようになりました。

fmsadmin stop adminserver -u admin -p password
fmsadmin start adminserver -u admin -p password
fmsadmin stop wpe -u admin -p password
fmsadmin start wpe -u admin -p password

しかし、まだ、Webサーバがこれでも動きません。次に行ったのは、次のようなコマンドです。

cd /Library/FileMaker\ Server/HTTPServer/
sudo touch stop
sudo touch start

つまり、FileMaker ServerのディレクトリにあるHTTPServerというディレクトリの中のstopおよびstartというファイルの更新日時を現在にします。別の記事で紹介した通り、このファイルが変更されると、FileMaker ServerのWebサーバが停止あるいは開始します。ここまでやると、実際にWebサーバが動き出しました。

fmsadminによるwpeサービスのstart/stopは不要なのかもしれませんが、Webサーバのデーモンhttpdを強制的に動かさないといけないのかもしれません。

ノートをつけてみた

コンピュータ使いにとって、メモにはこだわりがあるのが普通だろう。情報機器を常日頃から使いこなしているのなら、スマートにメモを取るなんてことは当然のことだ。…といいたいところではあるが、実際にはどうだろう? あれこれ試しては新しいサービスやソフトに乗り換えてみたり、戻ってみたりと試行錯誤する日々ではないだろうか?

そんな中、STAP細胞の真偽を巡って「ノート」という存在が明らかになった。そういえば、大学のときも化学系の連中はノートを使っていたような記憶があるが、私は電気系であり、あまり実験ノート的な縛りはなかった記憶がある。工学系はやはり再現性が最も基本だから、ノートをエビデンスとして使うということより重要なことがあるのだが、それはさておき、ふと思って日々の仕事の内容を「ノート」を付けることでどうなるかを見てみた。ただ、1冊は以前からずっと使っているのだが、案件用に1冊追加し、それまでのノートはそれ以外の用途と2冊を使う事にした。それまでの記録の付け方は後に説明しよう。

さて、5月の1ヶ月間でどのくらいのページになったかと言えば、案件用23ページ、その他9ページとなった。もちろん、ノートにすべてを書いているわけでなく、Macに向かって打ち込む以外の「記述」をすべてノートでやってみた結果である。とりとめのないメモや、ざっくりとした図や表などをノートに書いて行った結果である。

スクリーンショット 2014-06-12 16.17.58

それまでどうしていたかと言うと、まず、案件ごとのメモは、きわめてシンプルに、その案件のファイルがあるフォルダにテキストファイルで残していた。URLやアカウント、情報の断片など、とにかくばんばんとコピペする。

そして、まとまった情報は、EvernoteとFileMakerで作ったデータベースに保存する。当初はFileMakerだけだったのだが、iOSで共有するとなるとEvernoteの方が手軽である。特に、URLを入れればリンクになるとか、そういうのはFileMakerではできないわけではないが、はっきり言ってたんに面倒になるだけなので、現在は作る文書によって使い分けている。FileMakerはどちらかと言えば書庫的であり、EvernoteはWiki的に使っている。複数人が関わるプロジェクトではBacklogを使い、場合によってはRedmineに入ることもある。

さらに、「紙切れ」を用意している。最近は、日めくりの裏を永年愛用している。スタートレックやギターの日めくりを買っており、翌年はメモとして活躍するのである。

思い付いたとき、そこでテキストファイルが開いていれば書き込み、Evernoteが開いていれば書き込み、それも面倒なときにはまずは紙切れに書いておく。紙切れに書いたものは、一定期間後に電子的な手段にコピーするが、その一定期間を過ぎて不要になったものまではわざわざ入力はしないで捨ててしまう。どうでもいい情報は適当にスクリーニングされるということだ。この「一定期間」を置くというのはなかなかみそで、メモ情報が爆発しないという利点もあると考える。もちろん、面倒を避けているという消極的な意味もあるが、短期間で情報価値がなくなるものは、長期間後に復活する可能性は極めて低いと言っていだろう。

ノートにして不便な事は、1つの案件ベースでのトレースがしづらくなるということだ。そうやってノートを分けるということをすると、きっと今度はノートだらけになるのだろう。案件事にテキストファイルを作っている理由は、トレースがしやすいからでもあるし、何年か後に開くことを考えての事だ。

一方、ノートを書くという習慣は、考えたことに対して自己評価をする機会が与えられる感じがする。ガシガシと打ち込んでしまうとそれで終わってしまうのだが、手で文字を書くという時間の幅が考える余地を生み出すように思える。それはそれで悪い事ではない。

さて、これからどうしようか? やはり全面的にノートにするより、今までの方法にじわじわ戻すつもりである。コンピュータ使いとしては「ノートは不要」と言いたいところだが、ノートにはノートのいいところもある。iPadでJot!で手書きしていた時期もあるものの、やはりノートの方が疲れない気がする。だけど、ノートで全て行うのは、むしろきちんと整理ができない。結局のところは使い分けということになるのだろう。そして、日々の記録方法を改善することも仕事のクオリティを高めることになるのじゃないかと改めて感じる。

米国からカルガリ経由で帰国

バンクーバー経由でアメリカに入った帰りは、やはりエアカナダだったのですが、サンフランシスコからカルガリーまでUnited Expressで行き、そこで乗り換えるというスケジュールでした。これも、2014年6月の状況ですが、メモとして残しておきます。

往路は先にエアカナダだったのでいろいろな意味でスムーズだったのでしょうけど、復路は先にエアカナダのコードシェア便でスタートです。そうすると、エアカナダの事前のチェックインは、カルガリーと成田間のフライトしかできず、最初にサンフランシスコで乗るフライトは、空港に行かないとチェックインができません。ちなみに、エアカナダはiOSのPassbookですので、紙はまったく使いませんが、その前には昔ながらのチケットを持ってカルガリーまで移動することになります。

まず、出発のサンフランシスコからの便は、ユナイテッドとエアカナダのコードシェア便で、運用はユナイテッドです。エアカナダのカウンターは国際線側にしかないため、この場合は運用しているユナイテッドの、しかも国内線のターミナル3に行って手続きをすることになります。カルガリーはもちろんカナダなのですが、国内線扱いですね。自動チェックイン機で、パスポートをかざせば、問題なく、チェックインがそこでできました。荷物もそこであずけて、成田行きというタグを貼ってもらいます。そして、セキュリティチェックを受けて普通に乗り込みます。23Aという席だったのですが、いちばん後ろの席でした。小さなジェットで約2時間半でカルガリーです。なお、最初に登場するときにバッグを預ければ、そのバッグの扱いがどうなるかを紙でもらえます。飛行機では税関深刻の用紙を記入しておきます。

カルガリーではまず入国審査をします。そこでけっこう並んだので焦ったのですが、幸か不幸かカルガリー発の飛行機も遅れたので、ダッシュしなくて済みました。アメリカからの便だと全員入国審査があり、たままた飛行機が重なったのか、かなりの列ができました。入国にはビザは不要で(乗り換えだけだから)、単にどこから来て、どこに行くのかを言って、滞在時間くらいを言っただけです。アメリカの入国のように、指紋や写真は撮らないのですぐに終わります。その後、ターンテーブルに行って、まず自分の荷物を持ち、関税申告の用紙の回収場所を通り抜けます。そこを出たすぐのところに、エアカナダのカウンタがあって、そこで荷物を再度預けます。単にチケットのチェックをして、ベルトコンベアに載せるだけです。この荷物の移動を自分でやらないといけないということです。

そして、後は飛行機に乗り込みますが、工事中なのか、迷路のように入り組んだところを通貨して乗り場に行きました。そして、カルガリー発の便に乗る時に、入り口の係の人がパスポートチェックをします。Passbookのチケットとパスポートを渡し、検索をして確認すればOKです。iPhoneとパスポートを返されるときも、紙のチケットと同じように、パスポートにiPhoneをはさんで返してくれます。まあいいのですが、習慣って変わらないのですね。カルガリーからの便は半分ほどしか席が埋まってない感じでした。

ちなみに、サンフランシスコ空港まではいつもBARTを使っているのですが、到着時に何も考えずに往復分のチャージをしました。しかし、今回、朝早い便だったのでBARTで行く事ができず、シャトルで移動しました。チャージされたBARTチケットが余ってしまって困っています(苦笑)。