summaryrefslogtreecommitdiff
path: root/docs/narr/project.rst
blob: 1cfac5b1e87b8068ab8c0966bf5298ad8fecaad2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
.. _project_narr:

Starting a :mod:`repoze.bfg` Project
====================================

You can use :mod:`repoze.bfg` 's sample application generator to get
started.  This generator leverages Paste templates to allow creation
of a new project by answering a series of questions.

Creating the Project
--------------------

To start a :mod:`repoze.bfg` project, use the ``paster create``
facility::

  $ paster create -t bfg
  Selected and implied templates:
    repoze.bfg#bfg  repoze.bfg starter project

  Enter project name: myproject
  Variables:
    egg:      myproject
    package:  myproject
    project:  myproject
  Creating template bfg
  Creating directory ./myproject
    Recursing into +package+
      Creating ./myproject/myproject/
      Copying __init__.py to ./myproject/myproject/__init__.py
      Copying configure.zcml to ./myproject/myproject/configure.zcml
      Copying models.py to ./myproject/myproject/models.py
      Copying run.py_tmpl to ./myproject/myproject/run.py
      Recursing into templates
        Creating ./myproject/myproject/templates/
        Copying mytemplate.pt to ./myproject/myproject/templates/mytemplate.pt
      Copying views.py_tmpl to ./myproject/myproject/views.py
    Copying +package+.ini_tmpl to ./myproject/myproject.ini
    Copying CHANGES.txt_tmpl to ./myproject/CHANGES.txt
    Copying README.txt_tmpl to ./myproject/README.txt
    Copying ez_setup.py to ./myproject/ez_setup.py
    Copying setup.py_tmpl to ./myproject/setup.py
  Running /Users/chrism/projects/repoze-devel/bfg/bin/python setup.py egg_info

The project will be created in a directory named ``myproject``.  That
directory is a :term:`setuptools` *project* directory from which a
Python setuptools *distribution* can be created.  The ``setup.py``
file in that directory can be used to distribute your application, or
install your application for deployment or development. A sample
PasteDeploy ``.ini`` file named ``myproject.ini`` will also be created
in the project directory.  You can use this to run your application.

The main ``myproject`` contains an additional subdirectory (also named
``myproject``) representing a Python pakckage which holds very simple
bfg sample code.  This is where you'll edit your application's Python
code and templates.

Installing your Newly Created Project for Development
-----------------------------------------------------

