cardstories plugins

The infrastructure for cardstories plugin was implemented. Each plugin may register when it loads and listen to game events and/or transform the incoming HTTP requests or the outgoing JSON result.

plugin infrastructure

A plugin is a single python file located at /usr/share/cardstories/plugin/plugin.py. It must provide a single class named Plugin that is instantiated with a single parameter : an instance of CardstoriesService.

class Plugin:
        def __init__(self, service):
            pass
        def name(self):
            return 'NAME'

The plugin is loaded if it shows in the –plugins option. For instance if cardstories is run with –plugins NAME the /usr/share/cardstories/plugins/NAME.py file will be loaded. The name method must return a string that is the name of the plugin that can be used in the –plugins-pre-process and –plugin-post-process options.
Each plugin is given a directory to store information in /var/lib/cardstories/plugins/NAME and a directory to store configuration files in /etc/cardstories/plugins/NAME. These paths are available in the services.settings map as follows:

class Plugin:
        def __init__(self, service):
            os.path.join(service.settings['plugins-libdir'], self.name()) # /var/lib/cardstories/plugins/NAME
            os.path.join(service.settings['plugins-confdir'], self.name()) # /etc/cardstories/plugins/NAME

        def name(self):
            return 'NAME'

listening to the service

A plugin can register to listen to all events occuring on the cardstories service as follows:

class Plugin:
    def __init__(self, service):
        self.service = service
        self.accept(None)

    def accept(self, event):
        self.service.plugin_event = event
        if event == None:
            pass
        # startService() was called on CardstoriesService
        elif event['type'] == 'start':
            pass
        # stopService() is about to be called on CardstoriesService
        elif event['type'] == 'stop':
            pass
        # a game is about to be deleted from memory
        elif event['type'] == 'delete':
            event['game'] # CardstoriesGame instance
        # a game was modified
        elif event['type'] == 'change':
            event['game'] # CardstoriesGame instance
            # a player is given cards to pick
            if event['details']['type'] == 'participate':
                event['details']['player_id'] # numerical player id
            # the author went to voting state
            elif event['details']['type'] == 'voting':
                pass
            # the player picks a card
            elif event['details']['type'] == 'pick':
                event['details']['player_id'] # numerical player id
                event['details']['card'] # numerical card id
            # the player votes for a card
            elif event['details']['type'] == 'vote':
                event['details']['player_id'] # numerical player id
                event['details']['vote'] # numerical card id
            # the author went to game complete state
            elif event['details']['type'] == 'complete':
                pass
            # the author invited players to play
            elif event['details']['type'] == 'invite':
                event['details']['invited'] # list of numerical player ids
        self.service.listen().addCallback(self.accept)

The accept function should act depending on the event, as shown above. When it is done it must register again to be notified of the next event.

HTTP request filtering

The plugins listed in the –plugins-pre-process option will be called, in that order, with the incoming HTTP request. For instance if –plugin-pre-process NAME is given to cardstories, the preprocess method of the Plugin instance will be called.

    def preprocess(self, result, request):
        request.preprocess = 'preprocess'
        return result

The example preprocess function adds a new data member to the Request object. It must return the result argument untouched.
The plugins listed in the –plugins-post-process option will be called, in that order, with the resulting JSON request. For instance if –plugin-post-process NAME is given to cardstories, the postprocess method of the Plugin instance will be called.

    def postprocess(self, result):
        result['postprocess'] = self.preprocess
        return result

The example postprocess function adds a entry to the map that will be converted into a JSON object before being sent back to the HTTP client. It must return a map with the entry error set in case an error must be notified to the client.
The plugin names listed in the –plugin-post-process and –plugin-pre-process options are those returned by the name functions of the corresponding Plugin instance. Although it is best to name the plugin file with the same name as the one returned by the name function to avoid confusion, it is not mandatory.

authentication

The default authentication module has been transformed into a plugin. The hack introduced to send invitation emails was removed.