Check your understanding of the previous reading: using dataclasses for a Drafter application’s state.

Question 1

Assuming that the following is defined, what are valid types for the state parameter in a route function?

from dataclasses import dataclass

@dataclass
class State:
    count: int
    name: str

Remember, just because a dataclass is defined does not mean that we have to use it.

Select every option that applies, then check.

  • Correct. Before dataclasses were introduced, this reading's own first example already uses a single string as state, and says the state "could only be a single value (e.g., a string or an integer)" - an int works.
  • Correct. The reading's very first example uses exactly this: def index(state: str) -> Page.
  • Correct. Nothing restricts state to int and str specifically - any single value, including a float, works the same way.
  • Correct. A dataclass like State is exactly the kind of type this reading uses to hold more complex state.
  • Not quite. State.count is not a type at all - it's an attribute access on the class itself, which does not hold a value until an instance exists.
  • Not quite. Page is what a route returns, not what it receives as state; nothing here uses a Page instance as an application's state.
  • Not quite. "index" is a route name, the kind of string passed as a Button's target - not a type.

Question 2

Which of the following are correct descriptions of Page?

Select every option that applies, then check.

  • Correct. The reading says so directly: "It turns out that Drafter has been using Dataclasses all along. The Page function is really the Page dataclass."
  • Not quite. This is exactly the assumption the reading corrects: what looks like a function call, Page(...), is really constructing a dataclass instance.
  • Correct. Calling Page(...) constructs a Page instance - a dataclass's own name doubles as its constructor function, as the Dataclasses reading explains.
  • Not quite. Page builds a value; it doesn't decide which code runs, the way an if statement does.
  • Not quite. Page(...) is an expression, the value inside a return statement - it isn't a statement itself.
  • Not quite. state and content are Page's attributes; Page itself is the dataclass they belong to.
  • Not quite. state is a parameter name used throughout these examples; Page is never a parameter, only ever a return value.

Question 3

The following is a Drafter application that runs, but does not correctly update the state of gamma when the button is clicked.

from drafter import *

@dataclass
class State:
    alpha: int
    beta: str
    gamma: bool

@route
def index(state: State) -> Page:
    return Page(state, Div(
        Text(f"Alpha: {state.alpha}"),
        Text(f"Beta: {state.beta}"),
        Text(f"Gamma: {state.gamma}"),
        Button("Toggle Gamma", "toggle_gamma")
    ))

@route
def toggle_gamma(state: State) -> Page:
    new_state = State(state.alpha, state.beta, state.gamma)
    new_state.gamma = not state.gamma
    return index(state)

start_server(State(10, "initial", False))

Why doesn't this code work as intended?

Select one answer, then check.

  • Correct. toggle_gamma builds new_state with the toggled gamma value, then calls index(state) with the original, untouched state - new_state is thrown away.
  • Not quite. A route function only needs state (plus any form fields); it never needs a separate parameter for every attribute of the state dataclass.
  • Not quite. A dataclass field can be any type, including bool; gamma being a bool is not the problem.
  • Not quite. A Button takes a label and a route name, as in Button(label, url); it never takes a state attribute as an argument.
  • Not quite. Calling index(state) to build the next Page is the same pattern the update route uses earlier in this reading; returning a Page some other way is not required.

Question 4

If a Drafter application wants to update its state, which of the following is required?

Select every option that applies, then check.

  • Not quite. The score-tracking example updates its state using only buttons, with no textbox at all - a textbox is one way to collect new values, not a requirement.
  • Correct. Every state change in this reading is triggered by a Button whose target names the route that performs the update.
  • Not quite. A Button only takes a label and a route name (Button(label, url)); it never takes the new state as an argument.
  • Not quite. Buttons don't have a "name" the way TextBox does; that naming rule applies to input components, not buttons.
  • Correct. This is the immutable style from Mutability vs Immutability: build a new State and pass it (directly or through another route) to Page.
  • Correct. This is the mutable style: update the existing state's fields, then pass that same instance along to Page, as plus_player1 does.

Question 5

Which of the following are correct unit tests for the index route below?

from drafter import *

@dataclass
class State:
    count: int

@route
def index(state: State) -> Page:
    state.count += 1
    return Page(state, Button("Increase", "index"))

start_server(State(0))

Select every option that applies, then check.

  • Correct. index mutates state.count in place, so calling it on State(0) leaves that same instance with count equal to 1.
  • Correct. Starting from count = 5, the += 1 makes it 6.
  • Not quite. index always increments count by one, so starting from 10 it becomes 11, not 10.
  • Correct. Dataclasses compare field by field, so the mutated state (count = 1) is equal to a fresh State(1).
  • Not quite. The mutated state has count = 11, not 10, so it isn't equal to State(10).
  • Not quite. .state is a State instance, not a bare number; comparing it to 1 can never be equal, no matter what count is.