Using your favorite Python interpreter (using a `virtualenv
<http://pypi.python.org/pypi/virtualenv>`_ is suggested in order to
isolate your application from your system Python's packages), invoke
the following command when inside the project directory against the
generated ``setup.py``::

  $ python setup.py develop
   ...
   Finished processing dependencies for myproject==0.1

This will install your application 's package into the interpreter so
it can be found and run as a WSGI application inside a WSGI server.

Running The Tests For Your Application
--------------------------------------

To run unit tests for your application, you should invoke them like
so::

  $ python setup.py test -q
  running test
  running egg_info
  writing requirements to myproject.egg-info/requires.txt
  writing myproject.egg-info/PKG-INFO
  writing top-level names to myproject.egg-info/top_level.txt
  writing dependency_links to myproject.egg-info/dependency_links.txt
  writing entry points to myproject.egg-info/entry_points.txt
  reading manifest file 'myproject.egg-info/SOURCES.txt'
  writing manifest file 'myproject.egg-info/SOURCES.txt'
  running build_ext
  .
  ----------------------------------------------------------------------
  Ran 1 test in 0.566s

  OK

The tests are found in the ``tests.py`` module in your
paster-create-generated project.  One sample test exists.

Runnning The Project Application
--------------------------------

Once the project is installed for development, you can run the
application it represents using the ``paster serve`` command against
the generated ``myproject.ini`` configuration file::

  $ paster serve myproject/myproject.ini
  Starting server in PID 16601.
  serving on 0.0.0.0:5432 view at http://127.0.0.1:5432

It will listen on port 5432.  

.. note:: During development, it's often useful to run ``paster
   serve`` using its ``--reload`` option.  When any Python module your
   project uses, changes, it will restart the server, which makes
   development easier, as changes to Python code under
   :mod:`repoze.bfg` is not put into effect until the server restarts.

Viewing the Application
-----------------------

Visit http://localhost:5432/ in your browser.  You will see::

  Welcome to myproject

The Project Structure
---------------------

Our generated :mod:`repoze.bfg` application is a setuptools *project*
(named ``myproject``), which contains a Python package (which is
*also* named ``myproject``; the paster template generates a project
which contains a package that shares its name).

The ``myproject`` project has the following directory structure::

  myproject/
  |-- CHANGES.txt
  |-- README.txt
  |-- ez_setup.py
  |-- myproject
  |   |-- __init__.py
  |   |-- configure.zcml
  |   |-- models.py
  |   |-- run.py
  |   |-- templates
  |   |   `-- mytemplate.pt
  |   |-- tests.py
  |   `-- views.py
  |-- myproject.ini
  `-- setup.py

The ``myproject`` *Project*
---------------------------

The ``myproject`` project is the distribution and deployment wrapper
for your application.  It contains both the ``myproject`` *package*
representing your application as well as files used to describe, run,
and test your application.

#. ``CHANGES.txt`` describes the changes you've made to the application.

#. ``README.txt`` describes the application in general.

#. ``ez_setup.py`` is a file that is used by ``setup.py`` to install
   setuptools if the executing user does not have it installed.

#. ``myproject.ini`` is a PasteDeploy configuration file that can be
   used to execute your application.

#. ``setup.py`` is the file you'll use to test and distribute your
   application.  It is a standard distutils/setuptools ``setup.py`` file.

It also contains the ``myproject`` *package*, described below.

The ``myproject`` *Package*
---------------------------

The ``myproject`` package lives inside the ``myproject`` project.  It
contains:

#. An ``__init__.py`` file which signifies that this is a Python
   package.  It is conventionally empty, save for a single comment at
   the top.

#. A ``configure.zcml`` file which maps view names to model types.
   This is also known as the "application registry", although it
   also often contains non-view-related declarations.

#. A ``models.py`` module, which contains model code.

#. A ``run.py`` module, which contains code that helps users run the
   application.

#. A ``templates`` directory, which is full of zc3.pt and/or XSL
   templates.

#. A ``tests.py`` module, which contains test code.

#. A ``views.py`` module, which contains view code.

These are purely conventions established by the Paster template:
:mod:`repoze.bfg` doesn't insist that you name things in any
particular way.

``configure.zcml``
~~~~~~~~~~~~~~~~~~

The ``configure.zcml`` (representing the application registry) looks
like so:

.. literalinclude:: myproject/myproject/configure.zcml
   :linenos:
   :language: xml

#. Lines 1-3 provide the root node and namespaces for the
   configuration language.  ``bfg`` is the namespace for
   :mod:`repoze.bfg`-specific configuration directives.

#. Line 6 initializes :mod:`repoze.bfg`-specific configuration
   directives by including it as a package.

#. Lines 8-11 register a single view.  It is ``for`` model objects
   that support the IMyModel interface.  The ``view`` attribute points
   at a Python function that does all the work for this view.

``views.py``
~~~~~~~~~~~~

Much of the heavy lifting in a :mod:`repoze.bfg` application comes in
the views.  Views are the bridge between the content in the model, and
the HTML given back to the browser.

.. literalinclude:: myproject/myproject/views.py
   :linenos:

#. Lines 3-5 provide the ``my_view`` that was registered as the view.
   ``configure.zcml`` said that the default URL for IMyModel content
   should run this ``my_view`` function.

   The function is handed two pieces of information: the ``context``
   and the ``request``.  The ``context`` is the data at the current
   hop in the URL.  (That data comes from the model.)  The request is
   an instance of a WebOb request.

#. The model renders a template and returns the result as the
   response.

.. note::

  This example uses ``render_template_to_response`` which allows the
  view author to think only in terms of templates.  If you want more
  control over the response, use ``render_template`` and create your
  own WebOb Response object to return.

``models.py``
~~~~~~~~~~~~~

In our sample app, the ``models.py`` module provides the model data.
We create an interface ``IMyModel`` that gives us the "type" for our
data.  We then write a class ``MyModel`` that provides the behavior
for instances of the ``IMyModel`` type.

.. literalinclude:: myproject/myproject/models.py
   :linenos:

#. Lines 4-5 define the interface.

#. Lines 7-9 provide a class that implements this interface.

#. Line 11 defines an instance of MyModel as the root.

#. Line 13 is a function that will be called by the :mod:`repoze.bfg`
   *Router* for each request when it wants to find the root of the model
   graph.  Conventionally this is called ``get_root``.

In a "real" application, the root object would not be such a simple
object.  Instead, it would be an object that could access some
persistent data store, such as a database.  :mod:`repoze.bfg` doesn't
make any assumption about which sort of datastore you'll want to use,
so the sample application uses an instance of ``MyModel`` to represent
the root.

``run.py``
~~~~~~~~~~

We need a small Python module that configures our application and
advertises itself to our Paste ``.ini`` file.  For convenience, we
also make it possible to run this module directory without the Paste
configuration file:

.. literalinclude:: myproject/myproject/run.py
   :linenos:

#. Lines 1 - 7 define a function that returns a :mod:`repoze.bfg`
   Router application.  This is meant to be called by the PasteDeploy
   framework as a result of running ``paster serve``.

#. Lines 9 - 12 allow this file to serve as a shortcut for executing
   our program if the ``run.py`` file is executed directly.  It starts
   our application under a web server on port 5432.

``templates/mytemplate.pt``
~~~~~~~~~~~~~~~~~~~~~~~~~~~

The single template in the project looks like so:

.. literalinclude:: myproject/myproject/templates/mytemplate.pt
   :linenos:
   :language: xml

``tests.py``
~~~~~~~~~~~~

The ``tests.py`` module includes unit tests for your application.

.. literalinclude:: myproject/myproject/tests.py
   :linenos: