Daniel Nouri's Old Blog

Note that this blog is discontinued. You can find my new blog here: Daniel Nouri's Blog.

Fri, 16 Jun 2006

pluggablecatalog

I added a new project called pluggablecatalog to the collective.

pluggablecatalog is a replacement (or rather: a wrapper) for Plone's portal catalog. It adds the ability to plug in default search restrictions without the need to subclass or monkey- patch the catalog.

From the docstring of pluggablecatalog/tool.py:

Wraps CMFPlone's CatalogTool to add default parameters
collected from IQueryDefaults utilities.

  >>> from Products.pluggablecatalog.tool import CatalogTool
  >>> catalog = self.portal.portal_catalog
  >>> isinstance(catalog, CatalogTool)
  True

We create two documents and make sure they're indexed:

  >>> len(catalog())
  0
  >>> self.folder.invokeFactory('Document', 'doc1')
  'doc1'
  >>> self.folder.invokeFactory('Document', 'doc2')
  'doc2'
  >>> doc1, doc2 = self.folder.doc1, self.folder.doc2
  >>> doc1.setTitle('First Document')
  >>> doc2.setTitle('Second Document')
  >>> doc1.reindexObject(); doc2.reindexObject()
  >>> len(catalog())
  2

Let's now add a rather stupid IQueryDefaults utility that
restricts searches by default to objects with the Title 'First
Document':

  >>> from zope import component
  >>> from zope import interface
  >>> from Products.pluggablecatalog.interfaces import IQueryDefaults
  >>> def myDefaults(context, request):
  ...     return {'Title': 'First Document'}
  >>> interface.directlyProvides(myDefaults, IQueryDefaults)
  >>> component.getService('Utilities').provideUtility(
  ...     IQueryDefaults, myDefaults)

With this utility in place, we should only retrieve doc1 now,
unless we explicitly provide a 'Title' query parameter:

  >>> len(catalog())
  1
  >>> catalog()[0].getObject().aq_base is doc1.aq_base
  True

  >>> len(catalog(Title='Second Document'))
  1
  >>> (catalog(Title='Second Document')[0].getObject().aq_base is
  ...  doc2.aq_base)
  True

posted at: 06:07 | 0 comments | category: /devel/zope rss | permanent link | add to del.icio.us or digg it

Sat, 10 Jun 2006

Record and test Plone with zope.testrecorder and zope.testbrowser

I've just updated the HOWTO for using Testbrowser and Testrecorder with Plone. The Testbrowser/Testrecorder combination is so easy; also for non-programmers. We need more structured and well-documented functional tests like this, because they make our lives easier [*]. Maybe you can help out?

[*]... and our tests slower? ;-)

And Now for Something Completely Different

While I played around with Testrecorder, I wrote a small script for text match and replace using regular expressions. This is arguably one of the most unreadable pieces of code I've written in a while. But to the rescue come the doctests, which I've used to develop this test-first.

I don't know anything about Ron Jeffries but this is how he describes it and how it felt:

When you get this right, development turns into a very pleasant
cycle of testing, seeing a simple thing to fix, fixing it, testing,
getting positive feedback all the way.

Guaranteed flow. And you go so fast! Try it, you'll like it.

Update: Martin incorporated the Testbrowser and Testrecorder HOWTO in his tutorial. I updated the link to point to that.

posted at: 18:10 | 0 comments | category: /devel/zope rss | permanent link | add to del.icio.us or digg it

Sat, 03 Jun 2006

Skeleton for Plone Core packages in ZopeSkel

Thanks to Hanno, ZopeSkel now has a template for starting Plone Core packages. Hanno is using ZopeSkel for the new plone.i18n package.

Get ZopeSkel

ZopeSkel has moved to the Collective. Get the latest and greatest of ZopeSkel via this command:

easy_install http://svn.plone.org/svn/collective/ZopeSkel/trunk#egg=ZopeSkel-dev

Create a package

Remember from last time that Paste Script is useful for creating a consistent directory structure and files for you so that you can get to the actual work quickly. Plus, your projects will be ready for distribution without any additional work.

Now let's say we want to start a plone.form package. What we need to do is invoke paster create, telling it that we want to use the plone_core template. This is done using the -t command-line option. After invoking the command, we're asked for some variables:

$ paster create -t plone_core
Selected and implied templates:
  ZopeSkel#plone_core  A Plone Core project

Enter project name: plone.form
Variables:
  package:  ploneform
  project:  plone.form
