1. Ploneでの開発におけるベストプラクティス
    1. 基本
      1. 主要機能のプロダクトとしての分割
      2. サイトのカスタマイズ情報をまとめたプロダクトの作成
      3. 外部メソッドやスクリプトなどの使用の回避
    2. ファイルシステムでの作業
      1. お気に入りの編集環境の使用
      2. 魔法の呪文: grep!
      3. ソースコード管理
      4. 導入/複製
      5. 目標: ZODBにはスキンやスクリプトを保存しない。コンテンツと設定だけを格納する。
      6. ファイルシステム上の新規コンテンツの追加
      7. .metadata files for title, security, proxy roles
      8. デバッグモードとファイルシステム上のコンテンツの関係
      9. metadataファイルの例
      10. New FS-Stored Types
      11. Atonement for Your Sins - FSDump
    3. ソースコードの管理
      1. 複数人での作業
      2. 大昔のコードとの付き合い
      3. ブランチ
      4. Subversionとは
      5. 1分でわかるSubversion
      6. プログラマ以外にもお勧め!
      7. ソースコード管理システムのうまい使い方
    4. 設定管理
      1. Writing Setup Code
      2. 私は如何にして心配するのを止めてAPIを愛するようになったか
      3. CMF 1.5
    5. ドキュメントの作成
      1. Developer Documentation
      2. Useful Documentation Products
    6. Ploneのデバッグ
      1. デバッグ
      2. デバッガはみんなの友達
      3. リクエストの実行
      4. ZODBの操作
      5. ZODBの変更
      6. グラフィカルなデバッガ
    7. 開発・導入・同期
      1. 開発・導入・同期
      2. You Have a Laptop: Use It
      3. Subversion for Sharing
      4. Simple Sandboxes
      5. Synchronizing of Content
    8. ユニットテスト
      1. Testing
      2. Sample Test Setup
      3. Sample Test
      4. More information on unit testing
    9. クレジット

Ploneでの開発におけるベストプラクティス

ウィーンで行われたPlone ConferenceにおけるJoel? Burtonの発表をもとにして、このチュートリアルではPloneの開発におけるベストプラクティスを説明します。Ploneを用いてサイトの開発をしている皆さん。このドキュメントは必読です。なお、以下の説明は、ある程度ZopeやPloneになじみのある人たちを想定しています。

基本

誰もが知っておくべきこと

主要機能のプロダクトとしての分割

別のサイトでも再利用できそうな機能はすべて、個別のプロダクトにまとめてしまいましょう。たとえば、私は最近「"友達に教える"機能を、すべてのページでPloneコンテンツではなくURL単位で使えるようにする」という改造を行いました。実際にはCMFPloneの3つのスキンを修正するだけで対応できたのですが、私はそれをSendToURLというプロダクトとして分離しました。そうすることで、他のサイトでも同じ機能を簡単に利用できるようになったのです。また、もし将来それを修正することになったとしても、それほど手間はかからないでしょう。

サイトのカスタマイズ情報をまとめたプロダクトの作成

個々のサイト用に、そのサイト専用の「サイトプロダクト」を作成し、設定スクリプトや画像、スキンなどをそこにまとめます。このプロダクトには、そのサイト固有の情報のみをまとめます。

外部メソッドやスクリプトなどの使用の回避

外部スクリプトやページテンプレート、スクリプトなどを使用すると、それらはZODBの管轄外になってしまって保守が面倒になります。そういったものはプロダクトにまとめてしまいましょう。

ファイルシステムでの作業

ZMIではなくファイルシステム上で作業することの利点

お気に入りの編集環境の使用

これを甘く見てはいけません。私はvimを使用していますが、これは非常に高速に立ち上がります(なので、ウェブ経由でExternalEditorとして使用することもできます)。また、事実上すべてのサーバにインストールされています(なので、ssh経由でも使用することができます)。しかし、それらを考慮したとしてもなお、実際に使用しているエディタ環境(マクロ、スクリプトなどが設定された状態)で開発できることで生産性は倍増します。

魔法の呪文: grep!

grepとfindは、ファイルシステム上での作業に欠かせないものです。これらの威力を知ってしまうと、きっと「ZMIで作業していたあの頃はいったい何だったんだろう?」と思いますよ。

ソースコード管理

普段使っているソースコード管理ツールを使用することができます。

