# Path: chamber/models/dispatchers.py
# class BaseDispatcher:
#     """
#     Base dispatcher class that can be subclassed to call a handler based on a change in some a SmartModel.
#     If you subclass, be sure the __call__ method does not change signature.
#     """
# 
#     signal = None
# 
#     def __init__(self, handler, signal=None):
#         self.handler = handler
#         assert (isinstance(handler, types.FunctionType)
#                 or isinstance(handler, BaseHandler)), 'Handler must be function or instance of ' \
#                                                       'chamber.models.handlers.BaseHandler, {}: {}'.format(self,
#                                                                                                            handler)
#         self._connected = defaultdict(list)
#         self._signal = signal if signal is not None else self.signal
# 
#     def connect(self, sender):
#         self._signal.connect(self, sender=sender)
# 
#     def __call__(self, instance, **kwargs):
#         """
#         `instance` ... instance of the SmartModel where the handler is being called
#         Some dispatchers require additional params to evaluate the handler can be dispatched,
#         these are hidden in args and kwargs.
#         """
#         if self._can_dispatch(instance, **kwargs):
#             self.handler(instance=instance, **kwargs)
# 
#     def _can_dispatch(self, instance, *args, **kwargs):
#         raise NotImplementedError
# 
# class StateDispatcher(BaseDispatcher):
#     """
#     Use this class to register a handler for transition of a model to a certain state.
#     """
# 
#     def _validate_init_params(self):
#         super()._validate_init_params()
#         if self.field_value not in {value for value, _ in self.enum.choices}:
#             raise ImproperlyConfigured('Enum of FieldDispatcher does not contain {}.'.format(self.field_value))
# 
#     def __init__(self, handler, enum, field, field_value, signal=None):
#         self.enum = enum
#         self.field = field
#         self.field_value = field_value
#         super().__init__(handler, signal=signal)
# 
#     def _can_dispatch(self, instance, changed, changed_fields, *args, **kwargs):
#         return (
#             self.field.get_attname() in changed_fields and
#             getattr(instance, self.field.get_attname()) == self.field_value
#         )
# 
# Path: chamber/shortcuts.py
# def change_and_save(obj, update_only_changed_fields=False, save_kwargs=None, **changed_fields):
#     """
#     Changes a given `changed_fields` on object, saves it and returns changed object.
#     """
#     from chamber.models import SmartModel
# 
#     save_kwargs = save_kwargs if save_kwargs is not None else {}
#     if update_only_changed_fields:
#         if isinstance(obj, SmartModel):
#             save_kwargs['update_only_changed_fields'] = True
#         else:
#             save_kwargs['update_fields'] = get_update_fields(obj, **changed_fields)
# 
#     change(obj, **changed_fields)
#     obj.save(**save_kwargs)
#     return obj


from nose.tools import raises  # pylint: disable=E0401
from django.core.exceptions import ImproperlyConfigured
from django.test import TransactionTestCase
from django.db import transaction
from chamber.models.dispatchers import BaseDispatcher, StateDispatcher
from chamber.shortcuts import change_and_save
from germanium.tools import assert_equal  # pylint: disable=E0401
from test_chamber.models import (
    CSVRecord, TestDispatchersModel, TestFieldsModel, TestSmartModel, TestOnDispatchModel
)  # pylint: disable=E0401






class DispatchersTestCase(TransactionTestCase):

    def test_state_dispatcher(self):
        m = TestDispatchersModel.objects.create()

        # Moving TestDispatcher model to SECOND state should create new TestSmartModel instance
        assert_equal(TestSmartModel.objects.count(), 0)

# Path: chamber/models/dispatchers.py
# class BaseDispatcher:
#     """
#     Base dispatcher class that can be subclassed to call a handler based on a change in some a SmartModel.
#     If you subclass, be sure the __call__ method does not change signature.
#     """
# 
#     signal = None
# 
#     def __init__(self, handler, signal=None):
#         self.handler = handler
#         assert (isinstance(handler, types.FunctionType)
#                 or isinstance(handler, BaseHandler)), 'Handler must be function or instance of ' \
#                                                       'chamber.models.handlers.BaseHandler, {}: {}'.format(self,
#                                                                                                            handler)
#         self._connected = defaultdict(list)
#         self._signal = signal if signal is not None else self.signal
# 
#     def connect(self, sender):
#         self._signal.connect(self, sender=sender)
# 
#     def __call__(self, instance, **kwargs):
#         """
#         `instance` ... instance of the SmartModel where the handler is being called
#         Some dispatchers require additional params to evaluate the handler can be dispatched,
#         these are hidden in args and kwargs.
#         """
#         if self._can_dispatch(instance, **kwargs):
#             self.handler(instance=instance, **kwargs)
# 
#     def _can_dispatch(self, instance, *args, **kwargs):
#         raise NotImplementedError
# 
# class StateDispatcher(BaseDispatcher):
#     """
#     Use this class to register a handler for transition of a model to a certain state.
#     """
# 
#     def _validate_init_params(self):
#         super()._validate_init_params()
#         if self.field_value not in {value for value, _ in self.enum.choices}:
#             raise ImproperlyConfigured('Enum of FieldDispatcher does not contain {}.'.format(self.field_value))
# 
#     def __init__(self, handler, enum, field, field_value, signal=None):
#         self.enum = enum
#         self.field = field
#         self.field_value = field_value
#         super().__init__(handler, signal=signal)
# 
#     def _can_dispatch(self, instance, changed, changed_fields, *args, **kwargs):
#         return (
#             self.field.get_attname() in changed_fields and
#             getattr(instance, self.field.get_attname()) == self.field_value
#         )
# 
# Path: chamber/shortcuts.py
# def change_and_save(obj, update_only_changed_fields=False, save_kwargs=None, **changed_fields):
#     """
#     Changes a given `changed_fields` on object, saves it and returns changed object.
#     """
#     from chamber.models import SmartModel
# 
#     save_kwargs = save_kwargs if save_kwargs is not None else {}
#     if update_only_changed_fields:
#         if isinstance(obj, SmartModel):
#             save_kwargs['update_only_changed_fields'] = True
#         else:
#             save_kwargs['update_fields'] = get_update_fields(obj, **changed_fields)
# 
#     change(obj, **changed_fields)
#     obj.save(**save_kwargs)
#     return obj


# Path: example/dj/apps/test_chamber/tests/models/dispatchers.py
from nose.tools import raises  # pylint: disable=E0401
from django.core.exceptions import ImproperlyConfigured
from django.test import TransactionTestCase
from django.db import transaction
from chamber.models.dispatchers import BaseDispatcher, StateDispatcher
from chamber.shortcuts import change_and_save
from germanium.tools import assert_equal  # pylint: disable=E0401
from test_chamber.models import (
    CSVRecord, TestDispatchersModel, TestFieldsModel, TestSmartModel, TestOnDispatchModel
)  # pylint: disable=E0401







class DispatchersTestCase(TransactionTestCase):

    def test_state_dispatcher(self):
        m = TestDispatchersModel.objects.create()

        # Moving TestDispatcher model to SECOND state should create new TestSmartModel instance
        assert_equal(TestSmartModel.objects.count(), 0)

        change_and_save(m, state=TestDispatchersModel.STATE.SECOND)