Extension System

Glim provides an extension system where developers can further improve the functionality of glim. The core package is currently providing solutions for the common problems of web development. However, you can easily develop extensions and integrate tightly to glim framework. Ok, ready ? let's get started!

Configuration

In glim, the default configuration comes with a dict namely "extensions". In this key, the enabled app extensions and its configuration should be defined. Consider an example of a redis extension namely "gredis". The extension configuration would be the following;

# app/config/<env>.py
config = {

    # ...
  
    'extensions': {
            'gredis' : {
              'default': {
                'host': 'localhost',
                'port': '6379',
                'db': 0
            }
        }
    },
  
    # ...
}

This configuration simply says to glim that an extension named "gredis" should be enabled with the configuration inside the '"default" key when the web server starts. The default key exists for connection aliasing. It's there for redis extension to handle multiple redis connections.

After this configuration is enabled, glim will look for an extension (a folder) called "gredis" in ext folder.

šŸ“˜

Configuration is optional but recommended

In glim, the extensions can also be loaded during runtime with explicitly instantiating the classes of the extension, But the configuration above would be much more easy to boot an extension. You can use manually if an extension doesn't require to be booted up once and used everywhere.

The extension module and start

The following file structure could be used to create extensions;

gredis
ā”œā”€ā”€ __init__.py
ā”œā”€ā”€ gredis.py
ā”œā”€ā”€ requirements
ā””ā”€ā”€ start.py

The gredis.py holds the core classes to use them and a Facade class that can boot these core objects with configuration and hold the instance of it. The start.py is used bunch of statements when the extension is enabled. "requirements" can be used for 3rd party dependencies of pypi. So, let's have a look at this extension;

# gredis.py
from glim.core import Facade
from glim.component import Extension
from glim.facades import Log

import redis

class GredisExtension(Extension):

        def __init__(self, config):
        self.config = config
        self.active = 'default'
        self.connections = {}

        for k, config in self.config.items():
            self.connections[k] = self.connect(config)

        def __getattr__(self, attr):
        try:
                        return getattr(self.connections[self.active], attr)
        except redis.RedisError, e:
            Log.error(e)
            return None

        def connection(self, key = None):
                if key:
                    self.active = key
                else:
                    self.active = 'default'
                return self

    def connect(self, config):
        try:
            connection = redis.StrictRedis(
                host = config['host'],
                    port = config['port'],
                    db = config['db'])

                connection.ping()
                return connection

        except redis.RedisError, e:
                Log.error(e)
                return None

class Redis(Facade):
        accessor = GredisExtension

As it can be noticed, this extension has the simplest two class to make redis available for glim. The GredisExtension includes of the whole implementation of the redis client. The Redis class is there for to persist the GredisExtension to the runtime. The Redis class is only there to keep the GredisExtension instance.

The start.py file would be the following in this case;

from ext.gredis import Redis

def before(config):
        Redis.register(config)

The before function is automatically called by glim after enabling the extension from configuration. The config is also passed by glim's extension loader. Redis.register statement simply makes the redis instance persist like in a singleton manner but not completely. Glim approach doesn't use any kind of private like constructors or singletons. Instead, it creates Facades to keep the instances in a static manner.

šŸ‘

Best practice

Don't use singletons in glim. Use facades to contain object's instance instead. Singletons are bad and they are much more difficult to test.

The installation of an extension

The installation of an extension is to move the extension folder inside ext folder and install its dependencies. In this case the installation would be the following;

$ . venv/bin/activate
$ cp <gredis-file-path> ext/.
$ pip install requirements

šŸš§

Manual installation.. pff.

Currently, the installation of a glim extension is manual. The further releases of glim will provide great feature to install and enable extensions from command line.

You can check the code of other extensions;