Skip to content

Commit fe11f37

Browse files
authored
Merge pull request #7 from lupodevelop/release/v1.1.0
Release v1.1.0: docs, formats, and refactors
2 parents 5e8c284 + d89041b commit fe11f37

30 files changed

Lines changed: 1030 additions & 842 deletions

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ jobs:
2121
for i in $(seq 1 60); do
2222
if curl -sSf http://localhost:8123/ >/dev/null 2>&1; then
2323
echo "ClickHouse is ready"
24-
break
24+
exit 0
2525
fi
2626
sleep 2
2727
done
28+
echo "ClickHouse did not become ready in time"
29+
exit 1
2830
2931
- name: Setup Erlang/OTP and Gleam
3032
uses: erlef/setup-beam@v1

.github/workflows/release.yml

Lines changed: 0 additions & 125 deletions
This file was deleted.

.github/workflows/smoke-integration.yml

Lines changed: 0 additions & 65 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
98

10-
### Added
9+
## [1.1.0] - 2026-03-20
10+
11+
### Changed
1112

12-
- (Unreleased changes go here)
13+
- Simplified and refactored `types.gleam`, `expr.gleam`, `format/registry.gleam`, `format/json_each_row.gleam`, `repo.gleam`, `retry.gleam`, `schema.gleam`, `changeset.gleam`, `decode.gleam`, `query.gleam` — reduced surface area, improved type safety, removed redundant code
14+
- Rewrote `docs/quickstart.md` with correct, runnable code examples
15+
- Added missing documentation for all core modules: `query`, `repo`, `encode`, `decode`, `changeset`, `types`, `schema`
16+
- Removed `release.yml` and `smoke-integration.yml` workflows; CI consolidated under `ci.yml`
17+
- Fixed stray character and incorrect function call in README
1318

14-
## [0.1.0] - 2025-11-09
19+
## [1.0.0] - 2025-11-09
1520

1621
### Added
1722

@@ -60,5 +65,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6065
- Style guide for contributions
6166

