Repository: opsdroid/opsdroid

Issue:
Connector and Database base classes
Connector and Database module classes should inherit from some kind of base class with required methods defined to raise not implemented errors.

This would also mean that instead of checking the class name when importing the modules we can instead check whether the class inherits from the base class.



Discussion:

Resolving patch:
diff --git a/opsdroid/connector.py b/opsdroid/connector.py
new file mode 100644
--- /dev/null
+++ b/opsdroid/connector.py
@@ -0,0 +1,59 @@
+"""A base class for connectors to inherit from."""
+
+from opsdroid.message import Message  # NOQA # pylint: disable=unused-import
+
+
+class Connector():
+    """A base connector.
+
+    Connectors are used to interact with a given chat service.
+
+    """
+
+    def __init__(self, config):
+        """Setup the connector.
+
+        Set some basic properties from the connector config such as the name
+        of this connector and the name the bot should appear with in chat
+        service.
+
+        Args:
+            config (dict): The config for this connector specified in the
+                           `configuration.yaml` file.
+
+        """
+        self.name = ""
+        self.config = config
+
+    def connect(self, opsdroid):
+        """Connect to chat service and parse all messages.
+
+        This method should block the thread with an infinite loop and create
+        Message objects for chat messages coming from the service. It should
+        then call `opsdroid.parse(message)` on those messages.
+
+        Due to this method blocking, if multiple connectors are configured in
+        opsdroid they will be run in parallel using the multiprocessing
+        library.
+
+        Args:
+            opsdroid (OpsDroid): An instance of the opsdroid core.
+
+        """
+        raise NotImplementedError
+
+    def respond(self, message):
+        """Send a message back to the chat service.
+
+        The message object will have a `text` property which should be sent
+        back to the chat service. It may also have a `room` and `user` property
+        which gives information on where the message should be directed.
+
+        Args:
+            message (Message): A message received by the connector.
+
+        Returns:
+            bool: True for message successfully sent. False otherwise.
+
+        """
+        raise NotImplementedError
diff --git a/opsdroid/database.py b/opsdroid/database.py
new file mode 100644
--- /dev/null
+++ b/opsdroid/database.py
@@ -0,0 +1,71 @@
+"""A base class for databases to inherit from."""
+
+
+class Database():
+    """A base database.
+
+    Database classes are used to persist key/value pairs in a database.
+
+    """
+
+    def __init__(self, config):
+        """Setup the database.
+
+        Set some basic properties from the database config such as the name
+        of this database. It could also be a good place to setup properties
+        to hold things like the database connection object and the database
+        name.
+
+        Args:
+            config (dict): The config for this database specified in the
+                           `configuration.yaml` file.
+
+        """
+        self.name = ""
+        self.config = config
+        self.client = None
+        self.database = None
+
+    def connect(self, opsdroid):
+        """Connect to chat service and store the connection object.
+
+        This method should connect to the given database using a native
+        python library for that database. The library will most likely involve
+        a connection object which will be used by the put and get methods.
+        This object should be stored in self.
+
+        Args:
+            opsdroid (OpsDroid): An instance of the opsdroid core.
+
+        """
+        raise NotImplementedError
+
+    def put(self, key, data):
+        """Store the data object in a database against the key.
+
+        The data object will need to be serialised in a sensible way which
+        suits the database being used and allows for reconstruction of the
+        object.
+
+        Args:
+            key (string): The key to store the data object under.
+            data (object): The data object to store.
+
+        Returns:
+            bool: True for data successfully stored, False otherwise.
+
+        """
+        raise NotImplementedError
+
+    def get(self, key):
+        """Return a data object for a given key.
+
+        Args:
+            key (string): The key to lookup in the database.
+
+        Returns:
+            object or None: The data object stored for that key, or None if no
+                            object found for that key.
+
+        """
+        raise NotImplementedError


Test patch:
diff --git a/tests/test_connector.py b/tests/test_connector.py
new file mode 100644
--- /dev/null
+++ b/tests/test_connector.py
@@ -0,0 +1,24 @@
+
+import unittest
+
+from opsdroid.connector import Connector
+
+
+class TestConnectorBaseClass(unittest.TestCase):
+    """Test the opsdroid connector base class."""
+
+    def test_init(self):
+        config = {"example_item": "test"}
+        connector = Connector(config)
+        self.assertEqual("", connector.name)
+        self.assertEqual("test", connector.config["example_item"])
+
+    def test_connect(self):
+        connector = Connector({})
+        with self.assertRaises(NotImplementedError):
+            connector.connect({})
+
+    def test_respond(self):
+        connector = Connector({})
+        with self.assertRaises(NotImplementedError):
+            connector.respond({})
diff --git a/tests/test_database.py b/tests/test_database.py
new file mode 100644
--- /dev/null
+++ b/tests/test_database.py
@@ -0,0 +1,29 @@
+
+import unittest
+
+from opsdroid.database import Database
+
+
+class TestDatabaseBaseClass(unittest.TestCase):
+    """Test the opsdroid database base class."""
+
+    def test_init(self):
+        config = {"example_item": "test"}
+        database = Database(config)
+        self.assertEqual("", database.name)
+        self.assertEqual("test", database.config["example_item"])
+
+    def test_connect(self):
+        database = Database({})
+        with self.assertRaises(NotImplementedError):
+            database.connect({})
+
+    def test_get(self):
+        database = Database({})
+        with self.assertRaises(NotImplementedError):
+            database.get("test")
+
+    def test_put(self):
+        database = Database({})
+        with self.assertRaises(NotImplementedError):
+            database.put("test", {})