public
Description: Turtles all the way down
Homepage: http://simonwillison.net/2009/May/19/djng/
Clone URL: git://github.com/simonw/djng.git
simonw (author)
Mon May 18 10:17:31 -0700 2009
commit  c892dddf064d5542c17119d02920ea4f5e9dd7f5
tree    b934dfc212f02a6f25dd6372e084add18980c59b
parent  77d7b7c20b8a0561b4741ef777915376db8dbb0d
djng / services_api_ideas.txt
100644 90 lines (71 sloc) 2.653 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
Services API
============
 
Requirements:
 
- Maintain a stack of implementations for each service
- Only one implementation of a service is "active" at a time
- The default API for a service uses the current active implementation
- Implementations can be used without participating in the stack at all
- Middleware can temporarily push a new service on to the stack, for the
  duration of the current request
- Services temporarily pushed on to the stack are reliably popped off again
  at the end of the current request, even if an exception is raised
 
It would be nice if the solution meant that the current Django APIs for things
like accessing the cache or loading a template could remain backwards
compatible.
 
Some ideas
----------
 
# Configure the default service (at the bottom of the stack)
djng.template.configure(template_dirs = ('templates/default',))
 
# Reconfigure for part of the URL space
app = djng.Router(
    (r'^foo', djng.reconfigure(
        foo_view, djng.template,
        template_dirs = ('templates/foo', 'templates/default')
    )),
    (r'^bar', bar_view),
)
 
djng.reconfigure is middleware which wrapes foo_view, then duplicates the
current djng.template service and applies a new template_dirs property to it ,
based on keyword argument.
 
Or... use a decorator:
 
app = djng.Router(
    (r'^foo', djng.reconfigure(
        djng.template, template_dirs = ('templates/foo', 'templates/default')
    )(foo_view)),
    (r'^bar', bar_view),
)
 
Which could also be written:
 
@djng.reconfigure(
    djng.template, template_dirs = ('templates/foo', 'templates/default')
)
def foo_view(request):
    # ...
 
Reconfigure is a bit strange though, because the majority of services will
probably want a completely new implementation rather than a tweak to the
existing one. Caching is a good example:
 
# Set the default cache to be an InMemoryCache
djng.cache.configure(djng.cache.InMemoryCache())
 
# One URL path gets to use memcache instead
app = djng.Router(
    (r'^foo', djng.reconfigure(
        foo_view, djng.cache, djng.cache.Memcache('127.0.0.1:11221')
    )),
    (r'^bar', bar_view),
)
 
Or as a decorator:
 
app = djng.Router(
    (r'^foo', djng.reconfigure(
        djng.cache, djng.cache.Memcache('127.0.0.1:11221')
    )(foo_view)),
    (r'^bar', bar_view),
)
 
The signature of djng.reconfigure feels a bit strange though:
 
def reconfigure(
        service_to_reconfigure,
        [optional new_service_instance],
        **reconfigure_kwargs
    ):
    # ...
 
Internally, reconfigure uses a try/finally block to ensure that the altered
service implementation pushed on to the stack is popped off by the end of the
request.