-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Middlewares #13
Middlewares #13
Conversation
Looks like Travis caught this, but we should make sure that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Small feedback
lib/loggerMiddleware.lua
Outdated
local function loggerPlugin(next) | ||
return function(store, action) | ||
print("Action dispatched:", prettyPrint(action)) | ||
next(action) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should middleware be passing store
as an argument as well as the next action? I figured that the self
argument for the main reducer would get clobbered here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops! You're right. Fixed now!
lib/init.lua
Outdated
|
||
return { | ||
Store = Store, | ||
createReducer = createReducer, | ||
combineReducers = combineReducers, | ||
logger = loggerMiddleware, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we name logger loggerMiddleware
in the public API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured just calling it logger
was okay, but I think loggerMiddleware
is a better name in hindsight - it makes it clear that it's a middleware function, not something else.
lib/Store.spec.lua
Outdated
end | ||
}) | ||
|
||
expect(store.dispatch).to.never.equal(Store.dispatch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to assert that this is the case -- if the middleware implementation didn't actually override the dispatch
method, that would satisfy what we want still!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
lib/Store.spec.lua
Outdated
next(store, action) | ||
end | ||
end | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should split out the middleware function (and maybe the reducer too?) to make this instantiation a little easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, should be good now.
Ooh, I just noticed that this tanks our coverage. Is there a way that we can change the |
Done. The whole |
lib/loggerMiddleware.lua
Outdated
loggerPlugin.outputFunction("State changed to:"..prettyPrint(store:getState())) | ||
end | ||
end | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning a not-actually-function thing from an API like this makes me kind of uncomfortable, but I'm not sure how else to structure this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither am I, without adding another layer to the logger >.<
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having this be a table is still a blocker in my mind. I'd really like to validate the return values of middleware as they're applied to the store, otherwise it might be kind of tricky to debug them.
The only other API I can think of that would let you choose the output function would be:
local function loggerMiddleware(logFunction)
logFunction = logFunction or print
return function(_, next)
return function(store, action)
-- ...
end
end
end
And then the primary use of the middleware would be
Rodux.Store.new(reducer, nil, { Rodux.loggerMiddleware() })
I think that's mostly fine?
Barring a sudden insight in how to simplify |
Any news on this @LPGhatguy? I'd like to get this landed and move on to #14 and #8 to pave the way for v1.0 to be released. |
lib/loggerMiddleware.lua
Outdated
loggerPlugin.outputFunction("State changed to:"..prettyPrint(store:getState())) | ||
end | ||
end | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having this be a table is still a blocker in my mind. I'd really like to validate the return values of middleware as they're applied to the store, otherwise it might be kind of tricky to debug them.
The only other API I can think of that would let you choose the output function would be:
local function loggerMiddleware(logFunction)
logFunction = logFunction or print
return function(_, next)
return function(store, action)
-- ...
end
end
end
And then the primary use of the middleware would be
Rodux.Store.new(reducer, nil, { Rodux.loggerMiddleware() })
I think that's mostly fine?
That's a lot better than the hacky table approach; I've switched things over. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks architecturally great! Last pieces are minor or just style nits.
lib/Store.spec.lua
Outdated
type = "test" | ||
}) | ||
|
||
expect(middlewareInvokeCount).to.equal(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this line got indented with spaces somehow!
lib/loggerMiddleware.lua
Outdated
return function(store, action) | ||
outputFunction("Action dispatched: "..prettyPrint(action)) | ||
next(store, action) | ||
outputFunction("State changed to:"..prettyPrint(store:getState())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we space out ..
like we would other operators?
lib/loggerMiddleware.spec.lua
Outdated
type = "test" | ||
}) | ||
|
||
expect(outputCount >= 1).to.be.ok() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can check that the outputCount is exactly one now that we've defined that middleware aren't run on the initial action.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That test will fail, actually - loggerMiddleware calls outputFunction
twice. I wrote this test so that it isn't super tied to the exact number of calls in the logger. I'm not sure if that's the best approach, but it was the one that seemed like the best option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, huh, I forgot about that.
Should we just make loggerMiddleware
only get invoked once at the end of the middleware, with both the new state and action? We probably don't want to produce two separate log messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed now!
lib/loggerMiddleware.lua
Outdated
table.insert(outputBuffer, strKey) | ||
table.insert(outputBuffer, " = ") | ||
|
||
if type(value) == "table" then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use typeof
here, since we normalized everywhere else!
"{\n" | ||
} | ||
|
||
for key, value in pairs(t) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have to go through with this for the first version of the logger, but sorting the table keys so that we get stable output would be a good enhancement in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In an ideal case, I'd actually like to switch the logger to displaying state deltas, so you only see what changed. That's something to revisit later, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hooray! Middleware!
This fixes #5. I wanted to open this to allow code reviews.
TODO:
Copy-pasted from my issue comment:
The API now:
Middlewares are applied left-to-right order - the rightmost middleware is invoked first.
Middlewares look like this:
Using this looks like: