Números e datas
Tradução automática
Esta página foi traduzida automaticamente do inglês e ainda não foi revisada por um falante nativo; correções são bem-vindas no GitHub. Se algo não bater, vale o original em inglês. O código e os resultados são os mesmos do original, importados dos arquivos testados.
Tarefa: receber um valor, uma contagem e uma data, e fazer contas com eles sem converter nada à mão.
# Recipe: numbers and dates arrive checked and converted, not as text.
paths:
components: ./components
migrations: ./migrations
datasources:
db:
driver: sqlite
database: ./data/expenses.dbCREATE TABLE expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
amount REAL NOT NULL,
people INTEGER NOT NULL,
spent_on DATE NOT NULL
);Um formulário envia texto. type="decimal" e type="integer" o transformam em número antes de a ação rodar, então amount / people funciona. min e max são verificados sobre esse número. type="date" aceita YYYY-MM-DD e um dia que existe, e o mantém como texto, pronto para o banco.
<q:component name="Expenses">
<q:action name="split" method="POST">
<!-- decimal and integer arrive as numbers, date as a checked date:
the arithmetic below needs no conversion. -->
<q:param name="amount" type="decimal" required="true" min="0.01" max="10000" />
<q:param name="people" type="integer" required="true" min="1" max="50" />
<q:param name="spent_on" type="date" required="true" />
<q:query name="saved" datasource="db">
INSERT INTO expenses (amount, people, spent_on) VALUES (:amount, :people, :spent_on)
<q:param name="amount" value="{amount}" type="decimal" />
<q:param name="people" value="{people}" type="integer" />
<q:param name="spent_on" value="{spent_on}" type="string" />
</q:query>
<q:redirect url="/" flash="{amount} on {spent_on}: {round(amount / people, 2)} each." />
</q:action>
<ui:window title="Split an expense">
<q:if condition="flash">
<ui:alert variant="{flashType == 'error' and 'danger' or 'success'}">{flash}</ui:alert>
</q:if>
<!-- The form gets type="number" (with min and max) and type="date". -->
<ui:form on-submit="split" submit="Split">
<ui:formitem label="Amount"><ui:input bind="amount" /></ui:formitem>
<ui:formitem label="People"><ui:input bind="people" /></ui:formitem>
<ui:formitem label="Date"><ui:input bind="spent_on" /></ui:formitem>
</ui:form>
</ui:window>
</q:component>O formulário ganha type="number" com os mesmos min e max, e type="date", então o navegador verifica primeiro. Os testes enviam direto para o servidor, como um script faria:
<q:test name="the numbers arrive as numbers" page="/">
<test:submit action="split" amount="100" people="3" spent_on="2026-09-20" />
<test:expect redirect="/" flash="100.0 on 2026-09-20: 33.33 each." />
<test:expect table="expenses" count="1" where="amount = 100 AND people = 3 AND spent_on = '2026-09-20'" />
</q:test>
<q:test name="text where a number goes is refused" page="/">
<test:submit action="split" amount="ten" people="3" spent_on="2026-09-20" />
<test:expect error="amount" message="Must be a number, got 'ten'" />
</q:test>
<q:test name="min and max hold" page="/">
<test:submit action="split" amount="0" people="51" spent_on="2026-09-20" />
<test:expect error="amount" message="Must be at least 0.01 (got 0.0)" />
<test:expect error="people" message="Must be at most 50 (got 51)" />
</q:test>
<q:test name="a date that does not exist is refused" page="/">
<test:submit action="split" amount="10" people="2" spent_on="2026-02-30" />
<test:expect error="spent_on" message="Must be a date (YYYY-MM-DD), got '2026-02-30'" />
<test:expect table="expenses" count="0" />
</q:test>tests/split.test.q
PASS the numbers arrive as numbers
PASS text where a number goes is refused
PASS min and max hold
PASS a date that does not exist is refused
4 passed, 0 failed