Architecture — Ringo 0.11.0 documentation

### Navigation

- index
- modules |
- next |
- previous |
- Ringo 0.11.0 documentation »

# Architecture

The architecture of ringo is shown below.

Ringo is a Pyramid based application which can be extended to build your own applications. This is a layered architecture where Pyramid brings in the basic functionality like session handling and handling requests and responses in web applications. Ringo sits on top of Pyramid and provides commonly used functionality often used in modern web applications. See Features for an overview.

Note

Ringo is also a standalone application. You do not need to build another application on top of ringo to get something working to get an impression of ringo or start developing. Ringo is under steady development. This is a benefit if you use Ringo as base for your application. Your application will get bug fixes, new features or improved functionality in most cases by simply updating Ringo to a new version.

Ringo itself uses some external libraries to provide some of its functionality. E.g the formbar library is used to build all forms and do validation. The access to the database is done with the ORM SQLAlchemy.

A ringo based application is another pyramid based application which basically extends ringo. See Exending An Existing Pyramid Application for more details on how this is done.

All this magic is already done in a pyramid scaffold which comes with ringo. Using this scaffold will create an application which uses ringo functionality by simply importing it at the right places and take care the basic configuration is done in the proper way. See Develop/Create on a Ringo based application for information on how to create an application using this scaffold.

## Modules

The term “Module” is central and often used in ringo. Therefore it is important to understand what a module is. This section tries to explain that.

You can think of a module in the way that it provides the infrastructure to work with a certain type of data in a web application. Where certain type of data means users, files, movies etc. rather than integers or datevalues. Lets call them items from now on.

A module will have a model for the items you want to work with. It will include views to handle incoming request and generating the proper responses for users. Further it has templates which define how the pages in the application will look like. Finally there are configuration files to define how the forms and overview tables will look like.

Ringo already come with many modules. One module per item. There is a module for the user management, a module for appointment and so on. There is also a module to handle the modules itself. So we can say in general: ringos functionality is the sum of all modules functionality. Ringo or a ringo based application can be extended by adding new modules. Fortunately you will not need to create this infrastructure for you own. See Add a new modul to your application for more information.

Each module has a common set of configuration options which can be done directly in the web interface. See the modules entry in the administion menu for more information.

### Modul actions

Modules provide actions which can be used to manipulate the item of a module. Ringo provides some basic CRUD [1] actions which are available on default for every module.

- Create: Create new items.
- Read: Show the item in detail in readonly mode.
- Update: Edit items of the module.
- Delete: Deleting items.
- List: Listing all items of the module. This is slightly different to the action to read a single item.

Every time you want to do something different to your items you will likely want to implement another action for the module. See Adding a single custom actions to a module for more details how to add actions to a module.

[1] | CRUD means: Create, Read, Update, Delete

## Filesystem

Ringo is organised in the following file layout:

    .
    ├── alembic
    ├── docs
    └── ringo
       ├── lib
       ├── locale
       ├── model
       ├── scaffolds
       ├── scripts
       ├── static
       │   ├── bootstrap
       │   ├── css
       │   ├── images
       │   └── js
       ├── templates
       │   ├── auth
       │   ├── default
       │   ├── internal
       │   ├── mails
       │   └── users
       ├── tests
       └── views
           ├── forms
           └── tables

alembic
  Migration scripts for the database.
docs
  Documentation of ringo. This documentation.
ringo
  The ringo application.
ringo/lib
  Helper libraries. lenderers, validators, security related functions.
ringo/locale
  i18n Internationalisation
ringo/model
  Models for users, usergroups, modules, roles etc.
ringo/scaffolds
  A scaffolds includes the boilerplate code to create a ringo based application.
ringo/scripts
  Administration commands and scripts.
ringo/static
  Static files like images or CSS and JS scripts.
ringo/templates
  Templates for various parts of the application. Templates in the internal folder are used internally by customs form or overview renderers. The default folder has the templates for default CRUD actions and confirmation dialogs.
ringo/tests
  Unit tests and behaviour driven tests. See tests for more details.
