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

before -> operate -> after #110

Merged
merged 7 commits into from
May 11, 2021
Merged

Conversation

leastbad
Copy link
Contributor

Building on #109, this PR aims to standardize the way operations are defined by introducing a light DSL that can be used in a consistent pattern.

Previously, we call dispatch directly to emit before and after events, which clutters the code, is non-DRY and leaves open the possibility of typos. Instead, I would like to switch to before and after functions which are intended to immediately preceed and immediately follow the operate closure.

What is the operate closure!?

I noticed that a number of features being added (or proposed) was adding code to every one of the operations. Again, this is a smell. Instead, I created operate to function as a pipeline for modifying all of the operations. This allowed me to move out the cancel: true check from each operation, and introduce a new option, delay, which allows developers to stagger execution of individual operations. This can be useful for UI/UX reasons, as well as giving 3rd-party components a few ms to "catch up" if necessary.

operate could someday be pluggable, as well, although I don't attempt it in this PR.

The cool thing about the processElements and operate functions is that developers building custom events can import them and use them, or not, as they wish. I already document using processElements in today's documentation... this just picks up where it left off.

@leastbad
Copy link
Contributor Author

Late thought: think of operate as the ETL pipeline of CableReady.

@hopsoft
Copy link
Contributor

hopsoft commented Apr 30, 2021

I like where this is going, and want to float an idea to see if we can push the concept further. What if we introduce a promise to facilitate the delay and move the before & after hooks into operate? Not sure if it will work but it might look something like this.

const operate = (element, operation, callee, callback) => {
  before(element, callee, operation)
  if (!operation.cancel) {
    if (operation.delay) {
      new Promise((resolve, reject) => {
        setTimeout(() => resolve(callback()), operation.delay)
      }).then(() => after(element, callee, operation))
    } else {
      callback()
      after(element, callee, operation)
    }
  }
}

The operations could then further simplify to something along these lines.

append: (operation, callee) => {
  processElements(operation, element => {
    operate(element, operation, callee, () => {
      const { html, focusSelector } = operation
      element.insertAdjacentHTML('beforeend', html || '')
      assignFocus(focusSelector)
    })
  })
}

I also wonder if passing callee around is necessary. Isn't that data already in the operation payload? At the very least we could pass it explicitly from within the operation's method itself. Perhaps something like,

append: (operation) => {
  processElements(operation, element => {
    operate('append', element, operation, () => { ...

@leastbad
Copy link
Contributor Author

Hey, my head hasn't been in this for a few months but:

  • I had a really frustrating night proving to myself that modern browsers have made it impossible to interrogate callee and caller from inside or outside (respectively) for "security" reasons, effectively hamstringing metaprogramming. If it's possible to do this without passing in the callee, that'd be great! I just don't actually have a way worked out. If you do, please jump in!
  • Not every operation has events, so moving them into operate creates some edge cases which would make things messy
  • If I'm misunderstanding, don't be shy

@hopsoft
Copy link
Contributor

hopsoft commented Apr 30, 2021

Not every operation has events, so moving them into operate creates some edge cases which would make things messy

That's a very good reason to keep before and after out of operate; though, I think using a promise in operate may have some merit.

I think we may be able to avoid passing callee around everywhere. Will try to take a stab at that tomorrow if possible.

@leastbad
Copy link
Contributor Author

I'm excited to make this even cleaner. Passing in the callee is a total PITA, I (clearly) just wasn't able to find a more ninja path.

@leastbad
Copy link
Contributor Author

Just to think for a moment about your proposal to move before and after into operate:

I'm also in the process of moving play_sound out of the core, and it is - by far - the ugliest complication wrt after events. So, depending on how that goes, it could be that my hesitation to standardize ends up disappearing.

That would mean that we'd add events to operations that really don't make sense to have events, most specifically dispatch_event. Do we need a before-dispatch-event event? Probably not. Is it terrible if there is one? Probably not.

Copy link
Contributor

@hopsoft hopsoft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried a few things that eliminated the need to pass callee around but haven't been satisfied with any of them. I think we can merge and perhaps continue exploring ways to make this cleaner in the future.

@leastbad leastbad merged commit c233dac into stimulusreflex:master May 11, 2021
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.

2 participants