diff --git a/README.md b/README.md index 89e4c91..e0d76e8 100644 --- a/README.md +++ b/README.md @@ -19,18 +19,21 @@ done before officially releasing on PyPI. ```python import mutations -class EmailToBouncedMessage(mutations.Mutation): +class UserSignup(mutations.Mutation): + """Define the inputs to your mutation here. """ email = mutations.fields.CharField(required=True) - send_welcome_email = mutations.fields.Boolean(required=False, default=False) + full_name = mutations.fields.CharField(required=True) + send_welcome_email = mutations.fields.Boolean(required=False, default=True) - def validate_email_object(self): + def validate_email_address(self): """Custom validation for a field. If you encounter any validation errors and want to raise, you should raise mutation.ValidationError or some sublcass thereof. Otherwise, it assumes there were no problems. - Any function beginning with `validate_` is assumed to be a validator function. + Any function beginning with `validate_` is assumed to be a validator + function and will be run before the mutation can execute. """ if not self.email.is_valid(): raise mutations.ValidationError("email_not_valid", "Email is not valid.") @@ -41,27 +44,26 @@ class EmailToBouncedMessage(mutations.Mutation): This method does the heavy lifting. You can call it by calling .run() on your mutation class. """ - new_object = "this is the new object" + user = User.objects.create(email=self.email, name=self.full_name) if self.send_welcome_email: - # You can access the inputs here, like this. PersonalEmailServer.deliver(recipient = self.email) - return new_object + return user ``` ## Calling Commands ```python ->>> result = EmailToBouncedMessage.run(email=email, send_welcome_email=True) +>>> result = EmailToBouncedMessage.run(email=email, full_name="Bob Boblob") >>> result.success True >>> result.value -"this is the new object" + >>> result.errors ``` ```python ->>> result = EmailToBouncedMessage.run(email = None) +>>> result = EmailToBouncedMessage.run(email=None) >>> result.success False >>> result.errors diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..80ad4dc --- /dev/null +++ b/TODO.md @@ -0,0 +1,31 @@ +# TODO: + +List of things we need to do/fix before releasing on PyPI: + +### General + +- [ ] Make sure you cannot create a field called "inputs" +- [ ] Make sure it raies if you give it a kwarg it doesn't know about. +- [ ] Think of a better way to pass exceptions. I don't like passing the + `nameduple` in the Exception. + +- [ ] Support for Python 2.X. +- [ ] Update README and provide more useful examples. +- [ ] Put test requirements into `test-requirements.txt` +- [ ] Make execute an `@abstractmethod`, so that an error gets raised if + you don't define `execute` in your mutation subclass. +- [ ] Release on pypi. +- [ ] Support for running commands in an atomic (all or nothing) fashion, + perhaps using a contextmanager. +- [x] Create setup.py file +- [x] Add `__version__` +- [x] Test that exceptions can be raised when you `run`. + + +### Testing + +- [ ] Make sure default values get overridden if there's a user-provided value. +- [ ] Make sure command fails if you provide unexpected inputs. +- [ ] Make sure `Mutation.__getattr__` raises if you ask it for something that does not exist. + + diff --git a/mutations/__init__.py b/mutations/__init__.py index 3f30c84..d41549c 100644 --- a/mutations/__init__.py +++ b/mutations/__init__.py @@ -1,3 +1,3 @@ from .core import Mutation -__version__ = '0.1.0' \ No newline at end of file +__version__ = '0.1.0' diff --git a/mutations/core.py b/mutations/core.py index d0d6b0c..86eff1f 100644 --- a/mutations/core.py +++ b/mutations/core.py @@ -7,6 +7,7 @@ Result = namedtuple('Result', ['success', 'return_value', 'errors']) + class MutationBase(type): def __new__(mcs, name, bases, attrs): attrs.update({ @@ -30,9 +31,10 @@ def __new__(mcs, name, bases, attrs): for v in extra_validator_list: validator = attrs.pop(v) attrs['extra_validators'][v].extend(wrap(validator)) - + return super().__new__(mcs, name, bases, attrs) + class Mutation(metaclass=MutationBase): def __init__(self, name, inputs=None): self.name = name diff --git a/mutations/error.py b/mutations/error.py index 33cb92a..a696fc8 100644 --- a/mutations/error.py +++ b/mutations/error.py @@ -1,5 +1,10 @@ from collections import UserDict, namedtuple + +class MutationError(Exception): + pass + + class ErrorDict(UserDict): def __init__(self, *args, default_factory=list, **kwargs): self.default_factory = default_factory @@ -11,6 +16,8 @@ def __getitem__(self, k): return self.data[k] def __add__(self, other): + if self.default_factory != other.default_factory: + raise MutationError("Cannot add two ErrorDicts with different default_factories.") context = {} context.update(self) for key, val in other.items(): @@ -25,12 +32,9 @@ def is_empty(self): return not bool(self.data) -class MutationError(Exception): - pass - - ErrorBody = namedtuple('ErrorBody', ['err', 'msg']) + class ValidationError(MutationError): def __init__(self, err=None, msg=None, *args, **kwargs): self.err = err diff --git a/mutations/fields.py b/mutations/fields.py index 237e358..5b27157 100644 --- a/mutations/fields.py +++ b/mutations/fields.py @@ -55,4 +55,4 @@ def __init__(self, instance_of, *args, **kwargs): @property def validators(self): - return validators.InstanceValidator(instance_of=self.instance_of) \ No newline at end of file + return validators.InstanceValidator(instance_of=self.instance_of) diff --git a/mutations/sample.py b/mutations/sample.py deleted file mode 100644 index 3498f68..0000000 --- a/mutations/sample.py +++ /dev/null @@ -1,7 +0,0 @@ -import mutations - - -class SimpleMutation(mutations.Mutation): - name = mutations.fields.CharField() - email = mutations.fields.CharField() - location = mutations.fields.CharField(required=False) \ No newline at end of file diff --git a/mutations/util.py b/mutations/util.py index fd209b9..88adedd 100644 --- a/mutations/util.py +++ b/mutations/util.py @@ -1,4 +1,4 @@ def wrap(item): if not isinstance(item, (list, tuple)): return [item] - return item \ No newline at end of file + return item diff --git a/mutations/validators.py b/mutations/validators.py index 09c8818..902929d 100644 --- a/mutations/validators.py +++ b/mutations/validators.py @@ -51,4 +51,4 @@ def is_valid(self, *args, **kwargs): class SavedObjectValidator(ValidatorBase): def is_valid(self, obj): - return obj.pk is not None and obj.pk != '' \ No newline at end of file + return obj.pk is not None and obj.pk != ''