.. _development:

Development
===========

We are always looking for people wanting to improve Pinax itself. This
document outlines the necessary bits to begin contributing to Pinax.

Getting started
---------------

The Pinax source code is hosted on GitHub_. This means you must have git_
installed locally. We recommend you create an account on GitHub allowing you
to watch and fork the Pinax source code.

You will want to be sure that your git configuration is set for making commits
to a repository. Check the following::

    git config user.name
    git config user.email

If the output of any of the two commands above are not entirely correct you
can easily correct them::

    git config --global user.name "First Last"
    git config --global user.email "email@somewhere.com"

It is critical you set this information up correctly. It helps us identify
who you are when you start giving us those awesome patches.

.. _GitHub: http://github.com/pinax/pinax/tree/master
.. _git: http://git-scm.com

Grabbing the source code
~~~~~~~~~~~~~~~~~~~~~~~~

Once you have forked the Pinax source code you can now make a clone of it to
your local disk. To do this::

    git clone git@github.com:<username>/pinax.git

This will create new directory named ``pinax`` which now contains the Pinax
source tree ready for you to get started.

Setting up your environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now that you've cloned the source code you are ready to get your environment
setup to work on Pinax. You should be a running a recent release of
`virtualenv`_ to setup Pinax. We'll assume that your current working directory
is from within the clone (the ``pinax`` directory)::

    virtualenv ../pinax-dev
    source ../pinax-dev/bin/activate
    pip install -e .

Finally, you need to install the dependencies for the project you want to work
against::

    cd pinax/projects/<project>
    pip install --requirement requirements/project.txt

In order to run the test suite, you'll also need PIL installed::

    pip install PIL

.. _virtualenv: http://pypi.python.org/pypi/virtualenv

Staying up-to-date
------------------

The development version of Pinax is always being updated. You'll want to make
sure you keep up. Your clone of Pinax can easily stay in-sync using git. You
will need to setup a git remote to pull in changes from upstream Pinax::

    git remote add upstream git://github.com/pinax/pinax.git

Now you will be able to merge in changes that are made upstream::

    git fetch upstream
    git merge upstream/master

The above will pull in all changes upstream and merge them with your current
branch. This can be simplified::

    git pull upstream master

To keep your virtual environment in-sync you will need to use pip (make sure
you are **inside** your virtual environment)::

    cd pinax/projects/<project>
    pip install --no-deps --requirement requirements/project.txt

Running the tests
-----------------

Before you begin committing code you'll want to make sure the Pinax test suite
passes. Running the test suite is simple. At the root of your Pinax clone
run::

    python tests/runner.py

You must be inside the development virtual environment for this to work. If
you only want to run a subset of tests (all tests are inside apps) you can
specify them explicitly::

    python tests/runner.py tasks projects

This useful while developing. However, be sure to run the full suite before
committing any code to ensure it doesn't break other parts of Pinax.

.. note::

    The test runner is new as of November 29, 2009. There are currently many
    failing tests. We are working on it. This message will be removed when
    the test suite is generally usable for contributors.

Committing code
---------------

The great thing about using a distributed versioning control system like git
is that everyone becomes a committer. When other people write good patches
it makes it very easy to include their fixes/features and give them proper
credit for the work.

We recommend that you do all your work on Pinax in a separate branch. When you
are ready to work on a bug or a new feature create yourself a new branch. The
reason why this is important is you can commit as often you like. When you are
ready you can merge in the change. Let's take a look at a common workflow::

    git checkout -b task-566
    ... fix and git commit often ...
    git push origin task-566

The reason we have created two new branches is to stay off of ``master``.
Keeping master clean of only upstream changes makes yours and ours lives
easier. You can then send us a pull request for the fix/feature. Then we can
easily review it and merge it when ready.

Writing commit messages
~~~~~~~~~~~~~~~~~~~~~~~

Writing a good commit message makes it simple for us to identify what your
commit does from a high-level. We are not too picky, but there are some basic
guidelines we'd like to ask you to follow.

::

    Fixed #1 — added some feature

We ask that you indicate which task you have fixed (if the commit fixes it) or
if you are working something complex you may want or be asked to only commits
parts::

    Refs #1 — added part one of feature X

As said earlier we are not too picky (some core developers may change commit
messages before pulling in your changes), but as you get the basics down you
make the process of getting your patch into core faster.

Another critical part is that you keep the **first** line as short and sweet
as possible. This line is important because when git shows commits and it has
limited space or a different formatting option is used the first line becomes
all someone might see. If you need to explain why you made this change or
explain something in detail use this format::

    Fixed #13 — added time travel
    
    You need to be driving 88 miles per hour to generate 1.21 gigawatts of
    power to properly use this feature.

Coding style
------------

When writing code to be included in Pinax keep our style in mind:

 * Follow PEP8_ — there are some cases where we do not follow PEP8. It is an
   excellent starting point.
 * Follow `Django's coding style`_ — we're pretty much in agreement on
   Django style outlined there.

We would like to enforce a few more strict guides not outlined by PEP8 or
Django's coding style:

 * PEP8 tries to keep line length at 80 characters. We follow it when we can,
   but not when it makes a line harder to read. It is okay to go a little bit
   over 80 characters if not breaking the line improves readability.
 * Use double quotes not single quotes. Single quotes are allowed in cases
   where a double quote is needed in the string. This makes the code read
   cleaner in those cases.
 * Blank lines are indented to the appropriate level for the block they are
   in.
 * Docstrings always use three double quotes on a line of their own, so, for
   example, a single line docstring should take up three lines not one.
 * Imports are grouped specifically and ordered alphabetically. This is shown
   in the example below.
 * Always use ``reverse`` and never ``@models.permalink``.
 * Tuples should be reserved for positional data structures and not used
   where a list is more appropriate.
 * URL patterns should use the ``url()`` function rather than a tuple.

Here is an example of these rules applied::

    # first set of imports are stdlib imports
    # non-from imports go first then from style import in their own group
    import csv
    
    from datetime import datetime
    
    # second set of imports are Django imports with contrib in their own
    # group.
    from django.core.urlresolvers import reverse
    from django.db import models
    from django.utils.translation import ugettext_lazy as _
    
    from django.contrib.auth.models import User
    
    # third set of imports are Pinax imports
    from pinax.utils.examplelib import function_name
    
    # forth set of imports are external apps (if applicable)
    from tagging.fields import TagField
    
    # fifth set of imports are local apps
    from pinax.apps.tasks.fields import MarkupField
    
    
    class Task(models.Model):
        """
        A model for storing a task.
        """
        
        creator = models.ForeignKey(User)
        created = models.DateTimeField(default=datetime.now)
        modified = models.DateTimeField(default=datetime.now)
        
        objects = models.Manager()
        
        class Meta:
            verbose_name = _("task")
            verbose_name_plural = _("tasks")
        
        def __unicode__(self):
            return self.summary
        
        def save(self, **kwargs):
            self.modified = datetime.now()
            super(Task, self).save(**kwargs)
        
        def get_absolute_url(self):
            return reverse("task_detail", kwargs={"task_id": self.pk})
        
        # custom methods
    
    
    class TaskComment(models.Model):
        # ... you get the point ...
        pass
    

.. _PEP8: http://www.python.org/dev/peps/pep-0008/
.. _`Django's coding style`: http://docs.djangoproject.com/en/dev/internals/contributing/#coding-style
