Skip to content
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

reference app.wrappers[] for each mw #718

Closed
wants to merge 6 commits into from
Closed

reference app.wrappers[] for each mw #718

wants to merge 6 commits into from

Conversation

fl0w
Copy link
Contributor

@fl0w fl0w commented Apr 25, 2016

This is for koajs/compose#55

@fl0w
Copy link
Contributor Author

fl0w commented Apr 25, 2016

I don't understand what's wrong here with this PR? Locally both make test and make lint passes, but travis throws

30:41  error  Unnecessary escape character: \"  no-useless-escape

Anyway, will add tests and docs if this PR is an acceptable approach.

@PlasmaPower
Copy link
Contributor

So sort of like a onUsed hook, but a variable instead?

@fl0w
Copy link
Contributor Author

fl0w commented Apr 25, 2016

@PlasmaPower Aye, simpler. Not necessarily better but I prefer the simple solution so I just threw this out there.

@juliangruber
Copy link
Contributor

sorry but I don't get the concept of wrappers you want to introduce here. can you give a high level explanation?

@PlasmaPower
Copy link
Contributor

@juliangruber Sure thing! Basically a wrapper replaces any middleware with the middleware called on the function (wrappers.forEach(wrapper => mw = wrapper(mw))). Here's how a wrapper works (this code is for Koa v2 + a wrappers patch):

app.wrappers = [co.wrap];

app.use(function * (ctx, next) {
  ctx.body = 'example';
  yield next();
});

This is also useful for profilers, for instance you can do this:

app.wrappers = [function (mw) {
  return function () {
    let start = process.hrtime();
    return Promise.resolve().then(() => mw).then(function (val) {
      let time = process.hrtime(start);
      console.log(mw._name || mw.name + ': ' + time.join('.') + ' seconds';
      return val;
    });
  };
}];

@juliangruber
Copy link
Contributor

juliangruber commented Apr 25, 2016

Gotcha, thanks!

What seems weird to me from an api perspective is that you use app.use() to add functions to the middleware stack, but there would set the "wrapper stack" directly via app.wrappers instead of something like app.wrap(fn).

Also, do wrappers get cascaded? For example if you have

app.wrappers = [profile];
const composed = compose([a, b]);
app.use(composed);

would you just get a profile of the composed middleware or the individual ones as well?

@juliangruber
Copy link
Contributor

the biggest concern I have though is that koa sets fn._wrapper and then relies on compose to consume this "hidden" property. feels like a weird coupling of components.

@PlasmaPower
Copy link
Contributor

PlasmaPower commented Apr 25, 2016

@juliangruber there are a couple different implementation PRs around, which do you prefer? As for app.wrappers vs app.wrap(mw), I prefer the raw array because you can do app.wrappers.push(wrapper) anyway. It makes more sense to me as an array rather than a function I guess. Also arrays are a bit more versatile.

Most implementations cascade wrappers, yes. That is the expected behavior. In most implementations, that requires changes to those libraries though.

@PlasmaPower
Copy link
Contributor

the biggest concern I have though is that koa sets fn._wrapper and then relies on compose to consume this "hidden" property. feels like a weird coupling of components.

Unfortunately, there doesn't seem to be a very clean way to do this. Which PR implementing wrappers do you prefer? Each has it's advantages and disadvantages IMO.

@fl0w
Copy link
Contributor Author

fl0w commented Apr 25, 2016

@juliangruber the main problem is; how do we allow wrapping for the entire app - no matter what library user uses. It's handled by compose because had we made this a koa solution, libraries such as koa-router wouldn't be affected properly by the wrapping.

This could easily have been a koa/external solution if we made a deep tree of mw's before a compose/equivalent. Though that's not the case today.

@PlasmaPower
Copy link
Contributor

@fl0w I'm still not convinced creating a tree of middleware is possible in a lot of cases without sacrificing performance.

@fl0w
Copy link
Contributor Author

fl0w commented Apr 25, 2016

@PlasmaPower aye, maybe. My point it, I don't think we can avoid wrapping in compose, right?

@PlasmaPower
Copy link
Contributor

PlasmaPower commented Apr 25, 2016

@fl0w I'm pretty sure that's a necessary evil, yes. We can still offload the logic though, like I've done in my PR.

@juliangruber
Copy link
Contributor

I'm bot sure cascading wrappers is a good idea, what happens inside a mw is its business and its business only.

@fl0w
Copy link
Contributor Author

fl0w commented Apr 26, 2016

@juliangruber it doesn't cascade, it just flat-wraps all mw in Application.middleware?

My PR in compose is messed up - wrong rebase apparently, working together a proper one as we speak (slowly, in a meeting)

@juliangruber
Copy link
Contributor

juliangruber commented Apr 26, 2016

@fl0w if i have this:

app.wrappers = [(mw) => {
  return () => {
    return mw().then(console.log('hi'))
  }
}];
const composed = compose([a, b]);
app.use(composed);

will it log hi 1x or 3x?

@fl0w
Copy link
Contributor Author

fl0w commented Apr 26, 2016

@juliangruber the goal is for compose to wrap all wrappers, once per mw. Answer: 2x.
Once for a, once for b.

The end goal is to wrap each individual mw with all provided wrappers once.

@fl0w
Copy link
Contributor Author

fl0w commented Apr 26, 2016

The equivalent today for koa@2 could be:

const wrapper = async (ctx, next) => {
  console.log('hi')
}

const wrappedMw1 = async (ctx, next) => {
  // .. mw1 logic
  await wrapper()
}

const wrappedMw2 = async (ctx, next) => {
  // .. mw2 logic
  await wrapper()
}

app.use(wrappedMw1)
app.use(wrappedMw2)

@juliangruber
Copy link
Contributor

hmmm...curious what @tj and @jonathanong think

@fl0w
Copy link
Contributor Author

fl0w commented Apr 26, 2016

@juliangruber there's lengthy bikeshedding at koajs/compose#51 as well.

@jonathanong
Copy link
Member

jonathanong commented Apr 26, 2016

30:41 error Unnecessary escape character: " no-useless-escape

eslint update. rm -rf node_modues; npm i :)

might be a separate PR if it's failing master/next right now

@jonathanong
Copy link
Member

there was some discussion in some PRs:

i'm not sure where i stated it, but my main concern are specifics. like @juliangruber stated, does it nest into compose? we have to spec it all out then write tests for all the cases. i haven't seen anyone either one yet

@codecov-io
Copy link

Current coverage is 100%

Merging #718 into v2.x will not change coverage

@@               v2.x       #718   diff @@
==========================================
  Files             4          4          
  Lines           414        424    +10   
  Methods          80         82     +2   
  Messages          0          0          
  Branches         99        102     +3   
==========================================
+ Hits            414        424    +10   
  Misses            0          0          
  Partials          0          0          

Powered by Codecov. Last updated by 41afac3...f132f79

@fl0w fl0w closed this May 14, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants