1 from __future__
import with_statement
4 from settings
import Settings
6 class _AggreggateFileSet
:
7 """ Provides a base class for aggreggating text files.
9 First generate some test content.
12 >>> if os.path.isfile(Settings.tempDir+"/test.js"): os.remove(Settings.tempDir+"/test.js")
13 >>> o = open(Settings.tempDir+"/test.js").write("function func1() { \nalert('with lots of space');}")
15 >>> if os.path.isfile(Settings.tempDir+"/test2.js"): os.remove(Settings.tempDir+"/test2.js")
16 >>> o = open(Settings.tempDir+"/test2.js").write("function func2() { alert('this is function 2');}")
19 Then use defaultFile() to load the test content into the file set.
21 >>> Cache.JS.defaultFile(Settings.tempDir+"/test.js")
22 >>> Cache.JS.defaultFile(Settings.tempDir+"/test2.js")
23 >>> hash = hashlib.sha1(''.join([Settings.tempDir+"/test.js",Settings.tempDir+"/test2.js"])).hexdigest()
24 >>> assert(Cache.JS.getOutputFile().startswith('cache/'+hash))
25 >>> Cache.JS.saveContent()
26 >>> assert(os.path.isfile(Cache.JS.getOutputFile())
27 >>> Cache.JS.getText()
28 'function func1(){alert('with lots of space');}function func2(){alert('this is function 2');}'
36 def defaultFile(self
, file):
37 if file is None or not os
.path
.isfile(file):
38 if Settings
.abortOnMissingFile
:
39 raise HTTPStatusError(404, "Required asset is missing: %s" % file)
40 if Settings
.warnOnMissingFile
:
41 log("WARNING: File Cache could not load requested file: %s" % file)
42 if self
._data
.get(file, False): # dont add the same file twice
43 if os
.path
.getmtime(file) > self
._mtimes
.get(file, 0): # unless its been modified
44 log("Reading file from disk into cache: %s" % file)
46 # log("Skipping read of unmodified file: %s %d <= %d " % (file, os.path.getmtime(file), self._mtimes.get(file, 0)))
48 if file is not None and os
.path
.isfile(file):
49 self
._order
.append(file)
50 # TODO: lazy reading, only read() if, in the end, the whole request needs to be written into the cache
51 self
._mtimes
[file] = os
.path
.getmtime(file)
52 with
open(file,"rb") as f
:
53 self
._data
[file] = f
.read()
55 def getOutputFile(self
):
56 """ Subclasses should implement this to add the proper extension """
57 fname_hash
= hashlib
.sha1(''.join(self
._order
)).hexdigest()
58 mtime_hash
= hashlib
.sha1(''.join([str(os
.path
.getmtime(f
)) for f
in self
._order
])).hexdigest()
59 return Settings
.cacheDir
+os
.path
.sep
+"%s-%s" %(fname_hash
, mtime_hash
)
61 def saveContent(self
):
62 f
= self
.getOutputFile()
63 if not os
.path
.isfile(f
):
64 # remove stale cache files first
67 for file in os.listdir(Settings.cacheDir):
69 file = os.path.sep.join([Settings.cacheDir,file])
70 log("Removing stale cache file: %s" % (file))
73 # then write the fresh one
74 log("Writing files: [%s] to cache: %s" % (', '.join(self
.order()), self
.getOutputFile()))
75 leader
= "/* Files: "+', '.join(self
.order())+" */\n"
76 packed
= ''.join([self
.filter(self
._data
[key
]) for key
in self
.order()])
77 with
open(self
.getOutputFile(),'wb') as f
:
90 with
open(z_file
,"rb") as f
:
93 def filter(self
, text
):
94 """ Subclasses should implement this in order to do minification, compression, etc. """
99 class _JSFileSet(_AggreggateFileSet
):
100 def filter(self
, text
):
101 if Settings
.developmentMode
:
104 return jsmin
.jsmin(text
)
105 def getOutputFile(self
):
106 return _AggreggateFileSet
.getOutputFile(self
)+".js"
108 class _CSSFileSet(_AggreggateFileSet
):
109 def filter(self
, text
):
110 if Settings
.developmentMode
:
113 return cssmin
.minimalize(text
)
114 def getOutputFile(self
):
115 return _AggreggateFileSet
.getOutputFile(self
)+".css"