導入/複製

作成した環境を複数のインスタンスで使用することが簡単にできます。

目標: ZODBにはスキンやスクリプトを保存しない。コンテンツと設定だけを格納する。

最終目標は、ZODBに格納する内容を実際のコンテンツオブジェクトと設定内容だけにしてしまうことです。それ以外のスキンやスクリプトはすべてファイルシステム上で管理するようにします。

スクリプトやスキンを移動可能にしておくと、非常に便利なことがあります。たとえば、同じサイト内で場所によって違うロゴを使用することになったとしましょう。Zope的な手法としては、サイトのルートと(たとえば) /about に別々の logo.jpg を配置することになるでしょう。しかし、こうするとサイト内にスキンを残してしまうことになり、保守性が下がります。よりよい方法としては、ルートフォルダと /about フォルダにそれぞれ(たとえば logo_name などの)プロパティを作成することが考えられます。そしてそれぞれの場所で使用するロゴをそこに指定するわけです。そうすれば、実際のロゴをファイルシステム上で管理することができ、「ZODBで管理するのは設定内容のみ」という目標に近づくことになります。

ファイルシステム上の新規コンテンツの追加

CMFPloneのコンテンツタイプの例を参考にします。

CMFPloneを見れば、プロキシの設定やセキュリティ設定といったファイルシステム上の内容を使用する例が見つかります。みなさんの大好きなgrepを使って

grep -r --include="*.metadata" proxy *

としてみましょう。.metadataファイルでプロキシの設定を使用する例が見つかるでしょう。

実際に動作するZSQLMethodの例は、CMFPloneやCMFDefault、あるいはCMFPloneといったところには存在しません。ZSQLMethodsについての議論やZSQLMethodをファイルシステムに保存する例については http://plone.org/Members/pupq/reldb を参照ください。

.metadata files for title, security, proxy roles

These are the accompanying files that hold all the "other stuff" for objects, titles, security settings, proxy roles for PythonScripts, and more. これは、以前のバージョンのCMFで使われていた .properties ファイルと同じ働きをするものです。

ありがちなミスとして考えられるのは、あるスキンファイル (foo.py とでもしましょう) を新しいディレクトリにコピーしてカスタマイズしようとしたときに foo.py.metadata のコピーを忘れるというものです。.metadataファイルは同一ディレクトリになければならないことに注意しましょう。つまり、この場合、カスタマイズしたfooオブジェクトは用いられますが、もともとmetadataファイルに記録されていた設定は用いられないということになります。もしmetadataファイルにセキュリティやプロキシなどの重要な設定が含まれていたら、恐ろしいことになってしまいます。

デバッグモードとファイルシステム上のコンテンツの関係

デバッグモードの場合は、ファイルシステムのコンテンツに加えた変更がすぐに反映されます。デバッグモードではない場合は、Zopeサーバを再起動するかプロダクトのリフレッシュを行う必要があります。

デバッグモードでSpeedPackを使っている場合は注意しましょう。同じディレクトリで作業している限りは、スキンオブジェクトに加えた変更はすぐに反映されます。しかし、スキンオブジェクトを別のディレクトリにコピーしてカスタマイズしている場合は、SpeedPackをデバッグモードで実行していてもZopeはもとの場所にあるスキンオブジェクトのキャッシュを見てしまいます。新しい場所にあるものが反映されません。場所が変わったことをZopeに教えてやるには、Zopeを再起動するかプロダクトをリフレッシュする必要があります。

metadataファイルの例

register.cpy.metadataの例です。

  [default]
  title=Register a User
  proxy=Manager,Anonymous

  [validators]
  validators = validate_registration

  [actions]
  action.failure=traverse_to:string:join_form
  action.success=traverse_to:string:registered
  action.prefs=traverse_to:string:prefs_users_overview

New FS-Stored Types

It is possible to create your own FS-stored types. If there are non-contentish objects you use often, it's worthwhile creating the small script that will allow it to be stored on the filesystem. You can look at the code in CMFCore for storing the existing types (PageTemplates, PythonScripts, Files, etc.)

An example of this is FSExternalMethod. It is taking an existing Zope object type and creating a FS-stored capable version of it. Found in the product "FileSystemSite".

Atonement for Your Sins - FSDump

