diff --git a/resync/resource_container.py b/resync/resource_container.py
index d2ed00f..0aec561 100644
--- a/resync/resource_container.py
+++ b/resync/resource_container.py
@@ -14,6 +14,22 @@ import collections.abc
from .w3c_datetime import datetime_to_str
+def _str_datetime_now(x='now'):
+ """Return datetime string for use with time attributes.
+
+ Handling depends on input:
+ 'now' - returns datetime for now
+ other string - no change, return same value
+ otherwise - assume datetime value, generate string
+ """
+ if (x == 'now' or not isinstance(x, str)):
+ # Now, this is wht datetime_to_str() with no arg gives
+ return datetime_to_str(x)
+ else:
+ # Assume valid datetime string
+ return x
+
+
class ResourceContainer(object):
"""Class containing resource-like objects.
@@ -26,6 +42,11 @@ class ResourceContainer(object):
- uri is optional identifier of this container object
- capability_name - name of this capability
+ The properties md_from, md_at, md_until and md_completed are datetime
+ strings. The setters acceopt either a string value (not checked), the value
+ 'now' to generate the current datetime, or otherwise an integer timestamp
+ value that is converted to a string.
+
Derived classes may add extra functionality such as len() etc..
However, any code designed to work with any ResourceContainer
should use only the core functionality.
@@ -45,19 +66,16 @@ class ResourceContainer(object):
Baseline implementation use iterator given by resources property.
"""
- return(iter(self.resources))
+ return iter(self.resources)
def __getitem__(self, index):
"""Feed through for __getitem__ of resources property."""
- return(self.resources[index])
+ return self.resources[index]
@property
def capability(self):
- """Get/set the attribute."""
- if ('capability' in self.md):
- return(self.md['capability'])
- else:
- return(None)
+ """Get/set the attribute, None if not set."""
+ return self.md.get('capability')
@capability.setter
def capability(self, capability):
@@ -65,65 +83,53 @@ class ResourceContainer(object):
@property
def md_from(self):
- """Get/set the attribute."""
- if ('md_from' in self.md):
- return(self.md['md_from'])
- else:
- return(None)
+ """Get/set the attribute, None if not set."""
+ return self.md.get('md_from')
@md_from.setter
def md_from(self, md_from):
- self.md['md_from'] = self._str_datetime_now(md_from)
+ self.md['md_from'] = _str_datetime_now(md_from)
@property
def md_until(self):
- """Get/set the attribute."""
- if ('md_until' in self.md):
- return(self.md['md_until'])
- else:
- return(None)
+ """Get/set the attribute, None if not set."""
+ return self.md.get('md_until')
@md_until.setter
def md_until(self, md_until):
- self.md['md_until'] = self._str_datetime_now(md_until)
+ self.md['md_until'] = _str_datetime_now(md_until)
@property
def md_at(self):
"""Get/set the attribute."""
- if ('md_completed' in self.md):
- return(self.md['md_completed'])
- else:
- return(None)
+ """Get/set the attribute, None if not set."""
+ return self.md.get('md_completed')
@md_completed.setter
def md_completed(self, md_completed):
- self.md['md_completed'] = self._str_datetime_now(md_completed)
+ self.md['md_completed'] = _str_datetime_now(md_completed)
def link(self, rel):
"""Look for link with specified rel, return else None."""
for link in self.ln:
if ('rel' in link and link['rel'] == rel):
- return(link)
- return(None)
+ return link
+ return None
def link_href(self, rel):
"""Look for link with specified rel, return href from it or None."""
link = self.link(rel)
if (link is not None):
link = link['href']
- return(link)
+ return link
def link_set(self, rel, href, **atts):
"""Set/create link with specified rel, set href and any other attributes.
@@ -132,16 +138,16 @@ class ResourceContainer(object):
also defines the type attributes and others are permitted also. See
description of allowed formats in
- http://www.openarchives.org/rs/resourcesync.html#DocumentFormats
+ http://www.openarchives.org/rs/resourcesync#DocumentFormats
"""
link = self.link(rel)
- if (link is not None):
- # overwrite current value
- link['href'] = href
- else:
+ if link is None:
# create new link
link = {'rel': rel, 'href': href}
self.ln.append(link)
+ else:
+ # overwrite current value
+ link['href'] = href
for k in atts:
link[k] = atts[k]
@@ -232,29 +238,3 @@ class ResourceContainer(object):
pruned2.append(r)
self.resources = pruned2
return(n)
-
- def __str__(self):
- """Return string of all resources in order given by interator."""
- s = ''
- for resource in self:
- s += str(resource) + "\n"
- return(s)
-
- def _str_datetime_now(self, x=None):
- """Return datetime string for use with time attributes.
-
- Handling depends on input:
- 'now' - returns datetime for now
- number - assume datetime values, generate string
- other - no change, return same value
- """
- if (x == 'now'):
- # Now, this is wht datetime_to_str() with no arg gives
- return(datetime_to_str())
- try:
- # Test for number
- junk = x + 0.0
- return datetime_to_str(x)
- except TypeError:
- # Didn't look like a number, treat as string
- return x
diff --git a/tests/test_resource_container.py b/tests/test_resource_container.py
index d60031f..844bb8e 100644
--- a/tests/test_resource_container.py
+++ b/tests/test_resource_container.py
@@ -1,6 +1,8 @@
+"""Tests for resync.resource_container."""
+
import unittest
from resync.resource import Resource
-from resync.resource_container import ResourceContainer
+from resync.resource_container import ResourceContainer, _str_datetime_now
class TestResourceContainer(unittest.TestCase):
@@ -8,9 +10,12 @@ class TestResourceContainer(unittest.TestCase):
def test01_create_and_add(self):
rc = ResourceContainer(resources=[])
self.assertEqual(len(rc.resources), 0, "empty")
- rc.resources.append(Resource('a', timestamp=1))
- rc.resources.append(Resource('b', timestamp=2))
+ rc.add(Resource('a', timestamp=1))
+ rc.add(Resource('b', timestamp=2))
self.assertEqual(len(rc.resources), 2, "two resources")
+ # Add two more
+ rc.add([Resource('c'), Resource('d')])
+ self.assertEqual(len(rc.resources), 4, "four resources")
def test02_iter(self):
rc = ResourceContainer(resources=[])
@@ -93,3 +98,37 @@ class TestResourceContainer(unittest.TestCase):
self.assertEqual(rc.up, None)
rc.up = "up_uri"
self.assertEqual(rc.up, "up_uri")
+
+ def test09_capability(self):
+ """Test capability property."""
+ rc = ResourceContainer()
+ self.assertEqual(rc.capability, None)
+ rc.capability = "fixit"
+ self.assertEqual(rc.capability, 'fixit')
+
+ def test10_link_set(self):
+ """Test link_set method."""
+ rc = ResourceContainer()
+ self.assertEqual(rc.link('alink'), None) # non-spec rel should be supported
+ rc.link_set('alink', 'uri:l', extra1='one', extra2='two')
+ ln = rc.link('alink')
+ self.assertEqual(rc.link('alink'), {'rel': 'alink', 'href': 'uri:l',
+ 'extra1': 'one', 'extra2': 'two'})
+ # Can update existing link
+ rc.link_set('alink', 'uri:new', extra1='1')
+ self.assertEqual(rc.link('alink'), {'rel': 'alink', 'href': 'uri:new',
+ 'extra1': '1', 'extra2': 'two'})
+
+ def test11_index(self):
+ """Test index property."""
+ rc = ResourceContainer()
+ self.assertEqual(rc.index, None)
+ rc.index = 'uri:i'
+ self.assertEqual(rc.index, 'uri:i')
+
+ def test12_str_datetime_now(self):
+ """Test _str_datetime_now function."""
+ self.assertTrue(isinstance(_str_datetime_now(), str))
+ self.assertTrue(isinstance(_str_datetime_now('now'), str))
+ self.assertEqual(_str_datetime_now(0), '1970-01-01T00:00:00Z')
+ self.assertEqual(_str_datetime_now('2020-12-24T16:01:00Z'), '2020-12-24T16:01:00Z')