Allow support for multiple meteor apps in a single django-ddp enabled project by reading METEOR_STAR_JSON as part of view init instead of app ready.

This commit is contained in:
Tyson Clugg 2015-07-23 11:07:20 +10:00
parent c9dd08761b
commit 00b1432974
2 changed files with 168 additions and 178 deletions

View file

@ -1,37 +1,9 @@
"""Django DDP Server app config."""
from __future__ import print_function, absolute_import, unicode_literals
import io
import mimetypes
import os.path
from django.apps import AppConfig
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from ejson import dumps, loads
import pybars
STAR_JSON_SETTING_NAME = 'METEOR_STAR_JSON'
def read(path, default=None, encoding='utf8'):
"""Read encoded contents from specified path or return default."""
if not path:
return default
try:
with io.open(path, mode='r', encoding=encoding) as contents:
return contents.read()
except IOError:
if default is not None:
return default
raise
def read_json(path):
"""Read JSON encoded contents from specified path."""
with open(path, mode='r') as json_file:
return loads(json_file.read())
class ServerConfig(AppConfig):
@ -41,149 +13,6 @@ class ServerConfig(AppConfig):
name = 'dddp.server'
verbose_name = 'Django DDP Meteor Web Server'
manifest = None
program_json = None
program_json_path = None
runtime_config = None
star_json = None # top level layout
server_json = None # web server layout
web_browser_json = None # web.browser (client) layout
url_map = None
internal_map = None
server_load_map = None
template_path = None # web server HTML template path
client_map = None # web.browser (client) URL to path map
html = '<!DOCTYPE html>\n<html><head><title>DDP App</title></head></html>'
def ready(self):
"""Configure Django DDP server app."""
mimetypes.init() # read and process /etc/mime.types
self.url_map = {}
try:
json_path = getattr(settings, STAR_JSON_SETTING_NAME)
except AttributeError:
raise ImproperlyConfigured(
'%s setting required by dddp.server.view.' % (
STAR_JSON_SETTING_NAME,
),
)
self.star_json = read_json(json_path)
star_format = self.star_json['format']
if star_format != 'site-archive-pre1':
raise ValueError(
'Unknown Meteor star format: %r' % star_format,
)
programs = {
program['name']: program
for program in self.star_json['programs']
}
server_json_path = os.path.join(
os.path.dirname(json_path),
os.path.dirname(programs['server']['path']),
'program.json',
)
self.server_json = read_json(server_json_path)
server_format = self.server_json['format']
if server_format != 'javascript-image-pre1':
raise ValueError(
'Unknown Meteor server format: %r' % server_format,
)
self.server_load_map = {}
for item in self.server_json['load']:
item['path_full'] = os.path.join(
os.path.dirname(server_json_path),
item['path'],
)
self.server_load_map[item['path']] = item
self.url_map[item['path']] = (
item['path_full'], 'text/javascript'
)
try:
item['source_map_full'] = os.path.join(
os.path.dirname(server_json_path),
item['sourceMap'],
)
self.url_map[item['sourceMap']] = (
item['source_map_full'], 'text/plain'
)
except KeyError:
pass
self.template_path = os.path.join(
os.path.dirname(server_json_path),
self.server_load_map[
'packages/boilerplate-generator.js'
][
'assets'
][
'boilerplate_web.browser.html'
],
)
web_browser_json_path = os.path.join(
os.path.dirname(json_path),
programs['web.browser']['path'],
)
self.web_browser_json = read_json(web_browser_json_path)
web_browser_format = self.web_browser_json['format']
if web_browser_format != 'web-program-pre1':
raise ValueError(
'Unknown Meteor web.browser format: %r' % (
web_browser_format,
),
)
self.client_map = {}
self.internal_map = {}
for item in self.web_browser_json['manifest']:
item['path_full'] = os.path.join(
os.path.dirname(web_browser_json_path),
item['path'],
)
if item['where'] == 'client':
if '?' in item['url']:
item['url'] = item['url'].split('?', 1)[0]
self.client_map[item['url']] = item
self.url_map[item['url']] = (
item['path_full'],
mimetypes.guess_type(
item['path_full'],
)[0] or 'application/octet-stream',
)
elif item['where'] == 'internal':
self.internal_map[item['type']] = item
config = {
'css': [
{'url': item['path']}
for item in self.web_browser_json['manifest']
if item['type'] == 'css' and item['where'] == 'client'
],
'js': [
{'url': item['path']}
for item in self.web_browser_json['manifest']
if item['type'] == 'js' and item['where'] == 'client'
],
'meteorRuntimeConfig': '"%s"' % (
dumps(self.runtime_config)
),
'rootUrlPathPrefix': '/app',
'bundledJsCssPrefix': '/app/',
'inlineScriptsAllowed': False,
'inline': None,
'head': read(
self.internal_map.get('head', {}).get('path_full', None),
default=u'',
),
'body': read(
self.internal_map.get('body', {}).get('path_full', None),
default=u'',
),
}
tmpl_raw = read(self.template_path, encoding='utf8')
compiler = pybars.Compiler()
tmpl = compiler.compile(tmpl_raw)
self.html = '<!DOCTYPE html>\n%s' % tmpl(config)

View file

