Skip to content

Commit

Permalink
Merge pull request #3 from workenvoy/0.0.7
Browse files Browse the repository at this point in the history
0.0.7
  • Loading branch information
rayattack committed Aug 4, 2019
2 parents e1fc507 + c4341cc commit 0564780
Show file tree
Hide file tree
Showing 34 changed files with 859 additions and 108 deletions.
45 changes: 28 additions & 17 deletions firestore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,35 @@
# limitations under the License.
#
from .containers import Collection, Document
from .datatypes import (Array,
Boolean, Byte, Datatype, Float, Geopoint, Integer,
Map, Null, Reference, String, Timestamp)
from .datatypes import (
Array,
Boolean,
Byte,
Datatype,
Float,
Geopoint,
Integer,
Map,
Null,
Reference,
String,
Timestamp,
)


__all__ = [
'Array',
'Boolean',
'Byte',
'Collection',
'Datatype',
'Document',
'Float',
'Geopoint',
'Integer',
'Map',
'Null',
'Reference',
'String',
'Timestamp'
"Array",
"Boolean",
"Byte",
"Collection",
"Datatype",
"Document",
"Float",
"Geopoint",
"Integer",
"Map",
"Null",
"Reference",
"String",
"Timestamp",
]
4 changes: 3 additions & 1 deletion firestore/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
_databases = {}


def is_online(host=GOOGLE_PUBLIC_DNS_A, port=GOOGLE_PUBLIC_DNS_TCP_PORT, timeout=TIMEOUT):
def is_online(
host=GOOGLE_PUBLIC_DNS_A, port=GOOGLE_PUBLIC_DNS_TCP_PORT, timeout=TIMEOUT
):
"""
Check if this machine is online as to determine if it is necessary to save to fs
simulating cloud firestore
Expand Down
15 changes: 11 additions & 4 deletions firestore/containers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
This package contains only 2 module.Classes [Collection, Document]
John Cleese (Commentator): Good afternoon and welcome to Hurlingham Park.
You join us just as the competitors are running out onto
the field on this lovely winter's afternoon here, with the going
firm underfoot and very little sign of rain. Well it certainly
looks as though we're in for a splendid afternoon's sport in this the
127th Upperclass Twit of the Year Show. Well the competitors will
be off in a moment so let me just identify for you.
(...continued in firestore.containers.collection.py)
:copyright: 2019 Workhamper
:license: MIT
"""
Expand All @@ -17,7 +27,4 @@
from .document import Document


__all__ = [
'Collection',
'Document'
]
__all__ = ["Collection", "Document"]
19 changes: 19 additions & 0 deletions firestore/containers/collection.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
"""
firestore.containers.collection.py
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
John Cleese (Commentator): Vivian Smith-Smythe-Smith has an O-level
in chemo-hygiene. Simon-Zinc-Trumpet-Harris, married to a very
attractive table lamp. Nigel Incubator-Jones, his best friend is a tree,
and in his spare time he's a stockbroker. Gervaise Brook-Hampster is in
the Guards, and his father uses him as a wastepaper basket.
And finally Oliver St John-Mollusc, Harrow and the Guards, thought
by many to be this year's outstanding twit. Now they're moving
up to the starting line, there's a jolly good crowd here today.
Now they're under starter's orders ... and they're off!
(...continued in firestore.containers.document.py)
:copyright: 2019 Wokhamper Inc
:license: MIT
"""

import asyncio


Expand Down
138 changes: 132 additions & 6 deletions firestore/containers/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,39 @@
firestore.containers.document
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Documents are the secondary unit of storage. They are the primary
containers under which data is stored.
Using the fundamental units of storage such as text, numbers, arrays etc.
to capture information; this captured information is grouped together
under documents.
John Cleese (Commentator): Ah no, they're not. No they didn't realize
they were supposed to start. Never mind, we'll soon sort that out, the
judge is explaining it to them now. I think Nigel and Gervaise have
got the idea. All set to go.
:copyright: 2019 Workhamper
:license: MIT
"""
from firestore.errors import InvalidDocumentError, UnknownFieldError, ValidationError

# from firestore.datatypes.base import Base


class Cache(dict):
"""
A class to make attribute lookup and writing
swift and fast without the need for attribute access
notation instead defaulting to object access notation
"""

def __init__(self, *args, **kwargs):
self._pk = False
dict.__init__(self, *args, **kwargs)

def __getattr__(self, key):
# less error prone
return self[key]

def __setattr__(self, key, value):
self[key] = value

def add(self, key, value):
self.__setattr__(key, value)


class Document(object):
Expand All @@ -22,4 +46,106 @@ class Document(object):
They also help to group together commonly used actions across documents
i.e. setting and saving, querying, and updating document instances.
"""
pass

@staticmethod
def __constructor__(self, *args, **kwargs):
"""custom method to load document constraints"""
# Document constraints are the constraints found on
# fields that pertain to the entire document and not
# just the field.
# For instance required, unique, pk etc... These fields
# do not have any meaning without the larger document, and or
# Collection in the picture.
pass

