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

Early Tutors: Initial pass at a new tutorial living in the repo #1846

Merged
merged 7 commits into from
Mar 29, 2018

Conversation

Shadowfiend
Copy link
Member

Comments/questions/remarks welcome. For comments on specific bits of the documents, ideally leave a line comment in the appropriate part of the diff :)

Copy link

@marcgrue marcgrue left a comment

Choose a reason for hiding this comment

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

Suggestion for clarification...

def sendMessage = {
var message = ChatMessage("", "")

"#new-message" #> SHtml.text(message, { body: String => message = ChatMessage(username.is, body) }) &
Copy link

@marcgrue marcgrue May 3, 2017

Choose a reason for hiding this comment

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

Some newbie questions that this line gave for me:

  • What is this curly bracket thing? - is it an anonymous function definition in Scala? text expects a function as its second parameter, so maybe String => message is the function type. But message isn't a type, hmm. No, wait, aha - body is a self type in an anonymous class and the mutable variable message is set to a new ChatMessage instance - or? Having it on one line is really confusing. Do we need an anonymous class with a String self type?

  • What is this method is on username? I pasted the code into a Lift project and realised it was simply a getter method on the username session variable to retrieve the value, duh. There's also a shadow method get that points to the is method. For a newbie tutorial, I would suggest to simply use the get method instead. For seasoned Lift users the is method is probably obvious but get is more generally understood when coming from the outside (as the audience of a tutorial would likely be).

Since message is now a ChatMessage, we need to say message.body for the first argument to text, no?

Could we instead say something like this?

"#new-message" #> SHtml.text(message.body, newMessage => { message = ChatMessage(username.get, newMessage) }) &

It would be great to describe the line in a similar manner as you so well described the previous lines in part 5:

    "#new-message" #> SHtml.text(message, message = _) &
    "type=submit" #> SHtml.submitButton(() => {
      messageEntries ::= message
    })

Once understanding passing NodeSes => NodeSeq functions around, css-selectors become more natural. So I think it is worth explaining examples of those thoroughly.

Copy link
Member Author

Choose a reason for hiding this comment

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

These are great points. A couple of responses:

  • One of the lines I try to walk here is making sure that I don't stray too far into explaining the basics of Scala. I don't think this tutorial is best served by trying to teach both Scala and Lift. That said, I do dabble very slightly in that in a few places, and I think it's absolutely a good idea to break this one down a little more.
  • I'm not using the thing => { body } style because I'm not a fan :)

Pushed some updates that hopefully address some of this, let me know!

Copy link
Member Author

Choose a reason for hiding this comment

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

Hah! I actually completely fumbled this one, updating the bit further down and forgetting this start part was here. That's what I get for trying to do it quickly and while tired. I'll do another pass tomorrow to clean things up :)

We now use asciidoc callouts to more clearly link back to particular parts of
the code that was updated, and break it down into two more digestible chunks.
@Shadowfiend
Copy link
Member Author

Okay, just completely reworked that whole hunk, and started using asciidoc code callouts to boot… I think it's looking a lot better, let me know if you agree @marcgrue !

@marcgrue
Copy link

marcgrue commented May 9, 2017

Excellent, Antonio! Clear description that is very helpful. I'm very happy :-)

@Shadowfiend
Copy link
Member Author

Awesome! Good to hear :) Thanks for looking through the content!

Copy link
Member

@farmdawgnation farmdawgnation left a comment

Choose a reason for hiding this comment

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

Made it through the first file before I had to walk out the door. Danger, danger - Matt is in English editing mode again! :P

Will try to get to the other files later tonight.

interface from the process of putting data from the system into it, in a way
that lets you stay focused on users when you're creating the user interface and
worry about the the interface between your backend and the HTML only when
you're working on the backend.
Copy link
Member

Choose a reason for hiding this comment

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

This might be easier to parse if it were broken into two sentences right around in a way

