Skip to content

Commit d91f6d3

Browse files
authored
Merge pull request #224 from optimizely/jordan/add-notify-logging
Add logger hooks for notify
2 parents 368f721 + 53f6357 commit d91f6d3

File tree

5 files changed

+74
-33
lines changed

5 files changed

+74
-33
lines changed

src/logging.js

+19-14
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ export const ConsoleGroupLogger = {
3939
/**
4040
* @param {ReactorState} reactorState
4141
* @param {Map} state
42-
* @param {Set} dirtyStores
42+
* @param {List} dirtyStores
43+
* @param {Map} previousState
4344
*/
4445
dispatchEnd: function(reactorState, state, dirtyStores, previousState) {
4546
if (!getOption(reactorState, 'logDispatches')) {
@@ -57,29 +58,33 @@ export const ConsoleGroupLogger = {
5758
console.groupEnd()
5859
}
5960
},
60-
}
61-
62-
/* eslint-enable no-console */
63-
64-
export const NoopLogger = {
6561
/**
6662
* @param {ReactorState} reactorState
67-
* @param {String} type
68-
* @param {*} payload
63+
* @param {ObserverState} observerState
6964
*/
70-
dispatchStart: function(reactorState, type, payload) {
65+
notifyStart: function(reactorState, observerState) {
7166
},
7267
/**
7368
* @param {ReactorState} reactorState
74-
* @param {Error} error
69+
* @param {Getter} getter
7570
*/
76-
dispatchError: function(reactorState, error) {
71+
notifyEvaluateStart: function(reactorState, getter) {
7772
},
7873
/**
7974
* @param {ReactorState} reactorState
80-
* @param {Map} state
81-
* @param {Set} dirtyStores
75+
* @param {Getter} getter
76+
* @param {Boolean} didCall
77+
* @param {*} currValue
78+
*/
79+
notifyEvaluateEnd: function(reactorState, getter, didCall, currValue) {
80+
},
81+
/**
82+
* @param {ReactorState} reactorState
83+
* @param {ObserverState} observerState
8284
*/
83-
dispatchEnd: function(reactorState, state, dirtyStores) {
85+
notifyEnd: function(reactorState, observerState) {
8486
},
8587
}
88+
89+
/* eslint-enable no-console */
90+

src/reactor.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Immutable from 'immutable'
22
import createReactMixin from './create-react-mixin'
33
import * as fns from './reactor/fns'
44
import { DefaultCache } from './reactor/cache'
5-
import { NoopLogger, ConsoleGroupLogger } from './logging'
5+
import { ConsoleGroupLogger } from './logging'
66
import { isKeyPath } from './key-path'
77
import { isGetter } from './getter'
88
import { toJS } from './immutable-helpers'
@@ -29,7 +29,7 @@ class Reactor {
2929
const baseOptions = debug ? DEBUG_OPTIONS : PROD_OPTIONS
3030
// if defined, merge the custom implementation over the noop logger to avoid undefined lookups,
3131
// otherwise, just use the built-in console group logger
32-
let logger = config.logger ? extend({}, NoopLogger, config.logger) : NoopLogger
32+
let logger = config.logger ? config.logger : {}
3333
if (!config.logger && debug) {
3434
logger = ConsoleGroupLogger
3535
}
@@ -225,6 +225,8 @@ class Reactor {
225225
return
226226
}
227227

228+
fns.getLoggerFunction(this.reactorState, 'notifyStart')(this.reactorState, this.observerState)
229+
228230
let observerIdsToNotify = Immutable.Set().withMutations(set => {
229231
// notify all observers
230232
set.union(this.observerState.get('any'))
@@ -244,10 +246,13 @@ class Reactor {
244246
// don't notify here in the case a handler called unobserve on another observer
245247
return
246248
}
249+
let didCall = false
247250

248251
const getter = entry.get('getter')
249252
const handler = entry.get('handler')
250253

254+
fns.getLoggerFunction(this.reactorState, 'notifyEvaluateStart')(this.reactorState, getter)
255+
251256
const prevEvaluateResult = fns.evaluate(this.prevReactorState, getter)
252257
const currEvaluateResult = fns.evaluate(this.reactorState, getter)
253258

@@ -259,13 +264,17 @@ class Reactor {
259264

260265
if (!Immutable.is(prevValue, currValue)) {
261266
handler.call(null, currValue)
267+
didCall = true
262268
}
269+
fns.getLoggerFunction(this.reactorState, 'notifyEvaluateEnd')(this.reactorState, getter, didCall, currValue)
263270
})
264271

265272
const nextReactorState = fns.resetDirtyStores(this.reactorState)
266273

267274
this.prevReactorState = nextReactorState
268275
this.reactorState = nextReactorState
276+
277+
fns.getLoggerFunction(this.reactorState, 'notifyEnd')(this.reactorState, this.observerState)
269278
}
270279

271280
/**

src/reactor/fns.js

+17-6
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,6 @@ export function replaceStores(reactorState, stores) {
7373
* @return {ReactorState}
7474
*/
7575
export function dispatch(reactorState, actionType, payload) {
76-
let logging = reactorState.get('logger')
77-
7876
if (actionType === undefined && getOption(reactorState, 'throwOnUndefinedActionType')) {
7977
throw new Error('`dispatch` cannot be called with an `undefined` action type.')
8078
}
@@ -83,7 +81,7 @@ export function dispatch(reactorState, actionType, payload) {
8381
let dirtyStores = reactorState.get('dirtyStores')
8482

8583
const nextState = currState.withMutations(state => {
86-
logging.dispatchStart(reactorState, actionType, payload)
84+
getLoggerFunction(reactorState, 'dispatchStart')(reactorState, actionType, payload)
8785

8886
// let each store handle the message
8987
reactorState.get('stores').forEach((store, id) => {
@@ -94,13 +92,13 @@ export function dispatch(reactorState, actionType, payload) {
9492
newState = store.handle(currState, actionType, payload)
9593
} catch(e) {
9694
// ensure console.group is properly closed
97-
logging.dispatchError(reactorState, e.message)
95+
getLoggerFunction(reactorState, 'dispatchError')(reactorState, e.message)
9896
throw e
9997
}
10098

10199
if (newState === undefined && getOption(reactorState, 'throwOnUndefinedStoreReturnValue')) {
102100
const errorMsg = 'Store handler must return a value, did you forget a return statement'
103-
logging.dispatchError(reactorState, errorMsg)
101+
getLoggerFunction(reactorState, 'dispatchError')(reactorState, errorMsg)
104102
throw new Error(errorMsg)
105103
}
106104

@@ -112,7 +110,7 @@ export function dispatch(reactorState, actionType, payload) {
112110
}
113111
})
114112

115-
logging.dispatchEnd(reactorState, state, dirtyStores, currState)
113+
getLoggerFunction(reactorState, 'dispatchEnd')(reactorState, state, dirtyStores, currState)
116114
})
117115

118116
const nextReactorState = reactorState
@@ -376,6 +374,17 @@ export function resetDirtyStores(reactorState) {
376374
return reactorState.set('dirtyStores', Immutable.Set())
377375
}
378376

377+
export function getLoggerFunction(reactorState, fnName) {
378+
const logger = reactorState.get('logger')
379+
if (!logger) {
380+
return noop
381+
}
382+
const fn = logger[fnName]
383+
return (fn)
384+
? fn.bind(logger)
385+
: noop
386+
}
387+
379388
/**
380389
* @param {ReactorState} reactorState
381390
* @param {CacheEntry} cacheEntry
@@ -438,3 +447,5 @@ function incrementStoreStates(storeStates, storeIds) {
438447
})
439448
})
440449
}
450+
451+
function noop() {}

src/reactor/records.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Map, Set, Record } from 'immutable'
22
import { DefaultCache } from './cache'
3-
import { NoopLogger } from '../logging'
43

54
export const PROD_OPTIONS = Map({
65
// logs information for each dispatch
@@ -41,7 +40,7 @@ export const ReactorState = Record({
4140
state: Map(),
4241
stores: Map(),
4342
cache: DefaultCache(),
44-
logger: NoopLogger,
43+
logger: {},
4544
// maintains a mapping of storeId => state id (monotomically increasing integer whenever store state changes)
4645
storeStates: Map(),
4746
dirtyStores: Set(),

tests/reactor-tests.js

+26-9
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ describe('Reactor', () => {
6767
spyOn(handler, 'dispatchStart')
6868
spyOn(handler, 'dispatchError')
6969
spyOn(handler, 'dispatchEnd')
70-
71-
spyOn(NoopLogger, 'dispatchError')
7270
})
7371

7472
afterEach(() => {
@@ -110,25 +108,44 @@ describe('Reactor', () => {
110108
expect(handler.dispatchError).toHaveBeenCalled()
111109
}
112110
})
113-
it('should use the NoopLogger implementation when a logging function is not defined on the custom implementation', () => {
111+
it('should noop when a logging function is not defined on the custom implementation', () => {
114112
var reactor = new Reactor({
115113
debug: true,
116114
logger: {
117115
dispatchStart() {},
118-
dispatchEnd() {},
119116
},
120117
options: {
121118
throwOnUndefinedActionType: false,
122119
},
123120
})
124121

125-
try {
126-
reactor.dispatch(undefined)
127-
} catch (e) {
128-
expect(NoopLogger.dispatchError).toHaveBeenCalled()
129-
}
122+
expect(() => {
123+
reactor.dispatch('setTax', 5)
124+
}).not.toThrow()
130125
})
131126

127+
it('should properly bind context to the logging function', () => {
128+
const loggerSpy = jasmine.createSpy()
129+
130+
function Logger() {
131+
}
132+
Logger.prototype.log = function() {
133+
loggerSpy()
134+
}
135+
136+
Logger.prototype.dispatchStart = function() {
137+
this.log()
138+
}
139+
140+
var reactor = new Reactor({
141+
debug: true,
142+
logger: new Logger(),
143+
})
144+
145+
reactor.dispatch('setTax', 5)
146+
147+
expect(loggerSpy).toHaveBeenCalled()
148+
})
132149
})
133150
})
134151

0 commit comments

Comments
 (0)