Usage
=====

Here's how to simply and quickly create sample objects using :mod:`chide`.
For the examples, these two classes will be used:

.. code-block:: python

  class ClassOne(object):

    def __init__(self, x, y):
        self.x, self.y = x, y

  class ClassTwo(object):

    def __init__(self, a, b):
        self.a, self.b = a, b

Creating sample objects
-----------------------

We can set up a registry of sample values as follows:

.. code-block:: python

  from chide import Collection

  samples = Collection({
      ClassOne: {'x': 1, 'y': 2},
      ClassTwo: {'a': 1, 'b': ClassOne},
  })

Now we can quickly make sample objects:

>>> samples.make(ClassOne)
<ClassOne ...>
>>> _.x, _.y
(1, 2)

We can provide our own overrides if we want:

>>> samples.make(ClassOne, y=3)
<ClassOne ...>
>>> _.x, _.y
(1, 3)

We can also create nested trees of objects:

>>> samples.make(ClassTwo)
<ClassTwo ...>
>>> _.b
<ClassOne ...>

These can also be overridden with another sample object:

>>> samples.make(ClassTwo, b=samples.make(ClassOne, x=-1))
<ClassTwo ...>
>>> _.b.x
-1

They can also be directly overridden with an appropriate instance:

>>> samples.make(ClassTwo, b=ClassOne(11, 3))
<ClassTwo ...>
>>> _.b.x, _.b.y
(11, 3)

Sets of unique sample objects
-----------------------------

In some circumstances, you may need a set of related objects
where the objects have a notion of an identifier, and only one
object of each type with a given identifier can exist. Here's an
oversimplified example:

.. code-block:: python

  class Person(object):
      def __init__(self, name, address):
          self.name = name
          self.address = address

  class Address(object):
      seen = set()
      def __init__(self, value):
          if value in self.seen:
              raise Exception(value)
          self.seen.add(value)
          self.value = value

So, given that there can be multiple people but each address can only
be instantiated once we want all people used in tests, by default, to be at
the same address. We can do this using a :class:`~chide.Set` and an `identify`
function as follows:

.. code-block:: python

  from chide import Collection, Set

  data = Collection({
      Person: {'name': 'Fred', 'address': Address},
      Address: {'value': 'some place in the clouds'},
  })

  def identify(type_, attrs):
      if type_ is Address:
          return attrs['value']

  samples = Set(data, identify)

Now, we can create multiple sample people without having to specify their
address:

>>> person1 = samples.get(Person, name='Chris')
>>> person2 = samples.get(Person, name='Kirsty')

They both just end up at the same address:

>>> person1.address.value
'some place in the clouds'
>>> person2.address.value
'some place in the clouds'
>>> person1.address is person2.address
True

We can still create people with different addresses:

>>> person3 = samples.get(Person, name='Fred', address=Address('elsewhere'))
>>> person3.address.value
'elsewhere'

The :class:`~chide.Set` can also be used to make sure that only one of each
address is used:

>>> person4 = samples.get(Person, name='Bob', address=samples.get(Address, value='here'))
>>> person5 = samples.get(Person, name='Joe', address=samples.get(Address, value='here'))

This way, we don't have to manually ensure that only one address for `here`
exists.

You'll notice that we can still get multiple people with the same name from the
:class:`~chide.Set`:

>>> person6 = samples.get(Person, name='Chris')
>>> person1 is person6
False

This because the :func:`identify` function above returns ``None`` for all types
other than :class:`Address`. Returning ``None`` from :func:`identify` is the
way to indicate that a new object should be returned, regardless of the
attributes it has.