Sometimes, it's not possible to work on the filesystem. You may only have access to a web browser or have created many existing skin objects in the ZODB. FSDump will take existing skin objects and dump them out to the filesystem.

FSDump:

  • Dumps existing ZODB stuff to FS
  • You need to write dumpers for your FS-Stored types.

If you've written custom FS-stored types, you'll have to write your own Dump plug-in for this. This is quite easy to do--see the code in FSDump for examples of how it dumps the basic types.

At the very least — use FSDump for backups

ソースコードの管理

ソースコード管理の基本とリビジョン管理システムの使用法(セットアップ・日々の運用)

複数人での作業

もし複数人で開発しているのなら、ソースコード管理は必要不可欠です。ソースコード管理システムを使えば「ちょっと待って!それ、今こっちで修正中だから!」とか「こんな変な修正をしたのはいったい誰?」とかいった騒ぎは起こりにくくなります。ソースコード管理システムをうまく導入すれば、複数人での開発作業の効率が大きく向上するでしょう。

大昔のコードとの付き合い

自分がかつて書いたコード、あるいは誰か他の人が書いたコードの中身を解析するときに、コミットログは非常に有用な情報となります。これにより、バグ修正などの保守がやりやすくなるでしょう。

ブランチ

「何日かかけて新しいアイデアを実装してみたものの、結局うまくいかなかった」なんてことはよくある話です。新機能を試すために、いろんなファイルに手を加えたことでしょう。結局、泣きながらそれを元に戻していく羽目になるわけです。こんなときのために便利なのが、バージョン管理システムの「ブランチ」機能です。

ふだんお使いのバージョン管理システムでのブランチの作成方法を調べてみましょう。たぶん思っているより簡単なはずです。Subversionなら特にそうです。

Subversionとは

  • CVSと似ていますが、新たに設計しなおされています。
  • ファイル単位でなく、リポジトリ全体でチェンジセットを管理します。
  • 複数コミット間の関係がわかりやすくなります。
  • Python APIが存在します。

1分でわかるSubversion

  • svnadmin create /var/lib/svn
  • ディレクトリ構造とファイルを作成します。
  • svn import *url to repository*
  • Subversionに関するすばらしい書籍がO'Reillyから出版されています - オンラインでも読めます。

プログラマ以外にもお勧め!

  • Windows、Linux、OS Xのすべてで、グラフィカルに使用できます。またコマンドラインからも使用できます。
  • デザイナさんでも使えるくらいにシンプルです。
  • 売り文句は「心配しなくてもいいからね」。

最初のうちは私も、開発者以外のメンバーにバージョン管理システムを使わせるのを躊躇していました。しかし、気づいたのです。自分のコンピュータで作業した内容を簡単にサーバと同期させられるってことは彼ら/彼女らにとっても便利なことじゃないかって。また、もし間違えてしまってもすぐ前の状態に戻せるというのも便利な点です。

ソースコード管理システムのうまい使い方

  • ログメッセージはみんなの友達
  • なので、うまく扱うようにしよう
関連するバグ番号をメッセージに含める
何らかの固定の書式を決めておくといいでしょう。私がよく使用しているのは "Collector #123" という形式です。このように決めておくと、このメッセージから実際のバグ情報ページに移動するためのウェブツールを作成することもできるでしょう。こうしておけば「いったい何でこんな変更をしたんだろう?」と後から悩むこともなくなります。
大事なのは「what」ではなく「why」
login_formを修正したんだってことは見ればわかります。でも、なぜ変更したの? どこがおかしかったの? お客さんからどんな要望があったの? こういった情報こそ、後から必要になるものなのです。ログメッセージには「なぜ」を、そして「何を」はソースコード中のコメントに記述するようにしましょう。
まず "まっさらな" 状態でチェックインする
たとえばPloneのlogin_formを改造することになったとしましょう。自分のプロダクトディレクトリにコピーしたファイルをいきなりさわりはじめてはいけません。そうではなく、まず手元にコピーしてきたものをそのままの状態でチェックインし、それから手を加えるようにします。こうすることで、2つの問題を解決できることになります。 (a) it's trivial for you to diff revision 1 and revision 2 of this file to find out why you were customizing it in the first place, and (b) when Plone is upgraded and there are changes to the shipped login_form, it's much easier to incorporate those, since you know exactly how login_form looked when you started, without having to dig around and find that version.

