Support cached sources.
[zzz-spline-frontpage.git] / splinext / frontpage / __init__.py
1 from collections import defaultdict, namedtuple
2 from pkg_resources import resource_filename
3 import re
4 import subprocess
5
6 from pylons import config
7
8 from spline.lib import helpers
9 from spline.lib.plugin import PluginBase, PluginLink, Priority
10 from spline.lib.plugin.load import run_hooks
11
12 import splinext.frontpage.controllers.frontpage
13 from splinext.frontpage.sources import FeedSource, GitSource
14
15 def add_routes_hook(map, *args, **kwargs):
16 """Hook to inject some of our behavior into the routes configuration."""
17 map.connect('/', controller='frontpage', action='index')
18
19 def load_sources_hook(*args, **kwargs):
20 """Hook to load all the known sources and stuff them in config. Run once,
21 on server startup.
22 """
23 # Extract source definitions from config and store as source_name => config
24 update_config = defaultdict(dict)
25 key_rx = re.compile(
26 '(?x) ^ spline-frontpage [.] sources [.] (\w+) (?: [.] (\w+) )? $')
27 for key, val in config.iteritems():
28 # Match against spline-frontpage.source.(source).(key)
29 match = key_rx.match(key)
30 if not match:
31 continue
32
33 source_name, subkey = match.groups()
34 if not subkey:
35 # This is the type declaration; use a special key
36 subkey = '__type__'
37
38 update_config[source_name][subkey] = val
39
40 # Figure out the global limit and expiration time, with reasonable
41 # defaults. Make sure they're integers.
42 global_limit = int(config.get('spline-frontpage.limit', 10))
43 # max_age is optional and can be None
44 try:
45 global_max_age = int(config['spline-frontpage.max_age'])
46 except KeyError:
47 global_max_age = None
48
49 config['spline-frontpage.limit'] = global_limit
50 config['spline-frontpage.max_age'] = global_max_age
51
52 # Ask plugins to turn configuration into source objects
53 sources = []
54 for source, source_config in update_config.iteritems():
55 hook_name = 'frontpage_updates_' + source_config['__type__']
56 del source_config['__type__'] # don't feed this to constructor!
57
58 # Default to global limit and max age. Source takes care of making
59 # integers and whatnot
60 source_config.setdefault('limit', global_limit)
61 source_config.setdefault('max_age', global_max_age)
62
63 # Hooks return a list of sources; combine with running list
64 sources += run_hooks(hook_name, **source_config)
65
66 # Save the list of sources, and done
67 config['spline-frontpage.sources'] = sources
68
69 def source_cron_hook(*args, **kwargs):
70 """Hook to pass on cron tics to all sources, should they need it for e.g.
71 caching.
72 """
73 for source in config['spline-frontpage.sources']:
74 source.do_cron(*args, **kwargs)
75
76 class FrontPagePlugin(PluginBase):
77 def controllers(self):
78 return dict(
79 frontpage = splinext.frontpage.controllers.frontpage.FrontPageController,
80 )
81
82 def template_dirs(self):
83 return [
84 (resource_filename(__name__, 'templates'), Priority.FIRST)
85 ]
86
87 def hooks(self):
88 return [
89 ('routes_mapping', Priority.NORMAL, add_routes_hook),
90 ('after_setup', Priority.NORMAL, load_sources_hook),
91 ('cron', Priority.NORMAL, source_cron_hook),
92 ('frontpage_updates_rss', Priority.NORMAL, FeedSource),
93 ('frontpage_updates_git', Priority.NORMAL, GitSource),
94 ]