forked from Botanical/BotanJS
Better caching mechanics
This commit is contained in:
parent
00180c815d
commit
70618b6b91
@ -1,11 +1,10 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import os;
|
import os, re, sys
|
||||||
import re;
|
|
||||||
import sys;
|
|
||||||
|
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from botanjs.utils import checksum
|
||||||
|
|
||||||
RegEx_N = re.compile( r"""
|
RegEx_N = re.compile( r"""
|
||||||
.*
|
.*
|
||||||
@ -136,7 +135,7 @@ class ClassMap:
|
|||||||
|
|
||||||
return True;
|
return True;
|
||||||
|
|
||||||
def drawMap( self, ns, ci, ce, cf ):
|
def drawMap( self, ns, ci, ce, cf, chksum ):
|
||||||
nsNode = self.getNode( ns )
|
nsNode = self.getNode( ns )
|
||||||
|
|
||||||
# Source every:
|
# Source every:
|
||||||
@ -150,6 +149,8 @@ class ClassMap:
|
|||||||
|
|
||||||
if not srcEvery:
|
if not srcEvery:
|
||||||
nsNode.setAttribute( "src", cf )
|
nsNode.setAttribute( "src", cf )
|
||||||
|
nsNode.setAttribute( "js", chksum["js"] )
|
||||||
|
nsNode.setAttribute( "css", chksum["css"] )
|
||||||
|
|
||||||
for ex in ce:
|
for ex in ce:
|
||||||
_t = eDef[ ex[0] ]
|
_t = eDef[ ex[0] ]
|
||||||
@ -164,6 +165,8 @@ class ClassMap:
|
|||||||
cNode.appendChild( impNode )
|
cNode.appendChild( impNode )
|
||||||
|
|
||||||
cNode.setAttribute( "src", cf )
|
cNode.setAttribute( "src", cf )
|
||||||
|
cNode.setAttribute( "js", chksum["js"] )
|
||||||
|
cNode.setAttribute( "css", chksum["css"] )
|
||||||
|
|
||||||
# the file dose not export classes
|
# the file dose not export classes
|
||||||
# Hence it import for itself
|
# Hence it import for itself
|
||||||
@ -187,7 +190,12 @@ class ClassMap:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
ns, ci, ce = classMeta( classFile )
|
ns, ci, ce = classMeta( classFile )
|
||||||
|
|
||||||
|
chksum = {}
|
||||||
|
chksum[ "js" ] = checksum( classFile )
|
||||||
|
chksum[ "css" ] = checksum( classFile[:-2] + "css" )
|
||||||
|
|
||||||
classFile = classFile.replace( self.R + os.path.sep, "" )
|
classFile = classFile.replace( self.R + os.path.sep, "" )
|
||||||
self.drawMap( ns, ci, ce, classFile )
|
self.drawMap( ns, ci, ce, classFile, chksum )
|
||||||
return self.DOM.toxml()
|
return self.DOM.toxml()
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ import os
|
|||||||
from sys import platform
|
from sys import platform
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from botanjs.config import Config as config
|
from botanjs.config import Config as config
|
||||||
|
from botanjs.service.jwork import log
|
||||||
|
|
||||||
COMPILER = config[ "BotanJS" ][ "ClosureCompiler" ]
|
COMPILER = config[ "BotanJS" ][ "ClosureCompiler" ]
|
||||||
|
|
||||||
if not os.path.isfile( COMPILER ):
|
AVAILABLE = os.path.isfile( COMPILER )
|
||||||
raise Exception( "Compiler not found" )
|
|
||||||
|
|
||||||
|
|
||||||
COMPILER_OPTIONS = [
|
COMPILER_OPTIONS = [
|
||||||
@ -42,6 +42,11 @@ class Wrapper:
|
|||||||
break
|
break
|
||||||
|
|
||||||
def compress( self, loc ):
|
def compress( self, loc ):
|
||||||
|
|
||||||
|
if not AVAILABLE:
|
||||||
|
log.error( "Compiler not found" )
|
||||||
|
return
|
||||||
|
|
||||||
content = ""
|
content = ""
|
||||||
with open( loc, "rb" ) as f:
|
with open( loc, "rb" ) as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
class log:
|
class log:
|
||||||
def info( self, *args ):
|
|
||||||
|
@staticmethod
|
||||||
|
def info( *args ):
|
||||||
|
print( *args )
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def error( *args ):
|
||||||
print( *args )
|
print( *args )
|
||||||
|
|
||||||
class dummyTask( object ):
|
class dummyTask( object ):
|
||||||
|
@ -30,7 +30,11 @@ class Resolver:
|
|||||||
|
|
||||||
def resource( self, elem ):
|
def resource( self, elem ):
|
||||||
if "src" in elem.attrib:
|
if "src" in elem.attrib:
|
||||||
return elem.attrib[ "src" ]
|
return {
|
||||||
|
"src": elem.attrib[ "src" ]
|
||||||
|
, "js": elem.attrib[ "js" ]
|
||||||
|
, "css": elem.attrib[ "css" ]
|
||||||
|
}
|
||||||
|
|
||||||
parent = self.parentMap[ elem ]
|
parent = self.parentMap[ elem ]
|
||||||
|
|
||||||
@ -153,9 +157,10 @@ class BotanClassResolver:
|
|||||||
if src not in classFiles:
|
if src not in classFiles:
|
||||||
classFiles.append( src )
|
classFiles.append( src )
|
||||||
|
|
||||||
def cssLookup( self, jsList, cssList ):
|
def cssLookup( self, classList, cssList ):
|
||||||
|
|
||||||
for f in jsList:
|
for cdef in classList:
|
||||||
|
f = cdef[ "src" ]
|
||||||
possibleList = []
|
possibleList = []
|
||||||
|
|
||||||
cssFile = os.path.splitext( f )[0] + ".css"
|
cssFile = os.path.splitext( f )[0] + ".css"
|
||||||
@ -183,7 +188,7 @@ class BotanClassResolver:
|
|||||||
if self.CR == None:
|
if self.CR == None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
md5 = hashlib.md5( bytearray( "".join( fileList ), "utf-8" ) ).hexdigest()
|
md5 = hashlib.md5( bytearray( "|".join( x[mode] for x in fileList ), "utf-8" ) ).hexdigest()
|
||||||
|
|
||||||
cName[0] = oFHash = md5 + "." + mode
|
cName[0] = oFHash = md5 + "." + mode
|
||||||
cFHash = md5 + ".c." + mode
|
cFHash = md5 + ".c." + mode
|
||||||
@ -193,19 +198,10 @@ class BotanClassResolver:
|
|||||||
# Compressed file
|
# Compressed file
|
||||||
cFile = os.path.join( self.CR, cFHash )
|
cFile = os.path.join( self.CR, cFHash )
|
||||||
|
|
||||||
dates = list(
|
if self.flagCompress and self.useCache( cFile ):
|
||||||
os.path.getmtime( os.path.join( self.R, x ) )
|
|
||||||
if os.path.exists( os.path.join( self.R, x ) ) else -1
|
|
||||||
for x in fileList
|
|
||||||
)
|
|
||||||
|
|
||||||
# Root file date
|
|
||||||
dates.append( os.path.getmtime( os.path.join( self.R, "_this.js" ) ) );
|
|
||||||
|
|
||||||
if self.flagCompress and self.useCache( cFile, dates ):
|
|
||||||
return self.BotanCache( cFile, cFHash, self.returnDynamic )
|
return self.BotanCache( cFile, cFHash, self.returnDynamic )
|
||||||
|
|
||||||
elif self.useCache( oFile, dates ):
|
elif self.useCache( oFile ):
|
||||||
self.JWork.saveCache(
|
self.JWork.saveCache(
|
||||||
oFile
|
oFile
|
||||||
# Content is None to initiate a compression
|
# Content is None to initiate a compression
|
||||||
@ -216,17 +212,8 @@ class BotanClassResolver:
|
|||||||
|
|
||||||
return self.BotanCache( oFile, oFHash, False )
|
return self.BotanCache( oFile, oFHash, False )
|
||||||
|
|
||||||
def useCache( self, f, dList ):
|
def useCache( self, f ):
|
||||||
if not os.path.exists( f ):
|
return os.path.exists( f )
|
||||||
return False
|
|
||||||
|
|
||||||
t = os.path.getmtime( f )
|
|
||||||
|
|
||||||
for i in dList:
|
|
||||||
if t < i:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def compileJs( self, cList, xList ):
|
def compileJs( self, cList, xList ):
|
||||||
md5 = [ None ]
|
md5 = [ None ]
|
||||||
@ -244,6 +231,7 @@ class BotanClassResolver:
|
|||||||
|
|
||||||
|
|
||||||
for f in cList:
|
for f in cList:
|
||||||
|
f = f[ "src" ]
|
||||||
path = (
|
path = (
|
||||||
os.path.splitext( f )[0]
|
os.path.splitext( f )[0]
|
||||||
.replace( PY_SEP, "." )
|
.replace( PY_SEP, "." )
|
||||||
@ -285,8 +273,8 @@ class BotanClassResolver:
|
|||||||
if cacheFile != None:
|
if cacheFile != None:
|
||||||
return cacheFile;
|
return cacheFile;
|
||||||
|
|
||||||
# The root file
|
struct = "/* @ */"
|
||||||
outputCss = self.BotanFile( "_this.css" )
|
outputCss = struct + self.BotanFile( "_this.css" )
|
||||||
|
|
||||||
for f in self.cleanList( cList ):
|
for f in self.cleanList( cList ):
|
||||||
outputCss += self.BotanFile( f )
|
outputCss += self.BotanFile( f )
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
from botanjs.compressor.closure import Wrapper as ClosureWrapper
|
|
||||||
from botanjs.compressor.yui import Wrapper as YUIWrapper
|
|
||||||
from botanjs.classmap import ClassMap
|
from botanjs.classmap import ClassMap
|
||||||
|
|
||||||
CeleryExists = True
|
CeleryExists = True
|
||||||
@ -38,6 +36,7 @@ class JWork:
|
|||||||
|
|
||||||
@app.task()
|
@app.task()
|
||||||
def compressJs( md5, externs ):
|
def compressJs( md5, externs ):
|
||||||
|
from botanjs.compressor.closure import Wrapper as ClosureWrapper
|
||||||
log.info( "Compress js: " + md5 )
|
log.info( "Compress js: " + md5 )
|
||||||
w = ClosureWrapper()
|
w = ClosureWrapper()
|
||||||
w.scanExterns( externs )
|
w.scanExterns( externs )
|
||||||
@ -45,6 +44,7 @@ class JWork:
|
|||||||
|
|
||||||
@app.task()
|
@app.task()
|
||||||
def compressCss( md5 ):
|
def compressCss( md5 ):
|
||||||
|
from botanjs.compressor.yui import Wrapper as YUIWrapper
|
||||||
log.info( "Compress css: " + md5 )
|
log.info( "Compress css: " + md5 )
|
||||||
w = YUIWrapper()
|
w = YUIWrapper()
|
||||||
w.compress( md5 )
|
w.compress( md5 )
|
||||||
|
12
botanjs/utils.py
Normal file
12
botanjs/utils.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from functools import lru_cache
|
||||||
|
from zlib import adler32 as _HashFunc
|
||||||
|
HashFunc = lambda v: hex( _HashFunc( v ) )[2:]
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache( maxsize = 1024 )
|
||||||
|
def checksum( file_path ):
|
||||||
|
try:
|
||||||
|
with open( file_path, "rb" ) as f:
|
||||||
|
return HashFunc( f.read() )
|
||||||
|
except FileNotFoundError:
|
||||||
|
return HashFunc( b"" )
|
47
tests.py
Normal file
47
tests.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#!env/bin/python
|
||||||
|
import os, sys
|
||||||
|
sys.path.append( os.path.abspath( "." ) )
|
||||||
|
|
||||||
|
from botanjs.service.jwork import app, JWork
|
||||||
|
from botanjs.config import Config as config
|
||||||
|
|
||||||
|
SiteRoot = os.path.abspath( "." )
|
||||||
|
|
||||||
|
# Setting the SiteRoot for config
|
||||||
|
config["Paths"]["SiteRoot"] = SiteRoot
|
||||||
|
|
||||||
|
jsCache = config["Paths"]["Cache"]
|
||||||
|
jsRoot = config["BotanJS"]["SrcDir"]
|
||||||
|
|
||||||
|
bmap = os.path.join( jsCache, "botanjs", "bmap.xml" )
|
||||||
|
|
||||||
|
app.conf.update( broker_url = config["BotanJS"]["CeleryBroker"] )
|
||||||
|
|
||||||
|
JWork.buildClassMap.delay( jsRoot, bmap )
|
||||||
|
|
||||||
|
from botanjs.service.jclassresv import BotanClassResolver as JCResv
|
||||||
|
srvHandler = JCResv( JWork, jsRoot, bmap, jsCache )
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestStringMethods( unittest.TestCase ):
|
||||||
|
|
||||||
|
# Run each twice to test the cache capabilities
|
||||||
|
def test_ojscall( self ):
|
||||||
|
for _ in range(0,2):
|
||||||
|
s = srvHandler.getAPI( "System", mode = "rjs" )
|
||||||
|
self.assertTrue( "BotanJS.define( \"System\" );" in s )
|
||||||
|
|
||||||
|
def test_import( self ):
|
||||||
|
for _ in range(0,2):
|
||||||
|
s = srvHandler.getAPI( "System.Policy", mode = "rjs" )
|
||||||
|
self.assertTrue( "BotanJS.define( \"System.Policy\" );" in s )
|
||||||
|
self.assertTrue( "BotanJS.define( \"System.Global\" );" in s )
|
||||||
|
|
||||||
|
def test_cssInheritance( self ):
|
||||||
|
for _ in range(0,2):
|
||||||
|
s = srvHandler.getAPI( "System", mode = "rcss" )
|
||||||
|
self.assertTrue( "/* @ */" in s )
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user