diff --git a/.gitignore b/.gitignore index 046cca1..ba74660 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,57 @@ -*.pyc -MANIFEST -dist/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ *.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 test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/CHANGELOG.md b/CHANGELOG.md index e21b345..dee8a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.0.4 + +Enhancement: + + - Added option to save word order + - Added more tests + - Added Django 1.8 and dropped unsupported Django + ## 1.0.3 Enhancement: diff --git a/README.md b/README.md index 3888589..bc82470 100644 --- a/README.md +++ b/README.md @@ -78,11 +78,15 @@ Unicode Test self.assertEqual(r, "jaja-lol-a") txt = 'jaja---lol-méméméoo--a' - r = slugify(txt, max_length=19, word_boundary=True) + r = slugify(txt, max_length=17, word_boundary=True) self.assertEqual(r, "jaja-lol-mememeoo") txt = 'jaja---lol-méméméoo--a' - r = slugify(txt, max_length=20, word_boundary=True) + r = slugify(txt, max_length=18, word_boundary=True) + self.assertEqual(r, "jaja-lol-mememeoo") + + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, max_length=19, word_boundary=True) self.assertEqual(r, "jaja-lol-mememeoo-a") txt = 'jaja---lol-méméméoo--a' @@ -101,6 +105,22 @@ Unicode Test r = slugify(txt) self.assertEqual(r, "this-is-a-test") + txt = 'one two three four five' + r = slugify(txt, max_length=13, word_boundary=True, save_order=True) + self.assertEqual(r, "one-two-three") + + txt = 'one two three four five' + r = slugify(txt, max_length=13, word_boundary=True, save_order=False) + self.assertEqual(r, "one-two-three") + + txt = 'one two three four five' + r = slugify(txt, max_length=12, word_boundary=True, save_order=False) + self.assertEqual(r, "one-two-four") + + txt = 'one two three four five' + r = slugify(txt, max_length=12, word_boundary=True, save_order=True) + self.assertEqual(r, "one-two") + Uniqueness Test @@ -156,14 +176,14 @@ Uniqueness Test # Let's test it name = 'jaja---lol-méméméoo--a' - obj = SmartTruncatedSlug.objects.create(name=name) - print obj.slug # "jaja-lol-mememeoo" --- where 19 is max_length (first slug, no duplicate yet) + obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name) + self.assertEqual(obj.slug, "jaja-lol-mememeoo-a") # 19 is max_length - obj = SmartTruncatedSlug.objects.create(name=name) - print obj.slug # "jaja-lol-mememeoo-9" --- where 19 is max_length, start_no = 9 + obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name) + self.assertEqual(obj.slug, "jaja-lol-mememeoo-9") # 19 is max_length, start_no = 9 - obj = SmartTruncatedSlug.objects.create(name=name) - print obj.slug # "jaja-lol-mememeo-10" -- where 19 is max_length, smart appending "-10" + obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name) + self.assertEqual(obj.slug, "jaja-lol-mememeo-10") # 19 is max_length, readjust for "-10" Running the tests @@ -177,15 +197,14 @@ To run the tests against the current environment: License ==================== -Protected by ([BSD](LICENSE.md)) +Released under a ([BSD](LICENSE.md)) license. [build-status-image-travis]: https://secure.travis-ci.org/un33k/django-uuslug.png?branch=master -[travis]: http://travis-ci.org/tomchristie/django-uuslug?branch=master +[travis]: http://travis-ci.org/un33k/django-uuslug?branch=master [build-status-image-fury]: https://badge.fury.io/py/django-uuslug.png [fury]: http://badge.fury.io/py/django-uuslug [build-status-image-pypi]: https://pypip.in/d/django-uuslug/badge.png [pypi]: https://crate.io/packages/django-uuslug?version=latest - diff --git a/manage.py b/manage.py index ffb6e42..ed27fc3 100755 --- a/manage.py +++ b/manage.py @@ -3,8 +3,6 @@ import os import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "uuslug.testsettings") - + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "uuslug.tests.testsettings") from django.core.management import execute_from_command_line - execute_from_command_line(sys.argv) diff --git a/uuslug/__init__.py b/uuslug/__init__.py index 51dc7d2..a6b0725 100644 --- a/uuslug/__init__.py +++ b/uuslug/__init__.py @@ -1,52 +1,7 @@ -# -*- coding: utf-8 -*- +from .uuslug import * -__version__ = '1.0.3' +__author__ = 'Val Neekman @ Neekware Inc. [@vneekman]' +__description__ = 'A Python slugify application that also handles Unicode' +__version__ = '1.0.4' default_app_config = 'uuslug.apps.AppConfig' - -from django.utils import six - -if six.PY3: - from django.utils.encoding import smart_str -else: - from django.utils.encoding import smart_unicode as smart_str - -from slugify import slugify as pyslugify - -__all__ = ['slugify', 'uuslug'] - - -def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, word_boundary=False, separator='-'): - """ Make a slug from a given text """ - - return smart_str(pyslugify(text, entities, decimal, hexadecimal, max_length, word_boundary, separator)) - - -def uuslug(s, instance, entities=True, decimal=True, hexadecimal=True, - slug_field='slug', filter_dict=None, start_no=1, max_length=0, - word_boundary=False, separator='-'): - - """ This method tries a little harder than django's django.template.defaultfilters.slugify. """ - - if hasattr(instance, 'objects'): - raise Exception("Error: you must pass an instance to uuslug, not a model.") - - queryset = instance.__class__.objects.all() - if filter_dict: - queryset = queryset.filter(**filter_dict) - if instance.pk: - queryset = queryset.exclude(pk=instance.pk) - - slug = slugify(s, entities=entities, decimal=decimal, hexadecimal=hexadecimal, - max_length=max_length, word_boundary=word_boundary, separator=separator) - - new_slug = slug - counter = start_no - while queryset.filter(**{slug_field: new_slug}).exists(): - if max_length > 0: - if len(slug) + len(separator) + len(str(counter)) > max_length: - slug = slug[:max_length - len(slug) - len(separator) - len(str(counter))] - new_slug = "%s%s%s" % (slug, separator, counter) - counter += 1 - - return new_slug diff --git a/uuslug/tests/__init__.py b/uuslug/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/uuslug/tests.py b/uuslug/tests/tests.py similarity index 88% rename from uuslug/tests.py rename to uuslug/tests/tests.py index f6c00dd..1df0b53 100644 --- a/uuslug/tests.py +++ b/uuslug/tests/tests.py @@ -61,11 +61,15 @@ class SlugUnicodeTestCase(TestCase): self.assertEqual(r, "jaja-lol-a") txt = 'jaja---lol-méméméoo--a' - r = slugify(txt, max_length=19, word_boundary=True) + r = slugify(txt, max_length=17, word_boundary=True) self.assertEqual(r, "jaja-lol-mememeoo") txt = 'jaja---lol-méméméoo--a' - r = slugify(txt, max_length=20, word_boundary=True) + r = slugify(txt, max_length=18, word_boundary=True) + self.assertEqual(r, "jaja-lol-mememeoo") + + txt = 'jaja---lol-méméméoo--a' + r = slugify(txt, max_length=19, word_boundary=True) self.assertEqual(r, "jaja-lol-mememeoo-a") txt = 'jaja---lol-méméméoo--a' @@ -84,6 +88,22 @@ class SlugUnicodeTestCase(TestCase): r = slugify(txt) self.assertEqual(r, "this-is-a-test") + txt = 'one two three four five' + r = slugify(txt, max_length=13, word_boundary=True, save_order=True) + self.assertEqual(r, "one-two-three") + + txt = 'one two three four five' + r = slugify(txt, max_length=13, word_boundary=True, save_order=False) + self.assertEqual(r, "one-two-three") + + txt = 'one two three four five' + r = slugify(txt, max_length=12, word_boundary=True, save_order=False) + self.assertEqual(r, "one-two-four") + + txt = 'one two three four five' + r = slugify(txt, max_length=12, word_boundary=True, save_order=True) + self.assertEqual(r, "one-two") + class SlugUniqueTestCase(TestCase): """Tests for Slug - Unique""" @@ -149,7 +169,7 @@ class SlugUniqueTestCase(TestCase): name = 'jaja---lol-méméméoo--a' obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name) - self.assertEqual(obj.slug, "jaja-lol-mememeoo") # 19 is max_length + self.assertEqual(obj.slug, "jaja-lol-mememeoo-a") # 19 is max_length obj = SmartTruncatedExactWordBoundrySlug.objects.create(name=name) self.assertEqual(obj.slug, "jaja-lol-mememeoo-9") # 19 is max_length, start_no = 9 diff --git a/uuslug/testsettings.py b/uuslug/tests/testsettings.py similarity index 100% rename from uuslug/testsettings.py rename to uuslug/tests/testsettings.py diff --git a/uuslug/uuslug.py b/uuslug/uuslug.py new file mode 100644 index 0000000..0baca02 --- /dev/null +++ b/uuslug/uuslug.py @@ -0,0 +1,49 @@ +from slugify import slugify as pyslugify +from django.utils import six +if six.PY3: + from django.utils.encoding import smart_str +else: + from django.utils.encoding import smart_unicode as smart_str + +__all__ = ['slugify', 'uuslug'] + + +def slugify(text, entities=True, decimal=True, hexadecimal=True, max_length=0, + word_boundary=False, separator='-', save_order=False): + """ + Make a slug from a given text. + """ + + return smart_str(pyslugify(text, entities, decimal, hexadecimal, max_length, + word_boundary, separator, save_order)) + + +def uuslug(s, instance, entities=True, decimal=True, hexadecimal=True, + slug_field='slug', filter_dict=None, start_no=1, max_length=0, + word_boundary=False, separator='-', save_order=False): + + """ This method tries a little harder than django's django.template.defaultfilters.slugify. """ + + if hasattr(instance, 'objects'): + raise Exception("Error: you must pass an instance to uuslug, not a model.") + + queryset = instance.__class__.objects.all() + if filter_dict: + queryset = queryset.filter(**filter_dict) + if instance.pk: + queryset = queryset.exclude(pk=instance.pk) + + slug = slugify(s, entities=entities, decimal=decimal, hexadecimal=hexadecimal, + max_length=max_length, word_boundary=word_boundary, separator=separator, + save_order=save_order) + + new_slug = slug + counter = start_no + while queryset.filter(**{slug_field: new_slug}).exists(): + if max_length > 0: + if len(slug) + len(separator) + len(str(counter)) > max_length: + slug = slug[:max_length - len(slug) - len(separator) - len(str(counter))] + new_slug = "{}{}{}".format(slug, separator, counter) + counter += 1 + + return new_slug