Skip to content

Commit

Permalink
ignore rename events from fs.watch
Browse files Browse the repository at this point in the history
To avoid trying to read a file after receiving the event notifying it
was removed anything other than 'change' from fs.watch does not start a
new read. The cached data is still invalidated as usual though.
  • Loading branch information
keis committed Apr 15, 2015
1 parent d1328af commit f31f421
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 21 deletions.
14 changes: 7 additions & 7 deletions lib/reload.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ ReloadJSON.prototype.read = function (path) {
var data;

if (err) {
// If a error occurs the loader needs to be invalidated.
// If an error occurs the loader needs to be invalidated.
files[path] = null;
return self.emit('error', err);
}

// Once loaded store data in in cache and configure a watch on the
// Once loaded store data in cache and configure a watch on the
// file so that it can be reloaded when it is changed.
data = args[0];
files[path] = data
Expand All @@ -84,16 +84,16 @@ ReloadJSON.prototype.configureWatch = function (path) {
});

// Make sure old data is not used again
watch.on('change', function (filename) {
watch.on('change', function (event, filename) {
self.files[path] = null;
self.emit('change', filename);
self.emit('change', event, filename);
});

// Trigger a new read of the file after a debounce timeout, it's
// possible a read was started in the meantime in that case no read is
// triggered from the watch.
watch.on('change', debounce(function () {
if (!self.files[path]) {
watch.on('change', debounce(function (ev) {
if (ev === 'change' && !self.files[path]) {
self.read(path);
}
}, 10));
Expand All @@ -104,7 +104,7 @@ ReloadJSON.prototype.configureWatch = function (path) {
}

// Load json from the specified path. The result is cached and multiple request
// is consolidated into on.
// is consolidated into a single file read.
ReloadJSON.prototype.load = function (path, callback) {
var self = this,
files = this.files,
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"mocha": "^2.1.0",
"rewire": "^2.1.0",
"sinon": "^1.10.3",
"touch": "0.0.3"
"touch": "0.0.3",
"rimraf": "~2.3.2"
}
}
62 changes: 57 additions & 5 deletions test/component/reload.coffee
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
fs = require 'fs'
os = require 'os'
rimraf = require 'rimraf'
path = require 'path'
touch = require 'touch'
sinon = require 'sinon'

describe "reload-json", ->
Reload = require '../../lib/reload'
base = path.join process.cwd(), 'test/data/'
base = path.join os.tmpdir(), 'reload-json-test'
filepath = path.join base, 'test.json'
reload = null
errorcb = null

beforeEach ->
before (done) ->
fs.mkdir base, done

after (done) ->
rimraf base, done

beforeEach (done) ->
errorcb = sinon.stub()
reload = new Reload
reload.on 'error', errorcb
fs.writeFile filepath, JSON.stringify(file: 'test.json'), (err) ->
done()

it "returns the same data when reading the same file", (done) ->
filepath = path.join base, 'test.json'
readOne = null
readTwo = null

verify = ->
assert.strictEqual readOne, readTwo
assert.notCalled errorcb
done()

reload.load filepath, (err, data) ->
Expand All @@ -28,8 +44,7 @@ describe "reload-json", ->
readTwo = data
verify() if readOne

it "reads new data immediatly after the file is changed", (done) ->
filepath = path.join base, 'test.json'
it "reads new data immediately after the file is changed", (done) ->
reload.load filepath, (err, readOne) ->
assert.isNull err

Expand All @@ -39,4 +54,41 @@ describe "reload-json", ->
reload.load filepath, (err, readTwo) ->
assert.isNull err
assert.notStrictEqual readOne, readTwo
assert.notCalled errorcb
done()

it "does not use cached value after file is removed", (done) ->
reload.load filepath, (err, readOne) ->
assert.isNull err

fs.unlink filepath, (err) ->
assert.isNull err

reload.load filepath, (err, readTwo) ->
assert.isNull readTwo
assert.instanceOf err, Error
done()

it "does not trigger reload when file is removed", (done) ->
reload.load filepath, (err, readOne) ->
assert.isNull err

fs.unlink filepath, (err) ->
assert.isNull err
assert.notCalled errorcb

setTimeout (->
assert.notCalled errorcb
done()
), 25

it "reads new data when file is replaced", (done) ->
tmppath = path.join base, 'new.json'
fs.writeFile tmppath, JSON.stringify(file: 'new.json'), (err) ->
reload.load filepath, (err, readOne) ->
assert.isNull err
fs.rename tmppath, filepath, (err) ->
assert.isNull err
reload.load filepath, (err, readTwo) ->
assert.propertyVal readTwo, 'file', 'new.json'
done()
1 change: 0 additions & 1 deletion test/data/test.json

This file was deleted.

16 changes: 9 additions & 7 deletions test/unit/reload.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,16 @@ describe "reload-json", ->
reload.read 'foo'

describe "#configureWatch", ->
it "configures a filesystem watch", ->
it "configures a filesystem watch", (done) ->
reload.configureWatch 'foo'
assert.calledOnce fsMock.watch
assert.calledWith fsMock.watch, 'foo'
process.nextTick ->
assert.calledOnce fsMock.watch
assert.calledWith fsMock.watch, 'foo'
done()

it "triggers a read on change", (done) ->
reload.configureWatch 'foo'
watch.emit 'change'
watch.emit 'change', 'change', 'foo'

setTimeout (->
assert.calledOnce fsMock.readFile
Expand All @@ -73,7 +75,7 @@ describe "reload-json", ->

it "checks if a read is in progress before triggering", (done) ->
reload.configureWatch 'foo'
watch.emit 'change'
watch.emit 'change', 'change', 'foo'

setTimeout (->
reload.files['foo'] = {}
Expand All @@ -86,11 +88,11 @@ describe "reload-json", ->

it "forwards change event", (done) ->
reload.configureWatch 'foo'
reload.on 'change', (filename) ->
reload.on 'change', (ev, filename) ->
assert.equal filename, 'foo'
done()

watch.emit 'change', 'foo'
watch.emit 'change', 'change', 'foo'

it "forwards any error that occurs", (done) ->
err = new Error 'dummy-error'
Expand Down

0 comments on commit f31f421

Please sign in to comment.