@ -1,13 +1,35 @@
"""Django DDP Server views."""
from __future__ import print_function, absolute_import, unicode_literals
from ejson import dumps
from django.apps import apps
import io
import mimetypes
import os.path
from ejson import dumps, loads
from django.conf import settings
from django.http import HttpResponse
from django.views.generic import View
import pybars
STAR_JSON_SETTING_NAME = 'METEOR_STAR_JSON'
def read(path, default=None, encoding='utf8'):
"""Read encoded contents from specified path or return default."""
if not path:
return default
try:
with io.open(path, mode='r', encoding=encoding) as contents:
return contents.read()
except IOError:
if default is not None:
return default
raise
def read_json(path):
"""Read JSON encoded contents from specified path."""
with open(path, mode='r') as json_file:
return loads(json_file.read())
class MeteorView(View):
@ -16,13 +38,152 @@ class MeteorView(View):
http_method_names = ['get', 'head']
app = None
json_path = None
runtime_config = None
manifest = None
program_json = None
program_json_path = None
runtime_config = None
star_json = None # top level layout
url_map = None
internal_map = None
server_load_map = None
template_path = None # web server HTML template path
client_map = None # web.browser (client) URL to path map
html = '<!DOCTYPE html>\n<html><head><title>DDP App</title></head></html>'
root_url_path_prefix = ''
bundled_js_css_prefix = '/'
def __init__(self, **kwargs):
"""Initialisation for Django DDP server view."""
# super(...).__init__ assigns kwargs to instance.
super(MeteorView, self).__init__(**kwargs)
self.app = apps.get_app_config('server')
self.url_map = {}
# process `star_json`
self.star_json = read_json(self.json_path)
star_format = self.star_json['format']
if star_format != 'site-archive-pre1':
raise ValueError(
'Unknown Meteor star format: %r' % star_format,
)
programs = {
program['name']: program
for program in self.star_json['programs']
}
# process `bundle/programs/server/program.json` from build dir
server_json_path = os.path.join(
os.path.dirname(self.json_path),
os.path.dirname(programs['server']['path']),
'program.json',
)
server_json = read_json(server_json_path)
server_format = server_json['format']
if server_format != 'javascript-image-pre1':
raise ValueError(
'Unknown Meteor server format: %r' % server_format,
)
self.server_load_map = {}
for item in server_json['load']:
item['path_full'] = os.path.join(
os.path.dirname(server_json_path),
item['path'],
)
self.server_load_map[item['path']] = item
self.url_map[item['path']] = (
item['path_full'], 'text/javascript'
)
try:
item['source_map_full'] = os.path.join(
os.path.dirname(server_json_path),
item['sourceMap'],
)
self.url_map[item['sourceMap']] = (
item['source_map_full'], 'text/plain'
)
except KeyError:
pass
self.template_path = os.path.join(
os.path.dirname(server_json_path),
self.server_load_map[
'packages/boilerplate-generator.js'
][
'assets'
][
'boilerplate_web.browser.html'
],
)
# process `bundle/programs/web.browser/program.json` from build dir
web_browser_json_path = os.path.join(
os.path.dirname(self.json_path),
programs['web.browser']['path'],
)
web_browser_json = read_json(web_browser_json_path)
web_browser_format = web_browser_json['format']
if web_browser_format != 'web-program-pre1':
raise ValueError(
'Unknown Meteor web.browser format: %r' % (
web_browser_format,
),
)
self.client_map = {}
self.internal_map = {}
for item in web_browser_json['manifest']:
item['path_full'] = os.path.join(
os.path.dirname(web_browser_json_path),
item['path'],
)
if item['where'] == 'client':
if '?' in item['url']:
item['url'] = item['url'].split('?', 1)[0]
self.client_map[item['url']] = item
self.url_map[item['url']] = (
item['path_full'],
mimetypes.guess_type(
item['path_full'],
)[0] or 'application/octet-stream',
)
elif item['where'] == 'internal':
self.internal_map[item['type']] = item
config = {
'css': [
{'url': item['path']}
for item in web_browser_json['manifest']
if item['type'] == 'css' and item['where'] == 'client'
],
'js': [
{'url': item['path']}
for item in web_browser_json['manifest']
if item['type'] == 'js' and item['where'] == 'client'
],
'meteorRuntimeConfig': '"%s"' % (
dumps(self.runtime_config)
),
'rootUrlPathPrefix': self.root_url_path_prefix,
'bundledJsCssPrefix': self.bundled_js_css_prefix,
'inlineScriptsAllowed': False,
'inline': None,
'head': read(
self.internal_map.get('head', {}).get('path_full', None),
default=u'',
),
'body': read(
self.internal_map.get('body', {}).get('path_full', None),
default=u'',
),
}
tmpl_raw = read(self.template_path, encoding='utf8')
compiler = pybars.Compiler()
tmpl = compiler.compile(tmpl_raw)
self.html = '<!DOCTYPE html>\n%s' % tmpl(config)
def get(self, request, path):
"""Return HTML (or other related content) for Meteor."""
@ -46,7 +207,7 @@ class MeteorView(View):
content_type='text/javascript',
)
try:
file_path, content_type = self.app.url_map[path]
file_path, content_type = self.url_map[path]
with open(file_path, 'r') as content:
return HttpResponse(
content.read(),
@ -54,5 +215,5 @@ class MeteorView(View):
)
except KeyError:
print(path)
return HttpResponse(self.app.html)
return HttpResponse(self.html)
# raise Http404