Ind.ie is now Small Technology Foundation.
Cache2.coffee 3.45 KB
Newer Older
1 2 3 4 5
#
# Builder cache using last modified times
#

fs = require 'fs-extra-as-promised'
6 7
path = require 'path'
osPath = require 'ospath'
8 9 10 11 12 13

Promise = require 'thrush'

glob = require 'glob'
globAsync = Promise.promisify glob

14 15
app = require './App'

16 17
class Cache

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
  homeDirectory: null
  contentDirectory: null
  themesDirectory: null

  constructor: ->
    @homeDirectory = path.join osPath.home(), 'better.fyi'

    # In the integrated project, the content and themes are part of the same
    # base project that the builder is now also a part of. We keep the @homeDirectory,
    # above, as the cache will still be written out there.
    @contentDirectory = path.join __dirname, 'content'
    @themesDirectory = path.join __dirname, 'themes'


  build: =>
    startTime = new Date

    @cacheContent @contentDirectory
      .then =>
        @cacheContent @themesDirectory
      .then =>
        duration = (new Date) - startTime
        console.log "\t✓ Cache ready. (#{duration} ms)"
    .catch (e) =>
      console.log "Cache build error: #{e}"


  #
  # Enumerate the files in the passed directory and output a JSON
  # file with a hashatable of partial file paths to last modified times.
  #
  cacheContent: (directory) =>

    lastModifiedTimes = {}

    cacheFileName = (path.parse directory).name

    cacheDirectory = path.join @homeDirectory, '.private', 'cache'

    cacheCurrentModifiedTimesDirectory = path.join cacheDirectory, 'current-modified-times'
    cachePreviousModifiedTimesDirectory = path.join cacheDirectory, 'previous-modified-times'

    fs.ensureDirAsync cacheCurrentModifiedTimesDirectory
    .then =>
      fs.ensureDirAsync cachePreviousModifiedTimesDirectory
      .then =>
        fullyQualifiedFileName = "#{cacheFileName}.json"

        # ~/better.fyi/.private/cache/current-modified-times/content.json & …/themes.json
        cacheCurrentModifiedTimesFile = path.join cacheCurrentModifiedTimesDirectory, fullyQualifiedFileName

        # ~/better.fyi/.private/cache/previous-modified-times/content.json & …/themes.json
        cachePreviousModifiedTimesFile = path.join cachePreviousModifiedTimesDirectory, fullyQualifiedFileName

        # Copy existing current modified times file (if one exists) to the previous file folder
        # so we can compare and use the cache or render the content accordingly.
        if fs.existsSync cacheCurrentModifiedTimesFile
          fs.copySync cacheCurrentModifiedTimesFile, cachePreviousModifiedTimesFile
        else
          # If it doesn’t exist, create an empty one.
          fs.outputFileSync cachePreviousModifiedTimesFile, '{}', 'utf-8'

        # Go through all the files in the requested directory, recursively, and compile
        # their last modified times.
        (globAsync "#{directory}/**/*", {nodir: true})
          .series (file) =>

            fileComponents = file.split '/'

            # On production the route to the data folders has one fewer level.
            # Take that into account when creating the cache.
89
            indexToStartFrom = if app.isRunningInProduction then 4 else 6
90 91 92 93 94 95 96
            filePathFragment = fileComponents[indexToStartFrom..].join '/'

            # Using the sync version of stat is a little faster here
            stats = fs.statSync file
            lastModifiedTimes[filePathFragment] = stats.mtime
          .then =>
            fs.outputFileAsync cacheCurrentModifiedTimesFile, JSON.stringify lastModifiedTimes, null, "  "
97 98 99 100

module.exports = Cache

# Test
101 102
# cache = new Cache
# cache.build()