Creating template plone_core
Enter namespace_package (Namespace package) ['plone']:
Enter package (The package contained namespace package (like i18n)) ['']: form
Enter version (Version) ['0.1']:
Enter description (One-line description of the package) ['']: Plone compatibility layer for zope.app.schema and zope.formlib
Enter long_description (Multi-line description (in reST)) ['']:
Enter author (Author name) ['Plone Foundation']:
Enter author_email (Author email) ['plone-developers@NOSPAMlists.sourceforge.net']:
Enter keywords (Space-separated keywords/tags) ['']:
Enter url (URL of homepage) ['']:
Enter license_name (License name) ['GPL']:
Enter zip_safe (True/False: if the package can be distributed as a .zip file) [False]:
Creating directory ./plone.form
  Recursing into +namespace_package+
    Creating ./plone.form/plone/
    Recursing into +package+
      Creating ./plone.form/plone/form/
      Copying HISTORY.txt_tmpl to ./plone.form/plone/form/HISTORY.txt
      Copying LICENSE.GPL to ./plone.form/plone/form/LICENSE.GPL
      Copying LICENSE.txt_tmpl to ./plone.form/plone/form/LICENSE.txt
      Copying README.txt_tmpl to ./plone.form/plone/form/README.txt
      Copying __init__.py to ./plone.form/plone/form/__init__.py
      Copying configure.zcml to ./plone.form/plone/form/configure.zcml
      Copying version.txt_tmpl to ./plone.form/plone/form/version.txt
    Copying __init__.py to ./plone.form/plone/__init__.py
  Copying setup.cfg to ./plone.form/setup.cfg
  Copying setup.py_tmpl to ./plone.form/setup.py
Running /usr/bin/python setup.py egg_info

Note that we typed in values for the project, package and descriptions variables only. For the rest we decided that the default was good enough. We're ready to start working on plone.form now.

Non- Plone Core packages

There's no reason why we wouldn't use the plone_core template for our own packages. After all, the name of the namespace package and that of the contained package are variables, and the directory structure might well be useful for your own project.

Updating a ZopeSkel package

Whenever the plone_core template changes, we can simply rerun the paster create command to update our project:

$ paster create -t plone_core
Selected and implied templates:
  ZopeSkel#plone_core  A Plone Core project

Enter project name: plone.form
Variables:
  package:  ploneform
  project:  plone.form
Creating template plone_core
Enter namespace_package (Namespace package) ['plone']:
Enter package (The package contained namespace package (like i18n)) ['']: form

[...]

  Recursing into +namespace_package+
    Recursing into +package+
      ./plone.form/plone/form/HISTORY.txt already exists (same content)
      ./plone.form/plone/form/LICENSE.GPL already exists (same content)
      ./plone.form/plone/form/LICENSE.txt already exists (same content)
      ./plone.form/plone/form/README.txt already exists (same content)
      ./plone.form/plone/form/__init__.py already exists (same content)
      ./plone.form/plone/form/configure.zcml already exists (same content)
      ./plone.form/plone/form/version.txt already exists (same content)
    ./plone.form/plone/__init__.py already exists (same content)
  ./plone.form/setup.cfg already exists (same content)
  ./plone.form/setup.py already exists (same content)
Running /usr/bin/python setup.py egg_info

If something had changed, we would have been presented a diff of our version of the file and the new version inside the template.

An inconvenience here is that when running paster create, we're asked for all variables again. Fortunately, there's the --config=CONFIG option that'll store all our variables in a file. Both for initial creation and for updating, our command becomes:

$ paster create -t plone_core --config=plone.form/template_vars.cfg

Extending ZopeSkel

Extending the plone_core package to fit our own needs is really easy. Let's say we want to create a new template called my_package that's based on plone_core. We want our new template to use what's already there in plone_core and add a browser package in there with __init__.py and configure.zcml files.

Inside our SVN checkout of ZopeSkel, we create this directory:

$ mkdir -p zopeskel/templates/my_package/+namespace_package+/+package+/browser

Then we add our two files:

$ edit zopeskel/templates/my_package/+namespace_package+/+package+/browser/__init__.py_tmpl
$ edit zopeskel/templates/my_package/+namespace_package+/+package+/browser/configure.zcml

Note that we create an __init__.py_tmpl file, with the _tmpl suffix because setuptools would currently not include our .py file in the distribution if the file ended with .py. So although our __init__.py_tmpl file isn't necessarily a template (nothing is substituted), we make it a Paste Script template so that it gets picked up by setuptools.

The next thing we do is wire up the my_package template so that it's available from the command-line. Inside zopeskel/__init__.py we add this:

class MyPackage(PloneCore):
    _template_dir = 'templates/my_package'
    summary = 'A Plone package that has a browser subpackage'
    required_templates = ['plone_core']

Finally, we define an entry point for our template in setup.py:

[...]

entry_points="""
[paste.paster_create_template]
basic_zope = zopeskel:BasicZope
plone_core = zopeskel:PloneCore
my_package = zopeskel:MyPackage
""",

[...]

Note that we only added the my_package entry point here.

After calling setup.py develop we're ready to create a new project with our own template:

$ python setup.py develop
[...]
$ paster create --list-templates
Available templates:
  basic_package:  A basic setuptools-enabled package
  basic_zope:     A Zope project
  my_package:     A Plone package that has a browser subpackage
  paste_deploy:   A web application deployed through paste.deploy
  plone_core:     A Plone Core project

Easy as pie!

posted at: 08:30 | 3 comments | category: /devel/zope rss | permanent link | add to del.icio.us or digg it


< June 2006 >
SuMoTuWeThFrSa
     1 2 3
4 5 6 7 8 910
11121314151617
18192021222324
252627282930 

Feed of all categories: rss rss | atom

Categories: