Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
linkyndy committed Jan 23, 2015
2 parents 214a976 + 7a30649 commit cc35d8e
Show file tree
Hide file tree
Showing 16 changed files with 102 additions and 70 deletions.
10 changes: 10 additions & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Credits
=======

## Development Lead

* Andrei Horak (@linkyndy)

## Contributors

* Bob Jordan (@smewp)
21 changes: 21 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Contributing
============

Any contribution is **highly** appreciated, no matter whether it is a small bug fix, feature implementation or typo in the docs.

You can contribute in many ways:

* report bugs;
* fix bugs;
* implement features;
* write documentation;
* simply tell us what you love about remodel or what you'd like to see in it!


## Getting Started!

**Fork** this repo, `pip install -e .` the project, do your magic, `tox` and submit a pull request on the **develop** branch!

> Issues, features, bugs and questions are tracked at https://github.com/linkyndy/remodel/issues
> Documentation is found at https://github.com/linkyndy/remodel/wiki
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
include AUTHORS
include CONTRIBUTING.rst
include LICENSE
include README.md
29 changes: 10 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,31 +233,22 @@ shop.add(product1, produt2)

> Note that certain assignments of related objects can not be performed unless one (or both) of the objects is saved. You can not save a `GiftSize` with a `Gift` attached without saving the `Gift` object first (when having a `GiftSize belongs_to Gift`).
## Motivation

The main reason for Remodel's existence was the need of a light-weight ODM for RethinkDB, one that doesn't force you to ensure a document schema, one that provides a familiar interface and one that gracefully handles relations between models.

## Development

Remodel is under active development and it is not yet production-ready. Any contribution is highly appreciated, no matter it is a small bug fix, feature implementation or a typo in the docs.
## Documentation

### How to contribute
Can be found at https://github.com/linkyndy/remodel/wiki.

Just fork this repository, do your magic and submit a pull request on the development branch!
## Motivation

### Branches
The main reason for Remodel's existence was the need of a light-weight ODM for RethinkDB, one that doesn't force you to ensure a document schema, one that provides a familiar interface and one that gracefully handles relations between models.

Active development and up-to-date code can be found on the `develop` branch
## Status

Stable code can be found on the `master` branch
Remodel is under active development and it is not _yet_ production-ready.

### Running tests
## How to contribute?

```bash
pip install pytest
py.test tests/
```
Any contribution is **highly** appreciated! See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.

### Tracking issues
## License

All Remodel issues/feature requests/bugs/questions can be addressed on https://github.com/linkyndy/remodel/issues
See [LICENSE](LICENSE)
2 changes: 1 addition & 1 deletion remodel/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import monkey
import remodel.monkey
9 changes: 7 additions & 2 deletions remodel/connection.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@

import rethinkdb as r
from contextlib import contextmanager
from Queue import Queue, Empty
try:
from queue import Queue, Empty
except ImportError:
from Queue import Queue, Empty


from utils import Counter
from .utils import Counter


class Connection(object):
Expand Down
8 changes: 6 additions & 2 deletions remodel/errors.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
class OperationError(Exception): pass
class AlreadyRegisteredError(Exception): pass
class OperationError(Exception):
pass


class AlreadyRegisteredError(Exception):
pass
14 changes: 7 additions & 7 deletions remodel/field_handler.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from errors import AlreadyRegisteredError
import models
from registry import index_registry
from related import (HasOneDescriptor, BelongsToDescriptor, HasManyDescriptor,
from .errors import AlreadyRegisteredError
import remodel.models
from .registry import index_registry
from .related import (HasOneDescriptor, BelongsToDescriptor, HasManyDescriptor,
HasAndBelongsToManyDescriptor)
from utils import tableize
from .utils import tableize


class FieldHandlerBase(type):
def __new__(cls, name, bases, dct):
if not all(isinstance(dct[rel_type], tuple) for rel_type in models.REL_TYPES):
if not all(isinstance(dct[rel_type], tuple) for rel_type in remodel.models.REL_TYPES):
raise ValueError('Related models must be passed as a tuple')

# TODO: Find a way to pass model class to its field handler class
Expand Down Expand Up @@ -52,7 +52,7 @@ def __new__(cls, name, bases, dct):
field, lkey, rkey = tableize(other), 'id', 'id'
join_model = '_' + ''.join(sorted([model, other]))
try:
models.ModelBase(join_model, (models.Model,), {})
remodel.models.ModelBase(join_model, (remodel.models.Model,), {})
except AlreadyRegisteredError:
# HABTM join_model model has been registered, probably from the
# other end of the relation
Expand Down
39 changes: 19 additions & 20 deletions remodel/models.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import rethinkdb as r
from six import add_metaclass

from decorators import classaccessonlyproperty
from errors import OperationError
from field_handler import FieldHandlerBase, FieldHandler
from object_handler import ObjectHandler
from registry import model_registry
from utils import deprecation_warning, tableize
from .decorators import classaccessonlyproperty
from .errors import OperationError
from .field_handler import FieldHandlerBase, FieldHandler
from .object_handler import ObjectHandler
from .registry import model_registry
from .utils import deprecation_warning, tableize


REL_TYPES = ('has_one', 'has_many', 'belongs_to', 'has_and_belongs_to_many')


class ModelBase(type):
def __new__(cls, name, bases, dct):
super_new = super(ModelBase, cls).__new__
def __new__(mcs, name, bases, dct):
super_new = super(ModelBase, mcs).__new__

# Ensure the following are not done for the Model class itself
parents = [b for b in bases if isinstance(b, ModelBase)]
if not parents:
return super_new(cls, name, bases, dct)
return super_new(mcs, name, bases, dct)

# Set metadata
dct['_table'] = tableize(name)
Expand All @@ -30,20 +31,18 @@ def __new__(cls, name, bases, dct):
dict(rel_attrs, model=name))
object_handler_cls = dct.setdefault('object_handler', ObjectHandler)