Copy link
Member Author

Choose a reason for hiding this comment

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

Excellent point!

you're working on the backend.

The flip side of view-first development is that it takes some getting used to
if one is accustomed to the typical web MVC framework. The first stop when
Copy link
Member

Choose a reason for hiding this comment

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

if one is accustomed — I think we should feel free to use the second-person pronouns where applicable. We do so in the first paragraph, and it makes the sentence feel more personal and conversational to me to read if you're accustomed. Using one has more of an academic, aloof texture.

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer no contractions, since this is written, but I'm down to go second-person to make it less academic.

if one is accustomed to the typical web MVC framework. The first stop when
figuring out what's going on in a typical web MVC setup is the controller. In
Lift, your first stop is your HTML file. Everything starts in the HTML, and in
what it is that you want to present to the user. You don't just think about
Copy link
Member

Choose a reason for hiding this comment

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

Everything starts in the HTML, and in what it is that you want to present to the user.

This sentence took me a few reads to comprehend correctly. It might be a good candidate for some tweaking.

Copy link
Member Author

Choose a reason for hiding this comment

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

Rephrased, great point.

what it is that you want to present to the user. You don't just think about
user interactions first, you *build* them first, and let them guide your
development forward and inform it at every step of the way. Turning a usability
tested high fidelity mockup into a live page has never been so straightforward.
Copy link
Member

Choose a reason for hiding this comment

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

Comma after tested for clarity since you're chaining adjectives.

Copy link
Member Author

Choose a reason for hiding this comment

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

🙇

smart enough to strip all HTML comments in production mode.].

When it comes to user testing, notice that our view is fully-valid HTML, with
placeholder data. It is, in effect, a high-fidelity mockup. And now that we've
Copy link
Member

Choose a reason for hiding this comment

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

Here you use high-fidelity with a hyphen. Earlier you use it without. I think we should use the hyphen version consistently.

Copy link
Member Author

Choose a reason for hiding this comment

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

Your eye for detail brings a tear to my eye ;)

Copy link
Member

@farmdawgnation farmdawgnation left a comment

Choose a reason for hiding this comment

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

Alrighty, made it all the way through. This is a lot, so obviously don't worry about addressing everything. But I did find one case where I'm pretty sure your code doesn't compile. =)

Another distinguishing characteristic of Lift is that it is *secure by
default*. Amongst other things, this means that you can't access a file in your
`src/main/webapp` directory through your application unless you explicitly
define that it's meant to be accessed. You define this using Lift's menu
Copy link
Member

Choose a reason for hiding this comment

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

This isn't entirely true. If a SiteMap isn't defined at all, then we default to permitting access to all views under the webapp directory that aren't stored in a folder starting with _. Unless, of course, that's changed in a recent commit and I missed it. =)

Copy link
Member Author

Choose a reason for hiding this comment

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

! Nice catch. Rephrased a bit to take that into account.


Snippets let you refer to a block of code that is responsible for rendering a
particular part of your page. You add these references by augmenting your HTML
with a few completely valid `data-` attributes that get stripped before the
Copy link
Member

Choose a reason for hiding this comment

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

Would a link to the HTML5 specification or some other explanation for data attributes be useful here?

another way that manifests is that the template HTML is turned into a
first-class XML tree early in the processing cycle, and snippets just transform
that tree. That means script injection and a variety of other attacks are
significantly more difficult against a Lift codebase.].
Copy link
Member

Choose a reason for hiding this comment

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

I'm torn on whether or not the contents of this footnote should be part of the regular paragraph. No further comment than that, just wanted to bring it to your attention.

Copy link
Member Author

Choose a reason for hiding this comment

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

Because this is a tutorial, I decided I wanted to keep the top-level content super-focused on implementation rather than reasoning.


Let's look at our chat app specifically. We're going to bind two things: the
list of chat messages, and the text input that lets us actually chat. To the
`ol` that contains the chat messages, we add:
Copy link
Member