6267
<!-- Link references for versions -->
63-
[Unreleased]: https://github.com/lupodevelop/sparkling/compare/v0.1.0...HEAD
64-
[0.1.0]: https://github.com/lupodevelop/sparkling/releases/tag/v0.1.0
68+
[Unreleased]: https://github.com/lupodevelop/sparkling/compare/v1.1.0...HEAD
69+
[1.1.0]: https://github.com/lupodevelop/sparkling/compare/v1.0.0...v1.1.0
70+
[1.0.0]: https://github.com/lupodevelop/sparkling/compare/v0.1.0...v1.0.0

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![CI](https://github.com/lupodevelop/sparkling/actions/workflows/ci.yml/badge.svg)](https://github.com/lupodevelop/sparkling/actions/workflows/ci.yml) [![Hex](https://img.shields.io/hexpm/v/sparkling.svg)](https://hex.pm/packages/sparkling) [![License](https://img.shields.io/badge/license-Apache%202.0-yellow.svg)](LICENSE) [![Built with Gleam](https://img.shields.io/badge/Built%20with-Gleam-ffaff3)](https://gleam.run) [![Gleam Version](https://img.shields.io/badge/gleam-%3E%3D1.13.0-ffaff3)](https://gleam.run)
88

99
**Sparkling** is a *lightweight*, **type-safe** data layer for **ClickHouse** written in Gleam. It provides a small, focused API for defining schemas, building queries, and encoding/decoding ClickHouse formats.
10-
É
10+
1111
*No magic*, just small, composable functions that play nicely in Gleam apps.
1212

1313
> Why "Sparkling"? One rainy Tuesday a tiny inflatable rubber duck stole a shooting pink star and decided to become a freelance data wrangler, and it now guides queries through the night, humming 8-bit lullabies. Totally plausible.
@@ -24,7 +24,7 @@ import sparkling/repo
2424
let r = repo.new("http://localhost:8123")
2525
|> repo.with_database("mydb")
2626
27-
case r.execute_sql(r, "SELECT 1 as result FORMAT JSONEachRow") {
27+
case repo.execute_sql(r, "SELECT 1 as result FORMAT JSONEachRow") {
2828
Ok(body) -> io.println(body)
2929
Error(_) -> io.println("query failed")
3030
}
@@ -38,7 +38,7 @@ case r.execute_sql(r, "SELECT 1 as result FORMAT JSONEachRow") {
3838
- `sparkling/encode` / `sparkling/decode` — format handlers (JSONEachRow default)
3939
- `sparkling/types` — helpers for Decimal, DateTime64, UUID, LowCardinality
4040

41-
For more examples see `docs/examples/` and `docs/quickstart.md`.
41+
For more examples see [docs/quickstart.md](docs/quickstart.md) and [docs/examples/](docs/examples/).
4242

4343
**Design note:** Sparkling's API and composable query builder were partly inspired by *Ecto*;
4444
many ideas about schema definition and query composition borrow from its approach while keeping a small, Gleam-friendly surface.

docs/examples/changeset.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Changeset
2+
3+
`sparkling/changeset` provides Ecto-style data casting and validation.
4+
5+
## Basic usage
6+
7+
```gleam
8+
import gleam/option.{None, Some}
9+
import sparkling/changeset
10+
11+
type UserData {
12+
UserData(name: String, email: String, age: Int)
13+
}
14+
15+
let data = UserData(name: "", email: "", age: 0)
16+
17+
let cs =
18+
changeset.new(data)
19+
|> changeset.put_change("name", "Alice")
20+
|> changeset.put_change("email", "alice@example.com")
21+
|> changeset.put_change("age", "28")
22+
|> changeset.validate_required("name")
23+
|> changeset.validate_required("email")
24+
|> changeset.validate_email("email")
25+
|> changeset.validate_length("name", Some(2), Some(100))
26+
|> changeset.validate_number("age", Some(0), Some(120))
27+
28+
case changeset.apply(cs) {
29+
Ok(_) -> io.println("valid!")
30+
Error(errors) -> io.println(changeset.format_errors(errors))
31+
}
32+
```
33+
34+
## Validators
35+
36+
```gleam
37+
// Required — field must be present in changes
38+
|> changeset.validate_required("name")
39+
40+
// Length — min/max for string fields
41+
|> changeset.validate_length("name", Some(2), Some(100))
42+
|> changeset.validate_length("bio", None, Some(500)) // max only
43+
44+
// Number — min/max for int fields (value parsed from change string)
45+
|> changeset.validate_number("age", Some(0), Some(120))
46+
47+
// Email format
48+
|> changeset.validate_email("email")
49+
50+
// Not empty string
51+
|> changeset.validate_not_empty("name")
52+
53+
// Custom format check
54+
|> changeset.validate_format("slug", fn(v) {
55+
string.all(v, fn(c) { c == "-" || c >= "a" && c <= "z" || c >= "0" && c <= "9" })
56+
}, "must contain only lowercase letters, digits, and hyphens")
57+
```
58+
59+
## Reading changes and errors
60+
61+
```gleam
62+
changeset.is_valid(cs) // => True / False
63+
changeset.get_changes(cs) // => Dict(String, String)
64+
changeset.get_errors(cs) // => List(FieldError)
65+
66+
case changeset.get_change(cs, "name") {
67+
Ok(name) -> io.println("name is: " <> name)
68+
Error(Nil) -> io.println("name not set")
69+
}
70+
```
71+
72+
## Manual errors
73+
74+
```gleam
75+
let cs =
76+
changeset.new(data)
77+
|> changeset.put_change("email", "alice@example.com")
78+
|> changeset.add_error("email", "already taken")
79+
80+
changeset.format_errors(changeset.get_errors(cs))
81+
// => "email: already taken"
82+
```

0 commit comments

Comments
 (0)