These things take only a tiny bit of discipline — and they really pay off later. It makes it much easier to understand why you customized a Plone skin two months back.

設定管理

各Ploneサイトの設定の管理方法を扱います。設定スクリプトを作らずにZMIで我慢している人のあまりにも多いこと!

"Throw out your database. It's liberating." —Kapil Thangavelu

Everyone that's worked with Zope for more than a few months has encountered "ZODB Dread": that awful, sinking feeling that you've sunk a chunk of your very life into a single, binary-format object database, with no hope you'll ever be able to remember all the scripts, skins, properties, and settings you've put into it. You konw that if this puppy ever gets badly corrupted, you're going to be in a world of hurt. This is what we want to avoid, and that's why we create setup code on the file system.

Writing Setup Code

General advice:

  • Each setup function is method that you can call independently.
  • Registry of functions.
  • Can either prevent calling twice, or delete and re-create, as appropriate.

私は如何にして心配するのを止めてAPIを愛するようになったか

DocFinderTab
DocFinderTabを使用すると、ZMIから見える任意のオブジェクトのAPIをウェブ経由で調べられるようになります。これはソースコードを直接眺めるよりもかなり便利です。基底クラスのメソッドもすべて確認できるからです。また、基底クラスの順に並べ替えて表示されるので、デバッガで調べるよりも便利です。このプロダクトは、きわめて簡単にインストールして使用することができます。まだ使ったことがないという人は、今すぐインストールしてみましょう。これは、Zopeに標準で搭載されていてもおかしくないようなプロダクトです。
EpyDoc
Epydocは、Zopeに同梱されているpydocモジュールよりも賢くて高機能なモジュールです。あなたの作成したプロダクトについての、見栄えのいいAPIドキュメントを作成してくれます。もちろん自作のプロダクトだけでなくZopeやPloneそのもののドキュメントを作成することも可能です。また、ドキュメントをPDF化することもできます。このPDFは顧客に見せるのにも耐えうる高品質なものなので、この手のドキュメントを作成する手間を省くことができます。docstringの内容がそのままドキュメントになるので、よりよいdocstringを書くための動機付けとしても有用です。
他のプロダクトのInstall.pyファイル
何かの設定をする方法を知るために最適な方法は、他のプロダクトがそれをどのように実装しているのかを調べることです。お気に入りのプロダクトのInstall.pyを調べてみましょう。たとえば、新しいワークフローをディスクからインストールする方法が知りたければ、PloneHelpCenterを調べるといいでしょう。

CMF 1.5

Includes a XML dumper for many (but not yet all) CMFCore/Default tools
So, you can make the changes in the ZMI, quickly and intuitively, then get a snapshot of these chages. These snapshots can be checked into your version control system, diff'ed, etc. And you can restore from a snapshot, makig it easier to step back to a different setup.
Won't need to make API calls
For most things, at least.

CMF 1.5-compatibility and dumpers for our tools might land for Plone 2.1.

ドキュメントの作成

他の開発者向けのドキュメント作成を支援するツールについての概要

Developer Documentation

  • Use Python interfaces
  • Definitely use your docstrings
  • Alpha-test your documentation on other developers
  • Unit tests rule!

Useful Documentation Products

  • DCWorkflowGraph
  • DCWorkflowDump
  • EpyDoc
  • ArchGenXML

Ploneのデバッグ

デバッガなしでは生きていけません。よりよい人生を送るために知っておくべきことをここでまとめます。

デバッグ

(デバッガの実際の操作例についてはUsing PDBを見るといいでしょう)

  • デバッガのない生活なんて考えられません。だって、デバッガを使えば……
    • ちょっとした問題なら2分もあれば解決でき、
    • 複雑な問題だって解決することができます。

デバッガはみんなの友達

  • Using debugger with ZEO in Zope 2.7

zopectl debug will enter the debugger under ZEO.

  • Can examine anything
  • Can execute arbitrary code, change variables, change ZODB

リクエストの実行

  • Zope.debug(...)
    • Read documentation on debug with Zope
    • , u="user:password"
    • , pm=1 for postmortem

ZODBの操作