Choose a reason for hiding this comment

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

The original markup was two pages back. Perhaps we should reproduce it here so the reader is reminded of the overall structure?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes… But let me punt on that a bit. I'm wanting to do a little preprocessing pass on these guys that handles building some of that for you.

<form class="send-message" data-lift="Chat.sendMessage">
```

These two indicate two methods in a class called `Chat`, which Lift searches
Copy link
Member

Choose a reason for hiding this comment

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

"These two" -> "These two changes add data attributes that..."

they're easy to pattern match (as you can see, message handling is done via
pattern matching footnote:[Strictly speaking, `messageHandler` is a
`PartialFunction`. This means that it can match any subset of objects that it
wants to.]) and because they're generally immutable, so there's no chance of
Copy link
Member

Choose a reason for hiding this comment

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

Technically, there's little change of someone else trying to modify the message as we're processing it. But, eh, technicalities. heh.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I think that would actually obscure rather than illuminate here.

on the `Box`. We do this because the type of `messageEntries` is now a
`Box[List[ChatMessage]]`, meaning a box that *contains* a list of chat
messages. `flatten` will give us the plain list of messages if the `Box` is
`Full`, and an empty list if it's `Empty`, which is perfect.
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure about that?

@ Box(List("abc")).flatten
cmd2.sc:1: Cannot prove that String <:< net.liftweb.common.Box[B].
val res2 = Box(List("abc")).flatten
                            ^
Compilation Failed

Copy link
Member Author

Choose a reason for hiding this comment

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

I am… Not, all of a sudden, lol. Reworking to use openOr, which is a better pattern in this case anyway I think.

false. Using `Box.asA` ensures that, if someone changes the `ChatActor` later
to reply with something else, our snippet won't blow up in the user's face—it
will just not display the existing messages. The intrepid reader can then go
and fix the issue.
Copy link
Member

Choose a reason for hiding this comment

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

It's probably worth noting that someone should probably have unit tests on that actor to ensure people don't just go changing its contract all willy nilly 😉

Copy link
Member Author

Choose a reason for hiding this comment

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

True, though even so you can imagine someone changing the test + actor and missing the snippet because the compiler can't complain about it.

```

The ideal way for this to work would be for you to be able to change the value
of the field, and have it save. We can do exactly that using Lift's `ajaxText`
Copy link
Member

Choose a reason for hiding this comment

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

Not sure about the comma after field. Might also be worthwhile to make mention that the save occurs on blur?


Our first move will be to change how exactly we handle binding chat messages.
First, we'll do a quick conversion that puts everything in a `CometActor`, but
doesn't add any additional functionality. Instead of calling
Copy link
Member

Choose a reason for hiding this comment

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

AMG SUSPENSE

Copy link
Member Author

Choose a reason for hiding this comment

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

Lawl yeah this guy is… Incomplete 🎉

@Shadowfiend
Copy link
Member Author

🎵 I have not forgotten youuuuu 🎵

But I'll be trying to wrap up #1876 before circling back here.

@farmdawgnation
Copy link
Member

farmdawgnation commented Sep 13, 2017

This PR is...

This sets us up for some nice preprocessing later to fill in context around our
example blocks.
@Shadowfiend
Copy link
Member Author

Ok, pushed a variety of changes motivated by the above comments 🎉

I would say we can probably merge with these changes and then iterate on additional tweaks, but if you have time to give it a second read-through or at least look at the tweaks, @farmdawgnation, I won't complain :)

@farmdawgnation
Copy link
Member

Nah, I say we go ahead and ship this. We don't benefit much from the long lived branch and we benefit a lot from these being in master.

@farmdawgnation farmdawgnation merged commit 5ac794a into master Mar 29, 2018
@farmdawgnation farmdawgnation deleted the early-tutors branch March 29, 2018 22:00
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.

3 participants