new_class = super_new(cls, name, bases, dct)
new_class = super_new(mcs, name, bases, dct)
model_registry.register(name, new_class)
setattr(new_class, 'objects', object_handler_cls(new_class))
return new_class

# Proxies undefined attributes to Model.objects; useful for building
# ReQL queries directly on the Model (e.g.: User.order_by('name').run())
def __getattr__(cls, name):
return getattr(cls.objects, name)

def __getattr__(self, name):
return getattr(self.objects, name)

@add_metaclass(ModelBase)
class Model(object):
__metaclass__ = ModelBase

def __init__(self, **kwargs):
self.fields = self._field_handler_cls()

Expand All @@ -57,9 +56,9 @@ def save(self):
# Attempt update
id_ = fields_dict['id']
result = (r.table(self._table).get(id_).replace(r.row
.without(r.row.keys().difference(fields_dict.keys()))
.merge(fields_dict), return_changes=True)
.run())
.without(r.row.keys().difference(list(fields_dict.keys())))
.merge(fields_dict), return_changes=True).run())

except KeyError:
# Resort to insert
result = (r.table(self._table).insert(fields_dict, return_changes=True)
Expand Down Expand Up @@ -119,8 +118,8 @@ def __str__(self):
return '<%s object>' % self.__class__.__name__

@classaccessonlyproperty
def table(cls):
def table(self):
deprecation_warning('Model.table will be deprecated soon. Please use '
'Model.objects to build any custom query on a '
'Model\'s table (read more about ObjectHandler)')
return cls.objects
return self.objects
4 changes: 2 additions & 2 deletions remodel/monkey.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import rethinkdb as r

import connection
import remodel.connection


run = r.ast.RqlQuery.run
Expand All @@ -12,7 +12,7 @@ def remodel_run(self, c=None, **global_optargs):
"""

if not c:
with connection.get_conn() as conn:
with remodel.connection.get_conn() as conn:
return run(self, conn, **global_optargs)
else:
return run(self, c, **global_optargs)
Expand Down
6 changes: 3 additions & 3 deletions remodel/registry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections import defaultdict

from errors import AlreadyRegisteredError
import models
from .errors import AlreadyRegisteredError
import remodel.models


class ModelRegistry(object):
Expand All @@ -14,7 +14,7 @@ def __len__(self):
def register(self, name, cls):
if name in self._data:
raise AlreadyRegisteredError('Model "%s" has been already registered' % name)
if not issubclass(cls, models.Model):
if not issubclass(cls, remodel.models.Model):
raise ValueError('Registered model class "%r" must be a subclass of "Model"' % cls)
self._data[name] = cls

Expand Down
8 changes: 4 additions & 4 deletions remodel/related.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import rethinkdb as r

from decorators import cached_property
from object_handler import ObjectHandler
from registry import model_registry
from utils import tableize
from .decorators import cached_property
from .object_handler import ObjectHandler
from .registry import model_registry
from .utils import tableize


class RelationDescriptor(object):
Expand Down
13 changes: 6 additions & 7 deletions remodel/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,27 @@
from inflection import pluralize, underscore
from threading import Lock
from warnings import warn

import connection
from decorators import synchronized
import remodel.connection
from .decorators import synchronized


def tableize(what):
return pluralize(underscore(what))


def create_tables():
from registry import model_registry
from .registry import model_registry

for model_cls in model_registry.all().itervalues():
for model_cls in model_registry.all().values():
result = r.table_create(model_cls._table).run()
if result['created'] != 1:
raise RuntimeError('Could not create table %s for model %s' % (
model_cls._table, model_cls.__name__))

def create_indexes():
from registry import model_registry, index_registry
from .registry import model_registry, index_registry

for model, index_set in index_registry.all().iteritems():
for model, index_set in index_registry.all().items():
model_cls = model_registry.get(model)
for index in index_set:
r.table(model_cls._table).index_create(index).run()
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
platforms='any',
install_requires=[
'rethinkdb',
'inflection'
'inflection',
'six'
],
classifiers=[
'Environment :: Web Environment',
Expand Down
1 change: 0 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pytest
import rethinkdb as r
import unittest

from remodel.errors import OperationError
from remodel.models import Model
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27
envlist = py27, py34

[testenv]
commands = py.test tests/
Expand Down

0 comments on commit cc35d8e

Please sign in to comment.