Home
profile
User: [info]hisashim
Name: Hisashi Morita
page summary
calendar
Back December 2008
123456
78910111213
14151617181920
21222324252627
28293031
links
tags
    hisashim's Journal (2000-2008) - February 26th, 2006
    MVC Model
    ---------

    2005-07..2005-09ごろに書いたもの。

    --

    今さらながら、MVCがよく分かっていないことに気がついた。

    DocDiffという僕が作っているテキスト比較アプリケーションのいんちきクラス図を描いてみる。今まではMVCをそれぞれ別のクラスにするのが正しかろうと単純に考えてこのようにしていた。
         Model              View
                            Model             View
      +---------+       +----------+       +---------+
      |Document |       |Difference|       |View     |
      +---------+ 2   1 +----------+       +---------+
      |to_word()|-------|difference|-------|to_html()|
      |to_char()|       |new()     |       |to_tty() |
      |...      |       |...       |       |...      |
      +---------+       +----------+       +---------+
    

    アプリケーションの処理の流れをRubyで書くとこんな感じ。
      doc1 = Document.new("string to compare 1")
      doc2 = Document.new("string to compare 2")
      d =Difference.new(doc1, doc2, :by_word)
      v = View.new(d)
      print v.to_html
    

    でも考え直すと何だかおかしい。Differenceオブジェクトは一時的に生成して、名前のある変数に束縛して扱うけれども、実際のところModelではなく、2つのDocumentオブジェクトをあるアルゴリズムで比較したViewでしかないように思う。

    (SQLではselectでテーブルのviewから新しいテーブルを作って、さらにいろいろ操作できたような気がするけれど、それはSQLがOOPLじゃなくて手続き指向だからじゃないかと思うので、とりあえず置いておく)

    ModelといえるのはDocumentオブジェクトだけじゃないだろうか。つまりこう。
         Model              View              View
      +---------+       +----------+       +---------+
      |Document |       |Difference|       |View     |
      +---------+ 2   1 +----------+       +---------+
      |to_word()|-------|difference|-------|to_html()|
      |to_char()|       |new()     |       |to_tty() |
      |...      |       |...       |       |...      |
      +---------+       +----------+       +---------+
    

    でも、そうだとしたら、DifferenceクラスとViewクラスが別のクラスになっているのは、実装上区別したほうが分かりやすいという都合だけのような気がする。だったらViewクラスはclassではなくmoduleにしてしまって、Differenceクラスにmix-inしたほうがきれいな気がする。つまりこんな感じ。
         Model              View
      +---------+       +----------+
      |Document |       |Difference|
      +---------+ 2   1 +----------+
      |to_word()|-------|to_html() |
      |to_char()|       |to_tty()  |
      |...      |       |...       |
      +---------+       +----------+
    
      doc1 = Document.new("string to compare 1")
      doc2 = Document.new("string to compare 2")
      print Difference.new(doc1, doc2, :by_word).to_html
    

    どうせDocumentクラスだって中はCharStringやら何やらでできているんだし、DifferenceクラスにViewとしての機能を持たせたっていいだろう。きれいに書けさえすれば。

    しかしこれで正しいのかどうかよく分からない。小さなことでも正しくないやり方でやるのは性に合わないので、作業が滞っている。こんな小学生レベルのところで詰まってどうするという気もするけれど、分からないんだからしょうがない。青木淳さんのサイトでも見るか。

    Parametric Method
    -----------------

    DocDiffにはたくさんのオプションがあり、実際の呼び出しはこうなっている。
      doc1 = Document.new("Foo bar.\nBaz quux.", 'ASCII', 'LF')
      doc2 = Document.new("Foo.\nBaz quux.", 'ASCII', 'LF')
      docdiff = DocDiff.new
      expected = [[:change_elt,     ["Foo bar.\n"], ["Foo.\n"]],
                  [:common_elt_elt, ['Baz quux.'], ['Baz quux.']]]
      assert_equal(expected, docdiff.compare_by_line(doc1, doc2))
    

    将来はテキスト以外も比較できるように改良してこのようになる予定。
      doc1 = Document.new("Foo bar.\nBaz quux.", 'text/plain', 'ASCII', 'LF')
      doc2 = Document.new("Foo.\nBaz quux.", 'text/plain', 'ASCII', 'LF')
      docdiff = DocDiff.new()
      docdiff.update_config(config)
      expected = [[:change_elt,     ["Foo bar.\n"], ["Foo.\n"]],
                  [:common_elt_elt, ['Baz quux.'], ['Baz quux.']]]
      assert_equal(expected, docdiff.compare_by_line(doc1, doc2))
    

    内部で結果を得るにはこんな感じ。
      Difference.new(doc1, doc2,    # source1, source2 (Document)
                     'word',        # resolution, granularity (line, word, char)
                     'lcs'          # algorithm (lcs, ...)
      ).to_view('summary',          # terseness, conciseness, cut, truncation, entirety (whole or summary)
                'multiline',        # layout, arrangement (inline or multiline)
                'html',             # format (html, tty, manued, wdiff, ...)
                {:header=>'...'})   # tags
    

    でもパラメータ1種類につきcase文のwhenを手書きで1つ増やすのは嫌だ。それを避けるためには、CharStringでやったように、プラグイン構造にするのがよさそう。パラメータの実際の効果を定義するViewモジュール内の各moduleで、パラメータとしてメソッドに与えるシンボルを自己に結びつけて登録しておく。

    (続くかも)
    Todo, or things to improve
    --------------------------

    以下はかなり前、2005-02..2005-07ごろに書いたもの。0.3.3はこれをほとんど取り込まずにリリースしてしまったので、0.3.4に反映するかもしれない。

    DocDiff 0.3.3?
    --------------

    久しぶりにDocDiffのコードを見たら、最後に編集したときに何をしようとしていたのか全く思い出せなかった。解決策に気が付いたはずだったんだけれど、思い出せない。svn diffすると、テストケースが1個増えていて、メソッドの雛型が2つ増えている。何だこれは。どうするつもりだったんだ。仕事をやりかけにするんだったらコメントくらい書け。

    というわけで、読み解くのにしばらくかかったので、やりたいことのメモといっしょに書いておく。

    * multilineサポート

    cdifやdiff式の、複数行に分かれた表示をサポートする。機能の名前を決めること。
        --line=         same   | separate, --same,   --separate
        --change=       inline | separate, --inline, --separate
        --show-change=  inline | separate, --inline, --separate
        --counterpart=  inline | separate, --inline, --separate
        --display=      inline | multiline | 2columns | sidebyside | 3column...
        --display-type= inline | multiline
        --view-type=    inline | multiline
    

    流れはこう。どのコースをたどっても同じメソッドで処理できるようにすること。前は無理矢理m*n種類のメソッドを書いていたけど、*oが入ってきたのでもう無理。おとなしくまともな流れにすること。
    differenceのentryの[3],[4]にdoc内での位置を保持しておく? それともakr diffをもう一段包んで、意味のある名前で呼ぶ?(たぶん遅い)
                        m                o                n
    
                  /--[whole ]--\  /--[inline   ]--\  +--[html]--+
      difference--              --                 --+--[tty ]--+--format-->
                  \--[digest]--/  \--[multiline]--/  +--...   --+
    

    2つの行を作るのは簡単だけど(commonでもdelでもinsでもchangeでも、old行とnew行の両方にpushすればいい)、行の終わりを検知するのが難しそう。要素 == eolだったらそこで打ち切る?

    * DocDiffも、いつかはディレクトリの再帰的な比較をサポートしたい。無理か。

    * --ttyの種類と色を選べるようにする(cdifでいう-v, -nなど)。--tty-type=--vt100, --ansi, --monochrome, --color, --blackandwhite, --grayscale, --more, --less

    * ドキュメントにスクリーンショットを追加する(HTMLの標準と --charと--summary、それにtty, wdiff, Manued, user-definedも)。→0.3.3で追加した。

    * アルゴリズムを指定できるように(ライブラリに依存するので気が進まない)。

    * screenshotを配布パッケージに含め、ウェブに載せる。→0.3.3で追加した。

    * ViewDiffを作る。パッチを文字指向でハイライトする。GNU diffのdiff -cとdiff -uの両方をサポートするように。とりあえずDocDiffの一部分として配布する。

    * CharStringをライブラリとして独立させる?
    複数の文字コードと改行コードを扱うケースが多いので、他のスクリプトでもCharStringを使いたい。でもRuby自体のm17nが予想外に速く進むなら、待つのが吉か?

    --------

    digest(), whole(), inline(), multiline()はブロック付きメソッドとして実装して、それをinline(), multiline(), to_html(), to_tty(),...の中から呼び出して、異なる処理をブロックとして与えてやれば、きれいに書けるかもしれない。試しに書いて動かしてみたものを転記する。
    class Difference
      def initialize(src)
        @difference = src
      end
      def each_entry(&block)
        @difference.each{|entry| yield entry}
      end
    end
    
    class View
      def initialize(d)
        @difference = d
      end
      def each_entry(&block)
        @difference.each_entry(&block)
      end
      def html
        r = ""
        self.each_entry{|e|
          case e[0]
          when :common then r += e[1].join
          when :del    then r += "<del>#{e[1].join}</del>"
          when :add    then r += "<ins>#{e[2].join}</ins>"
          end
        }
        r
      end
    end
    
    d = Difference.new(
    [
     [:common, [:foo], [:foo]],
     [:del,    [:bar], []    ],
     [:add,    []    , [:baz]]
    ]
    )
    v = View.new(d)
    p v.html  #=> "foo<del>bar</del><ins>baz</ins>"
    
    Asset Management in CG Industry
    -------------------------------

    2005-02に書いたらしいメモを転記する。

    --------

    CG業界ではDAM(Digital Asset Management: デジタル資産管理)が当たり前らしい。出来上がるものの規模・かかわる人数・動く金額が違うとはいえ、うらやましい。

    * little things of mine * | asset management - 1
    http://www.ousam.com/blog/log/eid131.html

    最近,日本で制作をされている方にお会いすると,良く聞かれる事があります.それは,こちらの業界ではアセットの管理をどうしているのか,という質問です.自分が日本で制作をしていた頃は,あまり話題にならなかった事なので,こういう質問を受けるという事は,日本の制作環境も少しずつ改善されてきているのだな感じますが,今回はこの事について書きたいと思います.

    * little things of mine * | asset management - 2
    http://www.ousam.com/blog/log/eid132.html

    パイプラインとは,どんな方法を使い,どんな手順で制作するかを決めたルールのような物だと思って戴ければ良いかと思います.例えて言えば,車を製造するベルトコンベアの流れ作業みたいな物です.

    * little things of mine * | asset management - 3
    http://www.ousam.com/blog/log/eid133.html

    以上が基本的なアセット管理の方法です.想像するよりもずっと大きなシステムである事が分かると思います.

    自分が理解したところを箇条書きにしてみる。

    * データはローカルディスクには置かず、ネットワーク上の領域に置く
    * (ネットワーク上の)作業領域は、ポリシーに従って命名・整理されている
    * 共有するデータが完成したら、バージョン管理機能を持ったDBMSに登録(publish)する。するとバージョン管理されるようになる
    * データを中心にしたこの流れ作業的なワークフローを、パイプラインと呼ぶ。このパイプラインはスタジオによってさまざまに異なる
    * パイプラインを流れるものはすべてasset management systemの管理下に置く。何を管理下に置くかはスタジオやツールによって異なる。パイプライン構築の方法が各スタジオのノウハウになっている
    * ツール側でも、パイプラインに合わせるために必要に応じてカスタマイズを行う。通常、プロジェクトメンバの作業環境は、アセット管理システムと連携をとれるようになっている
    * アセット管理システムとその周辺は、AlienBrainのようなパッケージもあるが、たいてい各スタジオが独自に開発。こういう必要性から開発力は重視されている

    なるほど。どれももっともなことだ。

    shiroさんもSquare USA時代のアセット管理システムについてペーパーを書いて公開されている。この手の情報はただでさえ少ないので、これだけ面白く読ませてくれるものは大変ありがたい。

    Shooting A Moving Target: An Experience In Developing A Production Tracking Database
    http://www.shiro.dreamhost.com/scheme/docs/jlugm2000.html

    Tracking Assets in the Production of 'Final Fantasy : The Spirits Within'
    http://www.lava.net/~shiro/Private/essay/gdc2002.html

    でももっと詳細なことを知りたい。具体的にどういう要件で、どういうシステムを使って実現していて、どういうツールがサポートされていて、何人くらいが使っていて、どんな形式のどのくらいの大きさのデータを扱っているんだろう。それはOSSを使えば僕らにも実現できるものなのか。どのくらいの規模から導入してメリットがあるものなのか。導入と運用の注意点は何か。DAMには非常に興味があるのでもっと詳細が知りたい。差し支えない範囲で。

    というのも、規模は小さくても自分の仕事で同じようなことをやっているから。出版物の元データを中心にして、著者・監修者・訳者・監訳者・査読者・編集者・レイアウトオペレータ・デザイナなどがそれぞれの役割で製作物にかかわる。問題はパイプラインが分断されていることだ。編集者まではテキストベースで通すことは可能だけれども、その後に断絶があって、proprietryなバイナリ形式へと変換されてしまう。

    ----

    余談だけど、VCS(Version Control System)を流用してSCM (Software Configuration Management)やDAMのために使っているケースは少なくないと思う。特にSubversionはCVSに比べてバイナリデータの扱いが得意そうなので、注目している人は多そう。

    需要はあると思うので、OSSのSCMシステムやDAMシステムが今後どう発展するのか見守りたい。

    Advertisement