Often, even more helpful than debugging is the ability to simple examine your ZODB directly outside of any request or debug-stepping. Once you're used to this, you'll probably find you keep the debugger open all the time while you're developing and debugging, just to quickly see how things are actually created and working in Zope.

  • Most useful feature
  • Can interactively examine and change ZODB outside of debugging
  • The root of ZODB is app

ZODBの変更

  • To commit your transaction: get_transaction().commit()
  • To sync yourself to the current ZODB: app._p_jar.sync()

グラフィカルなデバッガ

  • BoaConstructor
    • ZODBのPythonスクリプトのデバッグ
    • Zopeオブジェクトのビルド
  • WingIDE
    • 協力なリモートデバッグ機能
    • ファイルシステム上のPythonスクリプトのデバッグ
  • Komodo
    • 正規表現によるデバッグ
    • すばらしいIDE

開発・導入・同期

ある場所で開発した内容を別の場所に移行する際のヒントをまとめます。

開発・導入・同期

  • Working off laptop and moving to server
  • Moving from staging server to production server
  • Working in teams

You Have a Laptop: Use It

  • It's not a $3000 ssh client
  • Faster edit/test/debug cycle.
  • No wires, no wireless
  • Easier to work privately

Subversion for Sharing

  • Work on your laptop
  • When at the "right point", check-in your code
  • rsync to your sandbox on server
  • Emergency changes on server can be handled, too

Simple Sandboxes

  • Each developer gets a skin folder in your product
  • rsync your skin changes to your skin folder
  • Each developer has their own skinpath, with their skinfolder as most customized
  • Developers can switch from their sandbox skinpath to common skinpath.

Synchronizing of Content

  • Create starter content in setup scripts
  • Create starter content in .zexp files
    • Can't change, though
  • Use ZSyncer to sync from one server to another
  • Use AT XMLTool or Marshall to export/import XML of content

ユニットテスト

ユニットテストの手法を身につけておくと非常に役立ちます。ユニットテストなしの開発にはかなりリスクがあります。大規模なプロジェクトをユニットテストなしで開発するなんて、無責任としかいいようがありません。

Testing

  • PloneTestCase makes it easy
  • Trade off a bit of time for a lot of worry
    • Think broadly about the benefits For example, good tests will let you re-use more of your code, because you'll have the confidence to do so.
  • Look at a product with good existing tests — such as ATContentTypes
  • Mechanize-driven testing for interface elements
  • Consider billing this separately to your customers

Sample Test Setup

  • Sample test setup for PloneHelpCenter:
        class PHCSiteTestCase(ArchetypesTestCase.ArcheSiteTestCase):
          """Test structure for PHC"""

          def afterSetUp(self):
              ArchetypesTestCase.ArcheSiteTestCase.afterSetUp(self)

              self._portal = self.app.portal
              # login as manager
              user = self.getManagerUser()
              newSecurityManager(None, user)

              self._portal.invokeFactory(type_name='HelpCenter',
                id='helpctr')
              self._hc=getattr(self._portal, 'helpctr')

          def beforeTearDown(self):
              ArchetypesTestCase.ArcheSiteTestCase.beforeTearDown(self)
              noSecurityManager()
              del self._hc

Sample Test

  • Sample test for PloneHelpCenter:
        class TestFAQ(PHCTestCase):
          """Test those parts of FAQ and FAQ Folder that don't 
          require a real site framework. Allows tests to run faster
          """

          def afterSetUp(self):
              PHCTestCase.afterSetUp(self)
              self._dummy = FAQ.HelpCenterFAQ(oid='dummy')
              self._dummy.initializeArchetype()

          def test_answerSTX(self):
              dummy = self._dummy

              dummy.setAnswer(example_stx, mimetype='text/structured')
              answer=no_whitespace.sub('',dummy.getAnswer())
              self.failUnless(answer==example_html, 'Value is %s' % answer)

More information on unit testing

  • Lots of material and tutorials available on the web
  • Get Stefan Holek's "Unit Testing Zope for Fun and Profit" presentation from EuroPython
  • A very good book is Kent Beck's Unit Testing book

クレジット

このチュートリアルの作成に協力してくれた人たち

Joel Burton
原作者。
Kapil Thangavelu
Inspiration to throw out my database and much more
Calvin Hendryx-Parker and David "Whit" Morriss
Ideas and feedback
Alan Runyan
For releasing several products teaching the "right path"
Alexander Limi
PloneHelpCenter conversion, insistence ;)