From bc9afefaddc8032b60b0b8d151e8cfeb2fdb1f13 Mon Sep 17 00:00:00 2001 From: Tyson Clugg Date: Thu, 5 Nov 2015 11:41:00 +1100 Subject: [PATCH] Support METEOR_SETTINGS env var, as well as introduce METEOR_PUBLIC_ENVS var. --- dddp/views.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/dddp/views.py b/dddp/views.py index eeb5dbf..dbb72a0 100644 --- a/dddp/views.py +++ b/dddp/views.py @@ -1,6 +1,7 @@ """Django DDP Server views.""" from __future__ import absolute_import, unicode_literals +from copy import deepcopy import io import logging import mimetypes @@ -14,6 +15,22 @@ from django.views.generic import View import pybars +# from https://www.xormedia.com/recursively-merge-dictionaries-in-python/ +def dict_merge(a, b): + '''recursively merges dict's. not just simple a['key'] = b['key'], if + both a and bhave a key who's value is a dict then dict_merge is called + on both values and the result stored in the returned dictionary.''' + if not isinstance(b, dict): + return b + result = deepcopy(a) + for k, v in b.iteritems(): + if k in result and isinstance(result[k], dict): + result[k] = dict_merge(result[k], v) + else: + result[k] = deepcopy(v) + return result + + def read(path, default=None, encoding='utf8'): """Read encoded contents from specified path or return default.""" if not path: @@ -56,12 +73,52 @@ class MeteorView(View): client_map = None # web.browser (client) URL to path map html = '\nDDP App' + meteor_settings = None + meteor_public_envs = None root_url_path_prefix = '' bundled_js_css_prefix = '/' def __init__(self, **kwargs): - """Initialisation for Django DDP server view.""" + """ + Initialisation for Django DDP server view. + + `Meteor.settings` is sourced from the following (later take precedence): + 1. django.conf.settings.METEOR_SETTINGS + 2. os.environ['METEOR_SETTINGS'] + 3. MeteorView.meteor_settings (class attribute) or empty dict + 4. MeteorView.as_view(meteor_settings=...) + + Additionally, `Meteor.settings.public` is updated with values from + environemnt variables specified from the following sources: + 1. django.conf.settings.METEOR_PUBLIC_ENVS + 2. os.environ['METEOR_PUBLIC_ENVS'] + 3. MeteorView.meteor_public_envs (class attribute) or empty dict + 4. MeteorView.as_view(meteor_public_envs=...) + """ self.runtime_config = {} + self.meteor_settings = reduce( + dict_merge, + [ + getattr(settings, 'METEOR_SETTINGS', {}), + loads(os.environ.get('METEOR_SETTINGS', '{}')), + self.meteor_settings or {}, + kwargs.pop('meteor_settings', {}), + ], + {}, + ) + self.meteor_public_envs = set() + self.meteor_public_envs.update( + getattr(settings, 'METEOR_PUBLIC_ENVS', []), + os.environ.get('METEOR_PUBLIC_ENVS', '').replace(',', ' ').split(), + self.meteor_public_envs or [], + kwargs.pop('meteor_public_envs', []), + ) + public = self.meteor_settings.setdefault('public', {}) + for env_name in self.meteor_public_envs: + try: + public[env_name] = os.environ[env_name] + except KeyError: + pass # environment variable not set # super(...).__init__ assigns kwargs to instance. super(MeteorView, self).__init__(**kwargs) @@ -203,6 +260,7 @@ class MeteorView(View): if path == 'meteor_runtime_config.js': config = { 'DDP_DEFAULT_CONNECTION_URL': request.build_absolute_uri('/'), + 'PUBLIC_SETTINGS': self.meteor_settings.get('public', {}), 'ROOT_URL': request.build_absolute_uri( '%s/' % self.runtime_config.get('ROOT_URL_PATH_PREFIX', ''), ),