ringo/views
  Views with the business logic for the application.
ringo/views/forms
  Configuration of forms for each modul.
ringo/views/tables
  Configuration of overview tables for each modul.

## Database

Below you see the basic schema of the ringo database. The schema only lists some central tables which help to understand how the things are wired together in ringo. The table colored orange example table in the top left is an example for any other table which will store items of a modul. Every item of an item has a reference to the module it belogs to. Further the current example has a reference to a user and a usergroup which defines the ownership [2] per item is is important for the authorisation.

Let us begin with the modules table. This table will store information on all available modules in the system. It basically stores the configuration per modul. As described in the Modules section each modul has (Modul actions) which are stored in the actions table. The NM-table nm_actions_roles define which roles are allowed to use the actions in the module. See Permission System for more information on how the Authorisation is implemented in ringo.

The users table stores all the users in ringo. The users table only holds minimal data which is required for authentification and authorisation. Additional information like the name, email etc. is stored in the profiles table. Every user has a profile.

Users can be organised in groups using the nm_user_groups table. All usergroups are stored in the usergroups table. Roles can be assigned to usergroups and users. This is done with the NM-table nm_user_roles and nm_usergroup_roles.

The table user_settings and password_reset_request are helper tables to save user settings like saved search queries or store the tokens to trigger the password reset.

[2] | The ownership feature can be added by using the Owned mixin.

## Mixins

Mixins can be used to add certain functionality to the items of a module. Mixins are used in multiple inheritance. The mixin ensures that the item will have all needed fields in the database and provides the proper interface to use the added functionality. Example:

    class Comment(BaseItem, Nested, Meta, Owned, Base):
        __tablename__ = 'comments'
        _modul_id = 99
        id = sa.Column(sa.Integer, primary_key=True)
        comment = sa.Column('comment', sa.Text)

        ...

The comment class in the example only defines the two fields id and comment. But as it inherits from Nested, Meta and Owned it also will have date fields with the creation and date of the last update, references to the user and group which ownes the Comment. Further the ‘Nested’ mixin will ensure the comments can reference each other to be able to build a hierarchy structure (e.g Threads in the example of the comments).

Important

As most of the mixins will add additional tables and database fields to your item it is needed to migrate your database to the new model. See Using alembic for database migration section for more information.

### Meta

class `ringo.model.mixins.` `Meta`

  Mixin to add a created and a updated datefield to items. The updated datefield will be updated on every update of the item with the datetime of the update.

### Owned

class `ringo.model.mixins.` `Owned`

  Mixin to add references to a user and a usergroup. This references are used to build some kind of ownership of the item. The ownership is used from the permission system.

  It is posible to configure inhertinace of the owner and group from a given parent element. This information is used only while creating new instances of the modul. If configured, the default group and owner information will be overwritten. This is done at the very end of the creation process. See ‘’save’’ method of the BaseItem. You can configure the inhertiance by setting the name of the relation to the parent item in the ‘’_inherit_gid’’ and ‘’_inherit_uid’’ class variable.

### Nested

class `ringo.model.mixins.` `Nested`

  Mixin to make nested (self-reference) Items possible. Each item can have a parent item and many children. The class will add two relation attribute to the inheriting class. The parent item is available under the parent attribute. The children items are available under the children attribute.

### Logged

class `ringo.model.mixins.` `Logged`

  Mixin to add logging functionallity to a modul. Adding this Mixin the item of a modul will have a “logs” relationship containing all the log entries for this item. Log entries can be created automatically by the system or may be created manual by the user. Manual log entries. Needs to be configured (Permissions)

### Versioned

class `ringo.model.mixins.` `Versioned`

  Mixin to add version functionallity to a modul. This mixin is used to store different “versions” of an item. On each update of the item, the serialized values of the item will be saved in the version table. Item modul will have a “versions” relationship containing all versions of the values for this item.

### StateMixin