def __deref__(self, doc_ref):
"""
Deref string based document references into classes
upon instance assignment by looking up the doc_ref
first in the globals of this module then walking
up the directory tree until an instance is found
or an error is thrown
"""
raise NotImplementedError(
"String dereferencing priority is low for now, will come back to this in a few weeks"
)

def __init__(self, *args, **kwargs):
"""
Root document holding all the utility methods
needed for persistence to cloud firestore
"""

# This is the internal cache that holds all the field
# values to be saved on google cloud firestore
self._data = Cache()

# Similar to the ._data instance cache. However this
# is a collection of all descriptor instances
# that exist on this document class.
# Useful for pk, unique, required and other document
# level validation.
self.fields_cache = {
k: v
for k, v in type(self).__dict__.items()
if k not in ["__module__", "__doc__"]
}

for k in kwargs:
if k not in self.fields_cache.keys(): # on the fly access to obviate the need for gc
raise UnknownFieldError(
f"Key {k} not found in document {type(self).__name__}"
)
self._data.add(k, kwargs.get(k))

def add_field(self, field, value):
"""
Add a field to this instance's data for persistence
taking into cognizance all the validations present on the field
"""
self._data.add(field._name, value)

def get_field(self, field):
"""
Get a field form the internal _data store of field values
"""
return self._data.get(field._name)

def _presave(self):
"""
Validates inputs and ensures all required fields and other
constraints are present before the save operation is called
"""
for k in self.fields_cache:
# get a local copy of the field instance
f = self.fields_cache.get(k)

# get the value saved in the local data cache
v = self._data.get(k)

if not v:
if f.default:
self._data.add(k, v.default)
if callable(v.default):
self._data.add(k, v.default())
elif f.required:
raise ValidationError(f'{f._name} is a required field of {type(self).__name__}')

def save(self):
"""
Save changes made to document to cloud firestore.
"""
self._presave()

def persist(self):
"""Save changes made to this document and any children of this
document to cloud firestore
"""
pass

def transaction(self):
"""
Perform a transaction i.e. persist all changes or roll back
entire transaction
"""
pass
2 changes: 1 addition & 1 deletion firestore/datatypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ class Datatype(object):
"Null",
"Reference",
"String",
"Timestamp"
"Timestamp",
]
25 changes: 22 additions & 3 deletions firestore/datatypes/array.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from .datatype import Datatype
from firestore.datatypes.base import Base

from firestore.errors import ValidationError

class Array(Datatype):

class Array(Base):
"""
An array is a sequence datatype and can not contain another array
as one of its elements.
Expand All @@ -19,4 +21,21 @@ class Array(Datatype):
element value. The array [1, 2, 3] has elements equal to the first three elements
of [1, 2, 3, 1] but is shorter in length.
"""
pass

__slots__ = ("minimum", "maximum", "value")

def __init__(self, *args, **kwargs):
self.minimum = kwargs.get("minimum")
self.maximum = kwargs.get("maximum")
super(Array, self).__init__(*args, **kwargs)

def validate(self, value):
"""Validate the value conforms to the dataype expected of Arrays"""
if self.minimum and self.minimum > len(value):
raise ValidationError(
f"Array {self._name} must me a minimum of len {self.minimum}"
)
if self.maximum and self.maximum < len(value):
raise ValidationError(
f"Array {self._name} must be a maximum of len {self.maximum}"
)
23 changes: 23 additions & 0 deletions firestore/datatypes/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class Base(object):
"""
Super class for document valid datatypes
"""

def __init__(self, *args, **kwargs):
self.pk = kwargs.get("pk")
self.required = kwargs.get("required")
self.default = kwargs.get("default")
self.unique = kwargs.get("unique")

def __get__(self, instance, metadata):
return instance.get_field(self)

def __set__(self, instance, value):
self.validate(value)
instance.add_field(self, value)

def __set_name__(self, cls, name):
self._name = name

def validate(self, value):
pass
24 changes: 21 additions & 3 deletions firestore/datatypes/boolean.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from .datatype import Datatype
from firestore.errors import ValidationError
from firestore.datatypes.base import Base


class Boolean(Datatype):
class Boolean(Base):
"""
Represents a boolean field in the firestore Document instance
Expand All @@ -10,4 +11,21 @@ class Boolean(Datatype):
Return an iterator that yields tubles of an index and an item of the
*sequence*. (And so on.)
"""
pass

__slots__ = ("value", "coerce", "_name")

def __init__(self, *args, **kwargs):
self.coerce = kwargs.get("coerce", True)
super(Boolean, self).__init__(*args, **kwargs)

def __set_name__(self, instance, name):
self._name = name

def validate(self, value):
if self.coerce:
return bool(value)
if not isinstance(value, bool):
raise ValidationError(
f"Can not assign non-boolean to {self._name} type boolean"
)
return value
1 change: 1 addition & 0 deletions firestore/datatypes/byte.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ class Byte(Datatype):
Firestore cloud db byte datatype. Up to 1,048,487 bytes (1 MiB - 89 bytes).
Only the first 1,500 bytes are considered by queries
"""

pass
Loading

0 comments on commit 0564780

Please sign in to comment.