Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
pawelzny committed Mar 12, 2017
0 parents commit 90749a5
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 0 deletions.
144 changes: 144 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit tests / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
coverage/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries

# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml

# Gradle:
.idea/**/gradle.xml
.idea/**/libraries

# Mongo Explorer plugin:
.idea/**/mongoSettings.xml

## File-based project format:
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

## Custom
/.idea/
15 changes: 15 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ISC License

Copyright (c) 2017, Paweł Zadrożny @pawelzny <[email protected]>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 changes: 33 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
============
Value Object
============

:Info: DDD Value Objects implementation.
:Author: Paweł Zadrożny <[email protected]>

Installation:
=============

.. code:: bash
pip install vo
**Package**: https://pypi.python.org/pypi/vo


Example
=======

.. code:: python
from vo.Value import Value
value1 = Value(test=True, some_text="I am some text string")
value2 = Value(some_text="I am some text string", test=True)
assert(value1 == value2) # True
value1.set("extra_attr", "whatever")
assert(value1 == value2) # False
assert("whatever" == value1.get("extra_attr")) # True
assert("default value" == value2.get("extra_attr", "default value")) # True
13 changes: 13 additions & 0 deletions bin/run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

# Get absolute path to this script directory
bin_dir=$(dirname $(readlink -f "$0"))
package_dir=${bin_dir}/../
coverage_dir=${package_dir}/tests/coverage

# Run tests with nose test runner
nosetests --where=${package_dir} \
--with-coverage --cover-erase --cover-branches \
--cover-html --cover-html-dir=${coverage_dir} \
--cover-xml --cover-xml-file=${package_dir}/coverage.xml \
--stop
24 changes: 24 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-

from setuptools import setup, find_packages


with open('README.rst') as f:
readme = f.read()

with open('LICENSE') as f:
license = f.read()

setup(name='vo',
version='0.1.0',
description='DDD Value Objects implementation',
long_description=readme,
keywords='value data object DDD',
author='Pawel Zadrozny @pawelzny',
author_email='[email protected]',
url='https://github.com/pawelzny/vo',
license=license,
test_suite='nose.collector',
tests_require=['nose', 'coverage'],
packages=find_packages(exclude=('tests', 'docs', 'bin')),
zip_safe=False)
Empty file added tests/__init__.py
Empty file.
36 changes: 36 additions & 0 deletions tests/test_Value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import unittest

from vo.Value import Value


class ValueTest(unittest.TestCase):
def test_assign_kwargs(self):
value = Value(test=True, text="Some text")

self.assertTrue(value.test)
self.assertEqual('Some text', value.text)

def test_equal(self):
v1 = Value(text="first text", other_attr=1243)
v2 = Value(other_attr=1243, text="first text")

self.assertEqual(v1, v2)
self.assertEqual(hash(v1), hash(v2))

v1.new_attr = True
v2.new_attr = False

self.assertNotEqual(v1, v2)
self.assertNotEqual(hash(v1), hash(v2))

def test_setter(self):
v1 = Value()
v1.set(some_attr="I am some attribute")
self.assertEqual('I am some attribute', v1.get('some_attr'))

v1.set(some_attr="Now i am different")
self.assertEqual("Now i am different", v1.some_attr)

def test_getter(self):
v1 = Value()
self.assertEqual("Example string", v1.get('undefined_attr', "Example string"))
114 changes: 114 additions & 0 deletions vo/Value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import hashlib


class Value:
"""
Basic implementation of DDD Value Object. These objects
are meant to holds data only without any business logic.
When data has no identifier so it is not an Entity,
there comes Value Objects.
Value Objects are consider same if they holds the same
data even if allocated in different parts of memory.
:Example:
import vo
value1 = vo.Value(name="test value", test=True)
value2 = vo.Value(test=True, name="test value")
assert(value1 == value2) # True
value1.set(extra="extra data")
assert(value1 == value2) # False
"""

@staticmethod
def to_bytes(string):
"""
Converts string to byte string
:param
string (str): String or number to convert
:return
(str): Byte string
"""

return bytes(repr(string), 'utf-8')

def __init__(self, **kwargs):
"""
Dynamically add all kwargs to self dictionary.
:param
kwargs: Key-value pairs
"""

self.set(**kwargs)

def __eq__(self, other):
"""
Predicate if checksum of Value Objects are the same.
:param
other: ValueObject
:return
(bool): Boolean
"""

return self.checksum() == other.checksum()

def __ne__(self, other):
"""
Predicate if checksum of Value Objects are different.
:param
other: ValueObject
:return
(bool): Boolean
"""

return self.checksum() != other.checksum()

def __hash__(self):
"""
Returns hash of checksum
:return
(int): Integer representing object Hash
"""

return hash(self.checksum())

def set(self, **kwargs):
"""
Set additional attributes from kwargs
:param
kwargs:
:return
(self): Returns self to allow method chaining.
"""

self.__dict__.update(kwargs)

return self

def get(self, name, default=None):
"""
Gets attribute value or default if not exists.
:param
name (str): Attribute name
default (any): Default value
:return
(any): Existing attribute value or default
"""

return getattr(self, name, default)

def checksum(self):
"""
Computes and returns sha224 string from self dict items.
:return
(str): SHA224 string representing checksum of object values.
"""

ck_sum = Value.to_bytes('checksum:')
for key, value in sorted(self.__dict__.items()):
ck_sum += Value.to_bytes(str(key) + str(value))

return hashlib.sha224(ck_sum).hexdigest()
Empty file added vo/__init__.py
Empty file.

0 comments on commit 90749a5

Please sign in to comment.