class `ringo.model.mixins.` `StateMixin`

  Mixin to add one or more Statemachines to an item. The statemachines are stored in a internal ‘_statemachines’ dictionary. The current state is stored as integer value per item. This field must be created manually. The name of the field which stores the value for the current state must be the keyname of the ‘_statemachines’ dictionary.

  Example Mixin usage:

      class FooMixin(StateMixin):
          # Mixin inherited from the StateMixin to add the Foobar
          # state machine

          # Attach the statemachines to an internal dictionary
          _statemachines = {'foo_state_id': FooStatemachine}

          # Configue a field in the model which saves the current
          # state per state machine
          foo_state_id = sa.Column(sa.Integer, default=1)

          # Optional. Create a property to access the statemachine
          # like an attribute. This gets usefull if you want to access
          # the state in overview lists.
          @property
          def foo_state(self):
              state = self.get_statemachine('foo_state_id')
              return state.get_state()

### Commented

class `ringo.model.mixins.` `Commented`

  Mixin to add comment functionallity to a modul. Adding this Mixin the item of a modul will have a “comments” relationship containing all the comment entries for this item.

### Tagged

class `ringo.model.mixins.` `Tagged`

  Mixin to add tag (keyword) functionallity to a modul. Adding this Mixin the item of a modul will have a “tags” relationship containing all the tag entries for this item.

### Todo

class `ringo.model.mixins.` `Todo`

  Mixin to add todo functionallity to a modul. Adding this Mixin the item of a modul will have a “todo” relationship containing all the todo entries for this item.

## States and Workflows

Ringo offers the option to equip items of the modules with a one or more state machines to model statefull workflows.

An classical example for a statefull workflow is a process where an item can be in an draft, review and published state.

The statemachine offers features like

> - Restrictions on access
> - Conditional transitions
> - Handlers.

Depending on the state the state machine can be configured to restrict access to the item for certain users. Further you can define conditions which must be true before the state can switch into another. Finally you can write certain handlers which are called right after the state has changed.

A state machine can be attached to the items using a StateMixin which organises the state machines an provides a unique interface.

For detailed descriptione of the involved classes see API documentation of Statemachine

## Permission System

The permission system addresses two basic questions:

1. Who is allowed to access some item in general and
2. What is allowed for the user to access, in case he is generally allowed to access the item.

To answer these two questions the permission system of Ringo is a combination of concepts of the permission system known from the Unix file system and a roles based permission system.

The Unix file system part answers the first question: Who is allowed? Therefor every item in the system inherited from the Owned stores information to which owner and which group it belongs to. Only the owner, members of the group or users with an administrational role are granted access to the item in general.

The role bases system answers the second question: What is allowed. Assuming the user has general access to the item. The permission system will now check which roles the users have and which actions are allowed for this role.

The permission system in Ringo uses the Pyramid Pyramid Authorisation and Authenfication API

### Authentification

Authentication is done once in the login process. If the user has logged in successful an auth cookie is saved. From then on the user object is loaded from the database on every request with the roles and groups attached to the user. This user object is used later for the Authorisation. If the user is not logged in the user object is empty.

### Authorisation

Authorisation is done on every request. Authorisation is done against resources.

A resource is an url or an item which is accessed by calling the url in your application. In all cases this resource is build from a resource factory for every request. The resource will have an ACL which determines if the user of the current request (See Authentification) is allowed to access the resource.

Ringo’s part in the authorisation process is to build the ACL. This ACL is then used by the Pyramid security API. Therefor ringo implements helper functions to build ACL lists which model the ringo permission system.

See Adding Authorization tutorial for more information how things work in general under the hood.

See Security for documentation on helper functions used to build the ACL.

## Event Handlers

Each class can implement one of the following event handlers to realize automatic modifications of the items:

> - create_event_handler(request, item, **kwargs)
> - update_event_handler(request, item, **kwargs)
> - delete_event_handler(request, item, **kwargs)

The functions will be called for all base classes of the item automatically in the base controller if the specific view function for the event (update, create, delete) is excecuted.

Some of the Mixin classes do already have some predefined event_handlers configured.

### Navigation

- index
- modules |
- next |
- previous |
- Ringo 0.11.0 documentation »