(trying to make some concept)
Some initial assumptions (in random order)
- Code is preferably executed as long-running process. So it serves requests and can have some incremental internal state (which is reproducible though).
- We want to build system which would be:
- Flexible (we should hardcode as little as possible)
- Efficient (we shouldn't have big (or any) overheads for frequent tasks)
- Secure (we should provide ability to build as tightly secure system as one wants)
- Easy to use (for users and admins)
- Easy to maintain (for core developers and plugin developers)
Concept itself
Core shouldn't implement any semantics, just give ability to load modules and make them work. This implies that we also have some modules which implement base functionality itself (including request serving, state storing and permission checking). So, what modules can:
- Provide services ("i can do this, this and this, just ask")
- Provide hookup points ("when i'm doing something, i can call you, just ask")
this two abilities are complement (that means that hookup providing declares interface and service is implementation of this interface; so without proper hookup we can't use service — this means that we should have ability to declare as broad interface as we want), and both are passive. Basically, collection of hookups is an interface, so be it. So, also there should be some action:
- Service discovery
- Hookup register
who should do them? Looks like it should be automatic and configuration-guided. Looks like there is some need in constrains (which service should be used for this hookup, how it would be decided automatically)
Interface is what one gets and what one returns. So, some objects as data holders should be declared (and we need ability to declare new types of objects). Types are defined by their methods.
And, at last, there some type declarations, some interface declarations and some implementations of interfaces and all this stuff is driven by core which can only load all these modules and connect them automatically configuration-driven (have to clarify automatics).
Looks like at least one predefined hookup is needed — entry point.
How it would look
- Core
- Core can provide hookups for scanning for plugins, loading them, adding hookup points and services. It gives ability to implement plugin providers.
- Module which defines type Request with its methods (get_params, get_path etc)
- Module which defines type Response
- Module with some server which provide hookup serve(in Request, out Response)
- Module with some dispatcher which implements serve(in Request, out Response) service and provide hookup serve(in Request, out Response)
- Looks like this is key point which should be heavily configured in this scheme
- This is the point where all ACL can be implemented
- Module which defines type Item (with access to metadata, contents, revhistory)
- Module with some Item viewer which implement serve(in Request, out Response) service and provide hookup get_item(in path, out Item)
- Module with generic storage backend interface which services get_item(in path, out Item), find_item, update_item, etc and provide hookups for
How it can be implemented
From my point of view, we can put all necessary logic in decorators. We can define global hierarchical namespace (like packages in many languages) for types, interfaces and hookups. For example:
1 @moin-hookup(Response) #have to provide output type
2 def serve_hookup():
3 pass
4
5 @moin-interface("MoinMoin.security.acl") # we can strictly connect interfaces with package names, but i'm not sure that we should do it currently
6 class AclBasedChecker(object): #btw, maybe we should have some basic class for interfaces? just for easing the work
7
8 @moin-hookup(bool, type=HOOKUP_ANY|HOOKUP_ORDERED) # type is possible solution related to hints for automatic hookup and service bonding
9 def check(self, Request):
10 pass
11
12 @moin-type("MoinMoin.security.ldap")
13 class LdapConnection(object):
14 def get_data(params):
15 pass
16
17 ...
18
19 @moin-provide-hookup("ldap_connect", Request, LdapConnection, type=HOOKUP_SINGLE)
20 class LdapAclChecker(Interface1)
21 def check(self, Request): # can be provided as service automatically
22 ...
23
24 @moin-service("MoinMoin.security.acl/check")
25 def alt_check(self, Request): # some alternative check
26 ...
27 if (conn = ldap_connect()): # using declared hookup
28 ...
29 ...
30
31 ...
32
33 class LocalLdapConnection(LdapConnection):
34 def get_data(params):
35 ...
36
37 ...
38
39 # stateless, so no object needed
40 @moin-service("ldap_connect")
41 def local_ldap_connect(Request):
42 return LocalLdapConnection(...)
What core will do
- Get possible plugin places from configuration (also can have some system/instance paths builtin)
- Load plugins (we need policy there — can be also setup in config and have reasonable default, for example, we load only explicitly mentioned modules, and all modules related to core)
- During loading, accumulate data about hookups and services and connect them based on policies.