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

High Wire: Fix appendJs when invoked from inside a toJsCmd def #1711

Merged
merged 3 commits into from Aug 10, 2015

Conversation

Shadowfiend
Copy link
Member

This fixes #1708 specifically.

appendJs should append JavaScript to be sent down with the
current request. However, if appendJs is called in a toJsCmd
method, and that method is defined as a def, not a val, then
toJsCmd will be called after the jsToAppend is read. This
means that this appended JS won't be sent down.

One place where this manifests is when a JsCmd's toJsCmd
method sends down a rendered snippet that contains a Wiring
component (because Wiring uses appendJs). In this case, the
appendJs that Wiring does is evaluated during the toJsCmd
invocation, which means the JsCmd that is sent down misses it.

When we send down JS in bulk for an AJAX or comet response,
we wrap it in a JsCommands, which is a collector of JsCmd.
We then call its toResponse to get a JavaScriptResponse.

Before, we passed the jsToAppend to JsCommands, and then
let JsCommands run toJsCmd on everyone while constructing
the response. In this PR, let the toResponse method call toJsCmd
on all the commands that are explicitly being sent down, and then
we read jsToAppend and concatenate that on the end of the other
commands inside toResponse. In the process, we remove external
invocations of jsToAppend.

So as to avoid accidental duplicate send-downs of jsToAppend, we
give jsToAppend a clearAfterReading boolean that, when set to
true (it defaults to false), will clear the list of JsCmds that need
to be appended. JsCommands and the jsToAppend reading in
CometActor, which sends down additional JS as a partial update,
both set it to true. Amongst other things, this ensures the two won't
both try to send down the same JS.

Still pending here: I (or someone else) need to test some of the
scenarios of appendJs, like calling it from inside a comet, from
a regular AJAX request, and from a regular screen render.

This will change the way you have to invoke jsToAppend to require
parens, but allows for jsToAppend to clear the appendable JS after
having assembled it to be returned. This ensures Lift’s internals won’t
double-evaluate jsToAppend in weird ways.

It may be worth making this private[http] and exposing a public version
that takes no parameters and sets clearAfterReading to false, we’ll see.
Places that return JS via a JsCommands toResponse call now add
jsToAppend after evaluating all JS commands passed to the JsCommands
class. What this allows is for JsCmd classes that implement toJsCmd as
a def that in turn appends JS via S.appendJs, to still send down the
appended JS.

Before, because the toJsCmd calls were done after jsToAppend was
evaluated, that JS would be lost or sent down in a future AJAX response.

Additionally, JsCommands and LiftMerge now clear the jsToAppend after
reading it, to ensure that if two JsCommands are used, you won’t get
duplicate appended JS.
@andreak
Copy link
Member

andreak commented Jul 13, 2015

I have a rather large app I can test with. We use JsCmds in all possible ways, I think.

@andreak
Copy link
Member

andreak commented Jul 13, 2015

I did some (quick) testing of our app now and it seems all usages of S.appendJS and def toJsCmd work as expected. The Wiring-UI issue certainly works.

I found an inssue where contextPath is not being prepended properly to attributes (href and src) when using val instead of def in toJsCmd but that's a separate issue.

@andreak
Copy link
Member

andreak commented Jul 13, 2015

It seems the contextPath-issue only manifests it self when the JS-content is generated in another thread (job on thread-pool), not having access to Req of Session. Forget about this for now...

@Shadowfiend
Copy link
Member Author

Sweet! I also need to tweak up the Scaladocs (or add them in the case of JsCommands's toResponse).

@andreak
Copy link
Member

andreak commented Aug 6, 2015

Ping:-)

Any progress on merging this?

@Shadowfiend
Copy link
Member Author

Haha I was just looking at it today. Want to do that scaladoc update and then merge… Tomorrow night, probably, if that doesn't seem too long.

@andreak
Copy link
Member

andreak commented Aug 6, 2015

Rock on!

This lets us be explicit that calling it clears S.jsToAppend.
@Shadowfiend
Copy link
Member Author

Looks like both failures were the typical failures with futures…

@farmdawgnation you interested in :shipit: ?

@farmdawgnation
Copy link
Member

👀

@Shadowfiend
Copy link
Member Author

@farmdawgnation Thinkin' 'bout merging this tomorrow come hell or high water; lemme know if you spotted anything problematic :)

@fmpwizard fmpwizard added this to the 3.0-M7 milestone Aug 10, 2015
@farmdawgnation
Copy link
Member

I didn't. I intended to build a sample project with it and try to break it but got distracted by other things. :(

@Shadowfiend
Copy link
Member Author

No worries. Let's ship it since Andreas confirmed it works on their existing codebase.

Shadowfiend added a commit that referenced this pull request Aug 10, 2015
High Wire: Fix appendJs when invoked from inside a toJsCmd def

appendJs should append JavaScript to be sent down with the
current request. However, if appendJs is called in a toJsCmd
method, and that method is defined as a def, not a val, then
toJsCmd will be called after the jsToAppend is read. This
means that this appended JS won't be sent down.

One place where this manifests is when a JsCmd's toJsCmd
method sends down a rendered snippet that contains a Wiring
component (because Wiring uses appendJs). In this case, the
appendJs that Wiring does is evaluated during the toJsCmd
invocation, which means the JsCmd that is sent down misses it.

When we send down JS in bulk for an AJAX or comet response,
we wrap it in a JsCommands, which is a collector of JsCmd.
We then call its toResponse to get a JavaScriptResponse.

Before, we passed the jsToAppend to JsCommands, and then
let JsCommands run toJsCmd on everyone while constructing
the response. In this PR, let the toResponse method call toJsCmd
on all the commands that are explicitly being sent down, and then
we read jsToAppend and concatenate that on the end of the other
commands inside toResponse. In the process, we remove external
invocations of jsToAppend.

So as to avoid accidental duplicate send-downs of jsToAppend, we
give jsToAppend a clearAfterReading boolean that, when set to
true (it defaults to false), will clear the list of JsCmds that need
to be appended. JsCommands and the jsToAppend reading in
CometActor, which sends down additional JS as a partial update,
both set it to true. Amongst other things, this ensures the two won't
both try to send down the same JS.
@Shadowfiend Shadowfiend merged commit f64f293 into master Aug 10, 2015
@Shadowfiend Shadowfiend deleted the high-wire branch August 10, 2015 17:48
@andreak
Copy link
Member

andreak commented Aug 10, 2015

Yahoo!

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.

Wiring-contents not updated correctly if response is generated from a JsCmd where toJsCmd is a def
4 participants