Commit 729daf5c by Calen Pennington

Move xmodule asset gathering out of server startup

This allows us to a) compile the coffeescript and sass from xmodules
using the new out-of-band method and b) reload xmodule static content
whenever it changes, which should make devs much happier.
parent bbab2d7d
...@@ -20,11 +20,8 @@ Longer TODO: ...@@ -20,11 +20,8 @@ Longer TODO:
""" """
import sys import sys
import os.path
import os
import lms.envs.common import lms.envs.common
from path import path from path import path
from xmodule.static_content import write_descriptor_styles, write_descriptor_js, write_module_js, write_module_styles
############################ FEATURE CONFIGURATION ############################# ############################ FEATURE CONFIGURATION #############################
...@@ -186,30 +183,7 @@ MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' ...@@ -186,30 +183,7 @@ MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage' STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
# Load javascript and css from all of the available descriptors, and from rooted_paths import rooted_glob
# prep it for use in pipeline js
from xmodule.raw_module import RawDescriptor
from xmodule.error_module import ErrorDescriptor
from rooted_paths import rooted_glob, remove_root
write_descriptor_styles(PROJECT_ROOT / "static/sass/descriptor", [RawDescriptor, ErrorDescriptor])
write_module_styles(PROJECT_ROOT / "static/sass/module", [RawDescriptor, ErrorDescriptor])
descriptor_js = [path.replace('.coffee', '.js') for path in remove_root(
PROJECT_ROOT / 'static',
write_descriptor_js(
PROJECT_ROOT / "static/coffee/descriptor",
[RawDescriptor, ErrorDescriptor]
)
)]
module_js = [path.replace('.coffee', '.js') for path in remove_root(
PROJECT_ROOT / 'static',
write_module_js(
PROJECT_ROOT / "static/coffee/module",
[RawDescriptor, ErrorDescriptor]
)
)]
PIPELINE_CSS = { PIPELINE_CSS = {
'base-style': { 'base-style': {
...@@ -217,7 +191,9 @@ PIPELINE_CSS = { ...@@ -217,7 +191,9 @@ PIPELINE_CSS = {
'js/vendor/CodeMirror/codemirror.css', 'js/vendor/CodeMirror/codemirror.css',
'css/vendor/ui-lightness/jquery-ui-1.8.22.custom.css', 'css/vendor/ui-lightness/jquery-ui-1.8.22.custom.css',
'css/vendor/jquery.qtip.min.css', 'css/vendor/jquery.qtip.min.css',
'sass/base-style.css' 'sass/base-style.css',
'xmodule/modules.css',
'xmodule/descriptor.css',
], ],
'output_filename': 'css/cms-base-style.css', 'output_filename': 'css/cms-base-style.css',
}, },
...@@ -232,7 +208,10 @@ PIPELINE_JS = { ...@@ -232,7 +208,10 @@ PIPELINE_JS = {
'output_filename': 'js/cms-application.js', 'output_filename': 'js/cms-application.js',
}, },
'module-js': { 'module-js': {
'source_filenames': descriptor_js + module_js, 'source_filenames': (
rooted_glob(COMMON_ROOT / 'static/', 'xmodule/descriptors/js/*.js') +
rooted_glob(COMMON_ROOT / 'static/', 'xmodule/modules/js/*.js')
),
'output_filename': 'js/cms-modules.js', 'output_filename': 'js/cms-modules.js',
}, },
'spec': { 'spec': {
......
...@@ -54,5 +54,5 @@ ...@@ -54,5 +54,5 @@
@import 'assets/content-types'; @import 'assets/content-types';
// xblock-related // xblock-related
@import 'module/module-styles.scss'; @import 'xmodule/modules/css/module-styles.scss';
@import 'descriptor/module-styles.scss'; @import 'xmodule/descriptors/css/module-styles.scss';
../../../common/static/sass/bourbon/
\ No newline at end of file
...@@ -4,13 +4,15 @@ setup( ...@@ -4,13 +4,15 @@ setup(
name="XModule", name="XModule",
version="0.1", version="0.1",
packages=find_packages(exclude=["tests"]), packages=find_packages(exclude=["tests"]),
install_requires=['distribute'], install_requires=[
'distribute',
'docopt',
'capa',
'path.py',
],
package_data={ package_data={
'xmodule': ['js/module/*'] 'xmodule': ['js/module/*']
}, },
requires=[
'capa',
],
# See http://guide.python-distribute.org/creation.html#entry-points # See http://guide.python-distribute.org/creation.html#entry-points
# for a description of entry_points # for a description of entry_points
...@@ -50,6 +52,11 @@ setup( ...@@ -50,6 +52,11 @@ setup(
"graphical_slider_tool = xmodule.gst_module:GraphicalSliderToolDescriptor", "graphical_slider_tool = xmodule.gst_module:GraphicalSliderToolDescriptor",
"annotatable = xmodule.annotatable_module:AnnotatableDescriptor", "annotatable = xmodule.annotatable_module:AnnotatableDescriptor",
"foldit = xmodule.foldit_module:FolditDescriptor", "foldit = xmodule.foldit_module:FolditDescriptor",
] "hidden = xmodule.hidden_module:HiddenDescriptor",
"raw = xmodule.raw_module:RawDescriptor",
],
'console_scripts': [
'xmodule_assets = xmodule.static_content:main',
]
} }
) )
# /usr/bin/env python
""" """
This module has utility functions for gathering up the static content This module has utility functions for gathering up the static content
that is defined by XModules and XModuleDescriptors (javascript and css) that is defined by XModules and XModuleDescriptors (javascript and css)
...@@ -6,40 +7,43 @@ that is defined by XModules and XModuleDescriptors (javascript and css) ...@@ -6,40 +7,43 @@ that is defined by XModules and XModuleDescriptors (javascript and css)
import hashlib import hashlib
import os import os
import errno import errno
import sys
from collections import defaultdict from collections import defaultdict
from docopt import docopt
from path import path
from .x_module import XModuleDescriptor from xmodule.x_module import XModuleDescriptor
def write_module_styles(output_root, extra_descriptors): def write_module_styles(output_root):
return _write_styles('.xmodule_display', output_root, _list_modules(extra_descriptors)) return _write_styles('.xmodule_display', output_root, _list_modules())
def write_module_js(output_root, extra_descriptors): def write_module_js(output_root):
return _write_js(output_root, _list_modules(extra_descriptors)) return _write_js(output_root, _list_modules())
def write_descriptor_styles(output_root, extra_descriptors): def write_descriptor_styles(output_root):
return _write_styles('.xmodule_edit', output_root, _list_descriptors(extra_descriptors)) return _write_styles('.xmodule_edit', output_root, _list_descriptors())
def write_descriptor_js(output_root, extra_descriptors): def write_descriptor_js(output_root):
return _write_js(output_root, _list_descriptors(extra_descriptors)) return _write_js(output_root, _list_descriptors())
def _list_descriptors(extra_descriptors): def _list_descriptors():
return [ return [
desc for desc in [ desc for desc in [
desc for (_, desc) in XModuleDescriptor.load_classes() desc for (_, desc) in XModuleDescriptor.load_classes()
] + extra_descriptors ]
] ]
def _list_modules(extra_descriptors): def _list_modules():
return [ return [
desc.module_class desc.module_class
for desc for desc
in _list_descriptors(extra_descriptors) in _list_descriptors()
] ]
...@@ -76,9 +80,12 @@ def _write_styles(selector, output_root, classes): ...@@ -76,9 +80,12 @@ def _write_styles(selector, output_root, classes):
css_imports[class_].add(fragment_name) css_imports[class_].add(fragment_name)
with open(output_root / '_module-styles.scss', 'w') as module_styles: with open(output_root / '_module-styles.scss', 'w') as module_styles:
module_styles.write("@import 'bourbon/bourbon';\n")
module_styles.write("@import 'bourbon/addons/button';\n")
for class_, fragment_names in css_imports.items(): for class_, fragment_names in css_imports.items():
imports = "\n".join('@import "{0}";'.format(name) for name in fragment_names) imports = "\n".join('@import "{0}";'.format(name) for name in fragment_names)
module_styles.write("""{selector}.xmodule_{class_} {{ {imports} }}""".format( module_styles.write("""{selector}.xmodule_{class_} {{ {imports} }}\n""".format(
class_=class_, imports=imports, selector=selector class_=class_, imports=imports, selector=selector
)) ))
...@@ -105,3 +112,22 @@ def _write_js(output_root, classes): ...@@ -105,3 +112,22 @@ def _write_js(output_root, classes):
module_js.append(path) module_js.append(path)
return module_js return module_js
def main():
"""
Generate
Usage: static_content.py <output_root>
"""
args = docopt(main.__doc__)
root = path(args['<output_root>'])
root.rmtree()
write_descriptor_js(root / 'descriptors/js')
write_descriptor_styles(root / 'descriptors/css')
write_module_js(root / 'modules/js')
write_module_styles(root / 'modules/css')
if __name__ == '__main__':
sys.exit(main())
...@@ -20,7 +20,6 @@ Longer TODO: ...@@ -20,7 +20,6 @@ Longer TODO:
""" """
import sys import sys
import os import os
from xmodule.static_content import write_module_styles, write_module_js
from path import path from path import path
...@@ -390,15 +389,7 @@ MIDDLEWARE_CLASSES = ( ...@@ -390,15 +389,7 @@ MIDDLEWARE_CLASSES = (
STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage' STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
from xmodule.hidden_module import HiddenDescriptor from rooted_paths import rooted_glob
from rooted_paths import rooted_glob, remove_root
write_module_styles(PROJECT_ROOT / 'static/sass/module', [HiddenDescriptor])
module_js = [path.replace('.coffee', '.js') for path in remove_root(
PROJECT_ROOT / 'static',
write_module_js(PROJECT_ROOT / 'static/coffee/module', [HiddenDescriptor])
)]
courseware_js = ( courseware_js = (
[ [
...@@ -436,7 +427,8 @@ PIPELINE_CSS = { ...@@ -436,7 +427,8 @@ PIPELINE_CSS = {
'css/vendor/jquery.treeview.css', 'css/vendor/jquery.treeview.css',
'css/vendor/ui-lightness/jquery-ui-1.8.22.custom.css', 'css/vendor/ui-lightness/jquery-ui-1.8.22.custom.css',
'css/vendor/jquery.qtip.min.css', 'css/vendor/jquery.qtip.min.css',
'sass/course.css' 'sass/course.css',
'xmodule/modules.css',
], ],
'output_filename': 'css/lms-course.css', 'output_filename': 'css/lms-course.css',
}, },
...@@ -472,7 +464,7 @@ PIPELINE_JS = { ...@@ -472,7 +464,7 @@ PIPELINE_JS = {
'output_filename': 'js/lms-main_vendor.js', 'output_filename': 'js/lms-main_vendor.js',
}, },
'module-js': { 'module-js': {
'source_filenames': module_js, 'source_filenames': rooted_glob(COMMON_ROOT / 'static', 'xmodule/modules/js/*.js'),
'output_filename': 'js/lms-modules.js', 'output_filename': 'js/lms-modules.js',
}, },
'discussion': { 'discussion': {
......
../../../common/static/sass/bourbon/
\ No newline at end of file
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
@import 'course/base/mixins'; @import 'course/base/mixins';
@import 'course/base/base'; @import 'course/base/base';
@import 'course/base/extends'; @import 'course/base/extends';
@import 'module/module-styles.scss'; @import 'xmodule/modules/css/module-styles.scss';
// courseware // courseware
@import 'course/courseware/courseware'; @import 'course/courseware/courseware';
......
...@@ -120,21 +120,29 @@ def report_dir_path(dir) ...@@ -120,21 +120,29 @@ def report_dir_path(dir)
end end
def compile_assets(watch=false) def compile_assets(watch=false)
coffee_cmd = "coffee #{watch ? '--watch' : ''} --compile */static" xmodule_cmd = 'xmodule_assets common/static/xmodule'
sass_cmd = "sass --style compressed --require ./common/static/sass/bourbon/lib/bourbon.rb #{watch ? '--watch' : '--update'} */static"
if watch if watch
background_process(coffee_cmd) xmodule_cmd = "watchmedo shell-command \
background_process(sass_cmd) --patterns='*.js;*.coffee;*.sass;*.scss;*.css' \
else --recursive \
coffee_pid = Process.spawn(coffee_cmd) --command='#{xmodule_cmd}' \
puts "Waiting for coffee to complete (pid #{coffee_pid})" common/lib/xmodule"
Process.wait(coffee_pid) end
puts "Coffee completed" coffee_cmd = "coffee #{watch ? '--watch' : ''} --compile */static"
sass_pid = Process.spawn(sass_cmd) sass_cmd = "sass --style compressed " +
puts "Waiting for sass to complete (pid #{sass_pid})" "--load-path ./common/static/sass " +
Process.wait(sass_pid) "--require ./common/static/sass/bourbon/lib/bourbon.rb " +
puts "Sass completed" "#{watch ? '--watch' : '--update'} */static"
[xmodule_cmd, coffee_cmd, sass_cmd].each do |cmd|
if watch
background_process(cmd)
else
pid = Process.spawn(cmd)
puts "Waiting for `#{cmd}` to complete (pid #{pid})"
Process.wait(pid)
puts "Completed"
end
end end
end end
......
...@@ -50,6 +50,8 @@ xmltodict==0.4.1 ...@@ -50,6 +50,8 @@ xmltodict==0.4.1
# Used for debugging # Used for debugging
ipython==0.13.1 ipython==0.13.1
# Used for development operation
watchdog==0.6.0
# Metrics gathering and monitoring # Metrics gathering and monitoring
dogapi==1.2.1 dogapi==1.2.1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment